diff --git a/cypress/integration/example.spec.ts b/cypress/integration/example.spec.ts
index 3130c00..6e1bb87 100644
--- a/cypress/integration/example.spec.ts
+++ b/cypress/integration/example.spec.ts
@@ -1,8 +1,8 @@
// https://docs.cypress.io/api/introduction/api.html
-describe("My First Test", () => {
- it("visits the app root url", () => {
- cy.visit("/");
- cy.contains("h1", "You did it!");
+describe('My First Test', () => {
+ it('visits the app root url', () => {
+ cy.visit('/');
+ cy.contains('h1', 'You did it!');
});
});
diff --git a/cypress/support/index.ts b/cypress/support/index.ts
index d076cec..37a498f 100644
--- a/cypress/support/index.ts
+++ b/cypress/support/index.ts
@@ -14,7 +14,7 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
-import "./commands";
+import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')
diff --git a/eslint.config.ts b/eslint.config.ts
index 5381806..3578bce 100644
--- a/eslint.config.ts
+++ b/eslint.config.ts
@@ -2,7 +2,6 @@ import { globalIgnores } from 'eslint/config'
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
import * as pluginVue from 'eslint-plugin-vue'
import * as pluginVitest from '@vitest/eslint-plugin'
-import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
// import { configureVueProject } from '@vue/eslint-config-typescript'
@@ -24,5 +23,9 @@ export default defineConfigWithVueTs(
...pluginVitest.default.configs.recommended,
files: ['src/**/__tests__/*'],
},
- skipFormatting,
+ {
+ rules: {
+ 'quotes': ['error', 'single', { 'allowTemplateLiterals': true }],
+ },
+ },
)
diff --git a/package.json b/package.json
index cd5e468..5dd91da 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"build": "vue-tsc --noEmit -p tsconfig.app.json --composite false && vite build",
"test:unit": "vitest --environment jsdom",
"test:e2e": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress open'",
- "lint": "eslint . --fix",
+ "lint": "eslint .",
"dev": "vite",
"preview": "vite preview --port 5050",
"test:e2e:ci": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress run'"
@@ -46,7 +46,6 @@
"@vitejs/plugin-vue-jsx": "^5.0.0",
"@vitest/eslint-plugin": "^1.2.7",
"@vue/compiler-sfc": "^3.5.17",
- "@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.5.1",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.7.0",
@@ -57,7 +56,6 @@
"jiti": "^2.4.2",
"jsdom": "^26.1.0",
"npm-run-all2": "^8.0.4",
- "prettier": "^3.5.3",
"sass": "^1.89.2",
"start-server-and-test": "^2.0.12",
"typescript": "^5.8.3",
diff --git a/src/App.vue b/src/App.vue
index c1addf2..111610c 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,15 +1,15 @@
diff --git a/src/components/ServerStatusOverlay.vue b/src/components/ServerStatusOverlay.vue
index c77202b..cbdbdc0 100644
--- a/src/components/ServerStatusOverlay.vue
+++ b/src/components/ServerStatusOverlay.vue
@@ -1,8 +1,8 @@
diff --git a/src/components/automation/LogViewer.vue b/src/components/automation/LogViewer.vue
index 01a570b..44378ca 100644
--- a/src/components/automation/LogViewer.vue
+++ b/src/components/automation/LogViewer.vue
@@ -1,6 +1,6 @@
diff --git a/src/components/chart/StreamLineChart.vue b/src/components/chart/StreamLineChart.vue
index fe5b3e3..d9d56a3 100644
--- a/src/components/chart/StreamLineChart.vue
+++ b/src/components/chart/StreamLineChart.vue
@@ -1,5 +1,5 @@
+
+
+ value !== null ? handleValueChange(value) : undefined"
+ @focus="handleStart"
+ @blur="handleEnd"
+ :disabled="disabled"
+ >
+
diff --git a/src/components/device/DeviceCard.vue b/src/components/device/DeviceCard.vue
index 0e0e0c8..84dce67 100644
--- a/src/components/device/DeviceCard.vue
+++ b/src/components/device/DeviceCard.vue
@@ -1,8 +1,8 @@
-
-
+
-
- deviceComm.setAttribute(attr.name, value)"
:disabled="attr.modifier === 'ro'"
- >
+ :debounce-ms="100"
+ />
-import type {DeviceAirValve} from "@/model/devices/slvctrl/DeviceAirValve";
-import { useSocketIO } from "@/plugins/vueSocketIOClient";
-import type { Socket } from "socket.io-client";
-import DeviceCommunicator from "../../../../helper/DeviceCommunicator";
-import DebouncedSlider from "@/components/device/DebouncedSlider.vue";
+import type {DeviceAirValve} from '@/model/devices/slvctrl/DeviceAirValve';
+import { useSocketIO } from '@/plugins/vueSocketIOClient';
+import type { Socket } from 'socket.io-client';
+import DeviceCommunicator from '../../../../helper/DeviceCommunicator';
+import DebouncedSlider from '@/components/device/DebouncedSlider.vue';
interface Props {
device: DeviceAirValve;
diff --git a/src/components/device/control/slvctrlplus/DeviceDisplayControl.vue b/src/components/device/control/slvctrlplus/DeviceDisplayControl.vue
index 9535641..1b76fda 100644
--- a/src/components/device/control/slvctrlplus/DeviceDisplayControl.vue
+++ b/src/components/device/control/slvctrlplus/DeviceDisplayControl.vue
@@ -1,6 +1,6 @@
diff --git a/src/components/device/control/slvctrlplus/DeviceEt312Control.vue b/src/components/device/control/slvctrlplus/DeviceEt312Control.vue
index f6ebf8b..b353c0b 100644
--- a/src/components/device/control/slvctrlplus/DeviceEt312Control.vue
+++ b/src/components/device/control/slvctrlplus/DeviceEt312Control.vue
@@ -1,9 +1,9 @@
diff --git a/src/components/device/control/virtual/DeviceVirtualDisplayControl.vue b/src/components/device/control/virtual/DeviceVirtualDisplayControl.vue
index 59fda49..1dac6cd 100644
--- a/src/components/device/control/virtual/DeviceVirtualDisplayControl.vue
+++ b/src/components/device/control/virtual/DeviceVirtualDisplayControl.vue
@@ -1,7 +1,7 @@
diff --git a/src/components/device/control/virtual/DeviceVirtualRandomGeneratorControl.vue b/src/components/device/control/virtual/DeviceVirtualRandomGeneratorControl.vue
index 6acc5d6..5be3745 100644
--- a/src/components/device/control/virtual/DeviceVirtualRandomGeneratorControl.vue
+++ b/src/components/device/control/virtual/DeviceVirtualRandomGeneratorControl.vue
@@ -1,6 +1,6 @@
diff --git a/src/components/icons/DeviceIcon.vue b/src/components/icons/DeviceIcon.vue
index c844985..ac4901f 100644
--- a/src/components/icons/DeviceIcon.vue
+++ b/src/components/icons/DeviceIcon.vue
@@ -4,8 +4,8 @@
diff --git a/src/main.ts b/src/main.ts
index 34073a1..765c532 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,14 +1,14 @@
-import { createApp } from "vue";
-import App from "./App.vue";
-import router from "./router";
-import "vuetify/styles";
-import "@mdi/font/css/materialdesignicons.css";
-import { loadFonts } from "./plugins/webfontloader";
-import { vueSocketIOClient } from "./plugins/vueSocketIOClient.js";
-import { createPinia } from "pinia";
-import { vuetify } from "./plugins/vuetify";
-import VueFullscreen from "vue-fullscreen";
-import {useBackendStore} from "@/stores/backend";
+import { createApp } from 'vue';
+import App from './App.vue';
+import router from './router';
+import 'vuetify/styles';
+import '@mdi/font/css/materialdesignicons.css';
+import { loadFonts } from './plugins/webfontloader';
+import { vueSocketIOClient } from './plugins/vueSocketIOClient.js';
+import { createPinia } from 'pinia';
+import { vuetify } from './plugins/vuetify';
+import VueFullscreen from 'vue-fullscreen';
+import {useBackendStore} from '@/stores/backend';
loadFonts().catch(console.log);
@@ -22,4 +22,4 @@ app
.use(router)
.use(vuetify)
.use(VueFullscreen)
- .mount("#app");
+ .mount('#app');
diff --git a/src/model/MapRule.ts b/src/model/MapRule.ts
index 103d768..4b005a6 100644
--- a/src/model/MapRule.ts
+++ b/src/model/MapRule.ts
@@ -1,6 +1,6 @@
-import Rule from "./Rule.js";
-import type Device from "./devices/Device";
-import type ValueMap from "./ValueMap.js";
+import Rule from './Rule.js';
+import type Device from './devices/Device';
+import type ValueMap from './ValueMap.js';
export default class MapRule extends Rule {
public fromDevice: Device | null = null;
diff --git a/src/model/devices/Device.ts b/src/model/devices/Device.ts
index eff65d3..e14d99a 100644
--- a/src/model/devices/Device.ts
+++ b/src/model/devices/Device.ts
@@ -23,6 +23,10 @@ export interface IntDeviceAttribute extends DeviceAttribute {
+ uom?: string;
+}
+
export interface IntRangeDeviceAttribute extends IntDeviceAttribute {
min: number;
max: number;
diff --git a/src/model/devices/estim2b/DeviceEstim2b.ts b/src/model/devices/estim2b/DeviceEstim2b.ts
index c15933b..6a75365 100644
--- a/src/model/devices/estim2b/DeviceEstim2b.ts
+++ b/src/model/devices/estim2b/DeviceEstim2b.ts
@@ -1,10 +1,10 @@
-import type Device from "../Device";
+import type Device from '../Device';
import type {
BoolDeviceAttribute,
IntRangeDeviceAttribute,
ListDeviceAttribute,
StrDeviceAttribute
-} from "../Device";
+} from '../Device';
export type PowerLevelDeviceEStim2bAttributes = {
channelALevel: IntRangeDeviceAttribute,
diff --git a/src/model/devices/slvctrl/DeviceAirValve.ts b/src/model/devices/slvctrl/DeviceAirValve.ts
index e1fe178..219230b 100644
--- a/src/model/devices/slvctrl/DeviceAirValve.ts
+++ b/src/model/devices/slvctrl/DeviceAirValve.ts
@@ -1,5 +1,5 @@
-import type Device from "../Device";
-import type { IntRangeDeviceAttribute } from "../Device";
+import type Device from '../Device';
+import type { IntRangeDeviceAttribute } from '../Device';
type DeviceAirValveAttributes = {
flow: IntRangeDeviceAttribute;
diff --git a/src/model/devices/slvctrl/DeviceDisplay.ts b/src/model/devices/slvctrl/DeviceDisplay.ts
index c53a395..05f29c8 100644
--- a/src/model/devices/slvctrl/DeviceDisplay.ts
+++ b/src/model/devices/slvctrl/DeviceDisplay.ts
@@ -1,5 +1,5 @@
-import type Device from "../Device";
-import type { StrDeviceAttribute } from "../Device";
+import type Device from '../Device';
+import type { StrDeviceAttribute } from '../Device';
type DeviceDisplayAttributes = {
content: StrDeviceAttribute;
diff --git a/src/model/devices/slvctrl/DeviceDistance.ts b/src/model/devices/slvctrl/DeviceDistance.ts
index a375fb7..b07e697 100644
--- a/src/model/devices/slvctrl/DeviceDistance.ts
+++ b/src/model/devices/slvctrl/DeviceDistance.ts
@@ -1,5 +1,5 @@
-import type Device from "../Device";
-import type { IntDeviceAttribute, StrDeviceAttribute } from "../Device";
+import type Device from '../Device';
+import type { IntDeviceAttribute, StrDeviceAttribute } from '../Device';
type DeviceDistanceAttributes = {
distance: IntDeviceAttribute;
diff --git a/src/model/devices/slvctrl/DeviceEt312.ts b/src/model/devices/slvctrl/DeviceEt312.ts
index 7951a96..3b43fde 100644
--- a/src/model/devices/slvctrl/DeviceEt312.ts
+++ b/src/model/devices/slvctrl/DeviceEt312.ts
@@ -1,9 +1,9 @@
-import type Device from "../Device";
+import type Device from '../Device';
import type {
BoolDeviceAttribute,
IntRangeDeviceAttribute,
ListDeviceAttribute
-} from "../Device";
+} from '../Device';
type DeviceEt312Attributes = {
connected: BoolDeviceAttribute;
diff --git a/src/model/devices/slvctrl/DeviceNogasm.ts b/src/model/devices/slvctrl/DeviceNogasm.ts
index 84ea83c..ed050cc 100644
--- a/src/model/devices/slvctrl/DeviceNogasm.ts
+++ b/src/model/devices/slvctrl/DeviceNogasm.ts
@@ -1,6 +1,6 @@
-import type Device from "../Device"
-import type {IntRangeDeviceAttribute, ListDeviceAttribute} from "../Device";
-import type { IntDeviceAttribute } from "../Device";
+import type Device from '../Device'
+import type {IntRangeDeviceAttribute, ListDeviceAttribute} from '../Device';
+import type { IntDeviceAttribute } from '../Device';
type DeviceNogasmAttributes = {
timestampMs: IntDeviceAttribute;
diff --git a/src/model/devices/slvctrl/DeviceStrikerMk2.ts b/src/model/devices/slvctrl/DeviceStrikerMk2.ts
index b3b62c6..9c6260a 100644
--- a/src/model/devices/slvctrl/DeviceStrikerMk2.ts
+++ b/src/model/devices/slvctrl/DeviceStrikerMk2.ts
@@ -1,5 +1,5 @@
-import type Device from "../Device";
-import type { IntRangeDeviceAttribute } from "../Device";
+import type Device from '../Device';
+import type { IntRangeDeviceAttribute } from '../Device';
type DeviceStrikerMk2Attributes = {
speed: IntRangeDeviceAttribute;
diff --git a/src/model/devices/virtual/VirtualDeviceDisplay.ts b/src/model/devices/virtual/VirtualDeviceDisplay.ts
index e6fa0bc..f71645a 100644
--- a/src/model/devices/virtual/VirtualDeviceDisplay.ts
+++ b/src/model/devices/virtual/VirtualDeviceDisplay.ts
@@ -1,5 +1,5 @@
-import type Device from "../Device";
-import type { StrDeviceAttribute } from "../Device";
+import type Device from '../Device';
+import type { StrDeviceAttribute } from '../Device';
type VirtualDeviceDisplayAttributes = {
content: StrDeviceAttribute;
diff --git a/src/model/devices/virtual/VirtualDevicePiperTts.ts b/src/model/devices/virtual/VirtualDevicePiperTts.ts
index cadc5ff..a521699 100644
--- a/src/model/devices/virtual/VirtualDevicePiperTts.ts
+++ b/src/model/devices/virtual/VirtualDevicePiperTts.ts
@@ -1,5 +1,5 @@
-import type Device from "../Device";
-import type { BoolDeviceAttribute, StrDeviceAttribute } from "../Device";
+import type Device from '../Device';
+import type { BoolDeviceAttribute, StrDeviceAttribute } from '../Device';
type VirtualDevicePiperTtsAttributes = {
text: StrDeviceAttribute;
diff --git a/src/model/devices/virtual/VirtualDeviceTts.ts b/src/model/devices/virtual/VirtualDeviceTts.ts
index 1e6319c..f22ed6c 100644
--- a/src/model/devices/virtual/VirtualDeviceTts.ts
+++ b/src/model/devices/virtual/VirtualDeviceTts.ts
@@ -1,5 +1,5 @@
-import type Device from "../Device";
-import type { BoolDeviceAttribute, IntDeviceAttribute, StrDeviceAttribute } from "../Device";
+import type Device from '../Device';
+import type { BoolDeviceAttribute, IntDeviceAttribute, StrDeviceAttribute } from '../Device';
type VirtualDeviceTtsAttributes = {
text: StrDeviceAttribute;
diff --git a/src/model/devices/virtual/VirtualRandomGenerator.ts b/src/model/devices/virtual/VirtualRandomGenerator.ts
index eadfeed..39d7956 100644
--- a/src/model/devices/virtual/VirtualRandomGenerator.ts
+++ b/src/model/devices/virtual/VirtualRandomGenerator.ts
@@ -1,5 +1,5 @@
-import type Device from "../Device";
-import type { IntDeviceAttribute } from "../Device";
+import type Device from '../Device';
+import type { IntDeviceAttribute } from '../Device';
type VirtualDeviceRandomGeneratorAttributes = {
value: IntDeviceAttribute;
diff --git a/src/model/devices/zc95/DeviceZc95.ts b/src/model/devices/zc95/DeviceZc95.ts
index b9c2c1b..628d3c3 100644
--- a/src/model/devices/zc95/DeviceZc95.ts
+++ b/src/model/devices/zc95/DeviceZc95.ts
@@ -1,10 +1,10 @@
-import type Device from "../Device";
+import type Device from '../Device';
import type {
BoolDeviceAttribute,
IntRangeDeviceAttribute,
ListDeviceAttribute
-} from "../Device";
-import type {AllOrNone} from "@/types";
+} from '../Device';
+import type {AllOrNone} from '@/types';
type RequiredDeviceZc95Attributes = {
activePattern: ListDeviceAttribute;
diff --git a/src/plugins/vueSocketIOClient.ts b/src/plugins/vueSocketIOClient.ts
index d2c5f08..7828d65 100644
--- a/src/plugins/vueSocketIOClient.ts
+++ b/src/plugins/vueSocketIOClient.ts
@@ -1,17 +1,17 @@
-import type { App, InjectionKey, Plugin } from "vue";
-import { inject } from "vue";
+import type { App, InjectionKey, Plugin } from 'vue';
+import { inject } from 'vue';
import {
io,
type ManagerOptions,
Socket,
type SocketOptions,
-} from "socket.io-client";
+} from 'socket.io-client';
interface SocketPluginOptions extends Partial {
connection: string;
}
-const injectionKey: InjectionKey = Symbol("socketIO");
+const injectionKey: InjectionKey = Symbol('socketIO');
export const useSocketIO = () => inject(injectionKey, null);
@@ -19,7 +19,7 @@ export const vueSocketIOClient: Plugin = {
install: async (app: App, options: SocketPluginOptions): Promise => {
const socketIO = io(options.connection, options);
- socketIO.on("connect", () => {
+ socketIO.on('connect', () => {
console.log(`WebSocket connection established: ${socketIO.id}`);
});
diff --git a/src/plugins/vuetify.ts b/src/plugins/vuetify.ts
index 8aaff9a..9da8341 100644
--- a/src/plugins/vuetify.ts
+++ b/src/plugins/vuetify.ts
@@ -1,45 +1,45 @@
// Styles
-import "@mdi/font/css/materialdesignicons.css";
-import "vuetify/styles";
+import '@mdi/font/css/materialdesignicons.css';
+import 'vuetify/styles';
// Vuetify
-import { createVuetify } from "vuetify";
-import type { App, Plugin } from "vue";
-import * as components from "vuetify/components";
-import * as directives from "vuetify/directives";
-import { aliases, mdi } from "vuetify/iconsets/mdi";
+import { createVuetify } from 'vuetify';
+import type { App, Plugin } from 'vue';
+import * as components from 'vuetify/components';
+import * as directives from 'vuetify/directives';
+import { aliases, mdi } from 'vuetify/iconsets/mdi';
const dark = {
dark2: true,
colors: {
- primary: "#00BD7E",
- "primary-darken-1": "#008055",
- "primary-darken-2": "#004d33",
- "primary-darken-3": "#094732",
- secondary: "#03DAC6",
- "secondary-darken-1": "#018786",
- error: "#B00020",
- info: "#2196F3",
- success: "#4CAF50",
- warning: "#FB8C00",
- logo: "#eee",
+ primary: '#00BD7E',
+ 'primary-darken-1': '#008055',
+ 'primary-darken-2': '#004d33',
+ 'primary-darken-3': '#094732',
+ secondary: '#03DAC6',
+ 'secondary-darken-1': '#018786',
+ error: '#B00020',
+ info: '#2196F3',
+ success: '#4CAF50',
+ warning: '#FB8C00',
+ logo: '#eee',
},
};
const light = {
dark: false,
colors: {
- primary: "#00BD7E",
- "primary-darken-1": "#008055",
- "primary-darken-2": "#004d33",
- "primary-darken-3": "#094732",
- secondary: "#03DAC6",
- "secondary-darken-1": "#018786",
- error: "#B00020",
- info: "#2196F3",
- success: "#4CAF50",
- warning: "#FB8C00",
- logo: "#222",
+ primary: '#00BD7E',
+ 'primary-darken-1': '#008055',
+ 'primary-darken-2': '#004d33',
+ 'primary-darken-3': '#094732',
+ secondary: '#03DAC6',
+ 'secondary-darken-1': '#018786',
+ error: '#B00020',
+ info: '#2196F3',
+ success: '#4CAF50',
+ warning: '#FB8C00',
+ logo: '#222',
},
};
@@ -49,14 +49,14 @@ export const vuetify: Plugin = {
components,
directives,
icons: {
- defaultSet: "mdi",
+ defaultSet: 'mdi',
aliases,
sets: {
mdi,
},
},
theme: {
- defaultTheme: "dark",
+ defaultTheme: 'dark',
themes: {
dark,
light,
diff --git a/src/plugins/webfontloader.ts b/src/plugins/webfontloader.ts
index e86aa7d..51caf7e 100644
--- a/src/plugins/webfontloader.ts
+++ b/src/plugins/webfontloader.ts
@@ -6,12 +6,12 @@
export async function loadFonts() {
const webFontLoader = await import(
- /* webpackChunkName: "webfontloader" */ "webfontloader"
+ /* webpackChunkName: "webfontloader" */ 'webfontloader'
);
webFontLoader.load({
google: {
- families: ["Roboto:100,300,400,500,700,900&display=swap"],
+ families: ['Roboto:100,300,400,500,700,900&display=swap'],
},
});
}
diff --git a/src/router/index.ts b/src/router/index.ts
index 12bf0d8..54aaadf 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,62 +1,62 @@
-import { createRouter, createWebHistory } from "vue-router";
-import LayoutWithMenu from "../layouts/LayoutWithMenu.vue";
-import {useBackendStore} from "@/stores/backend";
+import { createRouter, createWebHistory } from 'vue-router';
+import LayoutWithMenu from '../layouts/LayoutWithMenu.vue';
+import {useBackendStore} from '@/stores/backend';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
- path: "/",
- name: "welcome",
+ path: '/',
+ name: 'welcome',
components: {
- default: () => import("../views/WelcomeView.vue"),
+ default: () => import('../views/WelcomeView.vue'),
layout: LayoutWithMenu,
},
},
{
- path: "/mission-control",
- name: "mission-control",
+ path: '/mission-control',
+ name: 'mission-control',
components: {
- default: () => import("../views/MissionControlView.vue"),
+ default: () => import('../views/MissionControlView.vue'),
layout: LayoutWithMenu,
},
},
{
- path: "/mission-control/device/:id",
- component: () => import("../views/MissionControlSingleView.vue"),
+ path: '/mission-control/device/:id',
+ component: () => import('../views/MissionControlSingleView.vue'),
},
{
- path: "/automation",
- name: "automation",
+ path: '/automation',
+ name: 'automation',
components: {
- default: () => import("../views/AutomationView.vue"),
+ default: () => import('../views/AutomationView.vue'),
layout: LayoutWithMenu,
},
},
{
- path: "/devices",
- name: "devices",
+ path: '/devices',
+ name: 'devices',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
components: {
- default: () => import("../views/DevicesView.vue"),
+ default: () => import('../views/DevicesView.vue'),
layout: LayoutWithMenu,
},
},
{
- path: "/health",
- name: "health",
+ path: '/health',
+ name: 'health',
components: {
- default: () => import("../views/SystemHealthView.vue"),
+ default: () => import('../views/SystemHealthView.vue'),
layout: LayoutWithMenu,
},
},
{
- path: "/settings",
- name: "serverSettings",
+ path: '/settings',
+ name: 'serverSettings',
components: {
- default: () => import("../views/SettingsView.vue"),
+ default: () => import('../views/SettingsView.vue'),
layout: LayoutWithMenu,
},
},
@@ -69,7 +69,7 @@ const router = createRouter({
path: '/:pathMatch(.*)*',
name: 'not-found',
components: {
- default: () => import("../views/NotFoundView.vue"),
+ default: () => import('../views/NotFoundView.vue'),
layout: LayoutWithMenu,
},
},
diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts
index ef2e3a1..b52c925 100644
--- a/src/shims-vue.d.ts
+++ b/src/shims-vue.d.ts
@@ -1,8 +1,8 @@
-declare module "*.vue";
-declare module "*.js";
-import type { Socket } from "socket.io-client";
+declare module '*.vue';
+declare module '*.js';
+import type { Socket } from 'socket.io-client';
-declare module "@vue/runtime-core" {
+declare module '@vue/runtime-core' {
export interface ComponentCustomProperties {
$socket: Socket;
}
diff --git a/src/shims-vuetify.d.ts b/src/shims-vuetify.d.ts
index edeacab..0b620dd 100644
--- a/src/shims-vuetify.d.ts
+++ b/src/shims-vuetify.d.ts
@@ -1,3 +1,3 @@
-declare module "vuetify";
-declare module "vuetify/lib/components";
-declare module "vuetify/lib/directives";
+declare module 'vuetify';
+declare module 'vuetify/lib/components';
+declare module 'vuetify/lib/directives';
diff --git a/src/stores/app.ts b/src/stores/app.ts
index 09a7612..4997b81 100644
--- a/src/stores/app.ts
+++ b/src/stores/app.ts
@@ -1,5 +1,5 @@
-import { defineStore } from "pinia";
-import { reactive } from "vue";
+import { defineStore } from 'pinia';
+import { reactive } from 'vue';
export type AppState = {
snackbar: {
@@ -16,19 +16,19 @@ export type AppState = {
getVersion(): string;
};
-export const useAppStore = defineStore("app", (): AppState => {
+export const useAppStore = defineStore('app', (): AppState => {
// reactive state
- const snackbar = reactive({
+ const snackbar = reactive({
display: false,
- text: "",
- color: "primary",
+ text: '',
+ color: 'primary',
timeout: 5000,
});
// actions
function displaySnackbar(
text: string,
- color: string = "primary",
+ color: string = 'primary',
timeout: number = 5000
): void {
snackbar.text = text;
diff --git a/src/stores/automation.ts b/src/stores/automation.ts
index 6de6182..76c909f 100644
--- a/src/stores/automation.ts
+++ b/src/stores/automation.ts
@@ -1,7 +1,7 @@
-import { defineStore } from "pinia";
-import { ref, computed } from "vue";
-import type AutomationScript from "../model/AutomationScript";
-import {apiFetch} from "@/utils/apiFetch";
+import { defineStore } from 'pinia';
+import { ref, computed } from 'vue';
+import type AutomationScript from '../model/AutomationScript';
+import {apiFetch} from '@/utils/apiFetch';
const defaultCode = `// Write your automation here. The script will fire on device events:
//
@@ -14,7 +14,7 @@ const defaultCode = `// Write your automation here. The script will fire on devi
// variable. To access other devices you can use the global variable "devices" -> devices.getById(\`{device uuid}\`)
`;
-export const useAutomationStore = defineStore("automation", () => {
+export const useAutomationStore = defineStore('automation', () => {
// State
const scripts = ref([]);
const currentScriptName = ref(null);
@@ -47,7 +47,7 @@ export const useAutomationStore = defineStore("automation", () => {
data.items.forEach((v: AutomationScript) => {
scripts.value.push(v);
});
- console.log("List of scripts updated");
+ console.log('List of scripts updated');
return scripts.value;
}
@@ -65,11 +65,11 @@ export const useAutomationStore = defineStore("automation", () => {
}
async function saveScript(): Promise {
- if (!currentScriptName.value) throw new Error("No script selected to save");
+ if (!currentScriptName.value) throw new Error('No script selected to save');
const response = await apiFetch(`/automation/scripts/${currentScriptName.value}`, {
- headers: { "Content-Type": "text/plain" },
- method: "POST",
+ headers: { 'Content-Type': 'text/plain' },
+ method: 'POST',
body: currentCode.value,
});
if (response.status !== 201) {
@@ -79,8 +79,8 @@ export const useAutomationStore = defineStore("automation", () => {
async function deleteScript(scriptName: string): Promise {
const response = await apiFetch(`/automation/scripts/${scriptName}`, {
- headers: { "Content-Type": "text/plain" },
- method: "DELETE",
+ headers: { 'Content-Type': 'text/plain' },
+ method: 'DELETE',
});
if (response.status !== 204) {
throw new Error(`Could not delete script "${scriptName}": ${response.statusText}`);
@@ -89,8 +89,8 @@ export const useAutomationStore = defineStore("automation", () => {
async function runScript(): Promise {
const response = await apiFetch(`/automation/run`, {
- headers: { "Content-Type": "text/plain" },
- method: "POST",
+ headers: { 'Content-Type': 'text/plain' },
+ method: 'POST',
body: currentCode.value,
});
if (response.status !== 200) {
diff --git a/src/stores/backend.ts b/src/stores/backend.ts
index a8f04b4..a1628b4 100644
--- a/src/stores/backend.ts
+++ b/src/stores/backend.ts
@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'
-import { ref } from "vue";
+import { ref } from 'vue';
export const useBackendStore = defineStore('backend', () => {
@@ -29,7 +29,7 @@ export const useBackendStore = defineStore('backend', () => {
localStorage.setItem(backendUrlLocalStoreName, url);
backendUrl.value = url;
} catch (error) {
- console.error("Error setting backend URL:", error);
+ console.error('Error setting backend URL:', error);
}
if (!history.value.includes(url)) {
@@ -42,7 +42,7 @@ export const useBackendStore = defineStore('backend', () => {
try {
localStorage.setItem(historyLocalStoreName, JSON.stringify(history.value));
} catch (error) {
- console.error("Error saving backend history:", error);
+ console.error('Error saving backend history:', error);
}
}
}
@@ -54,7 +54,7 @@ export const useBackendStore = defineStore('backend', () => {
wasServerEverOnline.value = false;
isServerOnline.value = false;
} catch (error) {
- console.error("Error clearing backend URL:", error);
+ console.error('Error clearing backend URL:', error);
}
}
@@ -69,7 +69,7 @@ export const useBackendStore = defineStore('backend', () => {
try {
localStorage.setItem(historyLocalStoreName, JSON.stringify(history.value));
} catch (error) {
- console.error("Error saving backend history:", error);
+ console.error('Error saving backend history:', error);
}
}
diff --git a/src/stores/devices.ts b/src/stores/devices.ts
index 25a3224..fb2e03e 100644
--- a/src/stores/devices.ts
+++ b/src/stores/devices.ts
@@ -1,9 +1,9 @@
-import { defineStore } from "pinia";
-import { ref, reactive, computed } from "vue";
-import {apiFetch} from "@/utils/apiFetch";
-import type Device from "@/model/devices/Device";
+import { defineStore } from 'pinia';
+import { ref, reactive, computed } from 'vue';
+import {apiFetch} from '@/utils/apiFetch';
+import type Device from '@/model/devices/Device';
-export const useDevicesStore = defineStore("devices", () => {
+export const useDevicesStore = defineStore('devices', () => {
// State: use reactive for objects, ref for primitives
const devices = reactive<{ [key: string]: Device }>({});
const devicesLoaded = ref(false);
@@ -47,11 +47,18 @@ export const useDevicesStore = defineStore("devices", () => {
}
}
+ function clear() {
+ for (const key in devices) {
+ delete devices[key];
+ }
+ }
+
return {
devices,
devicesLoaded,
deviceList,
init,
+ clear,
removeDevice,
addDevice,
updateDevice,
diff --git a/src/stores/health.ts b/src/stores/health.ts
index 595bdb6..bd179b9 100644
--- a/src/stores/health.ts
+++ b/src/stores/health.ts
@@ -1,9 +1,9 @@
-import { defineStore } from "pinia";
-import { ref, reactive, type Ref } from "vue";
-import type { ChartData } from "chart.js";
-import ChartHelper from "../helper/ChartHelper";
-import { useBackendStore } from "@/stores/backend";
-import { apiFetch } from "@/utils/apiFetch";
+import { defineStore } from 'pinia';
+import { ref, reactive, type Ref } from 'vue';
+import type { ChartData } from 'chart.js';
+import ChartHelper from '../helper/ChartHelper';
+import { useBackendStore } from '@/stores/backend';
+import { apiFetch } from '@/utils/apiFetch';
interface SystemInfo {
process: {
@@ -39,9 +39,9 @@ interface SystemInfo {
}
type HealthChartData = {
- processMemory: ChartData<"line">;
- systemCpu: ChartData<"line">;
- systemMemory: ChartData<"line">;
+ processMemory: ChartData<'line'>;
+ systemCpu: ChartData<'line'>;
+ systemMemory: ChartData<'line'>;
}
type HealthStore = {
@@ -50,7 +50,7 @@ type HealthStore = {
init: () => void;
};
-export const useHealthStore = defineStore("health", (): HealthStore => {
+export const useHealthStore = defineStore('health', (): HealthStore => {
// state refs/reactive
const state = ref(undefined);
@@ -59,38 +59,38 @@ export const useHealthStore = defineStore("health", (): HealthStore => {
processMemory: {
datasets: [
ChartHelper.createEmptyDataSet({
- label: "Resident Set Size",
+ label: 'Resident Set Size',
color: { r: 0, g: 189, b: 126 },
tension: 0,
}),
ChartHelper.createEmptyDataSet({
- label: "Heap total", color: { r: 0, g: 189, b: 126 }, tension: 0,
+ label: 'Heap total', color: { r: 0, g: 189, b: 126 }, tension: 0,
}),
ChartHelper.createEmptyDataSet({
- label: "Heap used",
+ label: 'Heap used',
color: { r: 204, g: 0, b: 0 },
tension: 0,
}),
],
- } as ChartData<"line">,
+ } as ChartData<'line'>,
systemCpu: {
datasets: [
ChartHelper.createEmptyDataSet({
- label: "Percentage",
+ label: 'Percentage',
color: { r: 0, g: 189, b: 126 },
tension: 0,
}),
],
- } as ChartData<"line">,
+ } as ChartData<'line'>,
systemMemory: {
datasets: [
ChartHelper.createEmptyDataSet({
- label: "Percentage",
+ label: 'Percentage',
color: { r: 0, g: 189, b: 126 },
tension: 0,
}),
],
- } as ChartData<"line">,
+ } as ChartData<'line'>,
});
// actions
@@ -104,7 +104,7 @@ export const useHealthStore = defineStore("health", (): HealthStore => {
const response = await apiFetch(`/health`);
state.value = await response.json();
- }, 500);
+ }, 3000);
setInterval(() => {
if (!backendStore.isServerOnline || !chartData || !state.value) {
@@ -133,7 +133,7 @@ export const useHealthStore = defineStore("health", (): HealthStore => {
x: Date.now(),
y: Number(state.value.system.memory.usedMemPercentage.toFixed(2)),
});
- }, 425);
+ }, 1000);
}
return {
diff --git a/src/stores/settings.ts b/src/stores/settings.ts
index 9b4f0ba..5863e3e 100644
--- a/src/stores/settings.ts
+++ b/src/stores/settings.ts
@@ -1,6 +1,6 @@
-import { defineStore } from "pinia";
-import { ref } from "vue";
-import { apiFetch } from "@/utils/apiFetch";
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+import { apiFetch } from '@/utils/apiFetch';
type ValidationErrors = {
message: string;
@@ -14,10 +14,10 @@ type ValidationError = {
keyword: string;
}
-export const useSettingsStore = defineStore("serverSettings", () => {
+export const useSettingsStore = defineStore('serverSettings', () => {
// state as refs
- const theme = ref("dark");
- const serverSettings = ref("");
+ const theme = ref('dark');
+ const serverSettings = ref('');
const validationErrors = ref(null);
// actions
@@ -27,8 +27,8 @@ export const useSettingsStore = defineStore("serverSettings", () => {
async function saveServerSettings(): Promise {
const response = await apiFetch(`/settings`, {
- headers: { "Content-Type": "application/json", "Accept": "application/json" },
- method: "PUT",
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
+ method: 'PUT',
body: serverSettings.value,
});
if (response.status !== 200) {
diff --git a/src/utils/apiFetch.ts b/src/utils/apiFetch.ts
index 27444cf..7812302 100644
--- a/src/utils/apiFetch.ts
+++ b/src/utils/apiFetch.ts
@@ -1,5 +1,5 @@
import { useBackendStore } from '@/stores/backend';
-import { useAppStore } from "@/stores/app";
+import { useAppStore } from '@/stores/app';
export async function apiFetch(path: string, options?: RequestInit): Promise {
const backendStore = useBackendStore();
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index e44c372..ee9201a 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -1,4 +1,4 @@
-import type {IntRangeDeviceAttribute, ListDeviceAttribute} from "@/model/devices/Device";
+import type {BoolDeviceAttribute, FloatDeviceAttribute, IntDeviceAttribute, IntRangeDeviceAttribute, ListDeviceAttribute, StrDeviceAttribute} from '@/model/devices/Device';
type StringKey = Extract;
@@ -28,6 +28,22 @@ export const isListDeviceAttribute = (obj: object): obj is ListDeviceAttribute {
+ return hasType(obj, 'str') || hasType(obj, 'float') || hasType(obj, 'int');
+}
+
+export const isIntDeviceAttribute = (obj: object): obj is IntDeviceAttribute => {
+ return hasType(obj, 'int');
+}
+
+export const isFloatDeviceAttribute = (obj: object): obj is FloatDeviceAttribute => {
+ return hasType(obj, 'float');
+}
+
+export const isBoolDeviceAttribute = (obj: object): obj is BoolDeviceAttribute => {
+ return hasType(obj, 'bool');
+}
+
export const hasType = (obj: unknown, expectedType: string): boolean => {
return (
typeof obj === 'object' &&
diff --git a/src/views/AutomationView.vue b/src/views/AutomationView.vue
index 70f840b..c6e7a4a 100644
--- a/src/views/AutomationView.vue
+++ b/src/views/AutomationView.vue
@@ -1,12 +1,12 @@
diff --git a/src/views/BackendUrl.vue b/src/views/BackendUrl.vue
index 2676c5c..597af2d 100644
--- a/src/views/BackendUrl.vue
+++ b/src/views/BackendUrl.vue
@@ -1,7 +1,7 @@
diff --git a/src/vueSocketIOClient.d.ts b/src/vueSocketIOClient.d.ts
index b297940..003ff40 100644
--- a/src/vueSocketIOClient.d.ts
+++ b/src/vueSocketIOClient.d.ts
@@ -1,6 +1,6 @@
-import { Socket } from "socket.io-client";
+import { Socket } from 'socket.io-client';
-declare module "@vue/runtime-core" {
+declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$socket: typeof Socket;
}
diff --git a/vite.config.ts b/vite.config.ts
index d7c1261..c8e1591 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,9 +1,9 @@
import path from 'path';
-import { defineConfig } from "vite";
-import vue from "@vitejs/plugin-vue";
-import vueJsx from "@vitejs/plugin-vue-jsx";
-import monacoEditorPlugin from "vite-plugin-monaco-editor-esm";
+import { defineConfig } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import vueJsx from '@vitejs/plugin-vue-jsx';
+import monacoEditorPlugin from 'vite-plugin-monaco-editor-esm';
// https://vitejs.dev/config/
export default defineConfig({
@@ -14,7 +14,7 @@ export default defineConfig({
vue(),
vueJsx(),
monacoEditorPlugin({
- languageWorkers: ["editorWorkerService", "json", "typescript"],
+ languageWorkers: ['editorWorkerService', 'json', 'typescript'],
}),
],
resolve: {
diff --git a/vue.config.js b/vue.config.js
index b9df550..93ec4fa 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -1,4 +1,4 @@
-import { defineConfig } from "@vue/cli-service";
+import { defineConfig } from '@vue/cli-service';
module.exports = defineConfig({
pluginOptions: {
vuetify: {