Files
genieacs/ui/components/summon-button.ts
T
Zaid Humaimidi 6f3ce6f7c8 refactor: replace array-based expressions with typed class hierarchy
Replace the old Lisp-like array expression representation
(["AND", ["PARAM", ...], ...]) with a class hierarchy rooted in
an abstract Expression class (Literal, Parameter, Binary, Unary,
FunctionCall, Conditional). This gives compile-time type safety
to expression construction, evaluation, and pattern matching.

Key changes:
- New lib/common/expression.ts defines the class hierarchy with
  map/evaluate/toString methods
- Rewrite parser as hand-rolled recursive descent, removing the
  parsimmon dependency
- Extend Path with a colon separator to unify parameter identity
  and attributes into a single address space (e.g.
  "Device.WiFi.SSID.1.Name:writable"). The colon field tracks
  how many trailing segments are attribute qualifiers. PathSet
  rewritten with separate param/attr segment indexes
- Flatten device attribute representation in the UI to
  colon-delimited keys mirroring the new path syntax
2026-03-12 14:58:20 -07:00

87 lines
2.9 KiB
TypeScript

import { ClosureComponent } from "mithril";
import { m } from "../components.ts";
import * as taskQueue from "../task-queue.ts";
import * as store from "../store.ts";
import * as notifications from "../notifications.ts";
import Expression from "../../lib/common/expression.ts";
import { FlatDevice } from "../../lib/ui/db.ts";
interface Attrs {
device: FlatDevice;
parameters: Record<string, Expression>;
}
const component: ClosureComponent<Attrs> = () => {
return {
view: (vnode) => {
const device = vnode.attrs.device;
return m(
"button",
{
class:
"px-2.5 py-1.5 border border-transparent text-xs font-medium rounded-sm shadow-xs text-white bg-cyan-600 hover:bg-cyan-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-cyan-500",
title: "Initiate session and refresh basic parameters",
onclick: (e) => {
e.target.disabled = true;
const params = Object.values(vnode.attrs["parameters"])
.map((exp) => {
if (exp instanceof Expression.Parameter)
return exp.path.toString();
return null;
})
.filter((exp) => !!exp);
const task = {
name: "getParameterValues",
parameterNames: params,
device: device["DeviceID.ID"] as string,
};
taskQueue
.commit(
[task],
(deviceId, err, connectionRequestStatus, tasks2) => {
if (err) {
notifications.push("error", `${deviceId}: ${err.message}`);
return;
}
for (const t of tasks2)
if (t.status === "stale") taskQueue.deleteTask(t);
if (connectionRequestStatus !== "OK") {
notifications.push(
"error",
`${deviceId}: ${connectionRequestStatus}`,
);
} else if (tasks2[0].status === "stale") {
notifications.push(
"error",
`${deviceId}: No contact from device`,
);
} else if (tasks2[0].status === "fault") {
notifications.push("error", `${deviceId}: Refresh faulted`);
} else {
notifications.push("success", `${deviceId}: Summoned`);
}
},
)
.then(() => {
e.target.disabled = false;
store.setTimestamp(Date.now());
})
.catch((err) => {
e.target.disabled = false;
notifications.push("error", err.message);
});
},
},
"Summon",
);
},
};
};
export default component;