Skip to content

Commit 94984dc

Browse files
committed
Introduce system configuration
1 parent 968a0c0 commit 94984dc

5 files changed

Lines changed: 159 additions & 0 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { HTTPConfig } from "@device-ui/lib/device/http_config";
2+
import { Formatter } from "@device-ui/lib/fmt/formatter";
3+
4+
// UNIX time configuration over HTTP.
5+
export class HTTPSystemTimeConfig extends HTTPConfig {
6+
// Initialize.
7+
constructor(formatter: Formatter, url: string) {
8+
super(formatter, url);
9+
}
10+
11+
// Non-operational reset.
12+
override async reset(): Promise<Error | null> {
13+
return null;
14+
}
15+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { describe, test, expect } from "vitest";
2+
3+
import { FieldFormatter } from "@device-ui/lib/fmt/field_formatter";
4+
5+
describe("Field Formatter", () => {
6+
test("format empty data", () => {
7+
const data = new TextEncoder().encode("");
8+
const f = new FieldFormatter("timestamp");
9+
10+
const result = f.format(data);
11+
expect(result.data).not.toBeNull();
12+
expect(result.data).toEqual({ timestamp: "" });
13+
expect(result.error).toBeNull();
14+
});
15+
16+
test("format text data", () => {
17+
const data = new TextEncoder().encode("1733233869");
18+
const f = new FieldFormatter("timestamp");
19+
20+
const result = f.format(data);
21+
expect(result.data).not.toBeNull();
22+
expect(result.data).toEqual({ timestamp: "1733233869" });
23+
expect(result.error).toBeNull();
24+
});
25+
26+
test("format with different key name", () => {
27+
const data = new TextEncoder().encode("OK");
28+
const f = new FieldFormatter("status");
29+
30+
const result = f.format(data);
31+
expect(result.data).toEqual({ status: "OK" });
32+
expect(result.error).toBeNull();
33+
});
34+
35+
test("format binary data", () => {
36+
const data = new Uint8Array([72, 101, 108, 108, 111]);
37+
const f = new FieldFormatter("message");
38+
39+
const result = f.format(data);
40+
expect(result.data).toEqual({ message: "Hello" });
41+
expect(result.error).toBeNull();
42+
});
43+
});

‎src/lib/fmt/field_formatter.ts‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Formatter } from "@device-ui/lib/fmt/formatter";
2+
3+
// Format a single key-value pair.
4+
export class FieldFormatter implements Formatter {
5+
// Initialize.
6+
constructor(private readonly key: string) {}
7+
8+
format(data: Uint8Array): {
9+
data: Record<string, any> | null;
10+
error: Error | null;
11+
} {
12+
let ret: Record<string, any> | null = null;
13+
let err: Error | null = null;
14+
15+
try {
16+
const text = new TextDecoder().decode(data);
17+
ret = {};
18+
ret[this.key] = text;
19+
} catch (error) {
20+
err = error instanceof Error ? error : new Error(String(error));
21+
}
22+
23+
return { data: ret, error: err };
24+
}
25+
}

‎src/ui/preact/system_status_component.tsx‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { DataStore } from "@device-ui/lib/device/data_store";
55
import { TimeOps } from "@device-ui/lib/algo/time_ops";
66
import { ConnectionHandler } from "@device-ui/lib/device/connection_handler";
77
import { FanoutConnectionHandler } from "@device-ui/lib/device/fanout_connection_handler";
8+
import { Notificator } from "@device-ui/lib/system/notificator";
9+
import { Config } from "@device-ui/lib/device/config";
10+
11+
import { FormConfigComponent } from "@device-ui/ui/preact/form_config_component";
812

913
import "@device-ui/ui/preact/system_status_component.css";
1014

@@ -17,6 +21,15 @@ export type SystemStatusComponentProps = {
1721

1822
// Connection handler to receive the device connection status.
1923
connectionHandler: FanoutConnectionHandler;
24+
25+
// Notificator to send various notifications.
26+
notificator: Notificator;
27+
28+
// Config to configure the mDNS.
29+
mDNSConfig: Config;
30+
31+
// Config to configure the UNIX time.
32+
systemTimeConfig: Config;
2033
};
2134

2235
class TelemetryData {
@@ -45,6 +58,8 @@ class RegistrationData {
4558
type SystemStatusComponentState = {
4659
expanded: boolean;
4760
connected: boolean;
61+
enableConfiguration: boolean;
62+
config: Config | null;
4863
telemetry: TelemetryData | null;
4964
registration: RegistrationData | null;
5065
showCopied: boolean;
@@ -79,6 +94,8 @@ export class SystemStatusComponent
7994
this.state = {
8095
expanded: false,
8196
connected: false,
97+
enableConfiguration: false,
98+
config: null,
8299
telemetry: null,
83100
registration: null,
84101
showCopied: false,
@@ -137,6 +154,8 @@ export class SystemStatusComponent
137154
this.setState({
138155
expanded: false,
139156
connected: false,
157+
enableConfiguration: false,
158+
config: null,
140159
telemetry: null,
141160
registration: null,
142161
showCopied: false,
@@ -268,8 +287,38 @@ export class SystemStatusComponent
268287
</>
269288
)}
270289
</div>
290+
{/* Configuration mode */}
291+
{this.state.enableConfiguration && (
292+
<FormConfigComponent
293+
config={this.state.config!}
294+
onClose={this.handleConfigEnd}
295+
notificator={this.props.notificator}
296+
/>
297+
)}
271298
</div>
272299
)}
300+
301+
{this.state.expanded && !this.state.enableConfiguration && (
302+
<>
303+
<div className="config-button-container">
304+
<button
305+
onClick={this.handleConfigBeginMdns}
306+
className="config-button"
307+
>
308+
Configure mDNS
309+
</button>
310+
</div>
311+
312+
<div className="config-button-container">
313+
<button
314+
onClick={this.handleConfigBeginSystemTime}
315+
className="config-button"
316+
>
317+
Configure System Time
318+
</button>
319+
</div>
320+
</>
321+
)}
273322
</div>
274323
);
275324
}
@@ -313,6 +362,31 @@ export class SystemStatusComponent
313362
});
314363
};
315364

365+
private handleConfigBeginMdns = () => {
366+
this.setState({
367+
...this.state,
368+
enableConfiguration: true,
369+
config: this.props.mDNSConfig,
370+
});
371+
};
372+
373+
private handleConfigBeginSystemTime = () => {
374+
this.setState({
375+
...this.state,
376+
enableConfiguration: true,
377+
config: this.props.systemTimeConfig,
378+
});
379+
};
380+
381+
private handleConfigEnd = () => {
382+
this.setState({
383+
...this.state,
384+
expanded: false,
385+
enableConfiguration: false,
386+
config: null,
387+
});
388+
};
389+
316390
private copyDeviceId = async (e: Event) => {
317391
e.stopPropagation(); // Prevent card expansion/collapse
318392

‎vite.config.base.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@ export function createBaseConfig(deviceIP = "", projectConfig = {}) {
4242
"/api/v1/config/sensor/analog": createProxyConfig(deviceIP),
4343
"/api/v1/system/reboot": createProxyConfig(deviceIP),
4444
"/api/v1/system/locate": createProxyConfig(deviceIP),
45+
"/api/v1/system/time": createProxyConfig(deviceIP),
4546
"/api/v1/config/wifi/sta": createProxyConfig(deviceIP),
4647
"/api/v1/config/wifi/ap": createProxyConfig(deviceIP),
48+
"/api/v1/config/mdns": createProxyConfig(deviceIP),
4749
},
4850
},
4951
build: {

0 commit comments

Comments
 (0)