diff --git a/package.json b/package.json index 6992829..ee952f9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lightcontrol", - "version": "0.0.4", - "description": "A Quasar Project", + "version": "0.0.14", + "description": "A Tecamino App", "productName": "Light Control", "author": "A. Zuercher", "type": "module", diff --git a/src/components/dbm/DBMTree.vue b/src/components/dbm/DBMTree.vue index 711fd25..d2adc7d 100644 --- a/src/components/dbm/DBMTree.vue +++ b/src/components/dbm/DBMTree.vue @@ -10,6 +10,7 @@ no-transition :default-expand-all="false" v-model:expanded="expanded" + @update:expanded="onExpandedChange" @lazy-load="onLazyLoad" > diff --git a/src/components/lights/MovingHead.vue b/src/components/lights/MovingHead.vue index 6ea0b61..cb91c94 100644 --- a/src/components/lights/MovingHead.vue +++ b/src/components/lights/MovingHead.vue @@ -21,49 +21,59 @@ select @@ -102,8 +114,6 @@ import type { Settings } from 'src/models/MovingHead'; const $q = useQuasar(); const brightness = updateBrightnessValue('MovingHead:Brightness'); -const pan = updateValue('MovingHead:Pan', true); -const tilt = updateValue('MovingHead:Tilt', true); const state = updateValue('MovingHead:State'); const settings = ref({ show: false, @@ -125,7 +135,7 @@ onMounted(() => { .then((response) => { console.log(response); if (response?.subscribe) { - dbmData.value = buildTree(response.subscribe ?? []); + dbmData.splice(0, dbmData.length, ...buildTree(response.subscribe)); } else { NotifyResponse($q, response); } @@ -149,7 +159,7 @@ onUnmounted(() => { function changeState() { if (brightness.value === 0) { if (state.value === 0) { - brightness.value = 100; + brightness.value = 255; return; } brightness.value = state.value; @@ -164,14 +174,13 @@ function updateValue(path: string, isDouble = false) { get() { const sub = getSubscriptionsByPath(path); const value = sub ? Number(sub.value ?? 0) : 0; - return isDouble ? Math.round((100 / 255) * value) : Math.round((100 / 255) * value); + return isDouble ? value : value; }, set(val) { - const baseValue = Math.round((255 / 100) * val); - const setPaths = [{ path, value: baseValue }]; + const setPaths = [{ path, value: val }]; if (isDouble) { - setPaths.push({ path: `${path}Fine`, value: baseValue }); + setPaths.push({ path: `${path}Fine`, value: val }); } setValues(setPaths) @@ -186,12 +195,11 @@ function updateBrightnessValue(path: string) { get() { const sub = getSubscriptionsByPath(path); const value = sub ? Number(sub.value ?? 0) : 0; - return Math.round((100 / 255) * value); + return value; }, set(val) { - const baseValue = Math.round((255 / 100) * val); - const setPaths = [{ path, value: baseValue }]; - setPaths.push({ path: `${path}Fine`, value: baseValue }); + const setPaths = [{ path, value: val }]; + setPaths.push({ path: `${path}Fine`, value: val }); setPaths.push({ path: `MovingHead:Strobe`, value: 255 }); setValues(setPaths) diff --git a/src/components/scenes/ScenesTab.vue b/src/components/scenes/ScenesTab.vue new file mode 100644 index 0000000..6f2294c --- /dev/null +++ b/src/components/scenes/ScenesTab.vue @@ -0,0 +1,292 @@ + + + diff --git a/src/composables/dbm/dbmTree.ts b/src/composables/dbm/dbmTree.ts index ff0d18a..13871f6 100644 --- a/src/composables/dbm/dbmTree.ts +++ b/src/composables/dbm/dbmTree.ts @@ -1,12 +1,13 @@ -import type { Subs } from 'src/models/Subscribe'; -import { ref, nextTick, computed } from 'vue'; +import type { Subs, Subscribe } from 'src/models/Subscribe'; +import type { Ref } from 'vue'; +import { nextTick, computed, reactive, ref } from 'vue'; import { setValues } from 'src/services/websocket'; import { NotifyResponse } from 'src/composables/notify'; import type { QVueGlobals } from 'quasar'; -const Subscriptions = ref([]); +const Subscriptions = reactive>({}); -export const dbmData = ref([]); +export const dbmData = reactive([]); export interface TreeNode { path: string | undefined; @@ -28,9 +29,10 @@ export function buildTree(subs: Subs): TreeNode[] { const root: TreeMap = {}; - Subscriptions.value = subs; - for (const item of subs) { + if (item.path) { + Subscriptions[item.path] = item; + } const pathParts = item.path?.split(':') ?? []; let current = root; @@ -54,13 +56,15 @@ export function buildTree(subs: Subs): TreeNode[] { } function convert(map: TreeMap): TreeNode[] { - return Object.entries(map).map(([path, node]) => ({ - path, - key: node.uuid ?? path, // `key` is used by QTree - value: node.value, - lazy: node.lazy, - children: convert(node.__children), - })); + return reactive( + Object.entries(map).map(([path, node]) => ({ + path, + key: node.uuid ?? path, // `key` is used by QTree + value: node.value, + lazy: node.lazy, + children: convert(node.__children), + })), + ); } return [ @@ -74,24 +78,32 @@ export function buildTree(subs: Subs): TreeNode[] { } export function getTreeElementByPath(path: string) { - return dbmData.value.find((s) => s.path === path); + const sub = dbmData.find((s) => s.path === path); + return ref(sub); } -export function getSubscriptionsByUuid(uid: string | undefined) { - return Subscriptions.value.find((s) => s.uuid === uid); +export function getSubscriptionsByUuid(uid: string) { + const sub = Object.values(Subscriptions).find((sub) => sub.uuid === uid); + return ref(sub); } export function addChildrentoTree(subs: Subs) { const ZERO_UUID = '00000000-0000-0000-0000-000000000000'; - const existingIds = new Set(Subscriptions.value.map((sub) => sub.uuid)); + const existingIds = new Set(Object.values(Subscriptions).map((sub) => sub.uuid)); const newSubs = subs .filter((sub) => sub.uuid !== ZERO_UUID) // Skip UUIDs with all zeroes .filter((sub) => !existingIds.has(sub.uuid)); - Subscriptions.value.push(...newSubs); + for (const sub of newSubs) { + if (sub.path !== undefined) { + Subscriptions[sub.path] = sub; + } else { + console.warn('Skipping sub with undefined path', sub); + } + } void nextTick(() => { - dbmData.value = buildTree(Subscriptions.value); + dbmData.splice(0, dbmData.length, ...buildTree(Object.values(Subscriptions))); }); } @@ -111,39 +123,44 @@ export function removeSubtreeByParentKey(parentKey: string) { return false; } - removeChildrenAndMarkLazy(dbmData.value, parentKey); + removeChildrenAndMarkLazy(dbmData, parentKey); } -export function getSubscriptionsByPath(path: string | undefined) { - return Subscriptions.value.find((s) => s.path === path); +export function getSubscriptionsByPath(path: string) { + return ref(Subscriptions[path]); } export function getAllSubscriptions() { - return Subscriptions.value; + return Object.values(Subscriptions); } export function updateValue( path1: string, $q: QVueGlobals, + toggle?: Ref, path2?: string, path3?: string, value3?: number, ) { return computed({ get() { - const sub = getSubscriptionsByPath(path1); - const value = sub ? Number(sub.value ?? 0) : 0; - return Math.round((100 / 255) * value); + const sub = getSubscriptionsByPath(toggle?.value && path2 ? path2 : path1); + const value = sub?.value ? Number(sub.value.value ?? 0) : 0; + return value; }, set(val) { - const baseValue = Math.round((255 / 100) * val); - const setPaths = [{ path: path1, value: baseValue }]; - if (path2) { + const baseValue = val; + const setPaths = []; + if (toggle?.value && path2) { setPaths.push({ path: path2, value: baseValue }); + } else { + setPaths.push({ path: path1, value: baseValue }); } + if (path3) { setPaths.push({ path: path3, value: value3 ? value3 : baseValue }); } + setValues(setPaths) .then((response) => NotifyResponse($q, response)) .catch((err) => { diff --git a/src/composables/notify.ts b/src/composables/notify.ts index 8511431..d8b8072 100644 --- a/src/composables/notify.ts +++ b/src/composables/notify.ts @@ -16,7 +16,7 @@ export function NotifyResponse( icon = 'warning'; break; case 'error': - color = 'orange'; + color = 'red'; icon = 'error'; break; } @@ -26,8 +26,9 @@ export function NotifyResponse( if (message === '') { return; } - color = typeof response === 'string' ? response : response?.error ? 'red' : color; - icon = typeof response === 'string' ? response : response?.error ? 'error' : icon; + + color = typeof response === 'string' ? color : response?.error ? 'red' : color; + icon = typeof response === 'string' ? icon : response?.error ? 'error' : icon; $q?.notify({ message: message, color: color, @@ -37,3 +38,30 @@ export function NotifyResponse( }); } } + +export function NotifyDialog( + $q: QVueGlobals, + title: string, + text: string, + okText?: string, + cancelText?: string, +) { + return new Promise((resolve) => { + $q.dialog({ + title: title, + message: text, + persistent: true, + ok: okText ?? 'OK', + cancel: cancelText ?? 'CANCEL', + }) + .onOk(() => { + resolve(true); + }) + .onCancel(() => { + resolve(false); + }) + .onDismiss(() => { + resolve(false); + }); + }); +} diff --git a/src/models/Scene.ts b/src/models/Scene.ts new file mode 100644 index 0000000..d93c54a --- /dev/null +++ b/src/models/Scene.ts @@ -0,0 +1,9 @@ +import type { Value } from './Value'; + +export interface Scene { + name: string; + description?: string; + movingHead: boolean; + lightBar: boolean; + values?: Value[]; +} diff --git a/src/models/Set.ts b/src/models/Set.ts index f966c15..94a937d 100644 --- a/src/models/Set.ts +++ b/src/models/Set.ts @@ -2,7 +2,7 @@ export type Set = { uuid?: string | undefined; path: string; type?: string; - value: number | boolean | undefined; + value: string | number | boolean | undefined; create?: boolean; }; diff --git a/src/models/Subscribe.ts b/src/models/Subscribe.ts index 04cda90..d3dc684 100644 --- a/src/models/Subscribe.ts +++ b/src/models/Subscribe.ts @@ -1,8 +1,9 @@ +// API type (from backend) export type Subscribe = { - uuid?: string | undefined; - path?: string | undefined; + uuid?: string; + path?: string; depth?: number; - value?: string | number | boolean | undefined; + value?: string | number | boolean; hasChild?: boolean; }; diff --git a/src/models/Value.ts b/src/models/Value.ts new file mode 100644 index 0000000..f5aa642 --- /dev/null +++ b/src/models/Value.ts @@ -0,0 +1,7 @@ +import type { UUID } from 'crypto'; + +export interface Value { + uuid?: UUID; + path: string; + value: number | string | undefined; +} diff --git a/src/pages/DataPage.vue b/src/pages/DataPage.vue index 8848c6f..84afd57 100644 --- a/src/pages/DataPage.vue +++ b/src/pages/DataPage.vue @@ -10,6 +10,7 @@ import { NotifyResponse } from 'src/composables/notify'; import { useQuasar } from 'quasar'; const $q = useQuasar(); + function saveDBM() { api .get('saveData') diff --git a/src/pages/IndexPage.vue b/src/pages/MainPage.vue similarity index 100% rename from src/pages/IndexPage.vue rename to src/pages/MainPage.vue diff --git a/src/router/routes.ts b/src/router/routes.ts index a2a06a2..3227cbf 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -5,8 +5,9 @@ const routes: RouteRecordRaw[] = [ path: '/', component: () => import('layouts/MainLayout.vue'), children: [ - { path: '', component: () => import('pages/IndexPage.vue') }, + { path: '', component: () => import('pages/MainPage.vue') }, { path: '/data', component: () => import('pages/DataPage.vue') }, + { path: '/scenes', component: () => import('components/scenes/ScenesTab.vue') }, ], },