fix scene load and add first version UpdateValueDialog
This commit is contained in:
@@ -47,10 +47,23 @@ func (sh *ScenesHandler) SaveScene(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(path.Join(sh.dir)); err != nil {
|
if _, err := os.Stat(path.Join(sh.dir)); err != nil {
|
||||||
os.MkdirAll(sh.dir, 666)
|
err := os.MkdirAll(sh.dir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.OpenFile(path.Join(sh.dir, scene.Name+".scene"), os.O_CREATE|os.O_TRUNC|os.O_RDWR, 666)
|
f, err := os.OpenFile(path.Join(sh.dir, scene.Name+".scene"), os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
_, err = f.Write(body)
|
_, err = f.Write(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -59,7 +72,6 @@ func (sh *ScenesHandler) SaveScene(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"message": fmt.Sprintf("Scene '%s' saved", scene.Name),
|
"message": fmt.Sprintf("Scene '%s' saved", scene.Name),
|
||||||
@@ -155,6 +167,7 @@ func (sh *ScenesHandler) LoadScene(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var scene models.Scene
|
var scene models.Scene
|
||||||
|
|
||||||
err = json.Unmarshal(body, &scene)
|
err = json.Unmarshal(body, &scene)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
@@ -190,6 +203,13 @@ func (sh *ScenesHandler) LoadScene(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
c.JSON(http.StatusOK, scene)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": fmt.Errorf("scene '%s' not found", scene.Name),
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, scene)
|
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -27,6 +28,10 @@ func OpenBrowser(url string, logger *logging.Logger) error {
|
|||||||
{"open", url}, // fallback
|
{"open", url}, // fallback
|
||||||
}
|
}
|
||||||
default: // Linux
|
default: // Linux
|
||||||
|
if os.Getenv("DISPLAY") == "" && os.Getenv("WAYLAND_DISPLAY") == "" && os.Getenv("XDG_SESSION_TYPE") != "wayland" {
|
||||||
|
|
||||||
|
return fmt.Errorf("os is running i headless mode do not start browser")
|
||||||
|
}
|
||||||
commands = [][]string{
|
commands = [][]string{
|
||||||
{"chromium-browser", "--kiosk", url},
|
{"chromium-browser", "--kiosk", url},
|
||||||
{"google-chrome", "--kiosk", url},
|
{"google-chrome", "--kiosk", url},
|
||||||
@@ -47,14 +52,14 @@ func OpenBrowser(url string, logger *logging.Logger) error {
|
|||||||
return fmt.Errorf("could not open browser")
|
return fmt.Errorf("could not open browser")
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindAllFiles(rootDir, fileExtention string) (files []string, err error){
|
func FindAllFiles(rootDir, fileExtention string) (files []string, err error) {
|
||||||
err = filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error {
|
err = filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error {
|
||||||
if d.IsDir() {
|
if d.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
} else if filepath.Ext(d.Name()) == fileExtention{
|
} else if filepath.Ext(d.Name()) == fileExtention {
|
||||||
files = append(files, path)
|
files = append(files, path)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "lightcontrol",
|
"name": "lightcontrol",
|
||||||
"version": "0.0.5",
|
"version": "0.0.14",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "lightcontrol",
|
"name": "lightcontrol",
|
||||||
"version": "0.0.5",
|
"version": "0.0.14",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/extras": "^1.16.4",
|
"@quasar/extras": "^1.16.4",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.10.0",
|
||||||
"quasar": "^2.16.0",
|
"quasar": "^2.16.0",
|
||||||
"vue": "^3.4.18",
|
"vue": "^3.4.18",
|
||||||
"vue-router": "^4.0.12"
|
"vue-router": "^4.0.12"
|
||||||
@@ -2312,9 +2312,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.9.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
|
||||||
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
|
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "lightcontrol",
|
"name": "lightcontrol",
|
||||||
"version": "0.0.14",
|
"version": "0.0.15",
|
||||||
"description": "A Tecamino App",
|
"description": "A Tecamino App",
|
||||||
"productName": "Light Control",
|
"productName": "Light Control",
|
||||||
"author": "A. Zuercher",
|
"author": "A. Zuercher",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/extras": "^1.16.4",
|
"@quasar/extras": "^1.16.4",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.10.0",
|
||||||
"quasar": "^2.16.0",
|
"quasar": "^2.16.0",
|
||||||
"vue": "^3.4.18",
|
"vue": "^3.4.18",
|
||||||
"vue-router": "^4.0.12"
|
"vue-router": "^4.0.12"
|
||||||
|
13
src/components/dbm/UpdateValue.vue
Normal file
13
src/components/dbm/UpdateValue.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<QCard v-if="props.display"> Test </QCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
display: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
@@ -11,14 +11,24 @@
|
|||||||
row-key="path"
|
row-key="path"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
:rows-per-page-options="[0]"
|
:rows-per-page-options="[0]"
|
||||||
/>
|
>
|
||||||
|
<template v-slot:body-cell-value="props">
|
||||||
|
<q-td :props="props" @click="openDialog(props.row)">
|
||||||
|
<span :class="['cursor-pointer', open ? 'text-green' : '']"> {{ props.row.value }}</span>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
<Dialog dialogLabel="Update Value" :show-dialog="open" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import Dialog from 'src/components/dialog/UpdateValueDialog.vue';
|
||||||
import type { QTableProps } from 'quasar';
|
import type { QTableProps } from 'quasar';
|
||||||
import type { Subs } from 'src/models/Subscribe';
|
import type { Subs, Subscribe } from 'src/models/Subscribe';
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
const open = ref(false);
|
||||||
|
|
||||||
// we generate lots of rows here
|
// we generate lots of rows here
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -35,5 +45,16 @@ const columns = [
|
|||||||
field: 'value',
|
field: 'value',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'test',
|
||||||
|
label: '',
|
||||||
|
field: 'test',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
] as QTableProps['columns'];
|
] as QTableProps['columns'];
|
||||||
|
|
||||||
|
function openDialog(item: Subscribe) {
|
||||||
|
console.log(77, item);
|
||||||
|
open.value = true;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
101
src/components/dialog/UpdateValueDialog.vue
Normal file
101
src/components/dialog/UpdateValueDialog.vue
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<q-dialog v-model="internalShowDialog">
|
||||||
|
<q-card :style="'width:' + props.width">
|
||||||
|
<q-card-section
|
||||||
|
v-if="props.dialogLabel"
|
||||||
|
class="text-h6 text-center"
|
||||||
|
:class="'text-' + props.labelColor"
|
||||||
|
>{{ props.dialogLabel }}</q-card-section
|
||||||
|
>
|
||||||
|
<q-card-section>
|
||||||
|
<q-input v-model="inputValue"></q-input>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section v-if="props.text" class="text-center" style="white-space: pre-line">{{
|
||||||
|
props.text
|
||||||
|
}}</q-card-section>
|
||||||
|
<q-card-actions align="right" class="text-primary">
|
||||||
|
<q-btn v-if="props.buttonCancelLabel" flat :label="props.buttonCancelLabel" v-close-popup>
|
||||||
|
</q-btn>
|
||||||
|
<q-btn
|
||||||
|
v-if="props.buttonOkLabel"
|
||||||
|
flat
|
||||||
|
:label="props.buttonOkLabel"
|
||||||
|
v-close-popup
|
||||||
|
@click="closeDialog"
|
||||||
|
>
|
||||||
|
</q-btn>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
showDialog: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
buttonOkLabel: {
|
||||||
|
type: String,
|
||||||
|
default: 'OK',
|
||||||
|
},
|
||||||
|
labelColor: {
|
||||||
|
type: String,
|
||||||
|
default: 'primary',
|
||||||
|
},
|
||||||
|
dialogLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
buttonCancelLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '300px',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: [String, Number],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const inputValue = ref(props.value);
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:showDialog', 'update:value', 'confirmed', 'cancel']);
|
||||||
|
const internalShowDialog = ref(props.showDialog);
|
||||||
|
|
||||||
|
watch(inputValue, (val) => {
|
||||||
|
emit('update:value', val);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.showDialog,
|
||||||
|
(newValue) => {
|
||||||
|
console.log('watch showDialog', newValue);
|
||||||
|
internalShowDialog.value = newValue;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
watch(internalShowDialog, (newValue) => {
|
||||||
|
console.log('watch internalShowDialog', newValue);
|
||||||
|
emit('update:showDialog', newValue);
|
||||||
|
if (!newValue) {
|
||||||
|
console.log('emit cancel');
|
||||||
|
emit('cancel');
|
||||||
|
} else {
|
||||||
|
console.log('emit confirmed');
|
||||||
|
emit('confirmed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeDialog() {
|
||||||
|
internalShowDialog.value = false;
|
||||||
|
emit('update:showDialog', false);
|
||||||
|
}
|
||||||
|
</script>
|
@@ -104,17 +104,17 @@ select
|
|||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import LightSlider from './LightSlider.vue';
|
import LightSlider from './LightSlider.vue';
|
||||||
import { NotifyResponse } from 'src/composables/notify';
|
import { NotifyResponse } from 'src/composables/notify';
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
import { onBeforeUpdate, computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { subscribe, unsubscribe, setValues } from 'src/services/websocket';
|
import { subscribeToPath, unsubscribe, setValues } from 'src/services/websocket';
|
||||||
import { LocalStorage } from 'quasar';
|
import { LocalStorage } from 'quasar';
|
||||||
import { getSubscriptionsByPath, buildTree, dbmData } from 'src/composables/dbm/dbmTree';
|
import { getSubscriptionsByPath, updateValue } from 'src/composables/dbm/dbmTree';
|
||||||
import DragPad from 'src/components/lights/DragPad.vue';
|
import DragPad from 'src/components/lights/DragPad.vue';
|
||||||
import SettingDialog from './SettingMovingHead.vue';
|
import SettingDialog from './SettingMovingHead.vue';
|
||||||
import type { Settings } from 'src/models/MovingHead';
|
import type { Settings } from 'src/models/MovingHead';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
const brightness = updateBrightnessValue('MovingHead:Brightness');
|
const brightness = updateBrightnessValue('MovingHead:Brightness');
|
||||||
const state = updateValue('MovingHead:State');
|
const state = updateValue('MovingHead:State', $q);
|
||||||
const settings = ref<Settings>({
|
const settings = ref<Settings>({
|
||||||
show: false,
|
show: false,
|
||||||
reversePan: false,
|
reversePan: false,
|
||||||
@@ -125,24 +125,11 @@ const settings = ref<Settings>({
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
settings.value.reversePan = LocalStorage.getItem('reversePan') ?? false;
|
settings.value.reversePan = LocalStorage.getItem('reversePan') ?? false;
|
||||||
settings.value.reverseTilt = LocalStorage.getItem('reverseTilt') ?? false;
|
settings.value.reverseTilt = LocalStorage.getItem('reverseTilt') ?? false;
|
||||||
|
subscribeToPath($q, 'MovingHead:.*');
|
||||||
|
});
|
||||||
|
|
||||||
subscribe([
|
onBeforeUpdate(() => {
|
||||||
{
|
subscribeToPath($q, 'MovingHead:.*');
|
||||||
path: 'MovingHead:.*',
|
|
||||||
depth: 0,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.then((response) => {
|
|
||||||
console.log(response);
|
|
||||||
if (response?.subscribe) {
|
|
||||||
dbmData.splice(0, dbmData.length, ...buildTree(response.subscribe));
|
|
||||||
} else {
|
|
||||||
NotifyResponse($q, response);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
NotifyResponse($q, err, 'error');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -157,45 +144,28 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function changeState() {
|
function changeState() {
|
||||||
|
console.log(55, brightness.value);
|
||||||
|
console.log(56, state.value);
|
||||||
if (brightness.value === 0) {
|
if (brightness.value === 0) {
|
||||||
if (state.value === 0) {
|
if (state.value === 0) {
|
||||||
brightness.value = 255;
|
brightness.value = 255;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
brightness.value = state.value;
|
brightness.value = state.value;
|
||||||
|
console.log(57, brightness.value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.value = brightness.value;
|
state.value = brightness.value;
|
||||||
|
console.log(58, state.value);
|
||||||
brightness.value = 0;
|
brightness.value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateValue(path: string, isDouble = false) {
|
|
||||||
return computed({
|
|
||||||
get() {
|
|
||||||
const sub = getSubscriptionsByPath(path);
|
|
||||||
const value = sub ? Number(sub.value ?? 0) : 0;
|
|
||||||
return isDouble ? value : value;
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
const setPaths = [{ path, value: val }];
|
|
||||||
|
|
||||||
if (isDouble) {
|
|
||||||
setPaths.push({ path: `${path}Fine`, value: val });
|
|
||||||
}
|
|
||||||
|
|
||||||
setValues(setPaths)
|
|
||||||
.then((response) => NotifyResponse($q, response))
|
|
||||||
.catch((err) => console.error(`Failed to update ${path.split(':')[1]}:`, err));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateBrightnessValue(path: string) {
|
function updateBrightnessValue(path: string) {
|
||||||
return computed({
|
return computed({
|
||||||
get() {
|
get() {
|
||||||
const sub = getSubscriptionsByPath(path);
|
const sub = getSubscriptionsByPath(path);
|
||||||
const value = sub ? Number(sub.value ?? 0) : 0;
|
if (!sub.value) return 0;
|
||||||
return value;
|
return Number(sub.value.value);
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
const setPaths = [{ path, value: val }];
|
const setPaths = [{ path, value: val }];
|
||||||
|
356
src/components/scenes/ScenesPage.vue
Normal file
356
src/components/scenes/ScenesPage.vue
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
<template>
|
||||||
|
<!-- new edit scene dialog-->
|
||||||
|
<q-dialog v-model="showDialog" persistent>
|
||||||
|
<q-card style="min-width: 350px">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-primary text-h6">{{ dialogLabel }}</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-section class="q-pt-none">
|
||||||
|
<q-input
|
||||||
|
class="q-mb-md"
|
||||||
|
dense
|
||||||
|
v-model="newScene.name"
|
||||||
|
placeholder="Name"
|
||||||
|
autofocus
|
||||||
|
:rules="[(val) => !!val || 'Field is required']"
|
||||||
|
@keyup.enter="saveScene"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
v-model="newScene.description"
|
||||||
|
placeholder="Description"
|
||||||
|
autofocus
|
||||||
|
@keyup.enter="saveScene"
|
||||||
|
/>
|
||||||
|
<div class="q-py-md">
|
||||||
|
<div class="q-gutter-sm">
|
||||||
|
<q-checkbox v-model="newScene.movingHead" label="Moving Head" />
|
||||||
|
<q-checkbox v-model="newScene.lightBar" label="Light Bar" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-actions align="right" class="text-primary">
|
||||||
|
<q-btn flat label="Cancel" v-close-popup />
|
||||||
|
<q-btn flat :label="dialogLabel" @click="saveScene()" />
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
<Dialog
|
||||||
|
dialogLabel="Duplicate Scene"
|
||||||
|
:text="`Scene '${newScene.name}' exists already`"
|
||||||
|
:show-dialog="existsAlready"
|
||||||
|
v-on:update:show-dialog="existsAlready = $event"
|
||||||
|
/>
|
||||||
|
<q-list
|
||||||
|
bordered
|
||||||
|
v-if="scenes?.length > 0"
|
||||||
|
class="q-mx-auto"
|
||||||
|
style="max-width: 100%; max-width: 500px"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
rounded
|
||||||
|
color="primary"
|
||||||
|
:class="['q-ma-md', 'text-bold', 'text-white']"
|
||||||
|
@click="openDialog('add')"
|
||||||
|
>Add New Scene</q-btn
|
||||||
|
>
|
||||||
|
<q-item class="row">
|
||||||
|
<q-item-section :class="['text-black', 'text-bold', 'col-5']">Name</q-item-section>
|
||||||
|
<q-item-section :class="['text-black', 'text-left', 'text-bold', 'text-left']"
|
||||||
|
>Description</q-item-section
|
||||||
|
>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-for="(item, index) in scenes"
|
||||||
|
:key="item.name"
|
||||||
|
bordered
|
||||||
|
style="border: 0.1px solid lightgray; border-radius: 5px; margin-bottom: 1px"
|
||||||
|
>
|
||||||
|
<q-item-section
|
||||||
|
@click="openDialog('load', item)"
|
||||||
|
:class="['text-black', 'text-left', 'cursor-pointer']"
|
||||||
|
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
||||||
|
>{{ item.name }}</q-item-section
|
||||||
|
>
|
||||||
|
<q-item-section
|
||||||
|
@click="openDialog('load', item)"
|
||||||
|
:class="['text-black', 'text-left', 'cursor-pointer']"
|
||||||
|
left
|
||||||
|
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
||||||
|
>{{ item.description }}</q-item-section
|
||||||
|
>
|
||||||
|
<q-item-section top side>
|
||||||
|
<div class="text-grey-8 q-gutter-xs">
|
||||||
|
<q-btn size="12px" flat dense round icon="delete" @click="removeScene(item.name)" />
|
||||||
|
<q-btn
|
||||||
|
size="12px"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
round
|
||||||
|
icon="more_vert"
|
||||||
|
@click="openDialog('edit', item, index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
<!-- Fallback if list is empty -->
|
||||||
|
<div v-else class="q-pa-md text-grey text-center">
|
||||||
|
<div>No scenes available</div>
|
||||||
|
<q-btn
|
||||||
|
rounded
|
||||||
|
color="primary"
|
||||||
|
:class="['q-ma-md', 'text-bold', 'text-white']"
|
||||||
|
@click="openDialog('add')"
|
||||||
|
>Add First Scene</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { NotifyDialog } from 'src/composables/notify';
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import type { Scene } from 'src/models/Scene';
|
||||||
|
import type { Set } from 'src/models/Set';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { api } from 'boot/axios';
|
||||||
|
import { NotifyResponse } from 'src/composables/notify';
|
||||||
|
import Dialog from 'src/components/dialog/OkDialog.vue';
|
||||||
|
import { setValues } from 'src/services/websocket';
|
||||||
|
const $q = useQuasar();
|
||||||
|
const showDialog = ref(false);
|
||||||
|
const dialog = ref('');
|
||||||
|
const existsAlready = ref(false);
|
||||||
|
const editIndex = ref(-1);
|
||||||
|
const dialogLabel = ref('');
|
||||||
|
const newScene = reactive<Scene>({
|
||||||
|
name: '',
|
||||||
|
movingHead: false,
|
||||||
|
lightBar: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const scenes = ref<Scene[]>([]);
|
||||||
|
|
||||||
|
const quasarApi = axios.create({
|
||||||
|
baseURL: `http://localhost:9500`,
|
||||||
|
timeout: 10000,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
quasarApi
|
||||||
|
.get('/api/loadScenes')
|
||||||
|
.then((resp) => {
|
||||||
|
if (resp.data) {
|
||||||
|
scenes.value = resp.data;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => NotifyResponse($q, err.response.data.error, 'error'));
|
||||||
|
});
|
||||||
|
|
||||||
|
function removeScene(name: string) {
|
||||||
|
dialog.value = '';
|
||||||
|
NotifyDialog($q, 'Delete', 'Do you want to delete scene: ' + name, 'YES', 'NO')
|
||||||
|
.then((res) => {
|
||||||
|
if (res) {
|
||||||
|
scenes.value = scenes.value.filter((s) => s.name !== name);
|
||||||
|
|
||||||
|
quasarApi
|
||||||
|
.delete('/api/deleteScene', {
|
||||||
|
data: { name },
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
NotifyResponse($q, res.data, 'warning');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
NotifyResponse($q, err.response.data.error, 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => NotifyResponse($q, err.resp, 'warning'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDialog(dialogType: string, scene?: Scene, index?: number) {
|
||||||
|
switch (dialogType) {
|
||||||
|
case 'add':
|
||||||
|
dialog.value = 'add';
|
||||||
|
dialogLabel.value = 'Add Scene';
|
||||||
|
newScene.name = '';
|
||||||
|
newScene.movingHead = true;
|
||||||
|
newScene.lightBar = true;
|
||||||
|
showDialog.value = true;
|
||||||
|
break;
|
||||||
|
case 'edit':
|
||||||
|
if (!scene) return;
|
||||||
|
if (index === undefined) return;
|
||||||
|
dialog.value = 'edit';
|
||||||
|
dialogLabel.value = 'Update Scene';
|
||||||
|
editIndex.value = index;
|
||||||
|
Object.assign(newScene, JSON.parse(JSON.stringify(scene)));
|
||||||
|
showDialog.value = true;
|
||||||
|
break;
|
||||||
|
case 'load':
|
||||||
|
if (!scene) return;
|
||||||
|
dialog.value = 'load';
|
||||||
|
dialogLabel.value = 'Load Scene';
|
||||||
|
quasarApi
|
||||||
|
.post('/api/loadScene', scene)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
Object.assign(newScene, JSON.parse(JSON.stringify(res.data)));
|
||||||
|
showDialog.value = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => NotifyResponse($q, err.response.data.error, 'error'));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
showDialog.value = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveScene = async () => {
|
||||||
|
const sendValues = [];
|
||||||
|
|
||||||
|
if (!newScene.name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = scenes.value.some(
|
||||||
|
(item, index) => item.name === newScene.name && index !== editIndex.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (dialog.value) {
|
||||||
|
case 'add':
|
||||||
|
if (exists) {
|
||||||
|
existsAlready.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newScene.movingHead) {
|
||||||
|
sendValues.push({
|
||||||
|
path: 'MovingHead',
|
||||||
|
query: { depth: 0 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newScene.lightBar) {
|
||||||
|
sendValues.push({
|
||||||
|
path: 'LightBar',
|
||||||
|
query: { depth: 0 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendValues.length > 0) {
|
||||||
|
try {
|
||||||
|
const res = await api.post('/json_data', { get: sendValues });
|
||||||
|
newScene.values = res.data.get;
|
||||||
|
} catch (err) {
|
||||||
|
NotifyResponse($q, err as Error, 'error');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newScene.values = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
scenes.value = [...scenes.value, JSON.parse(JSON.stringify(newScene))];
|
||||||
|
|
||||||
|
// Sort alphabetically by scene name
|
||||||
|
scenes.value.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
quasarApi
|
||||||
|
.post('/api/saveScene', JSON.stringify(newScene))
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
NotifyResponse($q, res.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
NotifyResponse($q, err.response.data.error, 'error');
|
||||||
|
});
|
||||||
|
scenes.value = [...scenes.value];
|
||||||
|
break;
|
||||||
|
case 'edit':
|
||||||
|
if (exists) {
|
||||||
|
existsAlready.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newScene.movingHead) {
|
||||||
|
sendValues.push({
|
||||||
|
path: 'MovingHead',
|
||||||
|
query: { depth: 0 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newScene.lightBar) {
|
||||||
|
sendValues.push({
|
||||||
|
path: 'LightBar',
|
||||||
|
query: { depth: 0 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendValues.length > 0) {
|
||||||
|
try {
|
||||||
|
const res = await api.post('/json_data', { get: sendValues });
|
||||||
|
newScene.values = res.data.get;
|
||||||
|
} catch (err) {
|
||||||
|
NotifyResponse($q, err as Error, 'error');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newScene.values = [];
|
||||||
|
}
|
||||||
|
scenes.value.splice(editIndex.value, 1, JSON.parse(JSON.stringify(newScene)));
|
||||||
|
scenes.value.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
scenes.value = [...scenes.value];
|
||||||
|
|
||||||
|
quasarApi
|
||||||
|
.post('/api/saveScene', JSON.stringify(newScene))
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
NotifyResponse($q, res.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
NotifyResponse($q, err.response.data.error, 'error');
|
||||||
|
});
|
||||||
|
scenes.value = [...scenes.value];
|
||||||
|
break;
|
||||||
|
case 'load':
|
||||||
|
{
|
||||||
|
const setPaths = <Set[]>[];
|
||||||
|
|
||||||
|
if (newScene.movingHead) {
|
||||||
|
newScene.values?.forEach((element) => {
|
||||||
|
if (element.path && element.path.includes('MovingHead')) {
|
||||||
|
setPaths.push({ path: element.path, value: element.value });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newScene.lightBar) {
|
||||||
|
newScene.values?.forEach((element) => {
|
||||||
|
if (element.path && element.path.includes('LightBar')) {
|
||||||
|
setPaths.push({ path: element.path, value: element.value });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setValues(setPaths)
|
||||||
|
.then((response) => {
|
||||||
|
NotifyResponse($q, response);
|
||||||
|
})
|
||||||
|
.catch((err) => console.error(`Failed to load scene ${newScene.name}`, err));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.value = '';
|
||||||
|
showDialog.value = false;
|
||||||
|
};
|
||||||
|
</script>
|
@@ -1,292 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- new edit scene dialog-->
|
|
||||||
<q-dialog v-model="showDialog" persistent>
|
|
||||||
<q-card style="min-width: 350px">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-primary text-h6">{{ dialogLabel }}</div>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-card-section class="q-pt-none">
|
|
||||||
<q-input
|
|
||||||
class="q-mb-md"
|
|
||||||
dense
|
|
||||||
v-model="newScene.name"
|
|
||||||
placeholder="Name"
|
|
||||||
autofocus
|
|
||||||
:rules="[(val) => !!val || 'Field is required']"
|
|
||||||
@keyup.enter="saveScene"
|
|
||||||
/>
|
|
||||||
<q-input
|
|
||||||
dense
|
|
||||||
v-model="newScene.description"
|
|
||||||
placeholder="Description"
|
|
||||||
autofocus
|
|
||||||
@keyup.enter="saveScene"
|
|
||||||
/>
|
|
||||||
<div class="q-py-md">
|
|
||||||
<div class="q-gutter-sm">
|
|
||||||
<q-checkbox v-model="newScene.movingHead" label="Moving Head" />
|
|
||||||
<q-checkbox v-model="newScene.lightBar" label="Light Bar" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-card-actions align="right" class="text-primary">
|
|
||||||
<q-btn flat label="Cancel" v-close-popup />
|
|
||||||
<q-btn flat :label="dialogLabel" @click="saveScene" />
|
|
||||||
</q-card-actions>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
<Dialog
|
|
||||||
dialogLabel="Duplicate Scene"
|
|
||||||
:text="`Scene '${newScene.name}' exists already`"
|
|
||||||
:show-dialog="existsAlready"
|
|
||||||
v-on:update:show-dialog="existsAlready = $event"
|
|
||||||
/>
|
|
||||||
<q-list
|
|
||||||
bordered
|
|
||||||
v-if="items.length > 0"
|
|
||||||
class="q-mx-auto"
|
|
||||||
style="max-width: 100%; max-width: 500px"
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
rounded
|
|
||||||
color="primary"
|
|
||||||
:class="['q-ma-md', 'text-bold', 'text-white']"
|
|
||||||
@click="openAddDialog"
|
|
||||||
>Add New Scene</q-btn
|
|
||||||
>
|
|
||||||
<q-item class="row">
|
|
||||||
<q-item-section :class="['text-black', 'text-bold', 'col-5']">Name</q-item-section>
|
|
||||||
<q-item-section :class="['text-black', 'text-left', 'text-bold', 'text-left']"
|
|
||||||
>Description</q-item-section
|
|
||||||
>
|
|
||||||
</q-item>
|
|
||||||
<q-item
|
|
||||||
v-for="(item, index) in items"
|
|
||||||
:key="item.name"
|
|
||||||
bordered
|
|
||||||
style="border: 0.1px solid lightgray; border-radius: 5px; margin-bottom: 1px"
|
|
||||||
>
|
|
||||||
<q-item-section
|
|
||||||
@click="openLoadDialog(item.name)"
|
|
||||||
:class="['text-black', 'text-left', 'cursor-pointer']"
|
|
||||||
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
|
||||||
>{{ item.name }}</q-item-section
|
|
||||||
>
|
|
||||||
<q-item-section
|
|
||||||
@click="openLoadDialog(item.name)"
|
|
||||||
:class="['text-black', 'text-left', 'cursor-pointer']"
|
|
||||||
left
|
|
||||||
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
|
||||||
>{{ item.description }}</q-item-section
|
|
||||||
>
|
|
||||||
<q-item-section top side>
|
|
||||||
<div class="text-grey-8 q-gutter-xs">
|
|
||||||
<q-btn size="12px" flat dense round icon="delete" @click="removeScene(item.name)" />
|
|
||||||
<q-btn
|
|
||||||
size="12px"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
round
|
|
||||||
icon="more_vert"
|
|
||||||
@click="openEditDialog(item, index)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
<!-- Fallback if list is empty -->
|
|
||||||
<div v-else class="q-pa-md text-grey text-center">
|
|
||||||
<div>No scenes available</div>
|
|
||||||
<q-btn
|
|
||||||
rounded
|
|
||||||
color="primary"
|
|
||||||
:class="['q-ma-md', 'text-bold', 'text-white']"
|
|
||||||
@click="openAddDialog"
|
|
||||||
>Add First Scene</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { NotifyDialog } from 'src/composables/notify';
|
|
||||||
import { onMounted, ref } from 'vue';
|
|
||||||
import { useQuasar } from 'quasar';
|
|
||||||
import type { Scene } from 'src/models/Scene';
|
|
||||||
import type { Set } from 'src/models/Set';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { api } from 'boot/axios';
|
|
||||||
import { NotifyResponse } from 'src/composables/notify';
|
|
||||||
import Dialog from 'src/components/dialog/okDialog.vue';
|
|
||||||
import { setValues } from 'src/services/websocket';
|
|
||||||
const $q = useQuasar();
|
|
||||||
const showDialog = ref(false);
|
|
||||||
const isEdit = ref(false);
|
|
||||||
const isLoad = ref(false);
|
|
||||||
const existsAlready = ref(false);
|
|
||||||
const editIndex = ref(-1);
|
|
||||||
const dialogLabel = ref('');
|
|
||||||
const newScene = ref<Scene>({
|
|
||||||
name: '',
|
|
||||||
movingHead: false,
|
|
||||||
lightBar: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const items = ref<Scene[]>([]);
|
|
||||||
|
|
||||||
const quasarApi = axios.create({
|
|
||||||
baseURL: `http://localhost:9500`,
|
|
||||||
timeout: 10000,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
quasarApi
|
|
||||||
.get('/api/loadScenes')
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.data) {
|
|
||||||
items.value = resp.data as Scene[];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => NotifyResponse($q, err.response.data.error, 'error'));
|
|
||||||
});
|
|
||||||
|
|
||||||
function removeScene(name: string) {
|
|
||||||
NotifyDialog($q, 'Delete', 'Do you want to delete scene: ' + name, 'YES', 'NO')
|
|
||||||
.then((res) => {
|
|
||||||
if (res) {
|
|
||||||
items.value = items.value.filter((s) => s.name !== name);
|
|
||||||
quasarApi
|
|
||||||
.delete('/api/deleteScene', {
|
|
||||||
data: { name },
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res.data) {
|
|
||||||
NotifyResponse($q, res.data, 'warning');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
NotifyResponse($q, err.response.data.error, 'error');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => NotifyResponse($q, err.resp, 'warning'));
|
|
||||||
}
|
|
||||||
function openAddDialog() {
|
|
||||||
isEdit.value = false;
|
|
||||||
dialogLabel.value = 'Add Scene';
|
|
||||||
|
|
||||||
newScene.value = {
|
|
||||||
name: '',
|
|
||||||
movingHead: true,
|
|
||||||
lightBar: true,
|
|
||||||
};
|
|
||||||
showDialog.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openEditDialog(scene: Scene, index: number) {
|
|
||||||
isEdit.value = true;
|
|
||||||
dialogLabel.value = 'Update Scene';
|
|
||||||
console.log(76, scene);
|
|
||||||
newScene.value = { ...scene };
|
|
||||||
editIndex.value = index;
|
|
||||||
showDialog.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveScene = async () => {
|
|
||||||
if (!newScene.value.name) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const exists = items.value.some(
|
|
||||||
(item, index) => item.name === newScene.value.name && index !== editIndex.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (exists) {
|
|
||||||
if (isLoad.value) {
|
|
||||||
console.log(44, 'load');
|
|
||||||
const setPaths = <Set[]>[];
|
|
||||||
|
|
||||||
newScene.value.values?.forEach((element) => {
|
|
||||||
setPaths.push({ path: element.path, value: element.value });
|
|
||||||
});
|
|
||||||
|
|
||||||
setValues(setPaths)
|
|
||||||
.then((response) => NotifyResponse($q, response))
|
|
||||||
.catch((err) => console.error(`Failed to load scene ${newScene.value.name}`, err));
|
|
||||||
isLoad.value = false;
|
|
||||||
showDialog.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
existsAlready.value = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEdit.value && editIndex.value !== -1) {
|
|
||||||
items.value[editIndex.value] = { ...newScene.value };
|
|
||||||
} else {
|
|
||||||
items.value.push(newScene.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort alphabetically by scene name
|
|
||||||
items.value.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
|
|
||||||
const sendValues = [];
|
|
||||||
if (newScene.value.movingHead) {
|
|
||||||
sendValues.push({
|
|
||||||
path: 'MovingHead',
|
|
||||||
query: { depth: 0 },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newScene.value.lightBar) {
|
|
||||||
sendValues.push({
|
|
||||||
path: 'LightBar',
|
|
||||||
query: { depth: 0 },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log(33, sendValues);
|
|
||||||
if (sendValues.length > 0) {
|
|
||||||
try {
|
|
||||||
const res = await api.post('/json_data', { get: sendValues });
|
|
||||||
newScene.value.values = res.data.get;
|
|
||||||
} catch (err) {
|
|
||||||
NotifyResponse($q, err as Error, 'error');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newScene.value.values = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
quasarApi
|
|
||||||
.post('/api/saveScene', JSON.stringify(newScene.value))
|
|
||||||
.then((res) => {
|
|
||||||
if (res.data) {
|
|
||||||
NotifyResponse($q, res.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
NotifyResponse($q, err.response.data.error, 'error');
|
|
||||||
});
|
|
||||||
|
|
||||||
showDialog.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
function openLoadDialog(name: string) {
|
|
||||||
isLoad.value = true;
|
|
||||||
quasarApi
|
|
||||||
.post('/api/loadScene', { name })
|
|
||||||
.then((res) => {
|
|
||||||
if (res.data) {
|
|
||||||
const scene = res.data as Scene;
|
|
||||||
newScene.value = scene;
|
|
||||||
newScene.value.name = name;
|
|
||||||
showDialog.value = true;
|
|
||||||
isEdit.value = true;
|
|
||||||
dialogLabel.value = 'Load Scene';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => NotifyResponse($q, err.response.data.error, 'error'));
|
|
||||||
}
|
|
||||||
</script>
|
|
@@ -31,7 +31,7 @@ export function buildTree(subs: Subs): TreeNode[] {
|
|||||||
|
|
||||||
for (const item of subs) {
|
for (const item of subs) {
|
||||||
if (item.path) {
|
if (item.path) {
|
||||||
Subscriptions[item.path] = item;
|
addNewSubscription(item);
|
||||||
}
|
}
|
||||||
const pathParts = item.path?.split(':') ?? [];
|
const pathParts = item.path?.split(':') ?? [];
|
||||||
let current = root;
|
let current = root;
|
||||||
@@ -130,6 +130,11 @@ export function getSubscriptionsByPath(path: string) {
|
|||||||
return ref(Subscriptions[path]);
|
return ref(Subscriptions[path]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addNewSubscription(sub: Subscribe) {
|
||||||
|
if (!sub.path) return;
|
||||||
|
Subscriptions[sub.path] = sub;
|
||||||
|
}
|
||||||
|
|
||||||
export function getAllSubscriptions() {
|
export function getAllSubscriptions() {
|
||||||
return Object.values(Subscriptions);
|
return Object.values(Subscriptions);
|
||||||
}
|
}
|
||||||
@@ -145,8 +150,7 @@ export function updateValue(
|
|||||||
return computed({
|
return computed({
|
||||||
get() {
|
get() {
|
||||||
const sub = getSubscriptionsByPath(toggle?.value && path2 ? path2 : path1);
|
const sub = getSubscriptionsByPath(toggle?.value && path2 ? path2 : path1);
|
||||||
const value = sub?.value ? Number(sub.value.value ?? 0) : 0;
|
return sub?.value ? Number(sub.value.value ?? 0) : 0;
|
||||||
return value;
|
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
const baseValue = val;
|
const baseValue = val;
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
export type Publish = {
|
export type Publish = {
|
||||||
event: string;
|
event: string;
|
||||||
uuid?: string;
|
uuid: string;
|
||||||
path?: string;
|
path: string;
|
||||||
type?: string;
|
type: string;
|
||||||
value?: undefined;
|
value: undefined;
|
||||||
};
|
};
|
||||||
export type Pubs = Publish[];
|
export type Pubs = Publish[];
|
||||||
|
@@ -7,7 +7,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
children: [
|
children: [
|
||||||
{ path: '', component: () => import('pages/MainPage.vue') },
|
{ path: '', component: () => import('pages/MainPage.vue') },
|
||||||
{ path: '/data', component: () => import('pages/DataPage.vue') },
|
{ path: '/data', component: () => import('pages/DataPage.vue') },
|
||||||
{ path: '/scenes', component: () => import('components/scenes/ScenesTab.vue') },
|
{ path: '/scenes', component: () => import('components/scenes/ScenesPage.vue') },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@@ -3,18 +3,19 @@ import type { Publish } from 'src/models/Publish';
|
|||||||
import type { Request } from 'src/models/Request';
|
import type { Request } from 'src/models/Request';
|
||||||
import type { QVueGlobals } from 'quasar';
|
import type { QVueGlobals } from 'quasar';
|
||||||
import {
|
import {
|
||||||
getAllSubscriptions,
|
|
||||||
buildTree,
|
buildTree,
|
||||||
dbmData,
|
dbmData,
|
||||||
getSubscriptionsByUuid,
|
getSubscriptionsByPath,
|
||||||
|
getAllSubscriptions,
|
||||||
} from 'src/composables/dbm/dbmTree';
|
} from 'src/composables/dbm/dbmTree';
|
||||||
import { ref, reactive } from 'vue';
|
import { ref } from 'vue';
|
||||||
import type { Subs } from 'src/models/Subscribe';
|
import type { Subs } from 'src/models/Subscribe';
|
||||||
import type { Sets } from 'src/models/Set';
|
import type { Sets } from 'src/models/Set';
|
||||||
import type { PongMessage } from 'src/models/Pong';
|
import type { PongMessage } from 'src/models/Pong';
|
||||||
|
import { NotifyResponse } from 'src/composables/notify';
|
||||||
|
|
||||||
const pendingResponses = new Map<string, (data: Response | undefined) => void>();
|
const pendingResponses = new Map<string, (data: Response | undefined) => void>();
|
||||||
const lastKnownValues: Record<string, string> = reactive({});
|
//const lastKnownValues: Record<string, string> = reactive({});
|
||||||
|
|
||||||
export let socket: WebSocket | null = null;
|
export let socket: WebSocket | null = null;
|
||||||
const isConnected = ref(false);
|
const isConnected = ref(false);
|
||||||
@@ -88,29 +89,14 @@ export function initWebSocket(url: string, $q?: QVueGlobals) {
|
|||||||
pendingResponses.delete(id);
|
pendingResponses.delete(id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.publish) {
|
if (message.publish) {
|
||||||
(message.publish as Publish[]).forEach((pub) => {
|
(message.publish as Publish[]).forEach((pub) => {
|
||||||
const uuid = pub.uuid;
|
const sub = getSubscriptionsByPath(pub.path);
|
||||||
const value = pub.value ?? '';
|
if (sub.value && pub.value) {
|
||||||
|
sub.value.value = pub.value;
|
||||||
if (uuid === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldValue = lastKnownValues[uuid];
|
|
||||||
if (oldValue !== value) {
|
|
||||||
lastKnownValues[uuid] = value; // this is now reactive
|
|
||||||
if (pub.uuid) {
|
|
||||||
const existing = getSubscriptionsByUuid(pub.uuid);
|
|
||||||
|
|
||||||
if (existing.value) {
|
|
||||||
existing.value.value = value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
getAllSubscriptions().push({ value, uuid: uuid });
|
|
||||||
}
|
|
||||||
dbmData.splice(0, dbmData.length, ...buildTree(getAllSubscriptions())); // rebuild reactive tree
|
|
||||||
}
|
}
|
||||||
|
dbmData.splice(0, dbmData.length, ...buildTree(getAllSubscriptions())); // rebuild reactive tree
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,6 +143,26 @@ export function subscribe(data: Subs): Promise<Response | undefined> {
|
|||||||
return send({ subscribe: data });
|
return send({ subscribe: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function subscribeToPath(q: QVueGlobals, path: string) {
|
||||||
|
subscribe([
|
||||||
|
{
|
||||||
|
path: path,
|
||||||
|
depth: 0,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response);
|
||||||
|
if (response?.subscribe) {
|
||||||
|
dbmData.splice(0, dbmData.length, ...buildTree(response.subscribe));
|
||||||
|
} else {
|
||||||
|
NotifyResponse(q, response);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
NotifyResponse(q, err, 'error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function unsubscribe(data: Subs): Promise<Response | undefined> {
|
export function unsubscribe(data: Subs): Promise<Response | undefined> {
|
||||||
return send({ unsubscribe: data });
|
return send({ unsubscribe: data });
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user