@camstack/types 1.2.283 → 1.2.285
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.js +2 -2
- package/dist/addon.mjs +2 -2
- package/dist/capabilities/device-extension.cap.d.ts +8 -25
- package/dist/capabilities/device-manager.cap.d.ts +2 -2
- package/dist/capabilities/device-state.cap.d.ts +2 -2
- package/dist/capabilities/doorbell.cap.d.ts +13 -64
- package/dist/capabilities/index.d.ts +2 -2
- package/dist/capabilities/motion.cap.d.ts +9 -8
- package/dist/capabilities/notification-rules.cap.d.ts +230 -8
- package/dist/capabilities/pipeline-orchestrator.cap.d.ts +147 -15
- package/dist/capabilities/pipeline-runner.cap.d.ts +2 -0
- package/dist/capabilities/ptz-autotrack.cap.d.ts +2 -3
- package/dist/catalogs/sensor-active-state.d.ts +8 -10
- package/dist/composition/presets/doorbell-feature.d.ts +1 -1
- package/dist/{composition-Dq42XWp0.mjs → composition-43aqsZQj.mjs} +6 -7
- package/dist/{composition-DG4jVTfB.js → composition-w3gBWMer.js} +6 -7
- package/dist/device/hub-motion-read.d.ts +22 -0
- package/dist/device-extension/slot-descriptor.d.ts +3 -3
- package/dist/enums/event-category.d.ts +9 -0
- package/dist/enums.js +1 -1
- package/dist/enums.mjs +1 -1
- package/dist/{event-category-C5xZWqz6.mjs → event-category-BGmz_oxW.mjs} +9 -0
- package/dist/{event-category-MbnB-6AN.js → event-category-DmS_tLsU.js} +9 -0
- package/dist/generated/addon-api.d.ts +7 -0
- package/dist/generated/extension-slots.d.ts +2 -6
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/method-device-selectors.d.ts +2 -2
- package/dist/generated/system-proxy.d.ts +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +548 -217
- package/dist/index.mjs +523 -215
- package/dist/interfaces/event-bus.d.ts +5 -3
- package/dist/interfaces/pipeline-runner-capability.d.ts +2 -0
- package/dist/node.js +1 -1
- package/dist/node.mjs +1 -1
- package/dist/notification/alarm-settings.d.ts +58 -0
- package/dist/pipeline/camera-sensors.d.ts +133 -9
- package/dist/pipeline/motion-ownership.d.ts +13 -0
- package/dist/pipeline/provider-motion.d.ts +33 -0
- package/dist/{sleep-gU6hqv4_.mjs → sleep-CDCONfkt.mjs} +1 -1
- package/dist/{sleep-DCbD8sdg.js → sleep-G4F3X9X_.js} +1 -1
- package/dist/utils/addon-id.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_sleep = require("./sleep-
|
|
3
|
-
const require_event_category = require("./event-category-
|
|
4
|
-
const require_composition = require("./composition-
|
|
2
|
+
const require_sleep = require("./sleep-G4F3X9X_.js");
|
|
3
|
+
const require_event_category = require("./event-category-DmS_tLsU.js");
|
|
4
|
+
const require_composition = require("./composition-w3gBWMer.js");
|
|
5
5
|
const require_err_msg = require("./err-msg-Dx2Kor0g.js");
|
|
6
6
|
const require_enums = require("./enums.js");
|
|
7
7
|
let zod = require("zod");
|
|
@@ -2538,7 +2538,7 @@ var RecordingTriggersSchema = zod.z.object({
|
|
|
2538
2538
|
*
|
|
2539
2539
|
* Cosa vuol dire "alto" dipende dal TIPO di device e non è deciso qui:
|
|
2540
2540
|
* `SOURCE_CAP_ACTIVE_FIELD` (`catalogs/sensor-active-state.ts`) è LA tabella,
|
|
2541
|
-
* la stessa
|
|
2541
|
+
* la stessa del campanello composto (D671). Ed è il FRONTE, non
|
|
2542
2542
|
* il livello: un contatto trovato già aperto al riavvio del runner non fa
|
|
2543
2543
|
* registrare.
|
|
2544
2544
|
*/
|
|
@@ -14541,8 +14541,8 @@ var deviceManagerCapability = {
|
|
|
14541
14541
|
* (`value` / `missing` / `failed`, see {@link WrapperActivationReadSchema}).
|
|
14542
14542
|
*
|
|
14543
14543
|
* The narrow read of the durable authority behind `getBindings`: a
|
|
14544
|
-
* consumer that must know what the operator PERSISTED (
|
|
14545
|
-
*
|
|
14544
|
+
* consumer that must know what the operator PERSISTED (before it drops
|
|
14545
|
+
* work on the strength of an unbind) asks this instead of pulling device-manager's
|
|
14546
14546
|
* whole addon store through `addonSettingsRaw.getGlobal` — which answered
|
|
14547
14547
|
* `{}` on every isolated-engine hub and so never worked (D675). It reads
|
|
14548
14548
|
* ONE row (the activation map, ~1.6 KB), never the ~625 KB store.
|
|
@@ -14833,107 +14833,6 @@ var deviceManagerCapability = {
|
|
|
14833
14833
|
}
|
|
14834
14834
|
};
|
|
14835
14835
|
//#endregion
|
|
14836
|
-
//#region src/capabilities/doorbell.cap.ts
|
|
14837
|
-
/**
|
|
14838
|
-
* Doorbell button cap. Two kinds of providers coexist behind this cap
|
|
14839
|
-
* name (same pattern as `snapshot`):
|
|
14840
|
-
*
|
|
14841
|
-
* - **Native** providers: registered per-device by device-driver
|
|
14842
|
-
* addons via `ctx.registerNativeCap` — either on a
|
|
14843
|
-
* `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
|
|
14844
|
-
* or directly on the camera (Reolink registers at camera level).
|
|
14845
|
-
* Emits an `onPressed` event every time the firmware pushes a
|
|
14846
|
-
* ring; status tracks the last press and a pressCount since start
|
|
14847
|
-
* (diagnostic).
|
|
14848
|
-
*
|
|
14849
|
-
* - **Wrapper** provider: the `virtual-doorbell` system builtin
|
|
14850
|
-
* (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
|
|
14851
|
-
* device (contact / switch / event-emitter …) into a doorbell for
|
|
14852
|
-
* a camera. `defaultActive: false` — the operator explicitly binds
|
|
14853
|
-
* it per camera in the device-bindings UI, then picks the source
|
|
14854
|
-
* device + trigger in the per-device settings.
|
|
14855
|
-
*
|
|
14856
|
-
* The DeviceEventPropagator re-emits `onPressed` on the camera parent
|
|
14857
|
-
* — subscribers listening at the camera level receive ring events
|
|
14858
|
-
* with `via[]` populated. No code on the parent needed.
|
|
14859
|
-
*/
|
|
14860
|
-
var DoorbellStatusSchema = zod.z.object({
|
|
14861
|
-
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
14862
|
-
lastPressedAt: zod.z.number().nullable(),
|
|
14863
|
-
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
14864
|
-
pressCountSinceStart: zod.z.number()
|
|
14865
|
-
});
|
|
14866
|
-
var DoorbellPressEventSchema = zod.z.object({
|
|
14867
|
-
deviceId: zod.z.number(),
|
|
14868
|
-
timestamp: zod.z.number()
|
|
14869
|
-
});
|
|
14870
|
-
/**
|
|
14871
|
-
* The `doorbell-source` extension slot's editable value (D648): the camera's
|
|
14872
|
-
* virtual-doorbell trigger devices, exactly what the wrapper's
|
|
14873
|
-
* `doorbellSources` persists (as `{ deviceId }` records).
|
|
14874
|
-
*/
|
|
14875
|
-
var DoorbellSourceSlotValueSchema = zod.z.object({ deviceIds: zod.z.array(zod.z.number().int().positive()) });
|
|
14876
|
-
/**
|
|
14877
|
-
* The `doorbell-source` slot — which devices ring this camera's VIRTUAL
|
|
14878
|
-
* doorbell, owned by the `virtual-doorbell` wrapper over its
|
|
14879
|
-
* `doorbellSources` store (D648). A source rings when one of its
|
|
14880
|
-
* `SOURCE_CAPS` rises. Whether the wrapper is bound to the camera at all is a
|
|
14881
|
-
* different authority (`deviceManager.setWrapperActive`, D62) — the slot
|
|
14882
|
-
* reports it, it never writes it.
|
|
14883
|
-
*/
|
|
14884
|
-
var DOORBELL_SOURCE_SLOT = {
|
|
14885
|
-
id: "doorbell-source",
|
|
14886
|
-
label: "Doorbell trigger devices",
|
|
14887
|
-
description: "Any of these becoming active rings this camera's virtual doorbell: a binary sensor when it goes active, a switch when it turns on.",
|
|
14888
|
-
shape: "association",
|
|
14889
|
-
hostDeviceTypes: [require_err_msg.DeviceType.Camera],
|
|
14890
|
-
sourceCaps: require_composition.SOURCE_CAPS,
|
|
14891
|
-
cardinality: "many",
|
|
14892
|
-
value: DoorbellSourceSlotValueSchema
|
|
14893
|
-
};
|
|
14894
|
-
var doorbellCapability = {
|
|
14895
|
-
name: "doorbell",
|
|
14896
|
-
scope: "device",
|
|
14897
|
-
deviceNative: true,
|
|
14898
|
-
mode: "singleton",
|
|
14899
|
-
kind: "wrapper",
|
|
14900
|
-
defaultActive: false,
|
|
14901
|
-
deviceTypes: [require_err_msg.DeviceType.Button, require_err_msg.DeviceType.Camera],
|
|
14902
|
-
extensionSlots: [DOORBELL_SOURCE_SLOT],
|
|
14903
|
-
methods: {},
|
|
14904
|
-
events: {
|
|
14905
|
-
/**
|
|
14906
|
-
* Fires once per physical press. Reolink delivers via Baichuan
|
|
14907
|
-
* push (`ReolinkSimpleEvent.type === 'doorbell'`). There is no
|
|
14908
|
-
* release/duration — it's a pulse.
|
|
14909
|
-
*/
|
|
14910
|
-
onPressed: {
|
|
14911
|
-
data: DoorbellPressEventSchema,
|
|
14912
|
-
fromState: {
|
|
14913
|
-
field: "lastPressedAt",
|
|
14914
|
-
on: "advance"
|
|
14915
|
-
}
|
|
14916
|
-
} },
|
|
14917
|
-
status: {
|
|
14918
|
-
schema: DoorbellStatusSchema,
|
|
14919
|
-
kind: "push"
|
|
14920
|
-
},
|
|
14921
|
-
/**
|
|
14922
|
-
* Runtime-state slice — last press timestamp + lifetime press count.
|
|
14923
|
-
* Mirrored by the kernel and readable via
|
|
14924
|
-
* `device.state.doorbell.value`. UIs can show "last ring 5m ago"
|
|
14925
|
-
* without subscribing.
|
|
14926
|
-
*/
|
|
14927
|
-
runtimeState: DoorbellStatusSchema,
|
|
14928
|
-
/**
|
|
14929
|
-
* Runtime-state durability: **restored** — a monotonic accumulator: the slice IS the record. `lastPressedAt` is content here, not a clock, so it is deliberately NOT volatile.
|
|
14930
|
-
*
|
|
14931
|
-
* See `RuntimeStateDurability`. Enforced by
|
|
14932
|
-
* `scripts/check-runtime-state-durability.ts`.
|
|
14933
|
-
*/
|
|
14934
|
-
durability: "restored"
|
|
14935
|
-
};
|
|
14936
|
-
//#endregion
|
|
14937
14836
|
//#region src/generated/extension-slots.ts
|
|
14938
14837
|
/**
|
|
14939
14838
|
* AUTO-GENERATED by scripts/generate-extension-slots.ts — DO NOT EDIT.
|
|
@@ -14943,25 +14842,18 @@ onPressed: {
|
|
|
14943
14842
|
* caps' descriptors and value schemas — never the caps barrel — so
|
|
14944
14843
|
* `device-extension.cap.ts` can import this module without a cycle.
|
|
14945
14844
|
*
|
|
14946
|
-
* Coverage:
|
|
14845
|
+
* Coverage: 1 slot(s).
|
|
14947
14846
|
*/
|
|
14948
14847
|
/** Every declared slot id, in table order. */
|
|
14949
|
-
var EXTENSION_SLOT_IDS = ["
|
|
14848
|
+
var EXTENSION_SLOT_IDS = ["linked-devices"];
|
|
14950
14849
|
var ExtensionSlotIdSchema = zod.z.enum(EXTENSION_SLOT_IDS);
|
|
14951
|
-
var EXTENSION_SLOTS = {
|
|
14952
|
-
|
|
14953
|
-
|
|
14954
|
-
|
|
14955
|
-
|
|
14956
|
-
},
|
|
14957
|
-
"linked-devices": {
|
|
14958
|
-
descriptor: LINKED_DEVICES_SLOT,
|
|
14959
|
-
declaringCap: "device-manager",
|
|
14960
|
-
hostDeviceTypes: [require_err_msg.DeviceType.Camera]
|
|
14961
|
-
}
|
|
14962
|
-
};
|
|
14850
|
+
var EXTENSION_SLOTS = { "linked-devices": {
|
|
14851
|
+
descriptor: LINKED_DEVICES_SLOT,
|
|
14852
|
+
declaringCap: "device-manager",
|
|
14853
|
+
hostDeviceTypes: [require_err_msg.DeviceType.Camera]
|
|
14854
|
+
} };
|
|
14963
14855
|
/** A slot's editable value, discriminated on `slot`. */
|
|
14964
|
-
var ExtensionSlotValueSchema = zod.z.discriminatedUnion("slot", [
|
|
14856
|
+
var ExtensionSlotValueSchema = zod.z.discriminatedUnion("slot", [LinkedDevicesSlotValueSchema.extend({ slot: zod.z.literal("linked-devices") })]);
|
|
14965
14857
|
/** Every device type at least one slot is offered on — `device-extension`'s `deviceTypes`. */
|
|
14966
14858
|
var EXTENSION_HOST_DEVICE_TYPES = [require_err_msg.DeviceType.Camera];
|
|
14967
14859
|
//#endregion
|
|
@@ -14970,8 +14862,8 @@ var EXTENSION_HOST_DEVICE_TYPES = [require_err_msg.DeviceType.Camera];
|
|
|
14970
14862
|
* `device-extension` — ONE place to see and author what a device is extended
|
|
14971
14863
|
* with by OTHER devices (D648).
|
|
14972
14864
|
*
|
|
14973
|
-
* **A contract, not a store.** Every slot (`linked-devices
|
|
14974
|
-
*
|
|
14865
|
+
* **A contract, not a store.** Every slot (today one: `linked-devices`) is
|
|
14866
|
+
* declared by the cap that owns the function (`extensionSlots` in its
|
|
14975
14867
|
* `*.cap.ts`, D4) and SERVED by the addon that already owns its store, over
|
|
14976
14868
|
* that store and its existing write path. This cap persists nothing and has no config method (guarded by
|
|
14977
14869
|
* `scripts/check-extension-slots.ts`): D62 applied to the whole feature, and
|
|
@@ -14980,7 +14872,7 @@ var EXTENSION_HOST_DEVICE_TYPES = [require_err_msg.DeviceType.Camera];
|
|
|
14980
14872
|
* **Shape.** A device-scoped wrapper COLLECTION, the D554 shape `videoclips`
|
|
14981
14873
|
* uses: `resolveCapMount` gives it the `device-scoped` mount and
|
|
14982
14874
|
* `requireDeviceCollection` dispatches it. Its providers are the slot owners
|
|
14983
|
-
* (device-manager
|
|
14875
|
+
* (today device-manager alone), and a device's sources are its
|
|
14984
14876
|
* bindings (step 0 of `getBindings`, derived, persisted nowhere).
|
|
14985
14877
|
*
|
|
14986
14878
|
* - `listSources` — one row per provider, the D554 source-listing convention
|
|
@@ -15040,10 +14932,11 @@ var ExtensionSlotSourceSchema = zod.z.object({
|
|
|
15040
14932
|
});
|
|
15041
14933
|
/**
|
|
15042
14934
|
* A DIFFERENT authority that gates whether the slot's sources do anything —
|
|
15043
|
-
* reported read-only, named, never written through this contract (D62).
|
|
15044
|
-
*
|
|
15045
|
-
*
|
|
15046
|
-
* `unknown` means the gate could
|
|
14935
|
+
* reported read-only, named, never written through this contract (D62) — e.g.
|
|
14936
|
+
* a slot whose sources act only while a wrapper is bound to the host, a
|
|
14937
|
+
* binding that belongs to `deviceManager.setWrapperActive`. No slot reports
|
|
14938
|
+
* one today (`linked-devices` answers `null`). `unknown` means the gate could
|
|
14939
|
+
* not be read; it is not "inactive" (D315).
|
|
15047
14940
|
*/
|
|
15048
14941
|
var ExtensionSlotActivationSchema = zod.z.object({
|
|
15049
14942
|
state: zod.z.enum([
|
|
@@ -15060,7 +14953,7 @@ var ExtensionSlotStateSchema = zod.z.object({
|
|
|
15060
14953
|
/** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
|
|
15061
14954
|
slotRef: zod.z.string(),
|
|
15062
14955
|
slot: ExtensionSlotIdSchema,
|
|
15063
|
-
/** Names the store written (D62), e.g. `
|
|
14956
|
+
/** Names the store written (D62), e.g. `device-manager:_linkedDeviceIds`. */
|
|
15064
14957
|
authority: zod.z.string(),
|
|
15065
14958
|
activation: ExtensionSlotActivationSchema.nullable(),
|
|
15066
14959
|
value: ExtensionSlotValueSchema,
|
|
@@ -15264,8 +15157,8 @@ var deviceStateCapability = {
|
|
|
15264
15157
|
* Canonical cross-layer WRITE for one cap-keyed slice. Used by
|
|
15265
15158
|
* everyone who needs to publish runtime state for a device:
|
|
15266
15159
|
* device providers (via `BaseDevice.runtimeState.setCapState`
|
|
15267
|
-
* which delegates here), system-scope addons (e.g. the
|
|
15268
|
-
*
|
|
15160
|
+
* which delegates here), system-scope addons (e.g. the orchestrator
|
|
15161
|
+
* writing a camera's effective `motion` slice, D696), and
|
|
15269
15162
|
* direct cap-router callers in tests.
|
|
15270
15163
|
*
|
|
15271
15164
|
* Behavior on the hub:
|
|
@@ -21390,6 +21283,41 @@ var NcSnoozeSuppressedSchema = zod.z.object({
|
|
|
21390
21283
|
firstAt: zod.z.number(),
|
|
21391
21284
|
lastAt: zod.z.number()
|
|
21392
21285
|
});
|
|
21286
|
+
/** The bounds every alarm delay field carries — settings, per-mode overrides, both editors. */
|
|
21287
|
+
var NC_ALARM_DELAY_MAX_SEC = 600;
|
|
21288
|
+
var NC_ALARM_TRIGGERED_MAX_SEC = 3600;
|
|
21289
|
+
/** At most this many ids on one mode's non-blocking list. */
|
|
21290
|
+
var NC_ALARM_NONBLOCKING_MAX = 200;
|
|
21291
|
+
var NcAlarmDelaySecSchema = zod.z.number().int().min(0).max(600);
|
|
21292
|
+
var NcAlarmTriggeredSecSchema = zod.z.number().int().min(0).max(NC_ALARM_TRIGGERED_MAX_SEC);
|
|
21293
|
+
var uniqueModes = (entries) => new Set(entries.map((e) => e.mode)).size === entries.length;
|
|
21294
|
+
/** One mode's OVERRIDES. An absent field inherits the global value (D697). */
|
|
21295
|
+
var NcAlarmModeDelaysSchema = zod.z.object({
|
|
21296
|
+
mode: AlarmArmModeSchema,
|
|
21297
|
+
exitDelaySec: NcAlarmDelaySecSchema.optional(),
|
|
21298
|
+
entryDelaySec: NcAlarmDelaySecSchema.optional(),
|
|
21299
|
+
triggeredDurationSec: NcAlarmTriggeredSecSchema.optional()
|
|
21300
|
+
});
|
|
21301
|
+
var NcAlarmModeDelaysListSchema = zod.z.array(NcAlarmModeDelaysSchema).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
|
|
21302
|
+
/**
|
|
21303
|
+
* One mode's EDIT in a patch: a number sets the override, `null` removes it (the mode inherits
|
|
21304
|
+
* again), an absent field leaves it alone. Per field, so two editors changing two different
|
|
21305
|
+
* delays of one mode never undo each other.
|
|
21306
|
+
*/
|
|
21307
|
+
var NcAlarmModeDelaysEditSchema = zod.z.object({
|
|
21308
|
+
mode: AlarmArmModeSchema,
|
|
21309
|
+
exitDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
|
|
21310
|
+
entryDelaySec: NcAlarmDelaySecSchema.nullable().optional(),
|
|
21311
|
+
triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional()
|
|
21312
|
+
});
|
|
21313
|
+
var NcAlarmModeDelaysEditListSchema = zod.z.array(NcAlarmModeDelaysEditSchema).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
|
|
21314
|
+
/** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
|
|
21315
|
+
var NcAlarmNonBlockingEditSchema = zod.z.object({
|
|
21316
|
+
mode: AlarmArmModeSchema,
|
|
21317
|
+
deviceId: zod.z.number().int().positive(),
|
|
21318
|
+
/** true = on the list (may be open, does not block); false = off the list (blocks). */
|
|
21319
|
+
nonBlocking: zod.z.boolean()
|
|
21320
|
+
});
|
|
21393
21321
|
/**
|
|
21394
21322
|
* One arm mode's non-blocking list: devices that may be open when that mode
|
|
21395
21323
|
* arms, without the arm being refused.
|
|
@@ -21403,10 +21331,10 @@ var NcAlarmNonBlockingSchema = zod.z.object({
|
|
|
21403
21331
|
* meaning a reader could agree on (union? the last one?), so they are refused
|
|
21404
21332
|
* at the boundary rather than interpreted.
|
|
21405
21333
|
*/
|
|
21406
|
-
var NcAlarmNonBlockingListSchema = zod.z.array(NcAlarmNonBlockingSchema).max(8).refine(
|
|
21334
|
+
var NcAlarmNonBlockingListSchema = zod.z.array(NcAlarmNonBlockingSchema).max(8).refine(uniqueModes, { message: "nonBlocking: at most one list per arm mode" });
|
|
21407
21335
|
/**
|
|
21408
|
-
* The three durations
|
|
21409
|
-
* transitions, and which openings each mode tolerates.
|
|
21336
|
+
* The three durations — global, and per mode since D697 — the panel's state
|
|
21337
|
+
* machine runs on, who hears about its transitions, and which openings each mode tolerates.
|
|
21410
21338
|
*
|
|
21411
21339
|
* They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
|
|
21412
21340
|
* `alarm-panel` is `deviceNative` and its other provider mirrors somebody
|
|
@@ -21416,15 +21344,15 @@ var NcAlarmNonBlockingListSchema = zod.z.array(NcAlarmNonBlockingSchema).max(8).
|
|
|
21416
21344
|
*/
|
|
21417
21345
|
var NcAlarmSettingsSchema = zod.z.object({
|
|
21418
21346
|
/** Grace period between arming and the mode taking effect. 0 = immediate. */
|
|
21419
|
-
exitDelaySec:
|
|
21347
|
+
exitDelaySec: NcAlarmDelaySecSchema,
|
|
21420
21348
|
/** Grace period between a trigger and the alarm firing. 0 = immediate. */
|
|
21421
|
-
entryDelaySec:
|
|
21349
|
+
entryDelaySec: NcAlarmDelaySecSchema,
|
|
21422
21350
|
/**
|
|
21423
21351
|
* How long `triggered` lasts before the panel re-arms itself.
|
|
21424
21352
|
* **0 = until an operator disarms it** — the behaviour before this field
|
|
21425
21353
|
* existed, and therefore what an untouched install keeps doing.
|
|
21426
21354
|
*/
|
|
21427
|
-
triggeredDurationSec:
|
|
21355
|
+
triggeredDurationSec: NcAlarmTriggeredSecSchema,
|
|
21428
21356
|
/**
|
|
21429
21357
|
* Deprecated by D630 — every transition is announced. Parsed, never read;
|
|
21430
21358
|
* remove after one release. Optional so an older admin's patch (which still
|
|
@@ -21447,17 +21375,30 @@ var NcAlarmSettingsSchema = zod.z.object({
|
|
|
21447
21375
|
* per mode is meaningful. Defaulted to `[]` so a blob stored before D630
|
|
21448
21376
|
* parses as "every opening blocks" — the behaviour it was written under.
|
|
21449
21377
|
*/
|
|
21450
|
-
nonBlocking: NcAlarmNonBlockingListSchema.default([])
|
|
21378
|
+
nonBlocking: NcAlarmNonBlockingListSchema.default([]),
|
|
21379
|
+
/**
|
|
21380
|
+
* Per arm mode, the delays that mode OVERRIDES (D697). A mode with no entry, or an entry
|
|
21381
|
+
* without a field, uses the global value above — so `[]`, the default, is exactly the
|
|
21382
|
+
* behaviour of a blob stored before this field existed. Read through `resolveModeDelays`.
|
|
21383
|
+
*/
|
|
21384
|
+
modeDelays: NcAlarmModeDelaysListSchema.default([])
|
|
21451
21385
|
});
|
|
21452
21386
|
/**
|
|
21453
21387
|
* Every field optional — a tab edits one control at a time.
|
|
21454
21388
|
*
|
|
21455
|
-
* `nonBlocking`
|
|
21456
|
-
* remove an inner `.default()`, so a patch that never
|
|
21457
|
-
* parse to `
|
|
21458
|
-
* (the same trap {@link NcRulePatchSchema} documents).
|
|
21389
|
+
* `nonBlocking` and `modeDelays` are re-declared WITHOUT their defaults:
|
|
21390
|
+
* `.partial()` does not remove an inner `.default()`, so a patch that never
|
|
21391
|
+
* named the field would parse to `[]` and wipe every mode's list on an
|
|
21392
|
+
* unrelated edit (the same trap {@link NcRulePatchSchema} documents). Here
|
|
21393
|
+
* `modeDelays` means EDITS, not the stored overrides.
|
|
21459
21394
|
*/
|
|
21460
|
-
var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({
|
|
21395
|
+
var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({
|
|
21396
|
+
nonBlocking: NcAlarmNonBlockingListSchema,
|
|
21397
|
+
/** Per-mode, per-field edits — NOT the whole list (see NcAlarmModeDelaysEditSchema). */
|
|
21398
|
+
modeDelays: NcAlarmModeDelaysEditListSchema,
|
|
21399
|
+
/** ONE membership change; the panel applies it to the list it holds at its turn. */
|
|
21400
|
+
nonBlockingEdit: NcAlarmNonBlockingEditSchema
|
|
21401
|
+
}).partial().refine((p) => p.nonBlocking === void 0 || p.nonBlockingEdit === void 0, { message: "nonBlocking and nonBlockingEdit are exclusive — send the whole lists or one edit" });
|
|
21461
21402
|
/**
|
|
21462
21403
|
* What one arm mode actually arms, DERIVED from the enabled rules gated on it.
|
|
21463
21404
|
* Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
|
|
@@ -21557,6 +21498,30 @@ var NcAlarmConfigSchema = zod.z.object({
|
|
|
21557
21498
|
*/
|
|
21558
21499
|
live: NcAlarmLiveSchema.optional()
|
|
21559
21500
|
});
|
|
21501
|
+
/** At most this many ids in one `getAlarmSensors` call — a validation bound, not a target. */
|
|
21502
|
+
var NC_ALARM_SENSORS_MAX = 500;
|
|
21503
|
+
var NcAlarmSensorReadingSchema = zod.z.enum([
|
|
21504
|
+
"open",
|
|
21505
|
+
"closed",
|
|
21506
|
+
"unknown",
|
|
21507
|
+
"not-opening"
|
|
21508
|
+
]);
|
|
21509
|
+
/** One device's state as the alarm reads it — the classifier the arm check uses. */
|
|
21510
|
+
var NcAlarmSensorStateSchema = zod.z.object({
|
|
21511
|
+
deviceId: zod.z.number().int().positive(),
|
|
21512
|
+
reading: NcAlarmSensorReadingSchema,
|
|
21513
|
+
/** The word as read (`open`, `closed`, `unlocked`, `stopped`, …); null when unknown / not an opening. */
|
|
21514
|
+
state: zod.z.string().nullable(),
|
|
21515
|
+
/** Why `unknown`: the read failed, or the slice holds no word its cap defines (a placeholder included). */
|
|
21516
|
+
unknownWhy: zod.z.enum(["read-failed", "unknown-word"]).nullable(),
|
|
21517
|
+
/** The device manager's liveness as the centre mirrors it; null = not known. */
|
|
21518
|
+
online: zod.z.boolean().nullable()
|
|
21519
|
+
});
|
|
21520
|
+
var NcAlarmSensorsInputSchema = zod.z.object({ deviceIds: zod.z.array(zod.z.number().int().positive()).max(500) });
|
|
21521
|
+
var NcAlarmSensorsSchema = zod.z.object({
|
|
21522
|
+
sensors: zod.z.array(NcAlarmSensorStateSchema),
|
|
21523
|
+
readAt: zod.z.number().int().nonnegative()
|
|
21524
|
+
});
|
|
21560
21525
|
/**
|
|
21561
21526
|
* The most seconds of footage any notification rule may ask a camera to retain.
|
|
21562
21527
|
*
|
|
@@ -21816,8 +21781,9 @@ var notificationRulesCapability = {
|
|
|
21816
21781
|
*/
|
|
21817
21782
|
getAlarmConfig: require_sleep.method(zod.z.object({}), NcAlarmConfigSchema, { auth: "admin" }),
|
|
21818
21783
|
/**
|
|
21819
|
-
* Patch the settings (durations
|
|
21820
|
-
*
|
|
21784
|
+
* Patch the settings (durations — global and per mode —, recipients,
|
|
21785
|
+
* per-mode non-blocking lists or one membership edit; `announceArm` is
|
|
21786
|
+
* accepted and ignored — D630). Returns the WHOLE config,
|
|
21821
21787
|
* coverage and `live` included, so a
|
|
21822
21788
|
* client never has to guess what the server settled on — the panel clamps
|
|
21823
21789
|
* and normalises, and a form that re-rendered from its own input would
|
|
@@ -21826,7 +21792,15 @@ var notificationRulesCapability = {
|
|
|
21826
21792
|
setAlarmConfig: require_sleep.method(zod.z.object({ patch: NcAlarmSettingsPatchSchema }), NcAlarmConfigSchema, {
|
|
21827
21793
|
kind: "mutation",
|
|
21828
21794
|
auth: "admin"
|
|
21829
|
-
})
|
|
21795
|
+
}),
|
|
21796
|
+
/**
|
|
21797
|
+
* The CURRENT state of each asked device as the alarm reads it (D697) — open, closed,
|
|
21798
|
+
* unknown (why) or not an opening, plus liveness. Classified by the same table the arm
|
|
21799
|
+
* check uses (`alarm-openings.ts`), so the editor and the arm can never disagree. The
|
|
21800
|
+
* caller names the ids (the household openings it already holds); at most
|
|
21801
|
+
* `NC_ALARM_SENSORS_MAX`.
|
|
21802
|
+
*/
|
|
21803
|
+
getAlarmSensors: require_sleep.method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" })
|
|
21830
21804
|
}
|
|
21831
21805
|
};
|
|
21832
21806
|
//#endregion
|
|
@@ -22215,12 +22189,27 @@ var CameraSensorRefSchema = zod.z.object({
|
|
|
22215
22189
|
function cameraSensorRefKey(ref) {
|
|
22216
22190
|
return `${ref.addonId}/${ref.stableId}`;
|
|
22217
22191
|
}
|
|
22218
|
-
/**
|
|
22219
|
-
|
|
22220
|
-
|
|
22221
|
-
|
|
22222
|
-
|
|
22223
|
-
|
|
22192
|
+
/**
|
|
22193
|
+
* A sensor's mode (D673). `in-addition`: its motion is the camera's motion too.
|
|
22194
|
+
* `replace`: its motion is the camera's, and while one is READY the camera's
|
|
22195
|
+
* own motion no longer counts on its own. `confirm`: while one is READY the
|
|
22196
|
+
* camera's own motion counts only while a confirm sensor is active — with or
|
|
22197
|
+
* without a replace sensor (spec C2, the controller's ruling on Q1).
|
|
22198
|
+
*/
|
|
22199
|
+
var CameraSensorModeSchema = zod.z.enum([
|
|
22200
|
+
"in-addition",
|
|
22201
|
+
"confirm",
|
|
22202
|
+
"replace"
|
|
22203
|
+
]);
|
|
22204
|
+
/** A stored sensor with no mode — every CS-1 row — is in addition. */
|
|
22205
|
+
var DEFAULT_CAMERA_SENSOR_MODE = "in-addition";
|
|
22206
|
+
var CameraSensorEntrySchema = CameraSensorRefSchema.extend({ mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE) });
|
|
22207
|
+
/**
|
|
22208
|
+
* A camera's attached sensors. A CS-1 row carried an envelope
|
|
22209
|
+
* `mode: 'in-addition'`: `z.object` drops it on parse, and the PARSED value is
|
|
22210
|
+
* what the write stores (D672 rule 3), so it is never written again.
|
|
22211
|
+
*/
|
|
22212
|
+
var CameraSensorsConfigSchema = zod.z.object({ sensors: zod.z.array(CameraSensorEntrySchema).min(1).max(8) }).superRefine((value, ctx) => {
|
|
22224
22213
|
const seen = /* @__PURE__ */ new Set();
|
|
22225
22214
|
value.sensors.forEach((ref, index) => {
|
|
22226
22215
|
const key = cameraSensorRefKey(ref);
|
|
@@ -22232,39 +22221,41 @@ var CameraSensorsConfigSchema = zod.z.object({
|
|
|
22232
22221
|
seen.add(key);
|
|
22233
22222
|
});
|
|
22234
22223
|
});
|
|
22235
|
-
|
|
22224
|
+
/** Every attachment names its mode; an older orchestrator that does not is in addition. */
|
|
22225
|
+
var AttachmentFields = {
|
|
22236
22226
|
addonId: zod.z.string(),
|
|
22237
|
-
stableId: zod.z.string()
|
|
22227
|
+
stableId: zod.z.string(),
|
|
22228
|
+
mode: CameraSensorModeSchema.default(DEFAULT_CAMERA_SENSOR_MODE)
|
|
22238
22229
|
};
|
|
22239
22230
|
var CameraSensorAttachmentSchema = zod.z.discriminatedUnion("state", [
|
|
22240
22231
|
zod.z.object({
|
|
22241
|
-
...
|
|
22232
|
+
...AttachmentFields,
|
|
22242
22233
|
state: zod.z.literal("ready"),
|
|
22243
22234
|
deviceId: zod.z.number(),
|
|
22244
22235
|
name: zod.z.string(),
|
|
22245
22236
|
caps: zod.z.array(zod.z.string()).readonly()
|
|
22246
22237
|
}),
|
|
22247
22238
|
zod.z.object({
|
|
22248
|
-
...
|
|
22239
|
+
...AttachmentFields,
|
|
22249
22240
|
state: zod.z.literal("offline"),
|
|
22250
22241
|
deviceId: zod.z.number(),
|
|
22251
22242
|
name: zod.z.string(),
|
|
22252
22243
|
reason: zod.z.enum(["offline", "disabled"])
|
|
22253
22244
|
}),
|
|
22254
22245
|
zod.z.object({
|
|
22255
|
-
...
|
|
22246
|
+
...AttachmentFields,
|
|
22256
22247
|
state: zod.z.literal("missing"),
|
|
22257
22248
|
reason: zod.z.string()
|
|
22258
22249
|
}),
|
|
22259
22250
|
zod.z.object({
|
|
22260
|
-
...
|
|
22251
|
+
...AttachmentFields,
|
|
22261
22252
|
state: zod.z.literal("unsupported"),
|
|
22262
22253
|
deviceId: zod.z.number(),
|
|
22263
22254
|
name: zod.z.string(),
|
|
22264
22255
|
reason: zod.z.string()
|
|
22265
22256
|
}),
|
|
22266
22257
|
zod.z.object({
|
|
22267
|
-
...
|
|
22258
|
+
...AttachmentFields,
|
|
22268
22259
|
state: zod.z.literal("unreadable"),
|
|
22269
22260
|
reason: zod.z.string()
|
|
22270
22261
|
})
|
|
@@ -22277,17 +22268,49 @@ var CameraSensorAttachmentSchema = zod.z.discriminatedUnion("state", [
|
|
|
22277
22268
|
* say.
|
|
22278
22269
|
*/
|
|
22279
22270
|
var CameraSensorsNotForwardedSchema = zod.z.enum(["onboard-not-listed", "no-active-detection"]);
|
|
22271
|
+
/** Whether the camera's OWN motion counts on its own (D673 C2), from its READY sensors (C3). */
|
|
22272
|
+
var CameraOwnMotionSchema = zod.z.enum([
|
|
22273
|
+
"always",
|
|
22274
|
+
"when-confirmed",
|
|
22275
|
+
"never"
|
|
22276
|
+
]);
|
|
22277
|
+
/** Confirm / Replace cannot suppress this camera's motion: its provider still emits it directly (D673 C13). */
|
|
22278
|
+
var CameraSensorsNotEnforcedSchema = zod.z.enum(["provider-not-split"]);
|
|
22279
|
+
/**
|
|
22280
|
+
* The camera's OWN onboard motion (D696): the raw input the bridge combines,
|
|
22281
|
+
* never the effective value. The camera's `motion` slice is the effective one,
|
|
22282
|
+
* so a view of the camera's own row reads this.
|
|
22283
|
+
* - `atMs`: when the bridge received the camera's last own edge.
|
|
22284
|
+
* - `detected`: evaluated WHEN THE STATUS IS ASKED — the last edge said `true`
|
|
22285
|
+
* AND it is younger than the window the bridge counts the camera's own motion
|
|
22286
|
+
* for (`motionCooldownMs`, floored at two sustain ticks). A camera that never
|
|
22287
|
+
* sends a clear (heartbeats only) therefore reads `false` once no push came
|
|
22288
|
+
* within the window; a status read is a snapshot, so a live view re-asks.
|
|
22289
|
+
* Absent = this orchestrator has not received the camera's own motion since it
|
|
22290
|
+
* started (unknown, never "quiet"), or an orchestrator older than D696.
|
|
22291
|
+
*/
|
|
22292
|
+
var CameraRawLevelSchema = zod.z.object({
|
|
22293
|
+
detected: zod.z.boolean(),
|
|
22294
|
+
atMs: zod.z.number()
|
|
22295
|
+
});
|
|
22280
22296
|
var CameraSensorsStatusSchema = zod.z.discriminatedUnion("state", [
|
|
22281
|
-
zod.z.object({
|
|
22297
|
+
zod.z.object({
|
|
22298
|
+
state: zod.z.literal("not-configured"),
|
|
22299
|
+
cameraLevel: CameraRawLevelSchema.optional()
|
|
22300
|
+
}),
|
|
22282
22301
|
zod.z.object({
|
|
22283
22302
|
state: zod.z.literal("unreadable"),
|
|
22284
|
-
reason: zod.z.string()
|
|
22303
|
+
reason: zod.z.string(),
|
|
22304
|
+
cameraLevel: CameraRawLevelSchema.optional()
|
|
22285
22305
|
}),
|
|
22286
22306
|
zod.z.object({
|
|
22287
22307
|
state: zod.z.literal("configured"),
|
|
22288
|
-
|
|
22308
|
+
cameraLevel: CameraRawLevelSchema.optional(),
|
|
22289
22309
|
sensors: zod.z.array(CameraSensorAttachmentSchema).readonly(),
|
|
22290
|
-
|
|
22310
|
+
/** Absent = an orchestrator older than CS-2: it does not say (D315). */
|
|
22311
|
+
cameraMotion: CameraOwnMotionSchema.optional(),
|
|
22312
|
+
notForwarded: CameraSensorsNotForwardedSchema.optional(),
|
|
22313
|
+
notEnforced: CameraSensorsNotEnforcedSchema.optional()
|
|
22291
22314
|
})
|
|
22292
22315
|
]);
|
|
22293
22316
|
/** What a camera reads before its first settings read ANSWERED (D315). */
|
|
@@ -26631,8 +26654,10 @@ var RunnerCameraConfigSchema = zod.z.object({
|
|
|
26631
26654
|
* - `analyzer`: ML/frame-diff motion detection (motion-wasm or other
|
|
26632
26655
|
* analyzer providers) running on decoded frames in this runner.
|
|
26633
26656
|
* - `onboard`: camera-native motion pushes (Reolink Baichuan, ONVIF
|
|
26634
|
-
* analytics). The provider emits ProviderMotion
|
|
26635
|
-
*
|
|
26657
|
+
* analytics). The provider emits its raw push as `ProviderMotion`
|
|
26658
|
+
* (D673); the orchestrator's camera-sensors bridge re-emits the camera's
|
|
26659
|
+
* effective motion as `MotionOnMotionChanged`, which is what reaches
|
|
26660
|
+
* `reportMotion`.
|
|
26636
26661
|
*/
|
|
26637
26662
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
26638
26663
|
/**
|
|
@@ -26997,7 +27022,15 @@ var RunnerLocalLoadSchema = zod.z.object({
|
|
|
26997
27022
|
hasGpu: zod.z.boolean(),
|
|
26998
27023
|
inferenceBackend: zod.z.string().optional(),
|
|
26999
27024
|
cpuCores: zod.z.number().optional()
|
|
27000
|
-
})
|
|
27025
|
+
}),
|
|
27026
|
+
/**
|
|
27027
|
+
* Who writes a camera's `motion` runtime slice (D696). `'orchestrator'`: this
|
|
27028
|
+
* runner never writes it. ABSENT = a runner older than D696, which still
|
|
27029
|
+
* writes the slice from its phase machine; the orchestrator does not write
|
|
27030
|
+
* while any runner node is absent-or-unanswered, so one slice never has two
|
|
27031
|
+
* writers.
|
|
27032
|
+
*/
|
|
27033
|
+
motionSliceWriter: zod.z.literal("orchestrator").optional()
|
|
27001
27034
|
});
|
|
27002
27035
|
/**
|
|
27003
27036
|
* Aggregate runtime metrics for the runner's whole local pool. Mirrors the
|
|
@@ -34859,6 +34892,72 @@ onStatusChanged: { data: zod.z.object({
|
|
|
34859
34892
|
volatileStateFields: ["lastChangedAt"]
|
|
34860
34893
|
};
|
|
34861
34894
|
//#endregion
|
|
34895
|
+
//#region src/capabilities/doorbell.cap.ts
|
|
34896
|
+
var DoorbellStatusSchema = zod.z.object({
|
|
34897
|
+
/** Ms epoch of the last press. null = never observed since this provider started. */
|
|
34898
|
+
lastPressedAt: zod.z.number().nullable(),
|
|
34899
|
+
/** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
|
|
34900
|
+
pressCountSinceStart: zod.z.number()
|
|
34901
|
+
});
|
|
34902
|
+
var DoorbellPressEventSchema = zod.z.object({
|
|
34903
|
+
deviceId: zod.z.number(),
|
|
34904
|
+
timestamp: zod.z.number()
|
|
34905
|
+
});
|
|
34906
|
+
/**
|
|
34907
|
+
* Doorbell button cap. Two kinds of provider serve it, and to every consumer
|
|
34908
|
+
* they are the same cap:
|
|
34909
|
+
*
|
|
34910
|
+
* - **Native** providers, registered per device by a driver addon through
|
|
34911
|
+
* `ctx.registerNativeCap` — a `DeviceType.Button` accessory with
|
|
34912
|
+
* `role: DeviceRole.Doorbell`, or the camera itself (Reolink registers at
|
|
34913
|
+
* camera level). They emit `onPressed` on every firmware ring.
|
|
34914
|
+
* - **A composed doorbell** (D670, D671): a camera's customization gives it
|
|
34915
|
+
* the cap, its `lastPressedAt` rising from the sources the operator
|
|
34916
|
+
* assigned in Customize → Doorbell, and the composer emits `onPressed`
|
|
34917
|
+
* from that field's advance (`fromState`).
|
|
34918
|
+
*
|
|
34919
|
+
* The DeviceEventPropagator re-emits `onPressed` on the camera parent.
|
|
34920
|
+
*/
|
|
34921
|
+
var doorbellCapability = {
|
|
34922
|
+
name: "doorbell",
|
|
34923
|
+
scope: "device",
|
|
34924
|
+
deviceNative: true,
|
|
34925
|
+
mode: "singleton",
|
|
34926
|
+
deviceTypes: [require_err_msg.DeviceType.Button, require_err_msg.DeviceType.Camera],
|
|
34927
|
+
methods: {},
|
|
34928
|
+
events: {
|
|
34929
|
+
/**
|
|
34930
|
+
* Fires once per physical press. Reolink delivers via Baichuan
|
|
34931
|
+
* push (`ReolinkSimpleEvent.type === 'doorbell'`). There is no
|
|
34932
|
+
* release/duration — it's a pulse.
|
|
34933
|
+
*/
|
|
34934
|
+
onPressed: {
|
|
34935
|
+
data: DoorbellPressEventSchema,
|
|
34936
|
+
fromState: {
|
|
34937
|
+
field: "lastPressedAt",
|
|
34938
|
+
on: "advance"
|
|
34939
|
+
}
|
|
34940
|
+
} },
|
|
34941
|
+
status: {
|
|
34942
|
+
schema: DoorbellStatusSchema,
|
|
34943
|
+
kind: "push"
|
|
34944
|
+
},
|
|
34945
|
+
/**
|
|
34946
|
+
* Runtime-state slice — last press timestamp + lifetime press count.
|
|
34947
|
+
* Mirrored by the kernel and readable via
|
|
34948
|
+
* `device.state.doorbell.value`. UIs can show "last ring 5m ago"
|
|
34949
|
+
* without subscribing.
|
|
34950
|
+
*/
|
|
34951
|
+
runtimeState: DoorbellStatusSchema,
|
|
34952
|
+
/**
|
|
34953
|
+
* Runtime-state durability: **restored** — a monotonic accumulator: the slice IS the record. `lastPressedAt` is content here, not a clock, so it is deliberately NOT volatile.
|
|
34954
|
+
*
|
|
34955
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
34956
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
34957
|
+
*/
|
|
34958
|
+
durability: "restored"
|
|
34959
|
+
};
|
|
34960
|
+
//#endregion
|
|
34862
34961
|
//#region src/capabilities/enum-sensor.cap.ts
|
|
34863
34962
|
/**
|
|
34864
34963
|
* Enum-state sensor — a string value picked from a finite option set.
|
|
@@ -37306,11 +37405,11 @@ authKey: zod.z.string().optional() }), zod.z.object({
|
|
|
37306
37405
|
/**
|
|
37307
37406
|
* How long after a camera's last rising edge `pipeline-analytics` closes the
|
|
37308
37407
|
* motion EPISODE row it kept open for it (D475) — "il delay della cap", in
|
|
37309
|
-
* the operator's words. One authority for the whole system:
|
|
37310
|
-
*
|
|
37311
|
-
*
|
|
37312
|
-
* seed its internal 3 s inactivity timer here,
|
|
37313
|
-
* a different purpose), and `MotionEpisodeTracker` imports it directly rather
|
|
37408
|
+
* the operator's words. One authority for the whole system: the writer of a
|
|
37409
|
+
* `Camera` device's slice (the orchestrator, D696) writes this SAME constant
|
|
37410
|
+
* in `autoClearAfterMs` while `detected: true` (never a camera's own clock —
|
|
37411
|
+
* Hikvision used to seed its internal 3 s inactivity timer here, a different
|
|
37412
|
+
* clock for a different purpose), and `MotionEpisodeTracker` imports it directly rather
|
|
37314
37413
|
* than reading the live cap value on the hot path. A `Sensor` device (HA
|
|
37315
37414
|
* binary sensor, Homematic) gets a genuine push both ways and has no episode
|
|
37316
37415
|
* to close on a timer, so it writes `null`, never this constant.
|
|
@@ -37321,9 +37420,9 @@ var MotionStatusSchema = zod.z.object({
|
|
|
37321
37420
|
/** Ms epoch of the last detected-true observation. Null if never detected. */
|
|
37322
37421
|
lastDetectedAt: zod.z.number().nullable(),
|
|
37323
37422
|
/**
|
|
37324
|
-
* `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device
|
|
37325
|
-
* `null` while false and on every `Sensor`
|
|
37326
|
-
* constant's doc for the one-authority rule.
|
|
37423
|
+
* `MOTION_CLOSE_AFTER_MS` while `detected: true` on a `Camera` device (the
|
|
37424
|
+
* orchestrator writes it, D696), `null` while false and on every `Sensor`
|
|
37425
|
+
* device (D475) — see that constant's doc for the one-authority rule.
|
|
37327
37426
|
*
|
|
37328
37427
|
* ## Reading this field still arms nothing
|
|
37329
37428
|
*
|
|
@@ -37341,34 +37440,31 @@ var MotionStatusSchema = zod.z.object({
|
|
|
37341
37440
|
* `addon-export-alexa/src/motion-clear-hold.ts` and
|
|
37342
37441
|
* `addon-export-hap`'s `RESET_DEBOUNCE_MS` both do.
|
|
37343
37442
|
*
|
|
37344
|
-
* ## Who writes it
|
|
37345
|
-
*
|
|
37346
|
-
* - **Cameras** — the runner's phase machine, `active → watching` on
|
|
37347
|
-
* `cooldown_expired`, which then writes this slice with
|
|
37348
|
-
* `detected: false` (`handlePhaseChanged` in
|
|
37349
|
-
* `pipeline-runner/index.ts`). It produces the FALLING edge, which
|
|
37350
|
-
* matters most for the sources that only ever push a rising one:
|
|
37351
|
-
* Reolink emits `MotionOnMotionChanged { detected: true }` and never
|
|
37352
|
-
* a false.
|
|
37353
|
-
* - **Sensors** (Home Assistant binary sensors, Homematic) — the
|
|
37354
|
-
* provider pushes the false itself, from the upstream system's own
|
|
37355
|
-
* state change. No phase machine is involved.
|
|
37443
|
+
* ## Who writes it (D696)
|
|
37356
37444
|
*
|
|
37357
|
-
*
|
|
37445
|
+
* - **Cameras** — the orchestrator, and nothing else. It writes the camera's
|
|
37446
|
+
* EFFECTIVE motion (`addon-pipeline-orchestrator`
|
|
37447
|
+
* `camera-sensors/motion-slice.ts`), alongside the effective
|
|
37448
|
+
* `MotionOnMotionChanged` it emits or receives: the camera-sensors bridge
|
|
37449
|
+
* for a split camera, an unsplit emitter's direct event, the analyzer and
|
|
37450
|
+
* device activity, as the camera's `motionSources` counts them. A source
|
|
37451
|
+
* that never sends a clear falls one window after its last `true`.
|
|
37452
|
+
* `lastDetectedAt` moves only on an effective rise. No camera provider
|
|
37453
|
+
* writes this slice (not even a boot seed), and neither does the runner's
|
|
37454
|
+
* phase machine: its phase is "a session is attached" for every on-motion
|
|
37455
|
+
* session, not motion, and it now has its own `DetectionPhaseTransition`.
|
|
37456
|
+
* A provider's `isDetected` answers from the hub's slice.
|
|
37457
|
+
* - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
|
|
37458
|
+
* imports) — the provider pushes both edges itself, from the upstream
|
|
37459
|
+
* system's own state change. That is the sensor's own fact.
|
|
37358
37460
|
*
|
|
37359
|
-
*
|
|
37360
|
-
*
|
|
37361
|
-
* `hikvision-camera.ts:3464` and `amcrest-camera.ts:445` both call
|
|
37362
|
-
* `setCapSlice(motionCapability, …)` on their own rising edge, and
|
|
37363
|
-
* Hikvision's comment says why: it read THIS docblock, agreed the
|
|
37364
|
-
* runner is canonical, and wrote anyway to avoid per-tick churn. So
|
|
37365
|
-
* two authorities can disagree about one slice, which this repo
|
|
37366
|
-
* forbids, and the doc said otherwise — which is worse than saying
|
|
37367
|
-
* nothing, because it reads as verification.
|
|
37461
|
+
* `scripts/check-onboard-motion-emitters.ts` refuses any other writer of a
|
|
37462
|
+
* `motion` slice; each sensor writer is a named exemption.
|
|
37368
37463
|
*
|
|
37369
|
-
* This
|
|
37370
|
-
*
|
|
37371
|
-
*
|
|
37464
|
+
* This replaces the D475 §4 rule ("the phase machine is the sole writer").
|
|
37465
|
+
* That rule was never true: Hikvision and Amcrest wrote the slice raw beside
|
|
37466
|
+
* it, every provider seeded it at boot, and every session left it
|
|
37467
|
+
* `detected: true` after its detach.
|
|
37372
37468
|
*/
|
|
37373
37469
|
autoClearAfterMs: zod.z.number().nullable()
|
|
37374
37470
|
});
|
|
@@ -37440,11 +37536,12 @@ onMotionChanged: { data: MotionOnMotionChangedDataSchema } },
|
|
|
37440
37536
|
*/
|
|
37441
37537
|
runtimeState: MotionStatusSchema,
|
|
37442
37538
|
/**
|
|
37443
|
-
* Runtime-state durability: **session** —
|
|
37539
|
+
* Runtime-state durability: **session** — the writer of this slice
|
|
37444
37540
|
* writes only on an EDGE, so a restored `detected: true` would stay
|
|
37445
37541
|
* frozen until the next one instead of being corrected. The next edge
|
|
37446
|
-
* re-publishes the real state
|
|
37447
|
-
*
|
|
37542
|
+
* re-publishes the real state, and the orchestrator re-publishes every
|
|
37543
|
+
* camera when it starts writing (D696; on who writes it, see
|
|
37544
|
+
* `autoClearAfterMs` above).
|
|
37448
37545
|
*
|
|
37449
37546
|
* See `RuntimeStateDurability`. Enforced by
|
|
37450
37547
|
* `scripts/check-runtime-state-durability.ts`.
|
|
@@ -40427,8 +40524,7 @@ var ptzAutotrackCapability = {
|
|
|
40427
40524
|
deviceNative: true,
|
|
40428
40525
|
mode: "singleton",
|
|
40429
40526
|
/**
|
|
40430
|
-
* Two kinds of provider behind one name, the same shape `snapshot`
|
|
40431
|
-
* `doorbell` use:
|
|
40527
|
+
* Two kinds of provider behind one name, the same shape `snapshot` uses:
|
|
40432
40528
|
*
|
|
40433
40529
|
* - **Native**: the camera's own firmware autotrack, registered per device by
|
|
40434
40530
|
* the driver when the probe says it exists (Reolink: yes; Hikvision:
|
|
@@ -40436,7 +40532,7 @@ var ptzAutotrackCapability = {
|
|
|
40436
40532
|
* - **Wrapper**: the emulated implementation (`@camstack/system`'s `autotrack` builtin),
|
|
40437
40533
|
* which follows a subject with `ptz.move` pulses driven by the detection
|
|
40438
40534
|
* pipeline. `defaultActive: false` — the operator binds it per camera in
|
|
40439
|
-
* the bindings UI
|
|
40535
|
+
* the bindings UI.
|
|
40440
40536
|
*
|
|
40441
40537
|
* The binding chooses WHICH implementation answers; `setEnabled` stays the
|
|
40442
40538
|
* one switch for the function either way. A camera never has both driving its
|
|
@@ -46659,10 +46755,10 @@ function feederCustomizationExample(sources) {
|
|
|
46659
46755
|
//#endregion
|
|
46660
46756
|
//#region src/composition/presets/doorbell-feature.ts
|
|
46661
46757
|
/**
|
|
46662
|
-
* The doorbell of a camera as a customization (D671). ONE builder: the
|
|
46663
|
-
*
|
|
46664
|
-
*
|
|
46665
|
-
*
|
|
46758
|
+
* The doorbell of a camera as a customization (D671). ONE builder: the admin
|
|
46759
|
+
* UI's Customize → Doorbell card writes every doorbell feature through it, and
|
|
46760
|
+
* edits one only when it is exactly what this builds (`isDoorbellPresetFeature`)
|
|
46761
|
+
* — a composed doorbell has one canonical shape.
|
|
46666
46762
|
*/
|
|
46667
46763
|
/** A rise closer than this to the previous one is the same press (D671). */
|
|
46668
46764
|
var DOORBELL_RISE_DEBOUNCE_MS = 2e3;
|
|
@@ -46679,7 +46775,7 @@ function doorbellFeatureSources(source, boundCaps) {
|
|
|
46679
46775
|
}];
|
|
46680
46776
|
});
|
|
46681
46777
|
}
|
|
46682
|
-
/** The D671 feature
|
|
46778
|
+
/** The canonical D671 doorbell feature. Throws on zero sources. */
|
|
46683
46779
|
function buildDoorbellCompositionFeature(input) {
|
|
46684
46780
|
const sources = [...input.sources].toSorted((a, b) => `${a.source.addonId}/${a.source.stableId}#${a.cap}`.localeCompare(`${b.source.addonId}/${b.source.stableId}#${b.cap}`));
|
|
46685
46781
|
if (sources.length === 0) throw new Error("doorbell composition needs at least one source");
|
|
@@ -56409,6 +56505,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
56409
56505
|
addonId: null,
|
|
56410
56506
|
access: "view"
|
|
56411
56507
|
},
|
|
56508
|
+
"notificationRules.getAlarmSensors": {
|
|
56509
|
+
capName: "notification-rules",
|
|
56510
|
+
capScope: "system",
|
|
56511
|
+
addonId: null,
|
|
56512
|
+
access: "view"
|
|
56513
|
+
},
|
|
56412
56514
|
"notificationRules.getClipRetentionAsks": {
|
|
56413
56515
|
capName: "notification-rules",
|
|
56414
56516
|
capScope: "system",
|
|
@@ -61229,6 +61331,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
|
|
|
61229
61331
|
form: "single",
|
|
61230
61332
|
optional: false
|
|
61231
61333
|
}],
|
|
61334
|
+
"notificationRules.getAlarmSensors": [{
|
|
61335
|
+
name: "deviceIds",
|
|
61336
|
+
form: "array",
|
|
61337
|
+
optional: false
|
|
61338
|
+
}],
|
|
61232
61339
|
"notificationRules.setDeviceMuted": [{
|
|
61233
61340
|
name: "deviceId",
|
|
61234
61341
|
form: "single",
|
|
@@ -62467,6 +62574,7 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
|
|
|
62467
62574
|
"faceGallery.listRecentFaces",
|
|
62468
62575
|
"networkQuality.getDeviceStats",
|
|
62469
62576
|
"networkQuality.reportClientStats",
|
|
62577
|
+
"notificationRules.getAlarmSensors",
|
|
62470
62578
|
"notificationRules.setDeviceMuted",
|
|
62471
62579
|
"osdManager.clearSlotBinding",
|
|
62472
62580
|
"osdManager.copyDeviceConfiguration",
|
|
@@ -63407,7 +63515,8 @@ function createSystemProxy(api) {
|
|
|
63407
63515
|
createSnooze: (input) => dispatch("notificationRules", "createSnooze", "mutation", input),
|
|
63408
63516
|
cancelSnooze: (input) => dispatch("notificationRules", "cancelSnooze", "mutation", input),
|
|
63409
63517
|
getAlarmConfig: (input) => dispatch("notificationRules", "getAlarmConfig", "query", input),
|
|
63410
|
-
setAlarmConfig: (input) => dispatch("notificationRules", "setAlarmConfig", "mutation", input)
|
|
63518
|
+
setAlarmConfig: (input) => dispatch("notificationRules", "setAlarmConfig", "mutation", input),
|
|
63519
|
+
getAlarmSensors: (input) => dispatch("notificationRules", "getAlarmSensors", "query", input)
|
|
63411
63520
|
},
|
|
63412
63521
|
osdManager: {
|
|
63413
63522
|
getConditionSupport: (input) => dispatch("osdManager", "getConditionSupport", "query", input),
|
|
@@ -65442,6 +65551,176 @@ function sensorTransitionNeverFires(rule) {
|
|
|
65442
65551
|
/** The warning an editor shows for {@link sensorTransitionNeverFires}. */
|
|
65443
65552
|
var NC_SENSOR_TRANSITION_NEVER_FIRES_WARNING = "This rule belongs to the alarm, and an alarm rule fires only when an opening OPENS. Set to “only when it clears”, it can never fire.";
|
|
65444
65553
|
//#endregion
|
|
65554
|
+
//#region src/notification/alarm-settings.ts
|
|
65555
|
+
var NC_ALARM_DELAY_FIELDS = [
|
|
65556
|
+
"exitDelaySec",
|
|
65557
|
+
"entryDelaySec",
|
|
65558
|
+
"triggeredDurationSec"
|
|
65559
|
+
];
|
|
65560
|
+
/** The mode's value when it overrides the field, else the global one. */
|
|
65561
|
+
function pick(override, global) {
|
|
65562
|
+
return override === void 0 ? {
|
|
65563
|
+
value: global,
|
|
65564
|
+
source: "default"
|
|
65565
|
+
} : {
|
|
65566
|
+
value: override,
|
|
65567
|
+
source: "mode"
|
|
65568
|
+
};
|
|
65569
|
+
}
|
|
65570
|
+
/** What `mode` actually runs on: its own overrides, the global value for every other field. */
|
|
65571
|
+
function resolveModeDelays(settings, mode) {
|
|
65572
|
+
const entry = (settings.modeDelays ?? []).find((e) => e.mode === mode);
|
|
65573
|
+
const exit = pick(entry?.exitDelaySec, settings.exitDelaySec);
|
|
65574
|
+
const entryDelay = pick(entry?.entryDelaySec, settings.entryDelaySec);
|
|
65575
|
+
const triggered = pick(entry?.triggeredDurationSec, settings.triggeredDurationSec);
|
|
65576
|
+
return {
|
|
65577
|
+
exitDelaySec: exit.value,
|
|
65578
|
+
entryDelaySec: entryDelay.value,
|
|
65579
|
+
triggeredDurationSec: triggered.value,
|
|
65580
|
+
source: {
|
|
65581
|
+
exitDelaySec: exit.source,
|
|
65582
|
+
entryDelaySec: entryDelay.source,
|
|
65583
|
+
triggeredDurationSec: triggered.source
|
|
65584
|
+
}
|
|
65585
|
+
};
|
|
65586
|
+
}
|
|
65587
|
+
/** One field of an entry after an edit: number sets, null clears, undefined keeps. */
|
|
65588
|
+
function edited(current, edit) {
|
|
65589
|
+
if (edit === void 0) return current;
|
|
65590
|
+
return edit === null ? void 0 : edit;
|
|
65591
|
+
}
|
|
65592
|
+
/**
|
|
65593
|
+
* Apply per-field edits to the stored overrides. Returns new arrays, never mutates the input;
|
|
65594
|
+
* an entry with no field left is dropped (the mode inherits everything again).
|
|
65595
|
+
*/
|
|
65596
|
+
function applyModeDelaysEdits(current, edits) {
|
|
65597
|
+
const out = current.map((e) => ({ ...e }));
|
|
65598
|
+
for (const edit of edits) {
|
|
65599
|
+
const at = out.findIndex((e) => e.mode === edit.mode);
|
|
65600
|
+
const base = at >= 0 ? out[at] ?? { mode: edit.mode } : { mode: edit.mode };
|
|
65601
|
+
const next = { mode: edit.mode };
|
|
65602
|
+
const exit = edited(base.exitDelaySec, edit.exitDelaySec);
|
|
65603
|
+
const entry = edited(base.entryDelaySec, edit.entryDelaySec);
|
|
65604
|
+
const triggered = edited(base.triggeredDurationSec, edit.triggeredDurationSec);
|
|
65605
|
+
if (exit !== void 0) next.exitDelaySec = exit;
|
|
65606
|
+
if (entry !== void 0) next.entryDelaySec = entry;
|
|
65607
|
+
if (triggered !== void 0) next.triggeredDurationSec = triggered;
|
|
65608
|
+
if (at >= 0) out[at] = next;
|
|
65609
|
+
else out.push(next);
|
|
65610
|
+
}
|
|
65611
|
+
return out.filter((e) => e.exitDelaySec !== void 0 || e.entryDelaySec !== void 0 || e.triggeredDurationSec !== void 0);
|
|
65612
|
+
}
|
|
65613
|
+
/**
|
|
65614
|
+
* Put ONE device on, or take it off, ONE mode's list. A no-op answers `changed: false`; an add
|
|
65615
|
+
* to a full list is REFUSED by reason (never silently dropped); an emptied list drops its entry.
|
|
65616
|
+
*/
|
|
65617
|
+
function applyNonBlockingEdit(lists, edit) {
|
|
65618
|
+
const copy = lists.map((l) => ({
|
|
65619
|
+
mode: l.mode,
|
|
65620
|
+
deviceIds: [...l.deviceIds]
|
|
65621
|
+
}));
|
|
65622
|
+
const at = copy.findIndex((l) => l.mode === edit.mode);
|
|
65623
|
+
const held = at >= 0 ? copy[at]?.deviceIds ?? [] : [];
|
|
65624
|
+
const present = held.includes(edit.deviceId);
|
|
65625
|
+
if (edit.nonBlocking === present) return {
|
|
65626
|
+
kind: "applied",
|
|
65627
|
+
lists: copy,
|
|
65628
|
+
changed: false
|
|
65629
|
+
};
|
|
65630
|
+
if (edit.nonBlocking) {
|
|
65631
|
+
if (held.length >= 200) return {
|
|
65632
|
+
kind: "refused",
|
|
65633
|
+
reason: "list-full",
|
|
65634
|
+
mode: edit.mode,
|
|
65635
|
+
max: 200
|
|
65636
|
+
};
|
|
65637
|
+
const next = {
|
|
65638
|
+
mode: edit.mode,
|
|
65639
|
+
deviceIds: [...held, edit.deviceId]
|
|
65640
|
+
};
|
|
65641
|
+
if (at >= 0) copy[at] = next;
|
|
65642
|
+
else copy.push(next);
|
|
65643
|
+
} else {
|
|
65644
|
+
const next = {
|
|
65645
|
+
mode: edit.mode,
|
|
65646
|
+
deviceIds: held.filter((id) => id !== edit.deviceId)
|
|
65647
|
+
};
|
|
65648
|
+
if (at >= 0) copy[at] = next;
|
|
65649
|
+
}
|
|
65650
|
+
return {
|
|
65651
|
+
kind: "applied",
|
|
65652
|
+
lists: copy.filter((l) => l.deviceIds.length > 0),
|
|
65653
|
+
changed: true
|
|
65654
|
+
};
|
|
65655
|
+
}
|
|
65656
|
+
function sameSet(a, b) {
|
|
65657
|
+
const sa = new Set(a);
|
|
65658
|
+
const sb = new Set(b);
|
|
65659
|
+
return sa.size === sb.size && [...sa].every((x) => sb.has(x));
|
|
65660
|
+
}
|
|
65661
|
+
/** Per-mode id sets of a lists value, empty lists ignored. */
|
|
65662
|
+
function listsByMode(lists) {
|
|
65663
|
+
const out = /* @__PURE__ */ new Map();
|
|
65664
|
+
for (const l of lists) if (l.deviceIds.length > 0) out.set(l.mode, new Set(l.deviceIds));
|
|
65665
|
+
return out;
|
|
65666
|
+
}
|
|
65667
|
+
function sameLists(a, b) {
|
|
65668
|
+
const ma = listsByMode(a);
|
|
65669
|
+
const mb = listsByMode(b);
|
|
65670
|
+
if (ma.size !== mb.size) return false;
|
|
65671
|
+
for (const [mode, ids] of ma) {
|
|
65672
|
+
const other = mb.get(mode);
|
|
65673
|
+
if (other === void 0 || !sameSet([...ids], [...other])) return false;
|
|
65674
|
+
}
|
|
65675
|
+
return true;
|
|
65676
|
+
}
|
|
65677
|
+
/** The returned entry's value for one field of a mode-delays edit. */
|
|
65678
|
+
function fieldOf(entry, field) {
|
|
65679
|
+
if (entry === void 0) return void 0;
|
|
65680
|
+
switch (field) {
|
|
65681
|
+
case "exitDelaySec": return entry.exitDelaySec;
|
|
65682
|
+
case "entryDelaySec": return entry.entryDelaySec;
|
|
65683
|
+
case "triggeredDurationSec": return entry.triggeredDurationSec;
|
|
65684
|
+
}
|
|
65685
|
+
}
|
|
65686
|
+
/** The edit's value for one field. */
|
|
65687
|
+
function editFieldOf(edit, field) {
|
|
65688
|
+
switch (field) {
|
|
65689
|
+
case "exitDelaySec": return edit.exitDelaySec;
|
|
65690
|
+
case "entryDelaySec": return edit.entryDelaySec;
|
|
65691
|
+
case "triggeredDurationSec": return edit.triggeredDurationSec;
|
|
65692
|
+
}
|
|
65693
|
+
}
|
|
65694
|
+
/**
|
|
65695
|
+
* True iff every key the patch sent is reflected in what the host returned. A host older than
|
|
65696
|
+
* this build strips the keys it does not know; this is how a client tells "saved" from "parsed
|
|
65697
|
+
* and dropped". `announceArm` is ignored (parsed, never read). A `null` clear "lands" on a host
|
|
65698
|
+
* that stripped the key (it lacks the field) — harmless, because such a host stores no overrides.
|
|
65699
|
+
* A whole-list `nonBlocking` naming a mode the panel does not offer reads as "not landed" (the host
|
|
65700
|
+
* drops it); only older clients and the prune send whole lists, and they do not call this.
|
|
65701
|
+
*/
|
|
65702
|
+
function alarmPatchLanded(patch, returned) {
|
|
65703
|
+
if (patch.exitDelaySec !== void 0 && patch.exitDelaySec !== returned.exitDelaySec) return false;
|
|
65704
|
+
if (patch.entryDelaySec !== void 0 && patch.entryDelaySec !== returned.entryDelaySec) return false;
|
|
65705
|
+
if (patch.triggeredDurationSec !== void 0 && patch.triggeredDurationSec !== returned.triggeredDurationSec) return false;
|
|
65706
|
+
if (patch.announceTargets !== void 0 && !sameSet(patch.announceTargets, returned.announceTargets)) return false;
|
|
65707
|
+
if (patch.nonBlocking !== void 0 && !sameLists(patch.nonBlocking, returned.nonBlocking ?? [])) return false;
|
|
65708
|
+
for (const edit of patch.modeDelays ?? []) {
|
|
65709
|
+
const entry = (returned.modeDelays ?? []).find((e) => e.mode === edit.mode);
|
|
65710
|
+
for (const field of NC_ALARM_DELAY_FIELDS) {
|
|
65711
|
+
const want = editFieldOf(edit, field);
|
|
65712
|
+
if (want === void 0) continue;
|
|
65713
|
+
const got = fieldOf(entry, field);
|
|
65714
|
+
if (want === null ? got !== void 0 : got !== want) return false;
|
|
65715
|
+
}
|
|
65716
|
+
}
|
|
65717
|
+
if (patch.nonBlockingEdit !== void 0) {
|
|
65718
|
+
const e = patch.nonBlockingEdit;
|
|
65719
|
+
if (((returned.nonBlocking ?? []).find((l) => l.mode === e.mode)?.deviceIds.includes(e.deviceId) ?? false) !== e.nonBlocking) return false;
|
|
65720
|
+
}
|
|
65721
|
+
return true;
|
|
65722
|
+
}
|
|
65723
|
+
//#endregion
|
|
65445
65724
|
//#region src/notification/sensor-raised.ts
|
|
65446
65725
|
/**
|
|
65447
65726
|
* WHICH WAY A BOOLEAN SENSOR MOVED — read off the one slice a sensor event
|
|
@@ -67150,6 +67429,35 @@ function readTimelapseGeneratedAt(rule, deviceId) {
|
|
|
67150
67429
|
return rule.lastGeneratedAt ?? 0;
|
|
67151
67430
|
}
|
|
67152
67431
|
//#endregion
|
|
67432
|
+
//#region src/pipeline/motion-ownership.ts
|
|
67433
|
+
function cameraOwnsMotionViaSensor(motionSources) {
|
|
67434
|
+
return motionSources.some((s) => s !== "analyzer");
|
|
67435
|
+
}
|
|
67436
|
+
//#endregion
|
|
67437
|
+
//#region src/device/hub-motion-read.ts
|
|
67438
|
+
async function readHubMotionDetected(deviceState, deviceId) {
|
|
67439
|
+
let slice;
|
|
67440
|
+
try {
|
|
67441
|
+
slice = await deviceState.getCapSlice.query({
|
|
67442
|
+
deviceId,
|
|
67443
|
+
capName: "motion"
|
|
67444
|
+
});
|
|
67445
|
+
} catch (err) {
|
|
67446
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
67447
|
+
throw new Error(`motion state of device ${deviceId} is unreadable: ${reason}`, { cause: err });
|
|
67448
|
+
}
|
|
67449
|
+
const detected = slice?.["detected"];
|
|
67450
|
+
if (typeof detected !== "boolean") throw new Error(`motion state of device ${deviceId} is not published yet (unknown)`);
|
|
67451
|
+
return detected;
|
|
67452
|
+
}
|
|
67453
|
+
//#endregion
|
|
67454
|
+
//#region src/pipeline/provider-motion.ts
|
|
67455
|
+
/** The motion event's data without `triggeredBy`: only the bridge names a trigger. */
|
|
67456
|
+
var ProviderMotionDataSchema = MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
|
|
67457
|
+
function providerMotionEvent(source, data) {
|
|
67458
|
+
return require_sleep.createEvent(require_event_category.EventCategory.ProviderMotion, source, data);
|
|
67459
|
+
}
|
|
67460
|
+
//#endregion
|
|
67153
67461
|
//#region src/pipeline/step-format-runnable.ts
|
|
67154
67462
|
/** True when `model` ships a build for `format`. */
|
|
67155
67463
|
function hasBuild(model, format) {
|
|
@@ -68039,8 +68347,8 @@ function bestLocationMatch(externalName, existing, threshold = .8) {
|
|
|
68039
68347
|
* So `entry.providerAddonId === this.ctx.id` is silently, permanently false
|
|
68040
68348
|
* for a builtin — and only for a builtin, which is why it survives a green
|
|
68041
68349
|
* suite whose fake supplies the bare id. That is exactly how camera 615's
|
|
68042
|
-
* virtual doorbell latched `unbound` for twelve hours on
|
|
68043
|
-
* binding was intact ([D72](../../../../docs/decisions/adr-0072.md)).
|
|
68350
|
+
* virtual doorbell (retired since, D671) latched `unbound` for twelve hours on
|
|
68351
|
+
* 2026-08-07 while its binding was intact ([D72](../../../../docs/decisions/adr-0072.md)).
|
|
68044
68352
|
*
|
|
68045
68353
|
* Route every comparison between `ctx.id` and a registry/store addon id
|
|
68046
68354
|
* through {@link isSameAddonId}. `scripts/check-addon-id-comparison.ts`
|
|
@@ -69466,12 +69774,16 @@ exports.CameraMetricsSchema = CameraMetricsSchema;
|
|
|
69466
69774
|
exports.CameraMetricsWithDeviceIdSchema = CameraMetricsWithDeviceIdSchema;
|
|
69467
69775
|
exports.CameraMotionStatusSchema = CameraMotionStatusSchema;
|
|
69468
69776
|
exports.CameraOccupancySnapshotForDeviceSchema = CameraOccupancySnapshotForDeviceSchema;
|
|
69777
|
+
exports.CameraOwnMotionSchema = CameraOwnMotionSchema;
|
|
69778
|
+
exports.CameraRawLevelSchema = CameraRawLevelSchema;
|
|
69469
69779
|
exports.CameraRecordingModeSchema = CameraRecordingModeSchema;
|
|
69470
69780
|
exports.CameraRecordingStatusSchema = CameraRecordingStatusSchema;
|
|
69471
69781
|
exports.CameraSensorAttachmentSchema = CameraSensorAttachmentSchema;
|
|
69782
|
+
exports.CameraSensorEntrySchema = CameraSensorEntrySchema;
|
|
69783
|
+
exports.CameraSensorModeSchema = CameraSensorModeSchema;
|
|
69472
69784
|
exports.CameraSensorRefSchema = CameraSensorRefSchema;
|
|
69473
69785
|
exports.CameraSensorsConfigSchema = CameraSensorsConfigSchema;
|
|
69474
|
-
exports.
|
|
69786
|
+
exports.CameraSensorsNotEnforcedSchema = CameraSensorsNotEnforcedSchema;
|
|
69475
69787
|
exports.CameraSensorsNotForwardedSchema = CameraSensorsNotForwardedSchema;
|
|
69476
69788
|
exports.CameraSensorsStatusSchema = CameraSensorsStatusSchema;
|
|
69477
69789
|
exports.CameraSourceStatusSchema = CameraSourceStatusSchema;
|
|
@@ -69611,6 +69923,7 @@ exports.DECLARED_DEVICE_SWEEP_LIMIT = DECLARED_DEVICE_SWEEP_LIMIT;
|
|
|
69611
69923
|
exports.DECLARED_INTEGRATION_FIXED_KEY = DECLARED_INTEGRATION_FIXED_KEY;
|
|
69612
69924
|
exports.DEFAULT_ADDON_PLACEMENT = DEFAULT_ADDON_PLACEMENT;
|
|
69613
69925
|
exports.DEFAULT_AUDIO_ANALYZER_CONFIG = DEFAULT_AUDIO_ANALYZER_CONFIG;
|
|
69926
|
+
exports.DEFAULT_CAMERA_SENSOR_MODE = DEFAULT_CAMERA_SENSOR_MODE;
|
|
69614
69927
|
exports.DEFAULT_CLUSTER_STEP_MODELS = DEFAULT_CLUSTER_STEP_MODELS;
|
|
69615
69928
|
exports.DEFAULT_CLUSTER_STEP_SETTINGS = DEFAULT_CLUSTER_STEP_SETTINGS;
|
|
69616
69929
|
exports.DEFAULT_DECODER_HWACCEL_CONFIG = DEFAULT_DECODER_HWACCEL_CONFIG;
|
|
@@ -69652,7 +69965,6 @@ exports.DISPLAY_ICON_KEYS = DISPLAY_ICON_KEYS;
|
|
|
69652
69965
|
exports.DISPLAY_ICON_OFFERED_KEYS = DISPLAY_ICON_OFFERED_KEYS;
|
|
69653
69966
|
exports.DISPLAY_ICON_VOCABULARY = DISPLAY_ICON_VOCABULARY;
|
|
69654
69967
|
exports.DOORBELL_RISE_DEBOUNCE_MS = DOORBELL_RISE_DEBOUNCE_MS;
|
|
69655
|
-
exports.DOORBELL_SOURCE_SLOT = DOORBELL_SOURCE_SLOT;
|
|
69656
69968
|
exports.DataStoreEngineInfoSchema = EngineInfoSchema;
|
|
69657
69969
|
exports.DatabaseSnapshotResultSchema = DatabaseSnapshotResultSchema;
|
|
69658
69970
|
exports.DatabaseSnapshotTableSchema = DatabaseSnapshotTableSchema;
|
|
@@ -69697,7 +70009,6 @@ exports.DiscoveredTargetSchema = DiscoveredTargetSchema;
|
|
|
69697
70009
|
exports.DiskReconcileJobSchema = DiskReconcileJobSchema;
|
|
69698
70010
|
exports.DisposerChain = require_sleep.DisposerChain;
|
|
69699
70011
|
exports.DoorbellPressEventSchema = DoorbellPressEventSchema;
|
|
69700
|
-
exports.DoorbellSourceSlotValueSchema = DoorbellSourceSlotValueSchema;
|
|
69701
70012
|
exports.DoorbellStatusSchema = DoorbellStatusSchema;
|
|
69702
70013
|
exports.EMBEDDING_MODEL_FAILED = EMBEDDING_MODEL_FAILED;
|
|
69703
70014
|
exports.EMBEDDING_REQUIREMENTS_FAILED = EMBEDDING_REQUIREMENTS_FAILED;
|
|
@@ -70037,8 +70348,13 @@ exports.NATIVE_LEASE_TILE_BUDGET_KEY = NATIVE_LEASE_TILE_BUDGET_KEY;
|
|
|
70037
70348
|
exports.NAVIGATION_ACTION_CATALOG = NAVIGATION_ACTION_CATALOG;
|
|
70038
70349
|
exports.NAVIGATION_LIGHT_LEVEL_MAX = NAVIGATION_LIGHT_LEVEL_MAX;
|
|
70039
70350
|
exports.NAVIGATION_LIGHT_LEVEL_MIN = NAVIGATION_LIGHT_LEVEL_MIN;
|
|
70351
|
+
exports.NC_ALARM_DELAY_FIELDS = NC_ALARM_DELAY_FIELDS;
|
|
70352
|
+
exports.NC_ALARM_DELAY_MAX_SEC = NC_ALARM_DELAY_MAX_SEC;
|
|
70353
|
+
exports.NC_ALARM_NONBLOCKING_MAX = NC_ALARM_NONBLOCKING_MAX;
|
|
70040
70354
|
exports.NC_ALARM_OPENING_EVENT_KINDS = NC_ALARM_OPENING_EVENT_KINDS;
|
|
70355
|
+
exports.NC_ALARM_SENSORS_MAX = NC_ALARM_SENSORS_MAX;
|
|
70041
70356
|
exports.NC_ALARM_SYSTEM_EVENT_KINDS = NC_ALARM_SYSTEM_EVENT_KINDS;
|
|
70357
|
+
exports.NC_ALARM_TRIGGERED_MAX_SEC = NC_ALARM_TRIGGERED_MAX_SEC;
|
|
70042
70358
|
exports.NC_AUDIO_CONFIRM_HITS_DEFAULT = NC_AUDIO_CONFIRM_HITS_DEFAULT;
|
|
70043
70359
|
exports.NC_AUDIO_CONFIRM_HITS_MAX = NC_AUDIO_CONFIRM_HITS_MAX;
|
|
70044
70360
|
exports.NC_AUDIO_CONFIRM_HITS_MIN = NC_AUDIO_CONFIRM_HITS_MIN;
|
|
@@ -70126,6 +70442,13 @@ exports.NavigationRuntimeStateSchema = NavigationRuntimeStateSchema;
|
|
|
70126
70442
|
exports.NavigationStatusSchema = NavigationStatusSchema;
|
|
70127
70443
|
exports.NcAlarmConfigSchema = NcAlarmConfigSchema;
|
|
70128
70444
|
exports.NcAlarmModeCoverageSchema = NcAlarmModeCoverageSchema;
|
|
70445
|
+
exports.NcAlarmModeDelaysEditSchema = NcAlarmModeDelaysEditSchema;
|
|
70446
|
+
exports.NcAlarmModeDelaysSchema = NcAlarmModeDelaysSchema;
|
|
70447
|
+
exports.NcAlarmNonBlockingEditSchema = NcAlarmNonBlockingEditSchema;
|
|
70448
|
+
exports.NcAlarmSensorReadingSchema = NcAlarmSensorReadingSchema;
|
|
70449
|
+
exports.NcAlarmSensorStateSchema = NcAlarmSensorStateSchema;
|
|
70450
|
+
exports.NcAlarmSensorsInputSchema = NcAlarmSensorsInputSchema;
|
|
70451
|
+
exports.NcAlarmSensorsSchema = NcAlarmSensorsSchema;
|
|
70129
70452
|
exports.NcAlarmSettingsPatchSchema = NcAlarmSettingsPatchSchema;
|
|
70130
70453
|
exports.NcAlarmSettingsSchema = NcAlarmSettingsSchema;
|
|
70131
70454
|
exports.NcAlarmSkipReasonSchema = NcAlarmSkipReasonSchema;
|
|
@@ -70290,6 +70613,7 @@ exports.PrivacyMaskStatusSchema = PrivacyMaskStatusSchema;
|
|
|
70290
70613
|
exports.ProfileRtspEntrySchema = require_sleep.ProfileRtspEntrySchema;
|
|
70291
70614
|
exports.ProfileSlotSchema = require_sleep.ProfileSlotSchema;
|
|
70292
70615
|
exports.ProfileSlotStatusSchema = require_sleep.ProfileSlotStatusSchema;
|
|
70616
|
+
exports.ProviderMotionDataSchema = ProviderMotionDataSchema;
|
|
70293
70617
|
exports.ProviderStatusSchema = ProviderStatusSchema;
|
|
70294
70618
|
exports.PtzAutotrackRuntimeStateSchema = PtzAutotrackRuntimeStateSchema;
|
|
70295
70619
|
exports.PtzAutotrackSettingsSchema = PtzAutotrackSettingsSchema;
|
|
@@ -70697,8 +71021,11 @@ exports.addonsCapability = addonsCapability;
|
|
|
70697
71021
|
exports.adminUiCapability = require_sleep.adminUiCapability;
|
|
70698
71022
|
exports.airQualitySensorCapability = airQualitySensorCapability;
|
|
70699
71023
|
exports.alarmPanelCapability = alarmPanelCapability;
|
|
71024
|
+
exports.alarmPatchLanded = alarmPatchLanded;
|
|
70700
71025
|
exports.alertsCapability = alertsCapability;
|
|
70701
71026
|
exports.ambientLightSensorCapability = ambientLightSensorCapability;
|
|
71027
|
+
exports.applyModeDelaysEdits = applyModeDelaysEdits;
|
|
71028
|
+
exports.applyNonBlockingEdit = applyNonBlockingEdit;
|
|
70702
71029
|
exports.asBoolean = require_sleep.asBoolean;
|
|
70703
71030
|
exports.asJsonArray = require_sleep.asJsonArray;
|
|
70704
71031
|
exports.asJsonObject = require_sleep.asJsonObject;
|
|
@@ -70745,6 +71072,7 @@ exports.builtCapabilityBlockers = builtCapabilityBlockers;
|
|
|
70745
71072
|
exports.buttonCapability = buttonCapability;
|
|
70746
71073
|
exports.cameraCredentialsCapability = cameraCredentialsCapability;
|
|
70747
71074
|
exports.cameraGridLayoutCapability = cameraGridLayoutCapability;
|
|
71075
|
+
exports.cameraOwnsMotionViaSensor = cameraOwnsMotionViaSensor;
|
|
70748
71076
|
exports.cameraPipelineConfigCapability = cameraPipelineConfigCapability;
|
|
70749
71077
|
exports.cameraSensorRefKey = cameraSensorRefKey;
|
|
70750
71078
|
exports.cameraStreamsCapability = cameraStreamsCapability;
|
|
@@ -71119,6 +71447,7 @@ exports.pressureSensorCapability = pressureSensorCapability;
|
|
|
71119
71447
|
exports.principalMayReachAddon = principalMayReachAddon;
|
|
71120
71448
|
exports.privacyMaskCapability = privacyMaskCapability;
|
|
71121
71449
|
exports.procedureAuthKey = procedureAuthKey;
|
|
71450
|
+
exports.providerMotionEvent = providerMotionEvent;
|
|
71122
71451
|
exports.ptzAutotrackCapability = ptzAutotrackCapability;
|
|
71123
71452
|
exports.ptzCapability = ptzCapability;
|
|
71124
71453
|
exports.publicEventCategories = require_event_category.publicEventCategories;
|
|
@@ -71128,6 +71457,7 @@ exports.readClusterStepModels = readClusterStepModels;
|
|
|
71128
71457
|
exports.readClusterStepSettings = readClusterStepSettings;
|
|
71129
71458
|
exports.readDetailCropConvention = readDetailCropConvention;
|
|
71130
71459
|
exports.readDeviceStateFrom = readDeviceStateFrom;
|
|
71460
|
+
exports.readHubMotionDetected = readHubMotionDetected;
|
|
71131
71461
|
exports.readMotionTriggeredBy = readMotionTriggeredBy;
|
|
71132
71462
|
exports.readNativeLeaseOverride = readNativeLeaseOverride;
|
|
71133
71463
|
exports.readNodePin = require_sleep.readNodePin;
|
|
@@ -71164,6 +71494,7 @@ exports.resolveHydratedFieldValue = require_sleep.resolveHydratedFieldValue;
|
|
|
71164
71494
|
exports.resolveLocationIcon = resolveLocationIcon;
|
|
71165
71495
|
exports.resolveLocationMode = resolveLocationMode;
|
|
71166
71496
|
exports.resolveMethodAuth = resolveMethodAuth;
|
|
71497
|
+
exports.resolveModeDelays = resolveModeDelays;
|
|
71167
71498
|
exports.resolveMutate = resolveMutate;
|
|
71168
71499
|
exports.resolvePoolMemoryPolicy = resolvePoolMemoryPolicy;
|
|
71169
71500
|
exports.resolveRecordingProfiles = resolveRecordingProfiles;
|