@camstack/types 1.1.20 → 1.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon/base-addon.d.ts +30 -4
- package/dist/addon/per-node-store.d.ts +68 -0
- package/dist/addon.js +1 -1
- package/dist/addon.mjs +1 -1
- package/dist/capabilities/camera-streams.cap.d.ts +5 -5
- package/dist/capabilities/decoder.cap.d.ts +2 -0
- package/dist/capabilities/device-manager.cap.d.ts +269 -6
- package/dist/capabilities/index.d.ts +3 -1
- package/dist/capabilities/metrics-provider.cap.d.ts +2 -2
- package/dist/capabilities/motion-detection.cap.d.ts +20 -2
- package/dist/capabilities/pet-feeder.cap.d.ts +185 -0
- package/dist/capabilities/pipeline-executor.cap.d.ts +29 -2
- package/dist/capabilities/pipeline-orchestrator.cap.d.ts +7 -3
- package/dist/capabilities/platform-probe.cap.d.ts +1 -1
- package/dist/capabilities/schemas/streaming-shared.d.ts +2 -2
- package/dist/capabilities/stream-broker.cap.d.ts +1 -1
- package/dist/device/device-management.d.ts +61 -2
- package/dist/device/device-type.d.ts +8 -1
- package/dist/device/index.d.ts +1 -1
- package/dist/expression/ast.d.ts +56 -0
- package/dist/expression/builtins.d.ts +19 -0
- package/dist/expression/compile.d.ts +17 -0
- package/dist/expression/errors.d.ts +16 -0
- package/dist/expression/evaluator.d.ts +14 -0
- package/dist/expression/index.d.ts +25 -0
- package/dist/expression/limits.d.ts +30 -0
- package/dist/expression/link-expression.d.ts +44 -0
- package/dist/expression/parser.d.ts +12 -0
- package/dist/expression/tokenizer.d.ts +38 -0
- package/dist/generated/addon-api.d.ts +616 -12
- package/dist/generated/cap-status-types.d.ts +3 -1
- package/dist/generated/capability-router-map.d.ts +5 -2
- package/dist/generated/device-local-state.d.ts +3 -0
- package/dist/generated/device-proxy.d.ts +4 -1
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/system-proxy.d.ts +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1555 -37
- package/dist/index.mjs +1523 -38
- package/dist/interfaces/addon.d.ts +15 -2
- package/dist/interfaces/agent.d.ts +14 -0
- package/dist/interfaces/config-ui.d.ts +19 -1
- package/dist/interfaces/pipeline-executor-capability.d.ts +10 -0
- package/dist/{sleep-BabrCASa.js → sleep-B8cp-HUn.js} +193 -7
- package/dist/{sleep-MHm--th-.mjs → sleep-BiDFW0E7.mjs} +193 -7
- package/dist/units/convert.d.ts +49 -0
- package/dist/units/index.d.ts +8 -0
- package/dist/units/unit-table.d.ts +270 -0
- package/package.json +1 -1
|
@@ -262,8 +262,8 @@ export interface IKernelServices {
|
|
|
262
262
|
* own probe because hwaccel is inherently hardware-bound: decoder
|
|
263
263
|
* addons on an NVIDIA agent get `['cuda', 'nvdec']`, the same addon
|
|
264
264
|
* on a Mac hub gets `['videotoolbox']`. Cross-node queries for the
|
|
265
|
-
* admin UI go via the
|
|
266
|
-
*
|
|
265
|
+
* admin UI go via the node-pinned `platform-probe` capability
|
|
266
|
+
* (`resolveHwAccel`), not a bespoke Moleculer service.
|
|
267
267
|
*/
|
|
268
268
|
readonly hwaccel?: IKernelHwAccel;
|
|
269
269
|
/**
|
|
@@ -437,6 +437,15 @@ export interface AddonContext<TConfig = Record<string, unknown>> {
|
|
|
437
437
|
* and `global`-level values; they're disjoint by schema invariant so
|
|
438
438
|
* they can share one physical store. No defaults merged in. Returns
|
|
439
439
|
* an empty object if the addon has never written anything.
|
|
440
|
+
*
|
|
441
|
+
* @internal Low-level primitive — addons MUST NOT call this directly.
|
|
442
|
+
* Use the high-level, schema-driven, per-node-aware API instead:
|
|
443
|
+
* `this.getGlobalSettings()` / `this.updateGlobalSettings()`,
|
|
444
|
+
* `this.resolveGlobalStore()` (raw view for a `getGlobalSettings`
|
|
445
|
+
* override), or `this.state<T>()` (durable JSON blob). Enforced by the
|
|
446
|
+
* `no-raw-addon-store` biome guard over the addon source tree; the
|
|
447
|
+
* sanctioned `BaseAddon` wrappers (in `@camstack/types`) and the
|
|
448
|
+
* allowlisted engine-cascade holdouts are the only callers.
|
|
440
449
|
*/
|
|
441
450
|
readAddonStore(): Promise<Record<string, unknown>>;
|
|
442
451
|
/**
|
|
@@ -444,6 +453,10 @@ export interface AddonContext<TConfig = Record<string, unknown>> {
|
|
|
444
453
|
* preserved. Keys explicitly set to `null` are preserved as-is (they
|
|
445
454
|
* do NOT fall back to schema default on subsequent reads — that
|
|
446
455
|
* layering happens only at `hydrateSchema` time).
|
|
456
|
+
*
|
|
457
|
+
* @internal Low-level primitive — addons MUST NOT call this directly.
|
|
458
|
+
* Use `this.updateGlobalSettings()` or `this.state<T>().set()`. Enforced
|
|
459
|
+
* by the `no-raw-addon-store` biome guard (see `readAddonStore`).
|
|
447
460
|
*/
|
|
448
461
|
writeAddonStore(patch: Record<string, unknown>): Promise<void>;
|
|
449
462
|
/**
|
|
@@ -152,3 +152,17 @@ export interface AgentListItem {
|
|
|
152
152
|
/** Local IP addresses visible on the network */
|
|
153
153
|
readonly localIps?: readonly string[];
|
|
154
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Fan-out-free node liveness row for the `/health` surface, read from the
|
|
157
|
+
* hub's in-memory Moleculer node registry (`broker.registry.getNodeList`)
|
|
158
|
+
* — never a per-node RPC. `isOnline` is Moleculer's own availability
|
|
159
|
+
* verdict (`node.available`); a node that disconnected cleanly (or missed
|
|
160
|
+
* heartbeats) stays listed as offline until Moleculer purges it
|
|
161
|
+
* (~cleanOfflineNodesTimeout), which is what lets `/health` NAME the
|
|
162
|
+
* degraded node in `agents.offlineIds`.
|
|
163
|
+
*/
|
|
164
|
+
export interface NodeLiveness {
|
|
165
|
+
readonly id: string;
|
|
166
|
+
readonly isHub: boolean;
|
|
167
|
+
readonly isOnline: boolean;
|
|
168
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelCatalogEntry, ModelFormat, ModelOutputFormat
|
|
1
|
+
import type { CustomModelMetadata, ModelCatalogEntry, ModelFormat, ModelOutputFormat } from '../types/models.js';
|
|
2
2
|
/**
|
|
3
3
|
* Configuration UI Schema -- declarative form definition for any element.
|
|
4
4
|
*
|
|
@@ -111,6 +111,24 @@ export interface ConfigFieldBase {
|
|
|
111
111
|
* `updateAddonSettings`.
|
|
112
112
|
*/
|
|
113
113
|
readonly requiresRestart?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Per-node store scoping. `perNode: true` ⇒ the field's persisted store
|
|
116
|
+
* key is physically `<key>@<nodeId>` — one value per cluster node — while
|
|
117
|
+
* the SCHEMA field key stays bare (the UI form never sees the scoped key).
|
|
118
|
+
* `BaseAddon.getGlobalSettings` / `updateGlobalSettings` / `resolveConfig`
|
|
119
|
+
* scope reads and writes through `addon/per-node-store.ts`; a node with no
|
|
120
|
+
* scoped key resolves to the field's schema `default` — NEVER another
|
|
121
|
+
* node's value (deliberately NO bare-key fallback).
|
|
122
|
+
*
|
|
123
|
+
* Absent/false (default) ⇒ node-agnostic shared blob — a single value for
|
|
124
|
+
* the whole cluster, today's behavior for the vast majority of fields.
|
|
125
|
+
*
|
|
126
|
+
* This is SCHEMA metadata (like `immediate` / `requiresRestart`), NOT
|
|
127
|
+
* capability metadata: it lives on the field only — never in a `*.cap.ts`
|
|
128
|
+
* definition or an addon manifest — so it is invisible to
|
|
129
|
+
* `check-capability-metadata-drift.ts`.
|
|
130
|
+
*/
|
|
131
|
+
readonly perNode?: boolean;
|
|
114
132
|
/**
|
|
115
133
|
* Override the default placement of this field.
|
|
116
134
|
* Moves it to a different tab and/or section than its parent section.
|
|
@@ -2,6 +2,7 @@ import type { PipelineEngineChoice, PipelineConfig } from '../types/pipeline.js'
|
|
|
2
2
|
import type { PipelineSchema, PipelineDefaultStep, PipelineTemplateStep, PipelineTemplate } from '../types/pipeline-schema.js';
|
|
3
3
|
import type { ModelFormat } from '../types/models.js';
|
|
4
4
|
import type { FrameInput } from '../types/io.js';
|
|
5
|
+
import type { FrameHandle } from './frame-handle.js';
|
|
5
6
|
import type { FrameResult, DetectorOutput } from '../types/detection.js';
|
|
6
7
|
import type { ConfigUISchema } from './config-ui.js';
|
|
7
8
|
import type { InferenceCapabilities } from './inference-capabilities.js';
|
|
@@ -24,6 +25,9 @@ export interface PipelineStepInput {
|
|
|
24
25
|
/**
|
|
25
26
|
* Input for pipeline execution. Exactly one image source must be provided:
|
|
26
27
|
* - `frame`: runtime dispatch (pipeline-runner — decoded broker frame)
|
|
28
|
+
* - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the same
|
|
29
|
+
* decoded frame — the hub-local executor maps the segment and reads the
|
|
30
|
+
* pixels back zero-copy instead of paying the ~1.2MB UDS re-serialise.
|
|
27
31
|
* - `imageBase64`: one-shot benchmark (ImageTab "Run")
|
|
28
32
|
* - `referenceImage`: named file from the reference-image store
|
|
29
33
|
*
|
|
@@ -36,6 +40,12 @@ export interface PipelineRunInput {
|
|
|
36
40
|
readonly steps: readonly PipelineStepInput[];
|
|
37
41
|
readonly engine?: PipelineEngineChoice;
|
|
38
42
|
readonly frame?: FrameInput;
|
|
43
|
+
/**
|
|
44
|
+
* CB5 shm passthrough — names the ring slot the decoded pixels live in.
|
|
45
|
+
* A null read (recycled slot / foreign handle) degrades to an empty
|
|
46
|
+
* result; the runner ships `frame` as the fallback (queue-depth gated).
|
|
47
|
+
*/
|
|
48
|
+
readonly frameHandle?: FrameHandle;
|
|
39
49
|
readonly referenceImage?: string;
|
|
40
50
|
readonly imageBase64?: string;
|
|
41
51
|
/**
|
|
@@ -907,6 +907,102 @@ function createDurableState(deps) {
|
|
|
907
907
|
};
|
|
908
908
|
}
|
|
909
909
|
//#endregion
|
|
910
|
+
//#region src/addon/per-node-store.ts
|
|
911
|
+
/**
|
|
912
|
+
* Per-node scoping for the shared addon-settings blob.
|
|
913
|
+
*
|
|
914
|
+
* An addon's settings store is hub-central (the `addon-settings` cap is
|
|
915
|
+
* hub-routed — the hub instance answers for every node), so fields whose
|
|
916
|
+
* value is a NODE fact (decoder backend, engine pick, probed hardware
|
|
917
|
+
* capabilities) persist under the node-scoped key `<key>@<nodeId>` inside
|
|
918
|
+
* the single shared blob. The SCHEMA field key stays bare: the write path
|
|
919
|
+
* ({@link scopePatch}) maps the bare field onto the target node's scoped
|
|
920
|
+
* key; the read/hydrate path ({@link projectStore}) maps THIS node's value
|
|
921
|
+
* back, so UI forms and `resolveConfig` only ever see the bare key.
|
|
922
|
+
*
|
|
923
|
+
* Fields opt in via the `perNode: true` schema marker on `ConfigFieldBase`
|
|
924
|
+
* (`interfaces/config-ui.ts`); `BaseAddon` derives the key set from the
|
|
925
|
+
* schema and routes reads/writes through these helpers.
|
|
926
|
+
*
|
|
927
|
+
* ## No bare-key fallback — deliberate
|
|
928
|
+
*
|
|
929
|
+
* {@link readNodeValue} reads the node-scoped key ONLY. A node with no
|
|
930
|
+
* scoped key resolves to `undefined` so the field's schema `default` wins —
|
|
931
|
+
* NEVER `store[base]` and never another node's value. A bare legacy key in
|
|
932
|
+
* the store is invisible to every node, hub included, so one node's
|
|
933
|
+
* selection can never leak onto another. (This generalizes the
|
|
934
|
+
* `decoder-backend-keys.ts` semantics — the no-fallback variant — over an
|
|
935
|
+
* arbitrary set of per-node field keys.)
|
|
936
|
+
*
|
|
937
|
+
* Pure functions only — no I/O, no imports beyond the language. This is a
|
|
938
|
+
* LEAF module: import it via its deep path, never from the root barrel.
|
|
939
|
+
*/
|
|
940
|
+
/**
|
|
941
|
+
* Normalize a raw kernel node id to the bare node id used for scoping.
|
|
942
|
+
* `localNodeId` can carry a `<node>/<addon>` suffix on forked child
|
|
943
|
+
* processes; per-node settings are per-NODE, so strip the addon segment.
|
|
944
|
+
* `undefined` / `null` / empty falls back to `'hub'`.
|
|
945
|
+
*/
|
|
946
|
+
function normalizeNodeId(raw) {
|
|
947
|
+
if (raw === void 0 || raw === null || raw === "") return "hub";
|
|
948
|
+
const slashIdx = raw.indexOf("/");
|
|
949
|
+
if (slashIdx < 0) return raw;
|
|
950
|
+
const bare = raw.slice(0, slashIdx);
|
|
951
|
+
return bare === "" ? "hub" : bare;
|
|
952
|
+
}
|
|
953
|
+
/** The node-scoped store key for a per-node field: `<base>@<nodeId>`. */
|
|
954
|
+
function nodeScopedKey(base, nodeId) {
|
|
955
|
+
return `${base}@${normalizeNodeId(nodeId)}`;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Read a node's value for a per-node field from the raw shared store:
|
|
959
|
+
* the node-scoped key when present, otherwise `undefined`.
|
|
960
|
+
* Deliberately NO bare-key fallback (see module doc) — the caller lets the
|
|
961
|
+
* schema `default` win on `undefined`.
|
|
962
|
+
*/
|
|
963
|
+
function readNodeValue(store, base, nodeId) {
|
|
964
|
+
return store[nodeScopedKey(base, nodeId)];
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Re-map a UI/settings patch so every bare perNode field persists under the
|
|
968
|
+
* TARGET node's scoped key; all other keys pass through unchanged. Used on
|
|
969
|
+
* the write path so a save for one node never clobbers another node's value
|
|
970
|
+
* (and the bare key is never written). Returns a new object — the input
|
|
971
|
+
* patch is not mutated.
|
|
972
|
+
*/
|
|
973
|
+
function scopePatch(patch, perNodeKeys, nodeId) {
|
|
974
|
+
const out = {};
|
|
975
|
+
for (const [key, value] of Object.entries(patch)) out[perNodeKeys.has(key) ? nodeScopedKey(key, nodeId) : key] = value;
|
|
976
|
+
return out;
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Project the raw shared store onto the bare schema keys for ONE node so a
|
|
980
|
+
* UI schema (whose field keys are bare) hydrates from that node's own
|
|
981
|
+
* values:
|
|
982
|
+
*
|
|
983
|
+
* - EVERY `@`-scoped key (any node's) is dropped from the pass-through.
|
|
984
|
+
* - Every bare perNode key is dropped from the pass-through (a stray bare
|
|
985
|
+
* legacy key must never hydrate any node — no bare fallback).
|
|
986
|
+
* - THIS node's effective value ({@link readNodeValue}) is then laid onto
|
|
987
|
+
* each bare perNode key; when the node has no scoped key the bare key is
|
|
988
|
+
* left ABSENT so the field's schema `default` wins.
|
|
989
|
+
*
|
|
990
|
+
* Returns a new object — the input store is not mutated.
|
|
991
|
+
*/
|
|
992
|
+
function projectStore(store, perNodeKeys, nodeId) {
|
|
993
|
+
const out = {};
|
|
994
|
+
for (const [key, value] of Object.entries(store)) {
|
|
995
|
+
if (key.includes("@")) continue;
|
|
996
|
+
if (perNodeKeys.has(key)) continue;
|
|
997
|
+
out[key] = value;
|
|
998
|
+
}
|
|
999
|
+
for (const base of perNodeKeys) {
|
|
1000
|
+
const value = readNodeValue(store, base, nodeId);
|
|
1001
|
+
if (value !== void 0) out[base] = value;
|
|
1002
|
+
}
|
|
1003
|
+
return out;
|
|
1004
|
+
}
|
|
1005
|
+
//#endregion
|
|
910
1006
|
//#region src/addon/base-addon.ts
|
|
911
1007
|
/**
|
|
912
1008
|
* Base class for CamStack addons. Eliminates settings boilerplate:
|
|
@@ -1076,23 +1172,63 @@ var BaseAddon = class {
|
|
|
1076
1172
|
deviceSettingsSchema() {
|
|
1077
1173
|
return null;
|
|
1078
1174
|
}
|
|
1079
|
-
async getGlobalSettings(overlay, cap,
|
|
1175
|
+
async getGlobalSettings(overlay, cap, nodeId) {
|
|
1080
1176
|
const schema = this.globalSettingsSchema(cap);
|
|
1081
1177
|
if (!schema) return { sections: [] };
|
|
1082
|
-
const
|
|
1178
|
+
const projected = await this.resolveGlobalStore(nodeId, cap);
|
|
1083
1179
|
return hydrateSchema(schema, overlay ? {
|
|
1084
|
-
...
|
|
1180
|
+
...projected,
|
|
1085
1181
|
...overlay
|
|
1086
|
-
} :
|
|
1182
|
+
} : projected);
|
|
1087
1183
|
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1184
|
+
/**
|
|
1185
|
+
* The raw addon store PROJECTED onto the target node's bare per-node keys:
|
|
1186
|
+
* every `perNode: true` field carries THAT node's scoped value on its bare
|
|
1187
|
+
* key (absent scoped key ⇒ key absent, so the schema `default` wins — no
|
|
1188
|
+
* bare fallback), all `@`-scoped and stray bare per-node keys are dropped.
|
|
1189
|
+
* A no-op passthrough when the schema declares no `perNode` field.
|
|
1190
|
+
*
|
|
1191
|
+
* This is the sanctioned way for a `getGlobalSettings` OVERRIDE that needs
|
|
1192
|
+
* the store for custom option logic (option narrowing, value snapping) to
|
|
1193
|
+
* read it per-node — never `ctx.settings.readAddonStore()` directly.
|
|
1194
|
+
* `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
|
|
1195
|
+
*/
|
|
1196
|
+
async resolveGlobalStore(nodeId, cap) {
|
|
1197
|
+
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
1198
|
+
const keys = this.perNodeKeys(cap);
|
|
1199
|
+
const node = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
1200
|
+
return keys.size > 0 ? projectStore(raw, keys, node) : raw;
|
|
1201
|
+
}
|
|
1202
|
+
async updateGlobalSettings(patch, nodeId) {
|
|
1203
|
+
const keys = this.perNodeKeys();
|
|
1204
|
+
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
1205
|
+
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
1206
|
+
const barePatch = patch;
|
|
1207
|
+
const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
|
|
1208
|
+
await this._ctx?.settings?.writeAddonStore(scoped);
|
|
1209
|
+
if (target !== localNode) return;
|
|
1090
1210
|
await this.resolveConfig();
|
|
1091
1211
|
await this.onConfigChanged();
|
|
1092
1212
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
1093
1213
|
this.maybeAutoRestart(patch, this.globalSettingsSchema());
|
|
1094
1214
|
}
|
|
1095
1215
|
/**
|
|
1216
|
+
* The set of field keys the global settings schema declares `perNode: true`
|
|
1217
|
+
* — derived once per `cap` argument and memoized (schemas are static
|
|
1218
|
+
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
1219
|
+
* settings API behaves exactly like the legacy node-agnostic one.
|
|
1220
|
+
*/
|
|
1221
|
+
_perNodeKeysCache = /* @__PURE__ */ new Map();
|
|
1222
|
+
perNodeKeys(cap) {
|
|
1223
|
+
const cacheKey = cap ?? "";
|
|
1224
|
+
const cached = this._perNodeKeysCache.get(cacheKey);
|
|
1225
|
+
if (cached) return cached;
|
|
1226
|
+
const schema = this.globalSettingsSchema(cap);
|
|
1227
|
+
const keys = new Set(schema ? schema.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
1228
|
+
this._perNodeKeysCache.set(cacheKey, keys);
|
|
1229
|
+
return keys;
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1096
1232
|
* If any field in `patch` is marked `requiresRestart` in `schema`,
|
|
1097
1233
|
* schedule an addon restart for the next tick. Deferred via
|
|
1098
1234
|
* `setImmediate` so the tRPC mutation that triggered the write has
|
|
@@ -1245,12 +1381,19 @@ var BaseAddon = class {
|
|
|
1245
1381
|
* The merge is shallow: each key in `defaults` is checked against the store.
|
|
1246
1382
|
* Only keys present in defaults are read — the store can contain extra keys
|
|
1247
1383
|
* (e.g. from older versions) without polluting the typed config.
|
|
1384
|
+
*
|
|
1385
|
+
* Keys the global settings schema declares `perNode: true` resolve from
|
|
1386
|
+
* THIS node's scoped key (`<key>@<localNode>`) via `readNodeValue` — never
|
|
1387
|
+
* from the bare key — so a per-node field resolves to this node's own
|
|
1388
|
+
* selection at boot (absent scoped key ⇒ the constructor default wins).
|
|
1248
1389
|
*/
|
|
1249
1390
|
async resolveConfig() {
|
|
1250
1391
|
const stored = await this.readAddonStoreWithRetry();
|
|
1392
|
+
const perNode = this.perNodeKeys();
|
|
1393
|
+
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
1251
1394
|
const resolved = { ...this.defaults };
|
|
1252
1395
|
for (const key of Object.keys(this.defaults)) {
|
|
1253
|
-
const storedValue = stored[key];
|
|
1396
|
+
const storedValue = perNode.has(key) ? readNodeValue(stored, key, localNode) : stored[key];
|
|
1254
1397
|
if (storedValue !== void 0 && storedValue !== null) {
|
|
1255
1398
|
const defaultType = typeof this.defaults[key];
|
|
1256
1399
|
if (typeof storedValue === defaultType) resolved[key] = storedValue;
|
|
@@ -1334,6 +1477,27 @@ var BaseAddon = class {
|
|
|
1334
1477
|
}
|
|
1335
1478
|
};
|
|
1336
1479
|
/**
|
|
1480
|
+
* Collect the keys of every field marked `perNode: true`, recursing into
|
|
1481
|
+
* layout containers (`group` fields and `sub-tabs` tabs) the same way
|
|
1482
|
+
* `hydrateSchema` does. Valueless structural fields (separator/info/…)
|
|
1483
|
+
* don't declare `perNode` and are excluded by the `in` narrowing.
|
|
1484
|
+
*/
|
|
1485
|
+
function collectPerNodeFieldKeys(fields) {
|
|
1486
|
+
const collected = [];
|
|
1487
|
+
for (const field of fields) {
|
|
1488
|
+
if (field.type === "group") {
|
|
1489
|
+
collected.push(...collectPerNodeFieldKeys(field.fields));
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
if (field.type === "sub-tabs") {
|
|
1493
|
+
for (const tab of field.tabs) collected.push(...collectPerNodeFieldKeys(tab.fields));
|
|
1494
|
+
continue;
|
|
1495
|
+
}
|
|
1496
|
+
if ("perNode" in field && field.perNode === true) collected.push(field.key);
|
|
1497
|
+
}
|
|
1498
|
+
return collected;
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1337
1501
|
* Normalize an `ICamstackAddon.initialize()` return value into the
|
|
1338
1502
|
* `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
|
|
1339
1503
|
* envelopes pass through; void stays void.
|
|
@@ -2239,6 +2403,13 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
2239
2403
|
/** Single still-image entity (HA `image.*`). Read-only display of an
|
|
2240
2404
|
* `entity_picture` signed URL the browser loads directly. `image` cap. */
|
|
2241
2405
|
DeviceType["Image"] = "image";
|
|
2406
|
+
/** Smart pet feeder — cloud-connected food dispenser with a bowl food
|
|
2407
|
+
* level, battery, desiccant life, feeding state and manual-feed /
|
|
2408
|
+
* call-pet / maintenance actions. Installed with the `pet-feeder` cap;
|
|
2409
|
+
* dual-hopper models (D4S/D4SH) expose per-hopper portions. Sources:
|
|
2410
|
+
* native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
|
|
2411
|
+
* integrations sharing the same food/desiccant/hopper surface. */
|
|
2412
|
+
DeviceType["PetFeeder"] = "pet-feeder";
|
|
2242
2413
|
return DeviceType;
|
|
2243
2414
|
}({});
|
|
2244
2415
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -2898,6 +3069,7 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
2898
3069
|
nativeObjectDetection: createSliceHandle(stateSource, binding.deviceId, "native-object-detection"),
|
|
2899
3070
|
notifier: createSliceHandle(stateSource, binding.deviceId, "notifier"),
|
|
2900
3071
|
numericSensor: createSliceHandle(stateSource, binding.deviceId, "numeric-sensor"),
|
|
3072
|
+
petFeeder: createSliceHandle(stateSource, binding.deviceId, "pet-feeder"),
|
|
2901
3073
|
powerMeter: createSliceHandle(stateSource, binding.deviceId, "power-meter"),
|
|
2902
3074
|
presence: createSliceHandle(stateSource, binding.deviceId, "presence"),
|
|
2903
3075
|
pressureSensor: createSliceHandle(stateSource, binding.deviceId, "pressure-sensor"),
|
|
@@ -3121,6 +3293,19 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
3121
3293
|
setOverlay: (input) => dispatch("osd", "osd", "setOverlay", "mutation", input),
|
|
3122
3294
|
getStatus: (input) => dispatch("osd", "osd", "getStatus", "query", input)
|
|
3123
3295
|
},
|
|
3296
|
+
petFeeder: {
|
|
3297
|
+
feed: (input) => dispatch("pet-feeder", "petFeeder", "feed", "mutation", input),
|
|
3298
|
+
cancelFeed: (input) => dispatch("pet-feeder", "petFeeder", "cancelFeed", "mutation", input),
|
|
3299
|
+
resetDesiccant: (input) => dispatch("pet-feeder", "petFeeder", "resetDesiccant", "mutation", input),
|
|
3300
|
+
markFoodReplenished: (input) => dispatch("pet-feeder", "petFeeder", "markFoodReplenished", "mutation", input),
|
|
3301
|
+
callPet: (input) => dispatch("pet-feeder", "petFeeder", "callPet", "mutation", input),
|
|
3302
|
+
playSound: (input) => dispatch("pet-feeder", "petFeeder", "playSound", "mutation", input),
|
|
3303
|
+
setChildLock: (input) => dispatch("pet-feeder", "petFeeder", "setChildLock", "mutation", input),
|
|
3304
|
+
setIndicatorLight: (input) => dispatch("pet-feeder", "petFeeder", "setIndicatorLight", "mutation", input),
|
|
3305
|
+
setFeedSound: (input) => dispatch("pet-feeder", "petFeeder", "setFeedSound", "mutation", input),
|
|
3306
|
+
setVolume: (input) => dispatch("pet-feeder", "petFeeder", "setVolume", "mutation", input),
|
|
3307
|
+
getStatus: (input) => dispatch("pet-feeder", "petFeeder", "getStatus", "query", input)
|
|
3308
|
+
},
|
|
3124
3309
|
pipelineAnalytics: {
|
|
3125
3310
|
getActiveTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getActiveTracks", "query", input),
|
|
3126
3311
|
getTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getTrack", "query", input),
|
|
@@ -3280,6 +3465,7 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
3280
3465
|
setPrimaryChildEntityId: (input) => dispatchSystem("deviceManager", "setPrimaryChildEntityId", "mutation", input),
|
|
3281
3466
|
setChildLayout: (input) => dispatchSystem("deviceManager", "setChildLayout", "mutation", input),
|
|
3282
3467
|
setDeviceLinks: (input) => dispatchSystem("deviceManager", "setDeviceLinks", "mutation", input),
|
|
3468
|
+
setDisplay: (input) => dispatchSystem("deviceManager", "setDisplay", "mutation", input),
|
|
3283
3469
|
getWireableFields: (input) => dispatchSystem("deviceManager", "getWireableFields", "query", input),
|
|
3284
3470
|
setRole: (input) => dispatchSystem("deviceManager", "setRole", "mutation", input),
|
|
3285
3471
|
applyInitialMeta: (input) => dispatchSystem("deviceManager", "applyInitialMeta", "mutation", input),
|
|
@@ -907,6 +907,102 @@ function createDurableState(deps) {
|
|
|
907
907
|
};
|
|
908
908
|
}
|
|
909
909
|
//#endregion
|
|
910
|
+
//#region src/addon/per-node-store.ts
|
|
911
|
+
/**
|
|
912
|
+
* Per-node scoping for the shared addon-settings blob.
|
|
913
|
+
*
|
|
914
|
+
* An addon's settings store is hub-central (the `addon-settings` cap is
|
|
915
|
+
* hub-routed — the hub instance answers for every node), so fields whose
|
|
916
|
+
* value is a NODE fact (decoder backend, engine pick, probed hardware
|
|
917
|
+
* capabilities) persist under the node-scoped key `<key>@<nodeId>` inside
|
|
918
|
+
* the single shared blob. The SCHEMA field key stays bare: the write path
|
|
919
|
+
* ({@link scopePatch}) maps the bare field onto the target node's scoped
|
|
920
|
+
* key; the read/hydrate path ({@link projectStore}) maps THIS node's value
|
|
921
|
+
* back, so UI forms and `resolveConfig` only ever see the bare key.
|
|
922
|
+
*
|
|
923
|
+
* Fields opt in via the `perNode: true` schema marker on `ConfigFieldBase`
|
|
924
|
+
* (`interfaces/config-ui.ts`); `BaseAddon` derives the key set from the
|
|
925
|
+
* schema and routes reads/writes through these helpers.
|
|
926
|
+
*
|
|
927
|
+
* ## No bare-key fallback — deliberate
|
|
928
|
+
*
|
|
929
|
+
* {@link readNodeValue} reads the node-scoped key ONLY. A node with no
|
|
930
|
+
* scoped key resolves to `undefined` so the field's schema `default` wins —
|
|
931
|
+
* NEVER `store[base]` and never another node's value. A bare legacy key in
|
|
932
|
+
* the store is invisible to every node, hub included, so one node's
|
|
933
|
+
* selection can never leak onto another. (This generalizes the
|
|
934
|
+
* `decoder-backend-keys.ts` semantics — the no-fallback variant — over an
|
|
935
|
+
* arbitrary set of per-node field keys.)
|
|
936
|
+
*
|
|
937
|
+
* Pure functions only — no I/O, no imports beyond the language. This is a
|
|
938
|
+
* LEAF module: import it via its deep path, never from the root barrel.
|
|
939
|
+
*/
|
|
940
|
+
/**
|
|
941
|
+
* Normalize a raw kernel node id to the bare node id used for scoping.
|
|
942
|
+
* `localNodeId` can carry a `<node>/<addon>` suffix on forked child
|
|
943
|
+
* processes; per-node settings are per-NODE, so strip the addon segment.
|
|
944
|
+
* `undefined` / `null` / empty falls back to `'hub'`.
|
|
945
|
+
*/
|
|
946
|
+
function normalizeNodeId(raw) {
|
|
947
|
+
if (raw === void 0 || raw === null || raw === "") return "hub";
|
|
948
|
+
const slashIdx = raw.indexOf("/");
|
|
949
|
+
if (slashIdx < 0) return raw;
|
|
950
|
+
const bare = raw.slice(0, slashIdx);
|
|
951
|
+
return bare === "" ? "hub" : bare;
|
|
952
|
+
}
|
|
953
|
+
/** The node-scoped store key for a per-node field: `<base>@<nodeId>`. */
|
|
954
|
+
function nodeScopedKey(base, nodeId) {
|
|
955
|
+
return `${base}@${normalizeNodeId(nodeId)}`;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Read a node's value for a per-node field from the raw shared store:
|
|
959
|
+
* the node-scoped key when present, otherwise `undefined`.
|
|
960
|
+
* Deliberately NO bare-key fallback (see module doc) — the caller lets the
|
|
961
|
+
* schema `default` win on `undefined`.
|
|
962
|
+
*/
|
|
963
|
+
function readNodeValue(store, base, nodeId) {
|
|
964
|
+
return store[nodeScopedKey(base, nodeId)];
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Re-map a UI/settings patch so every bare perNode field persists under the
|
|
968
|
+
* TARGET node's scoped key; all other keys pass through unchanged. Used on
|
|
969
|
+
* the write path so a save for one node never clobbers another node's value
|
|
970
|
+
* (and the bare key is never written). Returns a new object — the input
|
|
971
|
+
* patch is not mutated.
|
|
972
|
+
*/
|
|
973
|
+
function scopePatch(patch, perNodeKeys, nodeId) {
|
|
974
|
+
const out = {};
|
|
975
|
+
for (const [key, value] of Object.entries(patch)) out[perNodeKeys.has(key) ? nodeScopedKey(key, nodeId) : key] = value;
|
|
976
|
+
return out;
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Project the raw shared store onto the bare schema keys for ONE node so a
|
|
980
|
+
* UI schema (whose field keys are bare) hydrates from that node's own
|
|
981
|
+
* values:
|
|
982
|
+
*
|
|
983
|
+
* - EVERY `@`-scoped key (any node's) is dropped from the pass-through.
|
|
984
|
+
* - Every bare perNode key is dropped from the pass-through (a stray bare
|
|
985
|
+
* legacy key must never hydrate any node — no bare fallback).
|
|
986
|
+
* - THIS node's effective value ({@link readNodeValue}) is then laid onto
|
|
987
|
+
* each bare perNode key; when the node has no scoped key the bare key is
|
|
988
|
+
* left ABSENT so the field's schema `default` wins.
|
|
989
|
+
*
|
|
990
|
+
* Returns a new object — the input store is not mutated.
|
|
991
|
+
*/
|
|
992
|
+
function projectStore(store, perNodeKeys, nodeId) {
|
|
993
|
+
const out = {};
|
|
994
|
+
for (const [key, value] of Object.entries(store)) {
|
|
995
|
+
if (key.includes("@")) continue;
|
|
996
|
+
if (perNodeKeys.has(key)) continue;
|
|
997
|
+
out[key] = value;
|
|
998
|
+
}
|
|
999
|
+
for (const base of perNodeKeys) {
|
|
1000
|
+
const value = readNodeValue(store, base, nodeId);
|
|
1001
|
+
if (value !== void 0) out[base] = value;
|
|
1002
|
+
}
|
|
1003
|
+
return out;
|
|
1004
|
+
}
|
|
1005
|
+
//#endregion
|
|
910
1006
|
//#region src/addon/base-addon.ts
|
|
911
1007
|
/**
|
|
912
1008
|
* Base class for CamStack addons. Eliminates settings boilerplate:
|
|
@@ -1076,23 +1172,63 @@ var BaseAddon = class {
|
|
|
1076
1172
|
deviceSettingsSchema() {
|
|
1077
1173
|
return null;
|
|
1078
1174
|
}
|
|
1079
|
-
async getGlobalSettings(overlay, cap,
|
|
1175
|
+
async getGlobalSettings(overlay, cap, nodeId) {
|
|
1080
1176
|
const schema = this.globalSettingsSchema(cap);
|
|
1081
1177
|
if (!schema) return { sections: [] };
|
|
1082
|
-
const
|
|
1178
|
+
const projected = await this.resolveGlobalStore(nodeId, cap);
|
|
1083
1179
|
return hydrateSchema(schema, overlay ? {
|
|
1084
|
-
...
|
|
1180
|
+
...projected,
|
|
1085
1181
|
...overlay
|
|
1086
|
-
} :
|
|
1182
|
+
} : projected);
|
|
1087
1183
|
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1184
|
+
/**
|
|
1185
|
+
* The raw addon store PROJECTED onto the target node's bare per-node keys:
|
|
1186
|
+
* every `perNode: true` field carries THAT node's scoped value on its bare
|
|
1187
|
+
* key (absent scoped key ⇒ key absent, so the schema `default` wins — no
|
|
1188
|
+
* bare fallback), all `@`-scoped and stray bare per-node keys are dropped.
|
|
1189
|
+
* A no-op passthrough when the schema declares no `perNode` field.
|
|
1190
|
+
*
|
|
1191
|
+
* This is the sanctioned way for a `getGlobalSettings` OVERRIDE that needs
|
|
1192
|
+
* the store for custom option logic (option narrowing, value snapping) to
|
|
1193
|
+
* read it per-node — never `ctx.settings.readAddonStore()` directly.
|
|
1194
|
+
* `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
|
|
1195
|
+
*/
|
|
1196
|
+
async resolveGlobalStore(nodeId, cap) {
|
|
1197
|
+
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
1198
|
+
const keys = this.perNodeKeys(cap);
|
|
1199
|
+
const node = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
1200
|
+
return keys.size > 0 ? projectStore(raw, keys, node) : raw;
|
|
1201
|
+
}
|
|
1202
|
+
async updateGlobalSettings(patch, nodeId) {
|
|
1203
|
+
const keys = this.perNodeKeys();
|
|
1204
|
+
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
1205
|
+
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
1206
|
+
const barePatch = patch;
|
|
1207
|
+
const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
|
|
1208
|
+
await this._ctx?.settings?.writeAddonStore(scoped);
|
|
1209
|
+
if (target !== localNode) return;
|
|
1090
1210
|
await this.resolveConfig();
|
|
1091
1211
|
await this.onConfigChanged();
|
|
1092
1212
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
1093
1213
|
this.maybeAutoRestart(patch, this.globalSettingsSchema());
|
|
1094
1214
|
}
|
|
1095
1215
|
/**
|
|
1216
|
+
* The set of field keys the global settings schema declares `perNode: true`
|
|
1217
|
+
* — derived once per `cap` argument and memoized (schemas are static
|
|
1218
|
+
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
1219
|
+
* settings API behaves exactly like the legacy node-agnostic one.
|
|
1220
|
+
*/
|
|
1221
|
+
_perNodeKeysCache = /* @__PURE__ */ new Map();
|
|
1222
|
+
perNodeKeys(cap) {
|
|
1223
|
+
const cacheKey = cap ?? "";
|
|
1224
|
+
const cached = this._perNodeKeysCache.get(cacheKey);
|
|
1225
|
+
if (cached) return cached;
|
|
1226
|
+
const schema = this.globalSettingsSchema(cap);
|
|
1227
|
+
const keys = new Set(schema ? schema.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
1228
|
+
this._perNodeKeysCache.set(cacheKey, keys);
|
|
1229
|
+
return keys;
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1096
1232
|
* If any field in `patch` is marked `requiresRestart` in `schema`,
|
|
1097
1233
|
* schedule an addon restart for the next tick. Deferred via
|
|
1098
1234
|
* `setImmediate` so the tRPC mutation that triggered the write has
|
|
@@ -1245,12 +1381,19 @@ var BaseAddon = class {
|
|
|
1245
1381
|
* The merge is shallow: each key in `defaults` is checked against the store.
|
|
1246
1382
|
* Only keys present in defaults are read — the store can contain extra keys
|
|
1247
1383
|
* (e.g. from older versions) without polluting the typed config.
|
|
1384
|
+
*
|
|
1385
|
+
* Keys the global settings schema declares `perNode: true` resolve from
|
|
1386
|
+
* THIS node's scoped key (`<key>@<localNode>`) via `readNodeValue` — never
|
|
1387
|
+
* from the bare key — so a per-node field resolves to this node's own
|
|
1388
|
+
* selection at boot (absent scoped key ⇒ the constructor default wins).
|
|
1248
1389
|
*/
|
|
1249
1390
|
async resolveConfig() {
|
|
1250
1391
|
const stored = await this.readAddonStoreWithRetry();
|
|
1392
|
+
const perNode = this.perNodeKeys();
|
|
1393
|
+
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
1251
1394
|
const resolved = { ...this.defaults };
|
|
1252
1395
|
for (const key of Object.keys(this.defaults)) {
|
|
1253
|
-
const storedValue = stored[key];
|
|
1396
|
+
const storedValue = perNode.has(key) ? readNodeValue(stored, key, localNode) : stored[key];
|
|
1254
1397
|
if (storedValue !== void 0 && storedValue !== null) {
|
|
1255
1398
|
const defaultType = typeof this.defaults[key];
|
|
1256
1399
|
if (typeof storedValue === defaultType) resolved[key] = storedValue;
|
|
@@ -1334,6 +1477,27 @@ var BaseAddon = class {
|
|
|
1334
1477
|
}
|
|
1335
1478
|
};
|
|
1336
1479
|
/**
|
|
1480
|
+
* Collect the keys of every field marked `perNode: true`, recursing into
|
|
1481
|
+
* layout containers (`group` fields and `sub-tabs` tabs) the same way
|
|
1482
|
+
* `hydrateSchema` does. Valueless structural fields (separator/info/…)
|
|
1483
|
+
* don't declare `perNode` and are excluded by the `in` narrowing.
|
|
1484
|
+
*/
|
|
1485
|
+
function collectPerNodeFieldKeys(fields) {
|
|
1486
|
+
const collected = [];
|
|
1487
|
+
for (const field of fields) {
|
|
1488
|
+
if (field.type === "group") {
|
|
1489
|
+
collected.push(...collectPerNodeFieldKeys(field.fields));
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
if (field.type === "sub-tabs") {
|
|
1493
|
+
for (const tab of field.tabs) collected.push(...collectPerNodeFieldKeys(tab.fields));
|
|
1494
|
+
continue;
|
|
1495
|
+
}
|
|
1496
|
+
if ("perNode" in field && field.perNode === true) collected.push(field.key);
|
|
1497
|
+
}
|
|
1498
|
+
return collected;
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1337
1501
|
* Normalize an `ICamstackAddon.initialize()` return value into the
|
|
1338
1502
|
* `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
|
|
1339
1503
|
* envelopes pass through; void stays void.
|
|
@@ -2239,6 +2403,13 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
2239
2403
|
/** Single still-image entity (HA `image.*`). Read-only display of an
|
|
2240
2404
|
* `entity_picture` signed URL the browser loads directly. `image` cap. */
|
|
2241
2405
|
DeviceType["Image"] = "image";
|
|
2406
|
+
/** Smart pet feeder — cloud-connected food dispenser with a bowl food
|
|
2407
|
+
* level, battery, desiccant life, feeding state and manual-feed /
|
|
2408
|
+
* call-pet / maintenance actions. Installed with the `pet-feeder` cap;
|
|
2409
|
+
* dual-hopper models (D4S/D4SH) expose per-hopper portions. Sources:
|
|
2410
|
+
* native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
|
|
2411
|
+
* integrations sharing the same food/desiccant/hopper surface. */
|
|
2412
|
+
DeviceType["PetFeeder"] = "pet-feeder";
|
|
2242
2413
|
return DeviceType;
|
|
2243
2414
|
}({});
|
|
2244
2415
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -2898,6 +3069,7 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
2898
3069
|
nativeObjectDetection: createSliceHandle(stateSource, binding.deviceId, "native-object-detection"),
|
|
2899
3070
|
notifier: createSliceHandle(stateSource, binding.deviceId, "notifier"),
|
|
2900
3071
|
numericSensor: createSliceHandle(stateSource, binding.deviceId, "numeric-sensor"),
|
|
3072
|
+
petFeeder: createSliceHandle(stateSource, binding.deviceId, "pet-feeder"),
|
|
2901
3073
|
powerMeter: createSliceHandle(stateSource, binding.deviceId, "power-meter"),
|
|
2902
3074
|
presence: createSliceHandle(stateSource, binding.deviceId, "presence"),
|
|
2903
3075
|
pressureSensor: createSliceHandle(stateSource, binding.deviceId, "pressure-sensor"),
|
|
@@ -3121,6 +3293,19 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
3121
3293
|
setOverlay: (input) => dispatch("osd", "osd", "setOverlay", "mutation", input),
|
|
3122
3294
|
getStatus: (input) => dispatch("osd", "osd", "getStatus", "query", input)
|
|
3123
3295
|
},
|
|
3296
|
+
petFeeder: {
|
|
3297
|
+
feed: (input) => dispatch("pet-feeder", "petFeeder", "feed", "mutation", input),
|
|
3298
|
+
cancelFeed: (input) => dispatch("pet-feeder", "petFeeder", "cancelFeed", "mutation", input),
|
|
3299
|
+
resetDesiccant: (input) => dispatch("pet-feeder", "petFeeder", "resetDesiccant", "mutation", input),
|
|
3300
|
+
markFoodReplenished: (input) => dispatch("pet-feeder", "petFeeder", "markFoodReplenished", "mutation", input),
|
|
3301
|
+
callPet: (input) => dispatch("pet-feeder", "petFeeder", "callPet", "mutation", input),
|
|
3302
|
+
playSound: (input) => dispatch("pet-feeder", "petFeeder", "playSound", "mutation", input),
|
|
3303
|
+
setChildLock: (input) => dispatch("pet-feeder", "petFeeder", "setChildLock", "mutation", input),
|
|
3304
|
+
setIndicatorLight: (input) => dispatch("pet-feeder", "petFeeder", "setIndicatorLight", "mutation", input),
|
|
3305
|
+
setFeedSound: (input) => dispatch("pet-feeder", "petFeeder", "setFeedSound", "mutation", input),
|
|
3306
|
+
setVolume: (input) => dispatch("pet-feeder", "petFeeder", "setVolume", "mutation", input),
|
|
3307
|
+
getStatus: (input) => dispatch("pet-feeder", "petFeeder", "getStatus", "query", input)
|
|
3308
|
+
},
|
|
3124
3309
|
pipelineAnalytics: {
|
|
3125
3310
|
getActiveTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getActiveTracks", "query", input),
|
|
3126
3311
|
getTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getTrack", "query", input),
|
|
@@ -3280,6 +3465,7 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
3280
3465
|
setPrimaryChildEntityId: (input) => dispatchSystem("deviceManager", "setPrimaryChildEntityId", "mutation", input),
|
|
3281
3466
|
setChildLayout: (input) => dispatchSystem("deviceManager", "setChildLayout", "mutation", input),
|
|
3282
3467
|
setDeviceLinks: (input) => dispatchSystem("deviceManager", "setDeviceLinks", "mutation", input),
|
|
3468
|
+
setDisplay: (input) => dispatchSystem("deviceManager", "setDisplay", "mutation", input),
|
|
3283
3469
|
getWireableFields: (input) => dispatchSystem("deviceManager", "getWireableFields", "query", input),
|
|
3284
3470
|
setRole: (input) => dispatchSystem("deviceManager", "setRole", "mutation", input),
|
|
3285
3471
|
applyInitialMeta: (input) => dispatchSystem("deviceManager", "applyInitialMeta", "mutation", input),
|