@camstack/system 1.1.10 → 1.1.12
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-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/builtins/device-manager/device-aggregation-merge.d.ts +22 -0
- package/dist/builtins/device-manager/device-aggregation.d.ts +220 -0
- package/dist/builtins/device-manager/device-bindings-store.d.ts +100 -0
- package/dist/builtins/device-manager/device-manager.addon.d.ts +41 -332
- package/dist/builtins/device-manager/device-manager.addon.js +3294 -2860
- package/dist/builtins/device-manager/device-manager.addon.mjs +3294 -2860
- package/dist/builtins/device-manager/device-meta-actions.d.ts +159 -0
- package/dist/builtins/device-manager/device-meta-store.d.ts +72 -0
- package/dist/builtins/device-manager/device-meta-types.d.ts +169 -0
- package/dist/builtins/device-manager/device-projection.d.ts +74 -0
- package/dist/builtins/device-manager/device-provider-context.d.ts +65 -0
- package/dist/builtins/device-manager/device-queries.d.ts +38 -0
- package/dist/builtins/device-manager/device-state-mirror.d.ts +137 -0
- package/dist/builtins/device-manager/device-sync-resolvers.d.ts +41 -0
- package/dist/index.js +216 -1
- package/dist/index.mjs +216 -3
- package/dist/kernel/cap-router-builder.d.ts +78 -0
- package/dist/kernel/index.d.ts +2 -0
- package/dist/{manifest-python-deps-BqId8WrC.mjs → manifest-python-deps-BfJEdXhi.mjs} +11 -3
- package/dist/{manifest-python-deps-Ba_eOEPT.js → manifest-python-deps-Cro19O4u.js} +9 -1
- package/package.json +1 -1
|
@@ -1,33 +1,8 @@
|
|
|
1
1
|
import { ProviderRegistration, BaseAddon, DeviceBindingEntry } from '@camstack/types';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* different `zod` minor than the core package. Keeping the shape local
|
|
7
|
-
* keeps the addon decoupled from the schema's internal type encoding.
|
|
8
|
-
*/
|
|
9
|
-
interface ContributionShape {
|
|
10
|
-
tabs?: Array<{
|
|
11
|
-
id: string;
|
|
12
|
-
label: string;
|
|
13
|
-
icon: string;
|
|
14
|
-
order?: number;
|
|
15
|
-
}>;
|
|
16
|
-
sections: Array<{
|
|
17
|
-
id: string;
|
|
18
|
-
title: string;
|
|
19
|
-
description?: string;
|
|
20
|
-
style?: 'card' | 'accordion';
|
|
21
|
-
defaultCollapsed?: boolean;
|
|
22
|
-
columns?: 1 | 2 | 3 | 4;
|
|
23
|
-
tab?: string;
|
|
24
|
-
/** Where the section renders. Default 'settings' (Config tab); 'top-tab' hoists into the device-detail tab bar via DeviceDetail discovery. */
|
|
25
|
-
location?: 'settings' | 'top-tab';
|
|
26
|
-
order?: number;
|
|
27
|
-
fields: unknown[];
|
|
28
|
-
}>;
|
|
29
|
-
}
|
|
30
|
-
export declare function mergeAggregates(parts: readonly ContributionShape[]): ContributionShape;
|
|
2
|
+
import { ContributionShape } from './device-meta-types.js';
|
|
3
|
+
import { mergeAggregates } from './device-aggregation-merge.js';
|
|
4
|
+
import { ResolvedTargetLink, DependentLink } from './device-link-overlay.js';
|
|
5
|
+
export { mergeAggregates };
|
|
31
6
|
export declare class DeviceManagerAddon extends BaseAddon {
|
|
32
7
|
constructor();
|
|
33
8
|
/** Shorthand for the kernel-injected capability registry. */
|
|
@@ -48,22 +23,13 @@ export declare class DeviceManagerAddon extends BaseAddon {
|
|
|
48
23
|
* (e.g. `battery.onStatusChanged`) get the same data via
|
|
49
24
|
* cap-specific events still emitted by the owning device.
|
|
50
25
|
*
|
|
51
|
-
* Key: deviceId. Value: per-cap slice map. Empty by default
|
|
52
|
-
*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
* Per-device disk-write debouncer for runtime-state. `setCapSlice`
|
|
57
|
-
* updates the in-memory mirror synchronously and emits the change
|
|
58
|
-
* event immediately, but the disk write is coalesced — frequent
|
|
59
|
-
* back-to-back writes (motion phase transitions, battery pushes,
|
|
60
|
-
* etc.) collapse to one `writeDeviceRuntimeState` per
|
|
61
|
-
* `RUNTIME_STATE_DEBOUNCE_MS` window. `flushRuntimeStateWrites`
|
|
62
|
-
* awaits any in-flight write + scheduled flush so shutdown is
|
|
63
|
-
* lossless.
|
|
26
|
+
* Key: deviceId. Value: per-cap slice map. Empty by default.
|
|
27
|
+
*
|
|
28
|
+
* The mirror + its debounced disk-writer + the cross-device link overlay
|
|
29
|
+
* live in `DeviceStateMirror`. Constructed once in `onInitialize` (needs
|
|
30
|
+
* `ctx`); `null` before initialise / when `ctx.settings` is absent.
|
|
64
31
|
*/
|
|
65
|
-
private
|
|
66
|
-
private static readonly RUNTIME_STATE_DEBOUNCE_MS;
|
|
32
|
+
private stateMirrorImpl;
|
|
67
33
|
/**
|
|
68
34
|
* Cross-process native-provider cache: deviceId (numeric) → capName → { addonId, nodeId }.
|
|
69
35
|
* Kept in sync with `DeviceBindingsChanged` push events emitted by forked
|
|
@@ -93,8 +59,6 @@ export declare class DeviceManagerAddon extends BaseAddon {
|
|
|
93
59
|
private linkTargets;
|
|
94
60
|
/** `${sourceDeviceId}:${sourceCap}` → targets to recompute when that source changes. */
|
|
95
61
|
private linkDependents;
|
|
96
|
-
/** Loop/churn guard: last overlaid slice emitted per `${deviceId}:${cap}`. */
|
|
97
|
-
private readonly lastEmittedOverlay;
|
|
98
62
|
/** Expected source `stableId`s (`${container}-${sourceKey}`) across all links,
|
|
99
63
|
* resolved or not — gates the `registerDevice` rebuild so only a registering
|
|
100
64
|
* device that IS a link source triggers a reindex (not every boot restore). */
|
|
@@ -102,173 +66,54 @@ export declare class DeviceManagerAddon extends BaseAddon {
|
|
|
102
66
|
/** Test/diagnostic accessors. */
|
|
103
67
|
linkTargetKeys(): string[];
|
|
104
68
|
linkDependentsKeys(): string[];
|
|
69
|
+
/** Replace the cross-device link reverse-index maps in one atomic swap.
|
|
70
|
+
* Sole writer of `linkTargets` / `linkDependents` / `expectedSourceStableIds`;
|
|
71
|
+
* called by `DeviceMetaStore.rebuildLinkDependents` via the `LinkIndexHost`. */
|
|
72
|
+
setLinkIndexes(targets: Map<string, ResolvedTargetLink[]>, dependents: Map<string, DependentLink[]>, expectedSourceStableIds: Set<string>): void;
|
|
73
|
+
/** Build the `LinkIndexHost` the meta store mutates — exposes this addon's
|
|
74
|
+
* `devicesWithLinks` gate (read) + `setLinkIndexes` (write) without leaking
|
|
75
|
+
* the private maps. */
|
|
76
|
+
private get linkIndexHost();
|
|
77
|
+
/** The runtime-state mirror, asserted present (constructed in onInitialize). */
|
|
78
|
+
private get stateMirror();
|
|
79
|
+
/** Build the `LinkOverlayHost` the state mirror reads when overlaying linked
|
|
80
|
+
* values — exposes the link reverse-index maps + registry via LIVE thunks
|
|
81
|
+
* (the maps are reassigned on every `rebuildLinkDependents`). Arrow thunks
|
|
82
|
+
* capture `this` lexically so the getters never go stale. */
|
|
83
|
+
private get linkOverlayHost();
|
|
84
|
+
/** Build the `ProviderHost` the extracted provider-method modules
|
|
85
|
+
* (`device-meta-actions`, `device-queries`) reach — exposes the addon's
|
|
86
|
+
* private state/methods they need without leaking the class internals.
|
|
87
|
+
*
|
|
88
|
+
* `expectedSourceStableIds` is exposed via a LIVE thunk: the field is
|
|
89
|
+
* reassigned to a fresh Set on every `rebuildLinkDependents`, so a captured
|
|
90
|
+
* reference would go stale. `devicesWithLinks` / `remoteNativeCaps` are
|
|
91
|
+
* mutated in place (never reassigned) so a direct reference preserves the
|
|
92
|
+
* original live semantics. The overlay-emit guard now lives in the state
|
|
93
|
+
* mirror — pruning is routed through `dropDeviceOverlays`. */
|
|
94
|
+
private get providerHost();
|
|
105
95
|
/** Wait for a device-provider by addonId, returning null on timeout. */
|
|
106
96
|
private waitDeviceProvider;
|
|
107
97
|
/** Require a device-provider by addonId — throws if not found. */
|
|
108
98
|
private requireDeviceProvider;
|
|
109
99
|
/** Require a device-adoption provider by addonId — throws if not found. */
|
|
110
100
|
private requireDeviceAdoptionProvider;
|
|
111
|
-
|
|
112
|
-
private
|
|
113
|
-
private resolveWrapperNodeId;
|
|
114
|
-
/**
|
|
115
|
-
* Reduce a provider node id to the routable form `DeviceProxy` can pin.
|
|
116
|
-
*
|
|
117
|
-
* Every addon runs in its own `addon-runner` with the composite node id
|
|
118
|
-
* `${parentNodeId}/${runnerId}` (see `addon-runner.ts`) — e.g.
|
|
119
|
-
* `hub/provider-reolink` or `dev-agent-0/provider-reolink`. That composite is
|
|
120
|
-
* an IDENTITY, not a routable target: the `CapRouteResolver` reaches a child
|
|
121
|
-
* only THROUGH its parent (the hub resolves a hub-local-uds child by
|
|
122
|
-
* cap+device; an agent forwards to its own child). `DeviceProxy` pins
|
|
123
|
-
* `entry.providerNodeId` on every cap call, so a binding entry must expose the
|
|
124
|
-
* parent node id — otherwise the explicit pin classifies as `remote-moleculer`
|
|
125
|
-
* to an unknown node → `no-provider`, which surfaces as
|
|
126
|
-
* "this camera doesn't expose …" for client-proxy-driven widget caps
|
|
127
|
-
* (motion-zones, privacy-mask). Wrappers already report the parent via
|
|
128
|
-
* `resolveWrapperNodeId`; this aligns natives with the same contract.
|
|
129
|
-
*
|
|
130
|
-
* A flat node id (a genuine standalone node with no `/`) is returned
|
|
131
|
-
* unchanged.
|
|
132
|
-
*/
|
|
133
|
-
private toRoutableProviderNodeId;
|
|
134
|
-
/**
|
|
135
|
-
* Resolve a remote native cap entry for a given `(capName, deviceId)` by
|
|
136
|
-
* consulting the handshake-fed `HubNodeRegistry` via
|
|
137
|
-
* `ctx.kernel.listClusterNativeCaps()`. Called when the push-based
|
|
138
|
-
* `remoteNativeCaps` cache misses — covers the Moleculer transport
|
|
139
|
-
* handshake window where `DeviceBindingsChanged` events were lost but the
|
|
140
|
-
* D3 re-handshake (post device restore) has already populated the registry.
|
|
141
|
-
*
|
|
142
|
-
* Returns `null` when the entry is genuinely not present in the cluster
|
|
143
|
-
* view (cap not registered on any worker for that device).
|
|
144
|
-
*/
|
|
145
|
-
private resolveRemoteNativeCapFromRegistry;
|
|
101
|
+
/** Build the dependency context the extracted binding resolvers consume. */
|
|
102
|
+
private get bindingsDeps();
|
|
146
103
|
getBindings(input: {
|
|
147
104
|
deviceId: number;
|
|
148
105
|
}): Promise<{
|
|
149
106
|
deviceId: number;
|
|
150
107
|
entries: DeviceBindingEntry[];
|
|
151
108
|
}>;
|
|
152
|
-
/**
|
|
153
|
-
* Whole-fleet binding dump. Iterates every device known to the
|
|
154
|
-
* deviceRegistry and reuses the per-device `getBindings` resolver
|
|
155
|
-
* for each — same routing rules, single round-trip. Used by
|
|
156
|
-
* `SystemManager.init()` for warm-boot.
|
|
157
|
-
*
|
|
158
|
-
* Bindings change rarely (wrapper toggle, device add/remove) so
|
|
159
|
-
* clients invalidate via the existing
|
|
160
|
-
* `capability.binding-changed` event rather than re-fetching this
|
|
161
|
-
* payload periodically.
|
|
162
|
-
*/
|
|
163
109
|
getAllBindings(): Promise<Array<{
|
|
164
110
|
deviceId: number;
|
|
165
111
|
entries: DeviceBindingEntry[];
|
|
166
112
|
}>>;
|
|
167
|
-
/**
|
|
168
|
-
|
|
169
|
-
* Used only by the device-identity section of the device-details
|
|
170
|
-
* aggregator (see `buildBaseDeviceSection`) to surface the stableId as
|
|
171
|
-
* a readonly display field. All runtime/registry lookups are keyed by
|
|
172
|
-
* numeric deviceId; this helper is display-only.
|
|
173
|
-
*/
|
|
174
|
-
private lookupPersistedStableId;
|
|
113
|
+
/** Build the dependency context the extracted aggregation functions consume. */
|
|
114
|
+
private get aggregationDeps();
|
|
175
115
|
getDeviceAggregate(deviceId: number, kind: 'settings' | 'live'): Promise<ContributionShape | null>;
|
|
176
|
-
/**
|
|
177
|
-
* System-scoped per-device contributions — the deliberate exception to
|
|
178
|
-
* binding-driven aggregation (D12).
|
|
179
|
-
*
|
|
180
|
-
* A `scope: 'system'` cap with `exposesDeviceSettings: true` holds per-device
|
|
181
|
-
* state but is NOT bound per-device: it is a system addon, not a BaseDevice,
|
|
182
|
-
* so it never calls `registerNativeCap` and never appears in
|
|
183
|
-
* `getBindings(deviceId)`. The binding-driven loop therefore skips it and its
|
|
184
|
-
* per-device panel never renders. Two such caps ship today:
|
|
185
|
-
* - `stream-broker` (singleton) — the RTSP-restream / pre-buffer settings
|
|
186
|
-
* tab, scoped to the cameras whose streams it serves.
|
|
187
|
-
* - `device-export` (collection) — the "Export" panel; a device can be
|
|
188
|
-
* exposed to MULTIPLE exporters at once (Alexa + HomeKit + HA-MQTT).
|
|
189
|
-
*
|
|
190
|
-
* D12 bans fanning a per-device call out to every same-named provider of a
|
|
191
|
-
* `scope: 'device'` cap, because vendor providers SHARE cap names
|
|
192
|
-
* (`stream-params`/`ptz` from reolink AND hikvision). System caps are NOT
|
|
193
|
-
* vendor-shared — a system cap name maps to one active provider (singleton)
|
|
194
|
-
* or one declared collection — so consulting the cap's own provider(s)
|
|
195
|
-
* directly is safe. Each provider gates internally by deviceId and returns
|
|
196
|
-
* null for devices it does not own.
|
|
197
|
-
*
|
|
198
|
-
* `seenCaps` skips any system cap that already contributed via a binding so
|
|
199
|
-
* its section is never duplicated.
|
|
200
|
-
*/
|
|
201
|
-
private collectSystemDeviceContributions;
|
|
202
|
-
/**
|
|
203
|
-
* D14: framework-derived device-config contribution.
|
|
204
|
-
*
|
|
205
|
-
* `kind === 'live'` — device-config caps contribute nothing to the live
|
|
206
|
-
* aggregate (they hold editable config, not live observables).
|
|
207
|
-
*
|
|
208
|
-
* `ui.kind === 'widget'` — emits a single structural `type:'widget'`
|
|
209
|
-
* section; the widget self-persists via the cap's own mutations.
|
|
210
|
-
*
|
|
211
|
-
* `ui.kind === 'derived-form'` — calls `getOptions`/`getStatus` on the
|
|
212
|
-
* bound provider, runs the registered pure builder, and returns the
|
|
213
|
-
* derived form sections. Returns null when the camera exposes nothing
|
|
214
|
-
* configurable or the provider is not yet registered.
|
|
215
|
-
*/
|
|
216
|
-
private deriveDeviceConfigContribution;
|
|
217
|
-
/**
|
|
218
|
-
* Build the device-manager's own contribution to the aggregator — the
|
|
219
|
-
* device identity (id, stableId, addonId, type, online) + the
|
|
220
|
-
* driver-specific config exposed by the device class via
|
|
221
|
-
* `zodEntriesToConfigUI`.
|
|
222
|
-
*
|
|
223
|
-
* Two paths, deliberately symmetric with `getSettingsSchema`:
|
|
224
|
-
*
|
|
225
|
-
* - Hub-local: device's IDevice instance lives in this process'
|
|
226
|
-
* DeviceRegistry, we read config + schema directly by reference.
|
|
227
|
-
* - Cross-process: device lives in a forked worker (RtspCamera on
|
|
228
|
-
* provider-rtsp, ONVIF on provider-onvif, …). We ask the worker's
|
|
229
|
-
* `device-ops.getSettingsSchema` native provider for a wire-
|
|
230
|
-
* serializable ConfigUISchema and merge it in under the same
|
|
231
|
-
* "Driver Config" section, so the UI sees the same shape regardless
|
|
232
|
-
* of where the IDevice physically runs.
|
|
233
|
-
*
|
|
234
|
-
* Returns `null` only when the device genuinely doesn't exist anywhere
|
|
235
|
-
* (no hub-local, no persisted ownership, no device-ops native). The
|
|
236
|
-
* aggregator falls back to contributor sections only in that case.
|
|
237
|
-
*/
|
|
238
|
-
private buildBaseDeviceSection;
|
|
239
|
-
/**
|
|
240
|
-
* Lookup the native owner for `device-ops` on `deviceId` — the native-cap
|
|
241
|
-
* registry (hub-local and remote) is keyed by numeric id.
|
|
242
|
-
*/
|
|
243
|
-
private resolveNativeDeviceOwner;
|
|
244
|
-
/**
|
|
245
|
-
* Aggregate `status` across every registered cap for a device.
|
|
246
|
-
*
|
|
247
|
-
* Walks the supplied cap list (or `CAP_NAMES_WITH_STATUS` when
|
|
248
|
-
* omitted), looks up a native provider per cap via the capability
|
|
249
|
-
* registry, calls `provider.getStatus({ deviceId })`, and validates
|
|
250
|
-
* the return against the cap's own `status.schema`. Validation
|
|
251
|
-
* failures log a warning and yield `null` for that cap so the
|
|
252
|
-
* overall aggregate stays usable — a single misbehaving provider
|
|
253
|
-
* must not blank out a device's entire status view.
|
|
254
|
-
*
|
|
255
|
-
* Returned shape is `Record<capName, unknown | null>`; the client-
|
|
256
|
-
* side hook tightens this to `CapStatusTypeMap` via the generated
|
|
257
|
-
* `cap-status-types.ts`.
|
|
258
|
-
*/
|
|
259
116
|
private getDeviceStatusAggregate;
|
|
260
|
-
/**
|
|
261
|
-
* Return the driver-specific device-settings contribution. Hub-local
|
|
262
|
-
* devices call `getSettingsUISchema()` directly; forked-worker devices
|
|
263
|
-
* go through the `device-ops.getSettingsSchema` cap method on the
|
|
264
|
-
* numeric-id-keyed native registry.
|
|
265
|
-
*
|
|
266
|
-
* Returns a discriminated result so callers can distinguish three states:
|
|
267
|
-
* 'ok' – schema obtained successfully
|
|
268
|
-
* 'none' – driver genuinely has no settings schema
|
|
269
|
-
* 'unavailable' – worker was unreachable after retries (transient)
|
|
270
|
-
*/
|
|
271
|
-
private resolveDriverConfigSchema;
|
|
272
117
|
updateDeviceField(input: {
|
|
273
118
|
deviceId: number;
|
|
274
119
|
writerCapName: string;
|
|
@@ -278,45 +123,6 @@ export declare class DeviceManagerAddon extends BaseAddon {
|
|
|
278
123
|
}): Promise<{
|
|
279
124
|
success: true;
|
|
280
125
|
}>;
|
|
281
|
-
/**
|
|
282
|
-
* Dispatch a device custom action. Hub-local devices run it directly;
|
|
283
|
-
* forked/remote devices route through the `device-ops` native cap
|
|
284
|
-
* (`runAction`) — the same hub-local-then-device-ops fan-out that
|
|
285
|
-
* `updateDeviceField` uses for `applySettingsPatch`.
|
|
286
|
-
*/
|
|
287
|
-
private dispatchDeviceAction;
|
|
288
|
-
/**
|
|
289
|
-
* Resolve the `DeviceSettingsContribution` provider that owns a tagged
|
|
290
|
-
* field. The `writerAddonId` on a tagged field equals `entry.providerAddonId`
|
|
291
|
-
* from the binding that produced it — for `kind:'wrapped'` entries, that is
|
|
292
|
-
* the wrapper addon id (e.g. 'snapshot-addon'); for native-only entries,
|
|
293
|
-
* the system addon id.
|
|
294
|
-
*
|
|
295
|
-
* Resolution order (Bug-3 fix):
|
|
296
|
-
* 1. `getProviderByAddon(capName, writerAddonId)` — resolves the
|
|
297
|
-
* system-registered provider by the addon id from the tagged field.
|
|
298
|
-
* For wrapper bindings (snapshot, motion-detection, etc.) this directly
|
|
299
|
-
* returns the wrapper provider, bypassing the native-first resolution
|
|
300
|
-
* order of `getProviderForDevice` that caused "method not found".
|
|
301
|
-
* 2. `getSingleton(capName)` — fallback for stale/mismatched writerAddonIds.
|
|
302
|
-
* The active singleton handles the contribution even if the addonId
|
|
303
|
-
* stored in the field is out of date (e.g. after an addon rename).
|
|
304
|
-
*
|
|
305
|
-
* `getProviderForDevice` is intentionally NOT used here: it returns the
|
|
306
|
-
* per-device native first when present (e.g. Reolink/ONVIF), and the native
|
|
307
|
-
* does NOT implement contribution methods — the root cause of Bug-3.
|
|
308
|
-
*/
|
|
309
|
-
private resolveContributionProvider;
|
|
310
|
-
/**
|
|
311
|
-
* Batched counterpart of `updateDeviceField`. Groups changes by
|
|
312
|
-
* `(writerCapName, writerAddonId)` so each contributor receives a
|
|
313
|
-
* single `applyDeviceSettingsPatch` with all of its updates merged —
|
|
314
|
-
* avoids N round-trips for simultaneous edits in the same save.
|
|
315
|
-
*
|
|
316
|
-
* Per-provider failures are captured in the `failures[]` output so the
|
|
317
|
-
* admin UI can highlight which sections didn't persist; a failure on
|
|
318
|
-
* one provider does NOT abort the others.
|
|
319
|
-
*/
|
|
320
126
|
updateDeviceFieldsBatch(input: {
|
|
321
127
|
deviceId: number;
|
|
322
128
|
changes: ReadonlyArray<{
|
|
@@ -333,10 +139,7 @@ export declare class DeviceManagerAddon extends BaseAddon {
|
|
|
333
139
|
error: string;
|
|
334
140
|
}[];
|
|
335
141
|
}>;
|
|
336
|
-
|
|
337
|
-
* `updateDeviceField` routing (special-case device-manager, else
|
|
338
|
-
* registry lookup). Used by `updateDeviceFieldsBatch`. */
|
|
339
|
-
private applyGroupPatch;
|
|
142
|
+
private dispatchDeviceAction;
|
|
340
143
|
listWrappersForCap(input: {
|
|
341
144
|
capName: string;
|
|
342
145
|
}): Promise<string[]>;
|
|
@@ -353,100 +156,6 @@ export declare class DeviceManagerAddon extends BaseAddon {
|
|
|
353
156
|
active: boolean;
|
|
354
157
|
}): Promise<void>;
|
|
355
158
|
protected onInitialize(): Promise<ProviderRegistration[] | void>;
|
|
356
|
-
/**
|
|
357
|
-
* Single-cap mirror update — diff against the current mirror,
|
|
358
|
-
* persist the new slice in-memory, emit `DeviceStateChanged` for
|
|
359
|
-
* this cap. No-op on identical writes (both same shape and same
|
|
360
|
-
* values). Called by `setCapSlice` provider.
|
|
361
|
-
*/
|
|
362
|
-
private applySingleCapUpdate;
|
|
363
|
-
/**
|
|
364
|
-
* Debounced disk writer. Coalesces frequent writes (motion phase
|
|
365
|
-
* transitions, battery pushes) into one `writeDeviceRuntimeState`
|
|
366
|
-
* per `RUNTIME_STATE_DEBOUNCE_MS` window. Reads the per-device
|
|
367
|
-
* blob from the live mirror at flush time so the disk picture is
|
|
368
|
-
* always the latest state — no risk of writing a stale snapshot.
|
|
369
|
-
*/
|
|
370
|
-
private scheduleRuntimeStateDiskWrite;
|
|
371
|
-
/**
|
|
372
|
-
* One-shot mirror seed used by `loadRuntimeState` at boot so the
|
|
373
|
-
* hub knows about every persisted slice without waiting for the
|
|
374
|
-
* first `setCapSlice` call. No events emitted — this is
|
|
375
|
-
* initial-state population, not a transition.
|
|
376
|
-
*
|
|
377
|
-
* Callers that must not carry a stale per-session probe across a
|
|
378
|
-
* restart pass the blob through `withResetSessionProbe` first (see
|
|
379
|
-
* `loadRuntimeState`).
|
|
380
|
-
*/
|
|
381
|
-
private seedMirror;
|
|
382
|
-
/**
|
|
383
|
-
* The hub mirror's `feature-probe.lastProbedAt` is a PER-SESSION liveness
|
|
384
|
-
* signal — it means "this worker process completed a probe THIS session".
|
|
385
|
-
* Persisted runtime state carries the PRE-RESTART timestamp, which is stale
|
|
386
|
-
* after a hub or worker restart: the device has not re-probed yet. Seeding it
|
|
387
|
-
* verbatim makes `resolveDeviceProbed` report `probed:true` during the
|
|
388
|
-
* restart→reprobe window, which defeats the export carry-forward gate
|
|
389
|
-
* (`resolveExportFingerprint`, gated on `device.probed`) and posts a spurious
|
|
390
|
-
* partial `AddOrUpdateReport` to Alexa/HAP before the real probe lands.
|
|
391
|
-
*
|
|
392
|
-
* Reset `lastProbedAt` to 0 for the MIRROR seed only — `probed:false` carries
|
|
393
|
-
* the last-advertised fingerprint forward until the worker republishes a
|
|
394
|
-
* fresh probe (its post-probe `setCapSlice` raises `lastProbedAt` again, which
|
|
395
|
-
* also fires `DeviceReady`). The worker's returned `initialRuntimeState` blob
|
|
396
|
-
* is untouched, and every non-probe slice (e.g. `device-status`/online) is
|
|
397
|
-
* preserved.
|
|
398
|
-
*/
|
|
399
|
-
private withResetSessionProbe;
|
|
400
|
-
/**
|
|
401
|
-
* Resolve a device's REAL `online` flag for the persisted/forked-worker
|
|
402
|
-
* list branch. Forked workers own the live `IDevice` in their own process,
|
|
403
|
-
* so the hub registry can't read `device.online` directly. The owning
|
|
404
|
-
* driver instead publishes its liveness through the auto-registered
|
|
405
|
-
* `device-status` runtime-state slice (`markOnline` → `setCapState`), which
|
|
406
|
-
* the canonical `deviceState.setCapSlice` write entrypoint mirrors into the
|
|
407
|
-
* hub-side `stateMirror`. We read that mirrored slice here so the list
|
|
408
|
-
* payload reflects the device's actual reachability instead of a constant.
|
|
409
|
-
*
|
|
410
|
-
* Fallback (`fallbackOnline`) preserves the legacy behaviour when no slice
|
|
411
|
-
* has been published yet: a persisted device with a live registry is
|
|
412
|
-
* assumed online (it was successfully registered by its owning process),
|
|
413
|
-
* and the null-registry "offline view" keeps reporting offline. We never
|
|
414
|
-
* regress a device to offline merely because its mirror is empty.
|
|
415
|
-
*/
|
|
416
|
-
private resolveDeviceOnline;
|
|
417
|
-
/**
|
|
418
|
-
* Derive the `probed` flag for an offline-view (forked-worker, not
|
|
419
|
-
* live in the hub registry) device projection. Reads the mirrored
|
|
420
|
-
* `feature-probe` slice the owning worker publishes. Mirrors the
|
|
421
|
-
* `toDeviceInfo` rule: no mirrored slice → ready (`true`, no probe seen);
|
|
422
|
-
* slice present → ready iff `lastProbedAt` has advanced past 0. The
|
|
423
|
-
* mirror is populated when the worker's first `setCapSlice` RPC arrives
|
|
424
|
-
* (BaseDevice seeds `feature-probe` `lastProbedAt:0` at construction, but
|
|
425
|
-
* cross-process delivery is async); until then the no-entry path returns
|
|
426
|
-
* `true` — a brief transient window, same as `resolveDeviceOnline`.
|
|
427
|
-
*/
|
|
428
|
-
private resolveDeviceProbed;
|
|
429
|
-
private snapshotForDevice;
|
|
430
|
-
/**
|
|
431
|
-
* Read-time overlay of a cap slice with its cross-device linked values.
|
|
432
|
-
* Returns a cloned raw mirror slice when the (device, cap) pair has no
|
|
433
|
-
* links. Sources are read from the same in-hub stateMirror — sync, no
|
|
434
|
-
* cross-process call. The disk writer must NOT use this method; it must
|
|
435
|
-
* persist raw provider truth via snapshotForDevice.
|
|
436
|
-
*/
|
|
437
|
-
private overlayedSlice;
|
|
438
|
-
/**
|
|
439
|
-
* Like snapshotForDevice but applies the device-link overlay per cap.
|
|
440
|
-
* Used exclusively by the device-state READ methods (getSnapshot,
|
|
441
|
-
* getAllSnapshots) so callers see overlayed values. The debounced disk
|
|
442
|
-
* writer must continue to call snapshotForDevice (raw truth).
|
|
443
|
-
*/
|
|
444
|
-
private snapshotForDeviceOverlayed;
|
|
445
|
-
private emitStateChanged;
|
|
446
|
-
/** Emit DeviceStateChanged for (deviceId, cap) using the OVERLAID slice,
|
|
447
|
-
* skipping when the overlay is unchanged since the last emit (loop/churn
|
|
448
|
-
* guard). Used for the written pair AND its dependent targets. */
|
|
449
|
-
private emitOverlayed;
|
|
450
159
|
protected onShutdown(): Promise<void>;
|
|
451
160
|
}
|
|
452
161
|
export default DeviceManagerAddon;
|