You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
3.7 KiB
154 lines
3.7 KiB
<script lang="ts" context="module">
|
|
import { fetchUIState } from "$lib/client/lucifer";
|
|
import type Device from "$lib/models/device";
|
|
import type { UIStatePatch } from "$lib/models/uistate";
|
|
import type UIState from "$lib/models/uistate";
|
|
import { getContext, onMount, setContext } from "svelte";
|
|
import { derived, writable, type Readable } from "svelte/store";
|
|
|
|
const ctxKey = {ctx: "StateContext"};
|
|
|
|
export interface StateContextData {
|
|
reload(): Promise<void>
|
|
state: Readable<UIState>
|
|
error: Readable<string | null>
|
|
deviceList: Readable<Device[]>
|
|
}
|
|
|
|
export function getStateContext(): StateContextData {
|
|
return getContext(ctxKey) as StateContextData;
|
|
}
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
const state = writable<UIState>({
|
|
assignments: {},
|
|
devices: {},
|
|
script: {},
|
|
});
|
|
const error = writable<string | null>(null);
|
|
|
|
const deviceList = derived(state, state => {
|
|
if (state == null) {
|
|
return [];
|
|
}
|
|
|
|
return Object.keys(state.devices)
|
|
.map(k => state.devices[k])
|
|
.sort((a,b) => a.id.localeCompare(b.id));
|
|
})
|
|
|
|
|
|
|
|
async function reload() {
|
|
error.set(null);
|
|
|
|
try {
|
|
const newState = await fetchUIState();
|
|
state.set(newState);
|
|
} catch(err) {
|
|
error.set(err?.toString ? err.toString() : String(err))
|
|
}
|
|
}
|
|
|
|
async function connectSocket() {
|
|
let url = `ws://${window.location.host}/subscribe`;
|
|
if (import.meta.env.VITE_LUCIFER4_BACKEND_URL != null) {
|
|
url = import.meta.env.VITE_LUCIFER4_BACKEND_URL.replace("http", "ws") +"/subscribe";
|
|
}
|
|
|
|
const socket = new WebSocket(url);
|
|
|
|
socket.onmessage = (msg) => {
|
|
const patch: UIStatePatch = JSON.parse(msg.data);
|
|
|
|
state.update(s => {
|
|
if (patch.device) {
|
|
if (patch.device.delete) {
|
|
const devices = {...s.devices};
|
|
delete devices[patch.device.id];
|
|
return {...s, devices};
|
|
} else {
|
|
return {
|
|
...s,
|
|
devices: {
|
|
...s.devices,
|
|
[patch.device.id]: {
|
|
...s.devices[patch.device.id],
|
|
...patch.device,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (patch.assignment) {
|
|
if (patch.assignment.delete) {
|
|
const assignments = {...s.assignments};
|
|
delete assignments[patch.assignment.id];
|
|
return {...s, assignments};
|
|
} else {
|
|
return {
|
|
...s,
|
|
assignments: {
|
|
...s.assignments,
|
|
[patch.assignment.id]: {
|
|
...s.assignments[patch.assignment.id],
|
|
...patch.assignment,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (patch.script) {
|
|
if (patch.script.delete) {
|
|
const scripts = {...s.scripts};
|
|
delete scripts[patch.script.id];
|
|
return {...s, scripts};
|
|
} else {
|
|
return {
|
|
...s,
|
|
scripts: {
|
|
...s.scripts,
|
|
[patch.script.id]: {
|
|
...s.scripts[patch.script.id],
|
|
...patch.script,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return s;
|
|
})
|
|
}
|
|
|
|
socket.onerror = err => {
|
|
console.warn("Socket failed:", err);
|
|
socket.close();
|
|
}
|
|
|
|
socket.onclose = () => {
|
|
setTimeout(() => connectSocket(), 3000);
|
|
}
|
|
}
|
|
|
|
onMount(() => {
|
|
reload();
|
|
const interval = setInterval(reload, 60000);
|
|
|
|
connectSocket();
|
|
|
|
return () => clearInterval(interval);
|
|
});
|
|
|
|
setContext<StateContextData>(ctxKey, {
|
|
reload,
|
|
error: { subscribe: error.subscribe },
|
|
state: { subscribe: state.subscribe },
|
|
deviceList,
|
|
});
|
|
</script>
|
|
|
|
<slot></slot>
|