From e263828a1892ac1828eda3c6f7b9b8b3c9580a8c Mon Sep 17 00:00:00 2001
From: DTTerastar
Date: Wed, 11 Sep 2024 17:49:05 -0400
Subject: [PATCH] WIP
---
src/Controllers/DeviceController.cs | 20 +++--
src/ui/src/lib/NodeActions.svelte | 14 ++--
src/ui/src/lib/NodeSettings.svelte | 31 +++-----
src/ui/src/lib/device.ts | 14 ++++
src/ui/src/lib/node.ts | 47 ++++++++++++
src/ui/src/lib/state.ts | 26 +++++++
src/ui/src/lib/stores.ts | 31 +++-----
src/ui/src/lib/types.ts | 15 ++--
src/ui/src/routes/devices/[id]/+page.svelte | 17 +++--
src/ui/src/routes/devices/[id]/+page.ts | 27 ++++---
src/ui/src/routes/nodes/[id]/+page.svelte | 83 +++++----------------
src/ui/src/routes/nodes/[id]/+page.ts | 2 +-
12 files changed, 176 insertions(+), 151 deletions(-)
create mode 100644 src/ui/src/lib/device.ts
create mode 100644 src/ui/src/lib/node.ts
create mode 100644 src/ui/src/lib/state.ts
diff --git a/src/Controllers/DeviceController.cs b/src/Controllers/DeviceController.cs
index 2f922128..42a74896 100644
--- a/src/Controllers/DeviceController.cs
+++ b/src/Controllers/DeviceController.cs
@@ -19,14 +19,20 @@ public DeviceController(ILogger logger, DeviceSettingsStore de
_state = state;
}
+ [HttpGet("{id}/details")]
+ public async Task>> Details(string id)
+ {
+ if (_state.Devices.TryGetValue(id, out var device))
+ return device.GetDetails().ToList();
+ return new List>();
+ }
+
[HttpGet("{id}/settings")]
- public DeviceSettingsDetails Get(string id)
+ public Task Get(string id)
{
- var deviceSettings = _deviceSettingsStore.Get(id);
- var details = new List>();
- if (deviceSettings?.Id != null && _state.Devices.TryGetValue(deviceSettings.Id, out var device))
- details.AddRange(device.GetDetails());
- return new DeviceSettingsDetails(deviceSettings ?? new DeviceSettings { Id = id, OriginalId = id }, details);
+ var settings = _deviceSettingsStore.Get(id);
+ settings ??= new DeviceSettings { OriginalId = id, Id = id };
+ return Task.FromResult(settings);
}
[HttpPut("{id}/settings")]
@@ -35,6 +41,4 @@ public async Task Set(string id, [FromBody] DeviceSettings value)
await _deviceSettingsStore.Set(id, value);
}
}
-
- public readonly record struct DeviceSettingsDetails(DeviceSettings? settings, IList> details);
}
diff --git a/src/ui/src/lib/NodeActions.svelte b/src/ui/src/lib/NodeActions.svelte
index 4a092f7d..c3ab6ee7 100644
--- a/src/ui/src/lib/NodeActions.svelte
+++ b/src/ui/src/lib/NodeActions.svelte
@@ -5,18 +5,18 @@
import { getModalStore, getToastStore, type ToastSettings } from '@skeletonlabs/skeleton';
import { updateMethod, flavor, version, artifact, flavorNames } from '$lib/firmware';
import Firmware from '$lib/modals/Firmware.svelte';
+ import { restartNode, updateNodeSelf } from '$lib/node';
const modalStore = getModalStore();
const toastStore = getToastStore();
async function onRestart(i: Node) {
try {
- var response = await fetch(`${base}/api/node/${i.id}/restart`, { method: 'POST' });
- if (response.status != 200) throw new Error(response.statusText);
+ await restartNode(i.id);
toastStore.trigger({ message: i.name + ' asked to reboot', background: 'variant-filled-primary' });
} catch (e) {
console.log(e);
- toastStore.trigger({ message: e, background: 'variant-filled-error' });
+ toastStore.trigger({ message: e instanceof Error ? e.message : String(e), background: 'variant-filled-error' });
}
}
@@ -44,12 +44,8 @@
});
} else {
if (i) {
- fetch(`${base}/api/node/${i.id}/update`, {
- method: 'POST',
- body: ''
- })
- .then((response) => {
- if (response.status != 200) throw new Error(response.statusText);
+ updateNodeSelf(i.id)
+ .then(() => {
const t: ToastSettings = { message: (i.name ?? i.id) + ' asked to update itself', background: 'variant-filled-primary' };
toastStore.trigger(t);
})
diff --git a/src/ui/src/lib/NodeSettings.svelte b/src/ui/src/lib/NodeSettings.svelte
index 32d72df6..ffa71c31 100644
--- a/src/ui/src/lib/NodeSettings.svelte
+++ b/src/ui/src/lib/NodeSettings.svelte
@@ -1,31 +1,22 @@
@@ -62,7 +53,7 @@
Max Distance
-
+
diff --git a/src/ui/src/lib/device.ts b/src/ui/src/lib/device.ts
new file mode 100644
index 00000000..a6d50b1b
--- /dev/null
+++ b/src/ui/src/lib/device.ts
@@ -0,0 +1,14 @@
+import { base } from '$app/paths';
+import type { DeviceSetting, DeviceDetail } from './types';
+
+export async function fetchDeviceSettings(fetch, id: string): Promise {
+ const response = await fetch(`${base}/api/device/${id}/settings`);
+ if (!response.ok) throw new Error("Something went wrong loading device details (error="+response.status+" "+response.statusText+")");
+ return await response.json();
+}
+
+export async function fetchDeviceDetails(id: string): Promise {
+ const response = await fetch(`${base}/api/device/${id}/details`);
+ if (!response.ok) throw new Error("Something went wrong loading device details (error="+response.status+" "+response.statusText+")");
+ return await response.json();
+}
diff --git a/src/ui/src/lib/node.ts b/src/ui/src/lib/node.ts
new file mode 100644
index 00000000..7bc8db8e
--- /dev/null
+++ b/src/ui/src/lib/node.ts
@@ -0,0 +1,47 @@
+import { base } from '$app/paths';
+import type { Settings, Node, NodeSetting } from './types';
+
+export async function loadSettings(id: string): Promise {
+ const response = await fetch(`${base}/api/node/${id}/settings`);
+ if (!response.ok) throw new Error("Something went wrong loading settings (error="+response.status+" "+response.statusText+")");
+ return await response.json();
+}
+
+export async function saveSettings(newSettings: Settings): Promise {
+ const response = await fetch(`${base}/api/node/${newSettings.id}/settings`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(newSettings),
+ });
+ if (!response.ok) throw new Error("Something went wrong loading settings (error="+response.status+" "+response.statusText+")");
+ return await response.json();
+}
+
+export async function restartNode(nodeId: string): Promise {
+ const response = await fetch(`${base}/api/node/${nodeId}/restart`, { method: 'POST' });
+ if (!response.ok) throw new Error(response.statusText);
+}
+
+export async function updateNodeSelf(nodeId: string): Promise {
+ const response = await fetch(`${base}/api/node/${nodeId}/update`, { method: 'POST' });
+ if (!response.ok) throw new Error(response.statusText);
+}
+
+export async function saveNodeSettings(nodeId: string, settings: NodeSetting): Promise {
+ const response = await fetch(`${base}/api/node/${nodeId}/settings`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(settings)
+ });
+ if (!response.ok) throw new Error(response.statusText);
+}
+
+export async function fetchNode(nodeId: string): Promise {
+ const response = await fetch(`${base}/api/node/${nodeId}/settings`);
+ if (!response.ok) throw new Error(response.statusText);
+ return await response.json();
+}
\ No newline at end of file
diff --git a/src/ui/src/lib/state.ts b/src/ui/src/lib/state.ts
new file mode 100644
index 00000000..82ca3016
--- /dev/null
+++ b/src/ui/src/lib/state.ts
@@ -0,0 +1,26 @@
+import { base } from '$app/paths';
+import type { Config, CalibrationData, Device } from './types';
+
+export async function fetchConfig(): Promise {
+ const response = await fetch(`${base}/api/state/config`);
+ if (!response.ok) throw new Error("Something went wrong loading config (error="+response.status+" "+response.statusText+")");
+ return await response.json();
+}
+
+export async function fetchCalibrationState(): Promise {
+ const response = await fetch(`${base}/api/state/calibration`);
+ if (!response.ok) throw new Error("Something went wrong loading calibration state (error="+response.status+" "+response.statusText+")");
+ return await response.json();
+}
+
+export async function fetchDeviceState(): Promise {
+ const response = await fetch(`${base}/api/state/devices`);
+ if (!response.ok) throw new Error("Something went wrong loading device state (error="+response.status+" "+response.statusText+")");
+ return await response.json();
+}
+
+export async function fetchNodeState(includeTele: boolean = true): Promise {
+ const response = await fetch(`${base}/api/state/nodes?includeTele=${includeTele}`);
+ if (!response.ok) throw new Error("Something went wrong loading node state (error="+response.status+" "+response.statusText+")");
+ return await response.json();
+}
\ No newline at end of file
diff --git a/src/ui/src/lib/stores.ts b/src/ui/src/lib/stores.ts
index 27ace365..939c6329 100644
--- a/src/ui/src/lib/stores.ts
+++ b/src/ui/src/lib/stores.ts
@@ -1,6 +1,8 @@
import { readable, writable, derived } from 'svelte/store';
import { base } from '$app/paths';
-import type { Device, Config, Node, CalibrationResponse } from './types';
+import type { Device, Config, Node, NodeSettings, CalibrationResponse } from './types';
+import { loadSettings, saveSettings } from './node';
+import { fetchConfig, fetchCalibrationState, fetchDeviceState, fetchNodeState } from './state';
export const showAll: SvelteStore = writable(false);
export const config = writable();
@@ -37,8 +39,8 @@ let socket: WebSocket;
export const history = writable(['/']);
async function getConfig() {
- const response = await fetch(`${base}/api/state/config`);
- config.set(await response.json());
+ const data = await fetchConfig();
+ config.set(data);
}
getConfig();
@@ -112,8 +114,7 @@ export const nodes = readable([], function start(set) {
const interval = setInterval(() => {
if (outstanding) return;
outstanding = true;
- fetch(`${base}/api/state/nodes?includeTele=true`)
- .then((d) => d.json())
+ fetchNodeState()
.then((r) => {
outstanding = false;
errors = 0;
@@ -133,15 +134,12 @@ export const nodes = readable([], function start(set) {
export const calibration = readable({matrix: {}}, function start(set) {
async function fetchAndSet() {
- const response = await fetch(`${base}/api/state/calibration`);
- var data = await response.json();
+ const data = await fetchCalibrationState();
set(data);
}
fetchAndSet();
- const interval = setInterval(() => {
- fetchAndSet();
- }, 1000);
+ const interval = setInterval(fetchAndSet, 1000);
return function stop() {
clearInterval(interval);
@@ -156,20 +154,11 @@ export const settings = (() => {
set,
update,
load: async () => {
- const response = await fetch(`${base}/api/node/*/settings`);
- if (!response.ok) throw new Error("Something went wrong loading settings (error="+response.status+" "+response.statusText+")");
- const data = await response.json();
+ const data = await loadSettings("*");
set(data);
},
save: async (newSettings: Settings) => {
- const response = await fetch(`${base}/api/node/*/settings`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(newSettings),
- });
- const data = await response.json();
+ const data = await saveSettings(newSettings);
set(data);
},
};
diff --git a/src/ui/src/lib/types.ts b/src/ui/src/lib/types.ts
index 885a05fe..233108b1 100644
--- a/src/ui/src/lib/types.ts
+++ b/src/ui/src/lib/types.ts
@@ -55,14 +55,7 @@ export interface Config {
devices: Device[];
}
-export type NodeSetting = {
- id: string | null;
- name: string | null;
- absorption: number | null;
- rx_adj_rssi: number | null;
- tx_ref_rssi: number | null;
- max_distance: number | null;
-};
+export type DeviceDetail = Array<{ key: string; value: string }>;
export type DeviceSetting = {
originalId: string;
@@ -142,7 +135,9 @@ export interface CalibrationResponse {
matrix: CalibrationMatrix;
}
-export type NodeSettings = {
+export type NodeSetting = {
+ id: string | null;
+ name: string | null;
updating: {
autoUpdate: boolean;
preRelease: boolean;
@@ -166,7 +161,7 @@ export type NodeSettings = {
calibration: {
rssiAt1m: number | null;
rssiAdjustment: number | null;
- absorptionFactor: number | null;
+ absorption: number | null;
iBeaconRssiAt1m: number | null;
};
};
diff --git a/src/ui/src/routes/devices/[id]/+page.svelte b/src/ui/src/routes/devices/[id]/+page.svelte
index dd6ce578..052dd381 100644
--- a/src/ui/src/routes/devices/[id]/+page.svelte
+++ b/src/ui/src/routes/devices/[id]/+page.svelte
@@ -2,22 +2,27 @@
import { base } from '$app/paths';
import { devices } from '$lib/stores';
import { readable } from 'svelte/store';
- import type { DeviceSetting } from '$lib/types';
+ import type { DeviceSetting, DeviceDetail } from '$lib/types';
import { Accordion, AccordionItem } from '@skeletonlabs/skeleton';
+ import { fetchDeviceDetails } from '$lib/device';
import Map from '$lib/Map.svelte';
import DeviceDetailTabs from '$lib/DeviceDetailTabs.svelte';
import DeviceSettings from '$lib/DeviceSettings.svelte';
export let tab = 'map';
- export let data: { settings?: DeviceSetting } = {};
- $: device = $devices.find((d) => d.id === data.settings?.id);
+ export let data: { id: string, settings: DeviceSetting };
+ $: device = $devices.find((d) => d.id === data.id);
- export const deviceDetails = readable([], (set) => {
+ export const deviceDetails = readable([], (set) => {
async function fetchAndSet() {
try {
- const response = await fetch(`${base}/api/device/${data.settings?.id}`);
- const result = await response.json();
+ const id = data.id;
+ if (!id) {
+ console.error('No device id');
+ return;
+ }
+ const result = await fetchDeviceDetails(id);
set(result.details);
} catch (ex) {
console.error(ex);
diff --git a/src/ui/src/routes/devices/[id]/+page.ts b/src/ui/src/routes/devices/[id]/+page.ts
index 6743de77..0ac0c4b3 100644
--- a/src/ui/src/routes/devices/[id]/+page.ts
+++ b/src/ui/src/routes/devices/[id]/+page.ts
@@ -1,13 +1,16 @@
-import { base } from '$app/paths';
+import { error } from '@sveltejs/kit';
+import type { PageLoad } from './$types';
+import { fetchDeviceSettings } from '$lib/device'
-export async function load({ fetch, params }) {
- return await fetch(`${base}/api/device/${params.id}`)
- .then((response) => {
- if (response.status != 200) throw new Error(response.statusText);
- var data = response.json();
- return data;
- })
- .catch((e) => {
- return { settings: { originalId: params.id, id: null, name: null, 'rssi@1m': null, error: e } };
- });
-}
+export const load: PageLoad = async ({ fetch, params }) => {
+ if (!params.id) {
+ throw error(400, 'No device id');
+ }
+ try {
+ var settings = fetchDeviceSettings(fetch, params.id);
+ return { id: params.id, settings: settings };
+ }
+ catch (e) {
+ return { settings: { originalId: params.id, id: null, name: null, 'rssi@1m': null, error: e } };
+ }
+};
diff --git a/src/ui/src/routes/nodes/[id]/+page.svelte b/src/ui/src/routes/nodes/[id]/+page.svelte
index d50bd5c4..67520027 100644
--- a/src/ui/src/routes/nodes/[id]/+page.svelte
+++ b/src/ui/src/routes/nodes/[id]/+page.svelte
@@ -1,72 +1,27 @@
-
- ESPresense Companion: Map
-
-
-
-
-
-
- {#if floorId !== 'settings'}
-
- {/if}
- {#if floorId === 'settings'}
-
- {/if}
-
-
-
+{#if error}
+ {error}
+{:else if node}
+ {node.name ?? node.id}
+
+{:else}
+ Loading...
+{/if}
diff --git a/src/ui/src/routes/nodes/[id]/+page.ts b/src/ui/src/routes/nodes/[id]/+page.ts
index dfb014c9..df7c64bb 100644
--- a/src/ui/src/routes/nodes/[id]/+page.ts
+++ b/src/ui/src/routes/nodes/[id]/+page.ts
@@ -1,7 +1,7 @@
import { base } from '$app/paths';
export async function load({ fetch, params }) {
- return await fetch(`${base}/api/node/${params.id}`)
+ return await fetch(`${base}/api/node/${params.id}/settings`)
.then((response) => {
if (response.status != 200) throw new Error(response.statusText);
var data = response.json();