@camstack/addon-pipeline-orchestrator 1.1.19 → 1.1.21
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/_stub.js +24 -24
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-C4w2ofvg.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-DVzgRExi.mjs} +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DPNDc6_1.mjs +26 -0
- package/dist/{_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-mTCScrpS.mjs → _virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DdDNCPJ5.mjs} +2 -2
- package/dist/{hostInit--XrsIsAe.mjs → hostInit-CEqZfaom.mjs} +3 -3
- package/dist/index.js +431 -71
- package/dist/index.mjs +431 -71
- package/dist/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-pdYzvu0J.mjs +0 -26
package/dist/index.js
CHANGED
|
@@ -4631,7 +4631,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4631
4631
|
return inst;
|
|
4632
4632
|
}
|
|
4633
4633
|
//#endregion
|
|
4634
|
-
//#region ../types/dist/sleep-
|
|
4634
|
+
//#region ../types/dist/sleep-BiDFW0E7.mjs
|
|
4635
4635
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4636
4636
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4637
4637
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5460,6 +5460,100 @@ function createDurableState(deps) {
|
|
|
5460
5460
|
};
|
|
5461
5461
|
}
|
|
5462
5462
|
/**
|
|
5463
|
+
* Per-node scoping for the shared addon-settings blob.
|
|
5464
|
+
*
|
|
5465
|
+
* An addon's settings store is hub-central (the `addon-settings` cap is
|
|
5466
|
+
* hub-routed — the hub instance answers for every node), so fields whose
|
|
5467
|
+
* value is a NODE fact (decoder backend, engine pick, probed hardware
|
|
5468
|
+
* capabilities) persist under the node-scoped key `<key>@<nodeId>` inside
|
|
5469
|
+
* the single shared blob. The SCHEMA field key stays bare: the write path
|
|
5470
|
+
* ({@link scopePatch}) maps the bare field onto the target node's scoped
|
|
5471
|
+
* key; the read/hydrate path ({@link projectStore}) maps THIS node's value
|
|
5472
|
+
* back, so UI forms and `resolveConfig` only ever see the bare key.
|
|
5473
|
+
*
|
|
5474
|
+
* Fields opt in via the `perNode: true` schema marker on `ConfigFieldBase`
|
|
5475
|
+
* (`interfaces/config-ui.ts`); `BaseAddon` derives the key set from the
|
|
5476
|
+
* schema and routes reads/writes through these helpers.
|
|
5477
|
+
*
|
|
5478
|
+
* ## No bare-key fallback — deliberate
|
|
5479
|
+
*
|
|
5480
|
+
* {@link readNodeValue} reads the node-scoped key ONLY. A node with no
|
|
5481
|
+
* scoped key resolves to `undefined` so the field's schema `default` wins —
|
|
5482
|
+
* NEVER `store[base]` and never another node's value. A bare legacy key in
|
|
5483
|
+
* the store is invisible to every node, hub included, so one node's
|
|
5484
|
+
* selection can never leak onto another. (This generalizes the
|
|
5485
|
+
* `decoder-backend-keys.ts` semantics — the no-fallback variant — over an
|
|
5486
|
+
* arbitrary set of per-node field keys.)
|
|
5487
|
+
*
|
|
5488
|
+
* Pure functions only — no I/O, no imports beyond the language. This is a
|
|
5489
|
+
* LEAF module: import it via its deep path, never from the root barrel.
|
|
5490
|
+
*/
|
|
5491
|
+
/**
|
|
5492
|
+
* Normalize a raw kernel node id to the bare node id used for scoping.
|
|
5493
|
+
* `localNodeId` can carry a `<node>/<addon>` suffix on forked child
|
|
5494
|
+
* processes; per-node settings are per-NODE, so strip the addon segment.
|
|
5495
|
+
* `undefined` / `null` / empty falls back to `'hub'`.
|
|
5496
|
+
*/
|
|
5497
|
+
function normalizeNodeId(raw) {
|
|
5498
|
+
if (raw === void 0 || raw === null || raw === "") return "hub";
|
|
5499
|
+
const slashIdx = raw.indexOf("/");
|
|
5500
|
+
if (slashIdx < 0) return raw;
|
|
5501
|
+
const bare = raw.slice(0, slashIdx);
|
|
5502
|
+
return bare === "" ? "hub" : bare;
|
|
5503
|
+
}
|
|
5504
|
+
/** The node-scoped store key for a per-node field: `<base>@<nodeId>`. */
|
|
5505
|
+
function nodeScopedKey(base, nodeId) {
|
|
5506
|
+
return `${base}@${normalizeNodeId(nodeId)}`;
|
|
5507
|
+
}
|
|
5508
|
+
/**
|
|
5509
|
+
* Read a node's value for a per-node field from the raw shared store:
|
|
5510
|
+
* the node-scoped key when present, otherwise `undefined`.
|
|
5511
|
+
* Deliberately NO bare-key fallback (see module doc) — the caller lets the
|
|
5512
|
+
* schema `default` win on `undefined`.
|
|
5513
|
+
*/
|
|
5514
|
+
function readNodeValue(store, base, nodeId) {
|
|
5515
|
+
return store[nodeScopedKey(base, nodeId)];
|
|
5516
|
+
}
|
|
5517
|
+
/**
|
|
5518
|
+
* Re-map a UI/settings patch so every bare perNode field persists under the
|
|
5519
|
+
* TARGET node's scoped key; all other keys pass through unchanged. Used on
|
|
5520
|
+
* the write path so a save for one node never clobbers another node's value
|
|
5521
|
+
* (and the bare key is never written). Returns a new object — the input
|
|
5522
|
+
* patch is not mutated.
|
|
5523
|
+
*/
|
|
5524
|
+
function scopePatch(patch, perNodeKeys, nodeId) {
|
|
5525
|
+
const out = {};
|
|
5526
|
+
for (const [key, value] of Object.entries(patch)) out[perNodeKeys.has(key) ? nodeScopedKey(key, nodeId) : key] = value;
|
|
5527
|
+
return out;
|
|
5528
|
+
}
|
|
5529
|
+
/**
|
|
5530
|
+
* Project the raw shared store onto the bare schema keys for ONE node so a
|
|
5531
|
+
* UI schema (whose field keys are bare) hydrates from that node's own
|
|
5532
|
+
* values:
|
|
5533
|
+
*
|
|
5534
|
+
* - EVERY `@`-scoped key (any node's) is dropped from the pass-through.
|
|
5535
|
+
* - Every bare perNode key is dropped from the pass-through (a stray bare
|
|
5536
|
+
* legacy key must never hydrate any node — no bare fallback).
|
|
5537
|
+
* - THIS node's effective value ({@link readNodeValue}) is then laid onto
|
|
5538
|
+
* each bare perNode key; when the node has no scoped key the bare key is
|
|
5539
|
+
* left ABSENT so the field's schema `default` wins.
|
|
5540
|
+
*
|
|
5541
|
+
* Returns a new object — the input store is not mutated.
|
|
5542
|
+
*/
|
|
5543
|
+
function projectStore(store, perNodeKeys, nodeId) {
|
|
5544
|
+
const out = {};
|
|
5545
|
+
for (const [key, value] of Object.entries(store)) {
|
|
5546
|
+
if (key.includes("@")) continue;
|
|
5547
|
+
if (perNodeKeys.has(key)) continue;
|
|
5548
|
+
out[key] = value;
|
|
5549
|
+
}
|
|
5550
|
+
for (const base of perNodeKeys) {
|
|
5551
|
+
const value = readNodeValue(store, base, nodeId);
|
|
5552
|
+
if (value !== void 0) out[base] = value;
|
|
5553
|
+
}
|
|
5554
|
+
return out;
|
|
5555
|
+
}
|
|
5556
|
+
/**
|
|
5463
5557
|
* Base class for CamStack addons. Eliminates settings boilerplate:
|
|
5464
5558
|
*
|
|
5465
5559
|
* - Typed `config` property with automatic resolution from store + defaults
|
|
@@ -5627,23 +5721,63 @@ var BaseAddon = class {
|
|
|
5627
5721
|
deviceSettingsSchema() {
|
|
5628
5722
|
return null;
|
|
5629
5723
|
}
|
|
5630
|
-
async getGlobalSettings(overlay, cap,
|
|
5724
|
+
async getGlobalSettings(overlay, cap, nodeId) {
|
|
5631
5725
|
const schema = this.globalSettingsSchema(cap);
|
|
5632
5726
|
if (!schema) return { sections: [] };
|
|
5633
|
-
const
|
|
5727
|
+
const projected = await this.resolveGlobalStore(nodeId, cap);
|
|
5634
5728
|
return hydrateSchema(schema, overlay ? {
|
|
5635
|
-
...
|
|
5729
|
+
...projected,
|
|
5636
5730
|
...overlay
|
|
5637
|
-
} :
|
|
5731
|
+
} : projected);
|
|
5638
5732
|
}
|
|
5639
|
-
|
|
5640
|
-
|
|
5733
|
+
/**
|
|
5734
|
+
* The raw addon store PROJECTED onto the target node's bare per-node keys:
|
|
5735
|
+
* every `perNode: true` field carries THAT node's scoped value on its bare
|
|
5736
|
+
* key (absent scoped key ⇒ key absent, so the schema `default` wins — no
|
|
5737
|
+
* bare fallback), all `@`-scoped and stray bare per-node keys are dropped.
|
|
5738
|
+
* A no-op passthrough when the schema declares no `perNode` field.
|
|
5739
|
+
*
|
|
5740
|
+
* This is the sanctioned way for a `getGlobalSettings` OVERRIDE that needs
|
|
5741
|
+
* the store for custom option logic (option narrowing, value snapping) to
|
|
5742
|
+
* read it per-node — never `ctx.settings.readAddonStore()` directly.
|
|
5743
|
+
* `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
|
|
5744
|
+
*/
|
|
5745
|
+
async resolveGlobalStore(nodeId, cap) {
|
|
5746
|
+
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
5747
|
+
const keys = this.perNodeKeys(cap);
|
|
5748
|
+
const node = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
5749
|
+
return keys.size > 0 ? projectStore(raw, keys, node) : raw;
|
|
5750
|
+
}
|
|
5751
|
+
async updateGlobalSettings(patch, nodeId) {
|
|
5752
|
+
const keys = this.perNodeKeys();
|
|
5753
|
+
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
5754
|
+
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
5755
|
+
const barePatch = patch;
|
|
5756
|
+
const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
|
|
5757
|
+
await this._ctx?.settings?.writeAddonStore(scoped);
|
|
5758
|
+
if (target !== localNode) return;
|
|
5641
5759
|
await this.resolveConfig();
|
|
5642
5760
|
await this.onConfigChanged();
|
|
5643
5761
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
5644
5762
|
this.maybeAutoRestart(patch, this.globalSettingsSchema());
|
|
5645
5763
|
}
|
|
5646
5764
|
/**
|
|
5765
|
+
* The set of field keys the global settings schema declares `perNode: true`
|
|
5766
|
+
* — derived once per `cap` argument and memoized (schemas are static
|
|
5767
|
+
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
5768
|
+
* settings API behaves exactly like the legacy node-agnostic one.
|
|
5769
|
+
*/
|
|
5770
|
+
_perNodeKeysCache = /* @__PURE__ */ new Map();
|
|
5771
|
+
perNodeKeys(cap) {
|
|
5772
|
+
const cacheKey = cap ?? "";
|
|
5773
|
+
const cached = this._perNodeKeysCache.get(cacheKey);
|
|
5774
|
+
if (cached) return cached;
|
|
5775
|
+
const schema = this.globalSettingsSchema(cap);
|
|
5776
|
+
const keys = new Set(schema ? schema.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
5777
|
+
this._perNodeKeysCache.set(cacheKey, keys);
|
|
5778
|
+
return keys;
|
|
5779
|
+
}
|
|
5780
|
+
/**
|
|
5647
5781
|
* If any field in `patch` is marked `requiresRestart` in `schema`,
|
|
5648
5782
|
* schedule an addon restart for the next tick. Deferred via
|
|
5649
5783
|
* `setImmediate` so the tRPC mutation that triggered the write has
|
|
@@ -5796,12 +5930,19 @@ var BaseAddon = class {
|
|
|
5796
5930
|
* The merge is shallow: each key in `defaults` is checked against the store.
|
|
5797
5931
|
* Only keys present in defaults are read — the store can contain extra keys
|
|
5798
5932
|
* (e.g. from older versions) without polluting the typed config.
|
|
5933
|
+
*
|
|
5934
|
+
* Keys the global settings schema declares `perNode: true` resolve from
|
|
5935
|
+
* THIS node's scoped key (`<key>@<localNode>`) via `readNodeValue` — never
|
|
5936
|
+
* from the bare key — so a per-node field resolves to this node's own
|
|
5937
|
+
* selection at boot (absent scoped key ⇒ the constructor default wins).
|
|
5799
5938
|
*/
|
|
5800
5939
|
async resolveConfig() {
|
|
5801
5940
|
const stored = await this.readAddonStoreWithRetry();
|
|
5941
|
+
const perNode = this.perNodeKeys();
|
|
5942
|
+
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
5802
5943
|
const resolved = { ...this.defaults };
|
|
5803
5944
|
for (const key of Object.keys(this.defaults)) {
|
|
5804
|
-
const storedValue = stored[key];
|
|
5945
|
+
const storedValue = perNode.has(key) ? readNodeValue(stored, key, localNode) : stored[key];
|
|
5805
5946
|
if (storedValue !== void 0 && storedValue !== null) {
|
|
5806
5947
|
const defaultType = typeof this.defaults[key];
|
|
5807
5948
|
if (typeof storedValue === defaultType) resolved[key] = storedValue;
|
|
@@ -5885,6 +6026,27 @@ var BaseAddon = class {
|
|
|
5885
6026
|
}
|
|
5886
6027
|
};
|
|
5887
6028
|
/**
|
|
6029
|
+
* Collect the keys of every field marked `perNode: true`, recursing into
|
|
6030
|
+
* layout containers (`group` fields and `sub-tabs` tabs) the same way
|
|
6031
|
+
* `hydrateSchema` does. Valueless structural fields (separator/info/…)
|
|
6032
|
+
* don't declare `perNode` and are excluded by the `in` narrowing.
|
|
6033
|
+
*/
|
|
6034
|
+
function collectPerNodeFieldKeys(fields) {
|
|
6035
|
+
const collected = [];
|
|
6036
|
+
for (const field of fields) {
|
|
6037
|
+
if (field.type === "group") {
|
|
6038
|
+
collected.push(...collectPerNodeFieldKeys(field.fields));
|
|
6039
|
+
continue;
|
|
6040
|
+
}
|
|
6041
|
+
if (field.type === "sub-tabs") {
|
|
6042
|
+
for (const tab of field.tabs) collected.push(...collectPerNodeFieldKeys(tab.fields));
|
|
6043
|
+
continue;
|
|
6044
|
+
}
|
|
6045
|
+
if ("perNode" in field && field.perNode === true) collected.push(field.key);
|
|
6046
|
+
}
|
|
6047
|
+
return collected;
|
|
6048
|
+
}
|
|
6049
|
+
/**
|
|
5888
6050
|
* Normalize an `ICamstackAddon.initialize()` return value into the
|
|
5889
6051
|
* `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
|
|
5890
6052
|
* envelopes pass through; void stays void.
|
|
@@ -6652,6 +6814,13 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
6652
6814
|
/** Single still-image entity (HA `image.*`). Read-only display of an
|
|
6653
6815
|
* `entity_picture` signed URL the browser loads directly. `image` cap. */
|
|
6654
6816
|
DeviceType["Image"] = "image";
|
|
6817
|
+
/** Smart pet feeder — cloud-connected food dispenser with a bowl food
|
|
6818
|
+
* level, battery, desiccant life, feeding state and manual-feed /
|
|
6819
|
+
* call-pet / maintenance actions. Installed with the `pet-feeder` cap;
|
|
6820
|
+
* dual-hopper models (D4S/D4SH) expose per-hopper portions. Sources:
|
|
6821
|
+
* native PetKit (`nodepetkit` `FeederDevice`), reusable by other feeder
|
|
6822
|
+
* integrations sharing the same food/desiccant/hopper surface. */
|
|
6823
|
+
DeviceType["PetFeeder"] = "pet-feeder";
|
|
6655
6824
|
return DeviceType;
|
|
6656
6825
|
}({});
|
|
6657
6826
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -10364,7 +10533,8 @@ var MotionAnalysisResultSchema = object({
|
|
|
10364
10533
|
});
|
|
10365
10534
|
method(object({
|
|
10366
10535
|
deviceId: number(),
|
|
10367
|
-
frame: FrameInputSchema
|
|
10536
|
+
frame: FrameInputSchema.optional(),
|
|
10537
|
+
frameHandle: FrameHandleSchema.optional()
|
|
10368
10538
|
}), MotionAnalysisResultSchema, { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), object({
|
|
10369
10539
|
deviceId: number(),
|
|
10370
10540
|
detected: boolean(),
|
|
@@ -10671,11 +10841,20 @@ var pipelineExecutorCapability = {
|
|
|
10671
10841
|
* legacy call shape used by existing benchmark code; once all
|
|
10672
10842
|
* callers pass it explicitly we make it required.
|
|
10673
10843
|
*
|
|
10674
|
-
* Exactly one of `frame`, `
|
|
10675
|
-
* provided:
|
|
10844
|
+
* Exactly one of `frame`, `frameHandle`, `imageBase64`,
|
|
10845
|
+
* `referenceImage` must be provided:
|
|
10676
10846
|
* - `frame`: runtime dispatch path (runner → decoded broker frame).
|
|
10677
10847
|
* Carries the raw buffer, dimensions, and format; the executor
|
|
10678
10848
|
* uses it directly without base64 round-tripping.
|
|
10849
|
+
* - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
|
|
10850
|
+
* decoded frame. Both runner and executor are hub-local processes
|
|
10851
|
+
* sharing `/dev/shm`, so the executor maps the named segment and
|
|
10852
|
+
* reads the pixels back zero-copy — eliminating the ~1.2MB
|
|
10853
|
+
* re-serialisation over UDS/MsgPack the `frame` path pays per call.
|
|
10854
|
+
* High-risk: the FrameRing is a latest-wins seqlock with no
|
|
10855
|
+
* refcount, so a recycled slot yields a null read; the executor
|
|
10856
|
+
* then degrades to an empty result and the runner ships pixels via
|
|
10857
|
+
* `frame` as the fallback (queue-depth gated on the runner side).
|
|
10679
10858
|
* - `imageBase64`: one-shot test path (benchmark ImageTab).
|
|
10680
10859
|
* - `referenceImage`: named file from the reference-image store.
|
|
10681
10860
|
*/
|
|
@@ -10683,6 +10862,12 @@ var pipelineExecutorCapability = {
|
|
|
10683
10862
|
engine: PipelineEngineChoiceSchema.optional(),
|
|
10684
10863
|
steps: array(PipelineStepInputSchema).min(1),
|
|
10685
10864
|
frame: FrameInputSchema.optional(),
|
|
10865
|
+
/**
|
|
10866
|
+
* CB5 shm passthrough — a `FrameHandle` naming the same ring slot
|
|
10867
|
+
* the decoded pixels live in. One more member of the one-of
|
|
10868
|
+
* frame/frameHandle/image/imageBase64/referenceImage group.
|
|
10869
|
+
*/
|
|
10870
|
+
frameHandle: FrameHandleSchema.optional(),
|
|
10686
10871
|
imageBase64: string().optional(),
|
|
10687
10872
|
/**
|
|
10688
10873
|
* Binary JPEG bytes — preferred over `imageBase64` on internal
|
|
@@ -11576,6 +11761,113 @@ object({
|
|
|
11576
11761
|
lastFetchedAt: number()
|
|
11577
11762
|
});
|
|
11578
11763
|
DeviceType.Sensor;
|
|
11764
|
+
/**
|
|
11765
|
+
* Feeder connectivity / power status — mirrors the HA petkit device-status
|
|
11766
|
+
* enum: `normal` (online, mains), `offline` (not reaching PetKit cloud),
|
|
11767
|
+
* `on_batteries` (running on battery backup). `null` until first reported.
|
|
11768
|
+
*/
|
|
11769
|
+
var PetFeederDeviceStatusSchema = _enum([
|
|
11770
|
+
"normal",
|
|
11771
|
+
"offline",
|
|
11772
|
+
"on_batteries"
|
|
11773
|
+
]);
|
|
11774
|
+
var gramsPortion = number().int().min(4).max(200);
|
|
11775
|
+
object({
|
|
11776
|
+
/** Food currently in the bowl (grams). Null when the device has not
|
|
11777
|
+
* reported a reading yet. On dual-hopper models this is the combined
|
|
11778
|
+
* bowl reading; per-hopper levels live in `food1`/`food2`. */
|
|
11779
|
+
foodLevel: number().nullable(),
|
|
11780
|
+
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
11781
|
+
* single-hopper models. */
|
|
11782
|
+
food1: number().nullable(),
|
|
11783
|
+
/** Hopper-2 food level (grams) on dual-hopper feeders; null on
|
|
11784
|
+
* single-hopper models. */
|
|
11785
|
+
food2: number().nullable(),
|
|
11786
|
+
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
11787
|
+
* (`device_class: problem`, on = low). True when the bowl is empty /
|
|
11788
|
+
* below the feeder's low threshold. */
|
|
11789
|
+
lowFood: boolean(),
|
|
11790
|
+
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
11791
|
+
* device has no battery reading. */
|
|
11792
|
+
batteryPower: number().min(0).max(100).nullable(),
|
|
11793
|
+
/** Days of desiccant life remaining. Null when the model has no
|
|
11794
|
+
* desiccant sensor. */
|
|
11795
|
+
desiccantLeftDays: number().nullable(),
|
|
11796
|
+
/** True while a feed is in progress. */
|
|
11797
|
+
feeding: boolean(),
|
|
11798
|
+
/** Decoded connectivity / power status (HA petkit device-status enum).
|
|
11799
|
+
* Null until the device has reported a status. */
|
|
11800
|
+
status: PetFeederDeviceStatusSchema.nullable(),
|
|
11801
|
+
/** Decoded human-readable fault message. Null (or the device's `no_error`
|
|
11802
|
+
* sentinel) means healthy; a non-null string is an active fault. Pairs
|
|
11803
|
+
* with `errorCode` for consumers that want the raw integer. */
|
|
11804
|
+
error: string().nullable(),
|
|
11805
|
+
/** Raw device error code (0 / null = no error). */
|
|
11806
|
+
errorCode: number().nullable(),
|
|
11807
|
+
/** True for D4S/D4SH dual-hopper hardware — gates the per-hopper UI. */
|
|
11808
|
+
isDualHopper: boolean(),
|
|
11809
|
+
/** Child-lock (manual-lock) setting — buttons on the unit are disabled. */
|
|
11810
|
+
childLock: boolean(),
|
|
11811
|
+
/** Front indicator-light setting. */
|
|
11812
|
+
indicatorLight: boolean(),
|
|
11813
|
+
/** Play a chime when dispensing. */
|
|
11814
|
+
feedSound: boolean(),
|
|
11815
|
+
/** Speaker / prompt volume level (device-scaled integer). */
|
|
11816
|
+
volume: number(),
|
|
11817
|
+
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
11818
|
+
lastFetchedAt: number()
|
|
11819
|
+
});
|
|
11820
|
+
DeviceType.PetFeeder, method(object({
|
|
11821
|
+
deviceId: number().int().nonnegative(),
|
|
11822
|
+
grams: gramsPortion.optional(),
|
|
11823
|
+
hopper1: gramsPortion.optional(),
|
|
11824
|
+
hopper2: gramsPortion.optional()
|
|
11825
|
+
}), _void(), {
|
|
11826
|
+
kind: "mutation",
|
|
11827
|
+
auth: "admin"
|
|
11828
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
11829
|
+
kind: "mutation",
|
|
11830
|
+
auth: "admin"
|
|
11831
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
11832
|
+
kind: "mutation",
|
|
11833
|
+
auth: "admin"
|
|
11834
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
11835
|
+
kind: "mutation",
|
|
11836
|
+
auth: "admin"
|
|
11837
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
11838
|
+
kind: "mutation",
|
|
11839
|
+
auth: "admin"
|
|
11840
|
+
}), method(object({
|
|
11841
|
+
deviceId: number().int().nonnegative(),
|
|
11842
|
+
soundId: number().int().nonnegative()
|
|
11843
|
+
}), _void(), {
|
|
11844
|
+
kind: "mutation",
|
|
11845
|
+
auth: "admin"
|
|
11846
|
+
}), method(object({
|
|
11847
|
+
deviceId: number().int().nonnegative(),
|
|
11848
|
+
on: boolean()
|
|
11849
|
+
}), _void(), {
|
|
11850
|
+
kind: "mutation",
|
|
11851
|
+
auth: "admin"
|
|
11852
|
+
}), method(object({
|
|
11853
|
+
deviceId: number().int().nonnegative(),
|
|
11854
|
+
on: boolean()
|
|
11855
|
+
}), _void(), {
|
|
11856
|
+
kind: "mutation",
|
|
11857
|
+
auth: "admin"
|
|
11858
|
+
}), method(object({
|
|
11859
|
+
deviceId: number().int().nonnegative(),
|
|
11860
|
+
on: boolean()
|
|
11861
|
+
}), _void(), {
|
|
11862
|
+
kind: "mutation",
|
|
11863
|
+
auth: "admin"
|
|
11864
|
+
}), method(object({
|
|
11865
|
+
deviceId: number().int().nonnegative(),
|
|
11866
|
+
level: number().int().nonnegative()
|
|
11867
|
+
}), _void(), {
|
|
11868
|
+
kind: "mutation",
|
|
11869
|
+
auth: "admin"
|
|
11870
|
+
});
|
|
11579
11871
|
object({
|
|
11580
11872
|
/** Instantaneous power draw in watts. */
|
|
11581
11873
|
watts: number().optional(),
|
|
@@ -13509,10 +13801,12 @@ method(object({ codec: string() }), boolean()), method(_void(), object({
|
|
|
13509
13801
|
url: string()
|
|
13510
13802
|
}), _void()), method(object({
|
|
13511
13803
|
sessionId: string(),
|
|
13512
|
-
maxCount: number().default(1)
|
|
13804
|
+
maxCount: number().default(1),
|
|
13805
|
+
waitMs: number().optional()
|
|
13513
13806
|
}), array(DecodedFrameSchema)), method(object({
|
|
13514
13807
|
sessionId: string(),
|
|
13515
|
-
maxCount: number().default(1)
|
|
13808
|
+
maxCount: number().default(1),
|
|
13809
|
+
waitMs: number().optional()
|
|
13516
13810
|
}), array(FrameHandleSchema)), method(object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()), method(object({ sessionId: string() }), ShmRingStatsSchema.nullable()), method(object({
|
|
13517
13811
|
sessionId: string(),
|
|
13518
13812
|
config: DecoderSessionConfigSchema.partial()
|
|
@@ -15484,7 +15778,10 @@ var AgentLoadSummarySchema = object({
|
|
|
15484
15778
|
online: boolean(),
|
|
15485
15779
|
load: RunnerLocalLoadSchema,
|
|
15486
15780
|
/** Computed score used by the L2 capacity balancer (lower = less loaded). */
|
|
15487
|
-
score: number()
|
|
15781
|
+
score: number(),
|
|
15782
|
+
/** This node's decode hwaccel backend (per-node `probedBestHwaccel`), or null
|
|
15783
|
+
* when not yet probed — for the cluster Pipeline table UI (P0.2). */
|
|
15784
|
+
decodeHwaccel: string().nullable()
|
|
15488
15785
|
});
|
|
15489
15786
|
/**
|
|
15490
15787
|
* Aggregate metrics across the whole detection cluster. Replaces the legacy
|
|
@@ -18177,7 +18474,10 @@ var HwAccelBackendInputSchema = _enum([
|
|
|
18177
18474
|
"webgpu",
|
|
18178
18475
|
"none"
|
|
18179
18476
|
]).nullable().optional();
|
|
18180
|
-
var HwAccelResolutionSchema = object({
|
|
18477
|
+
var HwAccelResolutionSchema = object({
|
|
18478
|
+
preferred: array(string()).readonly(),
|
|
18479
|
+
rationale: string()
|
|
18480
|
+
});
|
|
18181
18481
|
var HardwareEncoderIdSchema = _enum([
|
|
18182
18482
|
"h264_videotoolbox",
|
|
18183
18483
|
"hevc_videotoolbox",
|
|
@@ -18192,7 +18492,7 @@ var HardwareEncoderIdSchema = _enum([
|
|
|
18192
18492
|
"libx264",
|
|
18193
18493
|
"libx265"
|
|
18194
18494
|
]);
|
|
18195
|
-
|
|
18495
|
+
object({
|
|
18196
18496
|
encoders: array(object({
|
|
18197
18497
|
encoder: HardwareEncoderIdSchema,
|
|
18198
18498
|
codec: _enum(["H264", "H265"]),
|
|
@@ -18211,15 +18511,7 @@ var HardwareEncodersSchema = object({
|
|
|
18211
18511
|
defaultH265: HardwareEncoderIdSchema,
|
|
18212
18512
|
probedAt: number()
|
|
18213
18513
|
});
|
|
18214
|
-
|
|
18215
|
-
* Decode-side companion to {@link HardwareEncodersSchema}: the `-hwaccel`
|
|
18216
|
-
* methods the configured ffmpeg binary actually supports (parsed from
|
|
18217
|
-
* `ffmpeg -hwaccels`). Used to gate decode-hwaccel attempts so the transcode
|
|
18218
|
-
* egress never spends a spawn on a backend this build cannot offer. Per-stream
|
|
18219
|
-
* decode fragility (e.g. VideoToolbox HEVC) is still handled by the egress's
|
|
18220
|
-
* software fallback — this only filters out wholly-unsupported backends.
|
|
18221
|
-
*/
|
|
18222
|
-
var HardwareDecodeAccelsSchema = object({
|
|
18514
|
+
object({
|
|
18223
18515
|
methods: array(string()).readonly(),
|
|
18224
18516
|
probedAt: number()
|
|
18225
18517
|
});
|
|
@@ -18282,16 +18574,7 @@ var ResolvedInferenceConfigSchema = object({
|
|
|
18282
18574
|
format: ModelFormatSchema,
|
|
18283
18575
|
reason: string()
|
|
18284
18576
|
});
|
|
18285
|
-
method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema), method(object({ requirements: array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema), method(object({
|
|
18286
|
-
prefer: HwAccelBackendInputSchema,
|
|
18287
|
-
nodeId: string().optional()
|
|
18288
|
-
}), HwAccelResolutionSchema), method(_void(), HardwareEncodersSchema), method(_void(), HardwareEncodersSchema, {
|
|
18289
|
-
kind: "mutation",
|
|
18290
|
-
auth: "admin"
|
|
18291
|
-
}), method(_void(), HardwareDecodeAccelsSchema), method(_void(), HardwareDecodeAccelsSchema, {
|
|
18292
|
-
kind: "mutation",
|
|
18293
|
-
auth: "admin"
|
|
18294
|
-
});
|
|
18577
|
+
method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema), method(object({ requirements: array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema), method(object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema);
|
|
18295
18578
|
var PtzPresetSchema = object({
|
|
18296
18579
|
id: string(),
|
|
18297
18580
|
name: string()
|
|
@@ -21250,6 +21533,66 @@ Object.freeze({
|
|
|
21250
21533
|
addonId: null,
|
|
21251
21534
|
access: "create"
|
|
21252
21535
|
},
|
|
21536
|
+
"petFeeder.callPet": {
|
|
21537
|
+
capName: "pet-feeder",
|
|
21538
|
+
capScope: "device",
|
|
21539
|
+
addonId: null,
|
|
21540
|
+
access: "create"
|
|
21541
|
+
},
|
|
21542
|
+
"petFeeder.cancelFeed": {
|
|
21543
|
+
capName: "pet-feeder",
|
|
21544
|
+
capScope: "device",
|
|
21545
|
+
addonId: null,
|
|
21546
|
+
access: "create"
|
|
21547
|
+
},
|
|
21548
|
+
"petFeeder.feed": {
|
|
21549
|
+
capName: "pet-feeder",
|
|
21550
|
+
capScope: "device",
|
|
21551
|
+
addonId: null,
|
|
21552
|
+
access: "create"
|
|
21553
|
+
},
|
|
21554
|
+
"petFeeder.markFoodReplenished": {
|
|
21555
|
+
capName: "pet-feeder",
|
|
21556
|
+
capScope: "device",
|
|
21557
|
+
addonId: null,
|
|
21558
|
+
access: "create"
|
|
21559
|
+
},
|
|
21560
|
+
"petFeeder.playSound": {
|
|
21561
|
+
capName: "pet-feeder",
|
|
21562
|
+
capScope: "device",
|
|
21563
|
+
addonId: null,
|
|
21564
|
+
access: "create"
|
|
21565
|
+
},
|
|
21566
|
+
"petFeeder.resetDesiccant": {
|
|
21567
|
+
capName: "pet-feeder",
|
|
21568
|
+
capScope: "device",
|
|
21569
|
+
addonId: null,
|
|
21570
|
+
access: "delete"
|
|
21571
|
+
},
|
|
21572
|
+
"petFeeder.setChildLock": {
|
|
21573
|
+
capName: "pet-feeder",
|
|
21574
|
+
capScope: "device",
|
|
21575
|
+
addonId: null,
|
|
21576
|
+
access: "create"
|
|
21577
|
+
},
|
|
21578
|
+
"petFeeder.setFeedSound": {
|
|
21579
|
+
capName: "pet-feeder",
|
|
21580
|
+
capScope: "device",
|
|
21581
|
+
addonId: null,
|
|
21582
|
+
access: "create"
|
|
21583
|
+
},
|
|
21584
|
+
"petFeeder.setIndicatorLight": {
|
|
21585
|
+
capName: "pet-feeder",
|
|
21586
|
+
capScope: "device",
|
|
21587
|
+
addonId: null,
|
|
21588
|
+
access: "create"
|
|
21589
|
+
},
|
|
21590
|
+
"petFeeder.setVolume": {
|
|
21591
|
+
capName: "pet-feeder",
|
|
21592
|
+
capScope: "device",
|
|
21593
|
+
addonId: null,
|
|
21594
|
+
access: "create"
|
|
21595
|
+
},
|
|
21253
21596
|
"pipelineAnalytics.clearTracks": {
|
|
21254
21597
|
capName: "pipeline-analytics",
|
|
21255
21598
|
capScope: "device",
|
|
@@ -21856,30 +22199,6 @@ Object.freeze({
|
|
|
21856
22199
|
addonId: null,
|
|
21857
22200
|
access: "view"
|
|
21858
22201
|
},
|
|
21859
|
-
"platformProbe.getHardwareDecodeAccels": {
|
|
21860
|
-
capName: "platform-probe",
|
|
21861
|
-
capScope: "system",
|
|
21862
|
-
addonId: null,
|
|
21863
|
-
access: "view"
|
|
21864
|
-
},
|
|
21865
|
-
"platformProbe.getHardwareEncoders": {
|
|
21866
|
-
capName: "platform-probe",
|
|
21867
|
-
capScope: "system",
|
|
21868
|
-
addonId: null,
|
|
21869
|
-
access: "view"
|
|
21870
|
-
},
|
|
21871
|
-
"platformProbe.refreshHardwareDecodeAccels": {
|
|
21872
|
-
capName: "platform-probe",
|
|
21873
|
-
capScope: "system",
|
|
21874
|
-
addonId: null,
|
|
21875
|
-
access: "create"
|
|
21876
|
-
},
|
|
21877
|
-
"platformProbe.refreshHardwareEncoders": {
|
|
21878
|
-
capName: "platform-probe",
|
|
21879
|
-
capScope: "system",
|
|
21880
|
-
addonId: null,
|
|
21881
|
-
access: "create"
|
|
21882
|
-
},
|
|
21883
22202
|
"platformProbe.resolveHwAccel": {
|
|
21884
22203
|
capName: "platform-probe",
|
|
21885
22204
|
capScope: "system",
|
|
@@ -24622,6 +24941,14 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24622
24941
|
if (!this._cameraSettingsState) this._cameraSettingsState = this.state("cameraSettings", CameraSettingsMapSchema, {});
|
|
24623
24942
|
return this._cameraSettingsState;
|
|
24624
24943
|
}
|
|
24944
|
+
/** One-shot `migrateLegacyFlagsToBindings` guard flag. Absent or
|
|
24945
|
+
* corrupt ⇒ `false` (the migration runs — same fallback as the old
|
|
24946
|
+
* raw `store[key] === true` check). */
|
|
24947
|
+
_bindingsMigrationDoneState = null;
|
|
24948
|
+
get bindingsMigrationDoneState() {
|
|
24949
|
+
if (!this._bindingsMigrationDoneState) this._bindingsMigrationDoneState = this.state("bindingsMigration_v1_done", boolean(), false);
|
|
24950
|
+
return this._bindingsMigrationDoneState;
|
|
24951
|
+
}
|
|
24625
24952
|
/**
|
|
24626
24953
|
* Per-camera zones CRUD provider. Constructed lazily in `onInitialize`
|
|
24627
24954
|
* because it captures `this.ctx` for settings + api access; cleared
|
|
@@ -24801,7 +25128,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
24801
25128
|
async onInitialize() {
|
|
24802
25129
|
this.initTimestamp = Date.now();
|
|
24803
25130
|
try {
|
|
24804
|
-
const stored = await this.
|
|
25131
|
+
const stored = await this.resolveGlobalStore();
|
|
24805
25132
|
this.globalSettings = { ...stored };
|
|
24806
25133
|
this.applyRuntimeSettings(this.globalSettings);
|
|
24807
25134
|
} catch (err) {
|
|
@@ -25013,7 +25340,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25013
25340
|
this.pendingRetryTimer = setInterval(() => void this.retryPendingDispatches(), PENDING_RETRY_INTERVAL_MS);
|
|
25014
25341
|
this.ctx.logger.info("Pipeline orchestrator detection-wiring subscriptions installed");
|
|
25015
25342
|
this.migrateLegacyFlagsToBindings().catch((err) => {
|
|
25016
|
-
this.
|
|
25343
|
+
this.ctxIfReady?.logger.warn("bindings migration failed — will retry on next boot", { meta: { error: errMsg(err) } });
|
|
25017
25344
|
});
|
|
25018
25345
|
this.zoneRulesProvider = new ZoneRulesProvider({
|
|
25019
25346
|
logger: this.ctx.logger.child("zone-rules"),
|
|
@@ -25143,9 +25470,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25143
25470
|
* persist the flag after a successful pass.
|
|
25144
25471
|
*/
|
|
25145
25472
|
async migrateLegacyFlagsToBindings() {
|
|
25146
|
-
|
|
25147
|
-
const store = await this.ctx.settings?.readAddonStore() ?? {};
|
|
25148
|
-
if (store[MIGRATION_KEY] === true) return;
|
|
25473
|
+
if (await this.bindingsMigrationDoneState.get()) return;
|
|
25149
25474
|
let api = this.api;
|
|
25150
25475
|
for (let i = 0; !api && i < 30; i++) {
|
|
25151
25476
|
await new Promise((r) => setTimeout(r, 200));
|
|
@@ -25201,10 +25526,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25201
25526
|
});
|
|
25202
25527
|
detectionDisabled++;
|
|
25203
25528
|
}
|
|
25204
|
-
await this.
|
|
25205
|
-
...store,
|
|
25206
|
-
[MIGRATION_KEY]: true
|
|
25207
|
-
});
|
|
25529
|
+
await this.bindingsMigrationDoneState.set(true);
|
|
25208
25530
|
this.ctx.logger.info("bindings migration complete", { meta: {
|
|
25209
25531
|
cameras: cameras.length,
|
|
25210
25532
|
audioDisabled,
|
|
@@ -25609,6 +25931,7 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25609
25931
|
* re-dispatching).
|
|
25610
25932
|
*/
|
|
25611
25933
|
async getAgentLoad() {
|
|
25934
|
+
await this.refreshDecodeHwaccels();
|
|
25612
25935
|
await this.collectAgentLoad();
|
|
25613
25936
|
return [...this.cachedAgentLoad.values()];
|
|
25614
25937
|
}
|
|
@@ -25874,11 +26197,48 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
25874
26197
|
nodeId: load.nodeId,
|
|
25875
26198
|
online: true,
|
|
25876
26199
|
load,
|
|
25877
|
-
score: computeCapacityScore(load)
|
|
26200
|
+
score: computeCapacityScore(load),
|
|
26201
|
+
decodeHwaccel: this.decodeHwaccelByNode.get(load.nodeId) ?? null
|
|
25878
26202
|
});
|
|
25879
26203
|
this.cachedAgentLoad = next;
|
|
25880
26204
|
}
|
|
25881
26205
|
/**
|
|
26206
|
+
* Per-node decode hwaccel cache (`nodeId` → backend), refreshed by
|
|
26207
|
+
* {@link refreshDecodeHwaccels} before a UI-facing {@link getAgentLoad}. Read
|
|
26208
|
+
* from the decoder addon's per-node `probedBestHwaccel@<node>` setting — a
|
|
26209
|
+
* node-LOCAL probe published to a per-node setting — via `addonSettings`,
|
|
26210
|
+
* NEVER the `platform-probe` singleton (whose pin answers with hub hardware).
|
|
26211
|
+
*/
|
|
26212
|
+
decodeHwaccelByNode = /* @__PURE__ */ new Map();
|
|
26213
|
+
/** Refresh {@link decodeHwaccelByNode} for every known runner node. */
|
|
26214
|
+
async refreshDecodeHwaccels() {
|
|
26215
|
+
await Promise.all([...this.knownRunnerNodes].map(async (nodeId) => {
|
|
26216
|
+
const hwaccel = await this.readNodeDecodeHwaccel(nodeId);
|
|
26217
|
+
if (hwaccel !== void 0) this.decodeHwaccelByNode.set(nodeId, hwaccel);
|
|
26218
|
+
}));
|
|
26219
|
+
}
|
|
26220
|
+
/**
|
|
26221
|
+
* Read a node's `probedBestHwaccel` from the decoder-ffmpeg addon's per-node
|
|
26222
|
+
* settings via the hub-routed `addonSettings` cap. Returns `null` when the
|
|
26223
|
+
* value is empty/unset, `undefined` on a read failure (keep the last known).
|
|
26224
|
+
* Mirrors {@link readDetectionPipelineEngine}.
|
|
26225
|
+
*/
|
|
26226
|
+
async readNodeDecodeHwaccel(nodeId) {
|
|
26227
|
+
const api = this.ctx.api;
|
|
26228
|
+
if (!api?.addonSettings) return void 0;
|
|
26229
|
+
try {
|
|
26230
|
+
const schema = await api.addonSettings.getGlobalSettings.query({
|
|
26231
|
+
addonId: "decoder-ffmpeg",
|
|
26232
|
+
nodeId
|
|
26233
|
+
});
|
|
26234
|
+
if (!schema) return void 0;
|
|
26235
|
+
for (const s of schema.sections) for (const f of s.fields) if (f.key === "probedBestHwaccel") return typeof f.value === "string" && f.value.length > 0 ? f.value : null;
|
|
26236
|
+
return null;
|
|
26237
|
+
} catch {
|
|
26238
|
+
return;
|
|
26239
|
+
}
|
|
26240
|
+
}
|
|
26241
|
+
/**
|
|
25882
26242
|
* Per-node `getLocalLoad` budget (ms). A WEDGED runner — transport up
|
|
25883
26243
|
* enough to stay in the service registry, but whose `broker.call` neither
|
|
25884
26244
|
* resolves nor rejects (observed live as
|
|
@@ -27830,8 +28190,8 @@ var PipelineOrchestratorAddon = class PipelineOrchestratorAddon extends BaseAddo
|
|
|
27830
28190
|
] });
|
|
27831
28191
|
}
|
|
27832
28192
|
async updateGlobalSettings(patch) {
|
|
27833
|
-
await
|
|
27834
|
-
const full = await this.
|
|
28193
|
+
await super.updateGlobalSettings(patch);
|
|
28194
|
+
const full = await this.resolveGlobalStore();
|
|
27835
28195
|
this.globalSettings = { ...full };
|
|
27836
28196
|
this.applyRuntimeSettings(full);
|
|
27837
28197
|
const pausedIds = [...this.loadShedState.entries()].filter(([, s]) => s.pausedAt !== null).map(([id]) => id);
|