@camstack/addon-provider-hikvision 1.2.77 → 1.2.79

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 CHANGED
@@ -6,7 +6,7 @@ let node_crypto = require("node:crypto");
6
6
  let node_http = require("node:http");
7
7
  let node_https = require("node:https");
8
8
  let node_os = require("node:os");
9
- //#region ../types/dist/event-category-BZL-fdNj.mjs
9
+ //#region ../types/dist/event-category-zAv7pMUz.mjs
10
10
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
11
11
  EventCategory["SystemBoot"] = "system.boot";
12
12
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -378,6 +378,13 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
378
378
  */
379
379
  EventCategory["BatteryOnStatusChanged"] = "battery.onStatusChanged";
380
380
  /**
381
+ * Cap event fired by every device that registers the `network-link`
382
+ * capability. Mirrors the cap definition's `onStatusChanged`. Carries
383
+ * `{ deviceId, status: NetworkLinkStatus }` — a link switch or a signal
384
+ * reading that moved.
385
+ */
386
+ EventCategory["NetworkLinkOnStatusChanged"] = "network-link.onStatusChanged";
387
+ /**
381
388
  * Emitted by the battery cap provider WHEN `wakeForStream` enters the
382
389
  * "wake in progress" window — between the Baichuan wake-up issue and
383
390
  * the camera's first dialed-back RTP packet. The stream-broker
@@ -24160,6 +24167,88 @@ onStatusChanged: { data: object({
24160
24167
  volatileStateFields: ["lastUpdated"]
24161
24168
  };
24162
24169
  /**
24170
+ * Network-link snapshot. Same shape for every provider (a Reolink wifi
24171
+ * camera, a Home Assistant device with a signal-strength sensor, a Tapo
24172
+ * plug): one slice under `device.runtimeState['network-link']`, one badge,
24173
+ * one Home Assistant projection.
24174
+ */
24175
+ var NetworkLinkStatusSchema = object({
24176
+ /** The link the device is on. `'unknown'` = not read yet, not "no link". */
24177
+ type: _enum([
24178
+ "wifi",
24179
+ "ethernet",
24180
+ "cellular",
24181
+ "unknown"
24182
+ ]),
24183
+ /**
24184
+ * Link quality, 0..100 inclusive, normalised by the provider from whatever
24185
+ * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
24186
+ * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
24187
+ * one whose reading has not landed must not be drawn at 0 %. Consumers
24188
+ * SKIP a null rather than coerce it.
24189
+ */
24190
+ signalPercent: number().min(0).max(100).nullable(),
24191
+ /** Raw received signal strength in dBm, when the firmware reports one. */
24192
+ rssiDbm: number().optional(),
24193
+ /** Network name of a wireless link, when the firmware reports it. */
24194
+ ssid: string().optional(),
24195
+ /** Ms epoch of the last observation. Lets consumers reason about freshness. */
24196
+ lastUpdated: number()
24197
+ });
24198
+ var networkLinkCapability = {
24199
+ name: "network-link",
24200
+ scope: "device",
24201
+ deviceNative: true,
24202
+ mode: "singleton",
24203
+ deviceTypes: [
24204
+ DeviceType.Camera,
24205
+ DeviceType.Sensor,
24206
+ DeviceType.Button,
24207
+ DeviceType.Switch,
24208
+ DeviceType.Light,
24209
+ DeviceType.Lock,
24210
+ DeviceType.Siren
24211
+ ],
24212
+ methods: {},
24213
+ events: {
24214
+ /**
24215
+ * Emitted whenever the cached status changes (a link switch, a signal
24216
+ * reading that moved). Mirrored on the parent chain by the
24217
+ * DeviceEventPropagator like `battery.onStatusChanged`.
24218
+ */
24219
+ onStatusChanged: { data: object({
24220
+ deviceId: number(),
24221
+ status: NetworkLinkStatusSchema
24222
+ }) } },
24223
+ status: {
24224
+ schema: NetworkLinkStatusSchema,
24225
+ kind: "push",
24226
+ empty: {
24227
+ type: "unknown",
24228
+ signalPercent: null,
24229
+ lastUpdated: 0
24230
+ }
24231
+ },
24232
+ /**
24233
+ * Runtime-state slice — every provider stores the same shape under
24234
+ * `device.runtimeState['network-link']`, read once by the badge and the
24235
+ * Home Assistant projector regardless of the driver.
24236
+ */
24237
+ runtimeState: NetworkLinkStatusSchema,
24238
+ /**
24239
+ * Runtime-state durability: **restored** — a link reading is slow to
24240
+ * change and a sleeping battery camera may not report for hours; the
24241
+ * restored slice is what the badge shows until the next read.
24242
+ *
24243
+ * See `RuntimeStateDurability`. Enforced by
24244
+ * `scripts/check-runtime-state-durability.ts`.
24245
+ */
24246
+ durability: "restored",
24247
+ /** Clock fields: written, but excluded from the compare that decides
24248
+ * whether persisting is worth a SQLite commit. */
24249
+ volatileStateFields: ["lastUpdated"]
24250
+ };
24251
+ /**
24163
24252
  * Generic boolean sensor — last-resort fallback when no domain-
24164
24253
  * specific binary cap fits (Home Assistant `binary_sensor` without a
24165
24254
  * known `device_class`, or a domain we haven't typed yet). Pure
@@ -29378,6 +29467,287 @@ var ptzAutotrackCapability = {
29378
29467
  durability: "session"
29379
29468
  };
29380
29469
  /**
29470
+ * `navigation` — a device-scoped capability that natively expresses the FULL
29471
+ * navigation / action surface of a robot that DRIVES ITSELF and carries an
29472
+ * on-board camera (the Dreame robot-vacuum camera is the first provider).
29473
+ *
29474
+ * Why a NEW cap rather than overloading `ptz`:
29475
+ * - `ptz` moves a *gimbal* on a fixed camera (pan/tilt/zoom of the lens). A
29476
+ * robot vacuum has no gimbal — the whole chassis drives, turns and spins.
29477
+ * The two are different physical models: PTZ is absolute-position + presets,
29478
+ * navigation is momentary drive nudges + discrete robot ACTIONS
29479
+ * (dock / spot-clean / follow-pet / go-to-point / …).
29480
+ * - This cap is the SOURCE OF TRUTH. Two consumers adapt from it rather than
29481
+ * the reverse:
29482
+ * 1. a native CamStack navigation panel (data-driven from `listActions`
29483
+ * / `getOptions`), and
29484
+ * 2. the PTZ surface — a thin adapter mimics `ptz` from `navigation` so a
29485
+ * robot camera shows up in the existing PTZ control path without every
29486
+ * PTZ provider learning about robots. The mapping lives in the adapter,
29487
+ * not here (see the addon design note):
29488
+ * ptz.continuousMove({pan,tilt}) → navigation.move({pan,tilt})
29489
+ * ptz.stop() → navigation.stop()
29490
+ * ptz.goHome() → navigation.runAction('goHome')
29491
+ * ptz.getPresets() → navigation.listActions() (id→preset)
29492
+ * ptz.goToPreset(id) → navigation.runAction(id)
29493
+ *
29494
+ * ## Continuous drive
29495
+ *
29496
+ * `move` is MOMENTARY. Fluid navigation comes from the UI (or the PTZ adapter)
29497
+ * sending `move({pan,tilt})` REPEATEDLY at ~1 Hz while a direction is held, and
29498
+ * one `stop()` on release — exactly like the robot app's remote-drive joystick.
29499
+ * The provider forwards EACH `move` to one drive write; it must NOT debounce or
29500
+ * coalesce them. The UI owns the cadence.
29501
+ *
29502
+ * ## The action dictionary
29503
+ *
29504
+ * The discrete controls (dock / locate / spot-clean / follow / flash / sounds)
29505
+ * are a DATA-DRIVEN dictionary the cap exposes via `listActions()`. Each entry
29506
+ * carries `{ id, kind, label, icon }` (plus `soundId` for sound entries) so the
29507
+ * native panel AND the PTZ mimic render buttons WITHOUT hardcoding a
29508
+ * vendor-specific list. `kind: 'action'` entries are triggered with
29509
+ * `runAction({ actionId })`; `kind: 'sound'` entries with `playSound({ soundId })`
29510
+ * (the entry carries the `soundId` to pass). The general primitives — `move`,
29511
+ * `stop`, `goToPoint` — stay as first-class methods, not dictionary entries.
29512
+ *
29513
+ * NOTE (provider wiring): the first provider (Dreame) drives this with the RAW
29514
+ * `callAction(siid,aiid,in)` / `setProperty({siid,piid,value})` MIoT primitives
29515
+ * that the currently-published `@apocaliss92/nodedreame` already exposes on
29516
+ * every device handle. A future nodedreame publish adds a typed
29517
+ * `DreameCameraController` (drive / playPetSound / spotClean / findPet / …); the
29518
+ * provider can then swap the raw calls for the typed methods with no change to
29519
+ * THIS contract.
29520
+ */
29521
+ /**
29522
+ * A momentary drive nudge. The robot MOVES (no gimbal) for as long as the caller
29523
+ * keeps sending nudges (~1 Hz); an explicit `stop` (or letting the nudges lapse)
29524
+ * halts it.
29525
+ *
29526
+ * - `pan` — turn: negative = left, positive = right, 0 = straight.
29527
+ * - `tilt` — throttle: positive = forward, negative = spin / turn-around.
29528
+ * - `speed` — optional intensity hint [0, 1]; the provider may scale the drive
29529
+ * vector by it (drivers without proportional drive ignore it).
29530
+ *
29531
+ * `pan` / `tilt` are normalized [-1, 1]. Both optional so a caller can nudge one
29532
+ * axis alone; an all-undefined nudge is a no-op.
29533
+ */
29534
+ var NavigationMoveCommandSchema = object({
29535
+ pan: number().min(-1).max(1).optional(),
29536
+ tilt: number().min(-1).max(1).optional(),
29537
+ speed: number().min(0).max(1).optional()
29538
+ });
29539
+ /**
29540
+ * The enumerated discrete actions a navigation-capable robot can perform via
29541
+ * `runAction`. This is the CLOSED vocabulary; a given device advertises the
29542
+ * subset it supports through `listActions`. Sounds are NOT here — they go through
29543
+ * `playSound` (see the `sound` dictionary entries).
29544
+ */
29545
+ var NavigationActionIdSchema = _enum([
29546
+ "goHome",
29547
+ "locate",
29548
+ "spotClean",
29549
+ "findPet",
29550
+ "personFollow",
29551
+ "stop",
29552
+ "startClean",
29553
+ "pauseClean",
29554
+ "dockWash",
29555
+ "autoEmpty",
29556
+ "flashOn",
29557
+ "flashOff"
29558
+ ]);
29559
+ /** Whether a dictionary entry is a `runAction` action or a `playSound` sound. */
29560
+ var NavigationEntryKindSchema = _enum(["action", "sound"]);
29561
+ /**
29562
+ * One entry in the navigation action dictionary — the DATA-DRIVEN unit both the
29563
+ * native panel and the PTZ mimic render as a button.
29564
+ *
29565
+ * - `id` — stable id. For `kind:'action'` it is a {@link NavigationActionId}
29566
+ * (pass to `runAction`); for `kind:'sound'` it is a namespaced id
29567
+ * (`sound:meow`) whose `soundId` is passed to `playSound`.
29568
+ * - `icon` — icon HINT (lucide-style name; the UI maps it to its own set).
29569
+ * - `label` — operator-facing English label.
29570
+ * - `soundId` — wire sound id, present only on `kind:'sound'` entries.
29571
+ * - `enabled` — per-device FEATURE FLAG. `listActions` reports it so the UI /
29572
+ * PTZ render ONLY enabled entries. Data-driven: the provider
29573
+ * flips it from config, never by editing code.
29574
+ */
29575
+ var NavigationActionEntrySchema = object({
29576
+ id: string(),
29577
+ kind: NavigationEntryKindSchema,
29578
+ label: string(),
29579
+ icon: string(),
29580
+ /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
29581
+ soundId: number().int().optional(),
29582
+ /** Per-device feature flag — render this entry only when true. */
29583
+ enabled: boolean()
29584
+ });
29585
+ /** Coordinates for `goToPoint` — a point on the robot's live map. */
29586
+ var NavigationPointSchema = object({
29587
+ x: number(),
29588
+ y: number()
29589
+ });
29590
+ /**
29591
+ * Per-device FEATURE-FLAG report for the general (non-dictionary) primitives.
29592
+ * The cap reports which are enabled so the UI / PTZ render only the controls
29593
+ * that are turned on for THIS device. Data-driven: the provider derives these
29594
+ * from config + probe, never hardcoded in the UI. The per-DICTIONARY-entry flags
29595
+ * live on {@link NavigationActionEntrySchema.enabled}; these gate the primitives
29596
+ * that are not dictionary entries.
29597
+ *
29598
+ * - `move` / `stop` — the momentary drive joystick.
29599
+ * - `goToPoint` — send-to-map-coordinate. Ships OFF on Dreame until the
29600
+ * map-coordinate plumbing is wired.
29601
+ * - `runAction` — the discrete action buttons (dictionary `kind:'action'`).
29602
+ * - `playSound` — the sound buttons (dictionary `kind:'sound'`).
29603
+ * - `light` — the on/off fill-light toggle (works anytime).
29604
+ * - `lightMode` — the auto/manual selector + manual level slider (a
29605
+ * camera-service control; needs an active stream).
29606
+ */
29607
+ var NavigationFeaturesSchema = object({
29608
+ move: boolean(),
29609
+ stop: boolean(),
29610
+ goToPoint: boolean(),
29611
+ runAction: boolean(),
29612
+ playSound: boolean(),
29613
+ light: boolean(),
29614
+ lightMode: boolean()
29615
+ });
29616
+ /** Light mode: `auto` lets the camera choose brightness; `manual` uses `level`. */
29617
+ var NavigationLightModeSchema = _enum(["auto", "manual"]);
29618
+ /**
29619
+ * Live navigation state so the UI can reflect what the robot is doing:
29620
+ * - `mode` — coarse activity (idle / cleaning / following / …).
29621
+ * - `following` — person/pet follow is currently armed.
29622
+ * - `flash` — the on-camera fill light is on.
29623
+ * - `lightMode` — auto vs manual fill-light mode.
29624
+ * - `lightLevel` — manual fill-light level (40..100); meaningful when
29625
+ * `lightMode === 'manual'`.
29626
+ */
29627
+ var NavigationStatusSchema = object({
29628
+ mode: _enum([
29629
+ "idle",
29630
+ "cleaning",
29631
+ "spot",
29632
+ "following",
29633
+ "goto",
29634
+ "returning",
29635
+ "paused",
29636
+ "unknown"
29637
+ ]),
29638
+ following: boolean(),
29639
+ flash: boolean(),
29640
+ lightMode: NavigationLightModeSchema,
29641
+ lightLevel: number().min(40).max(100),
29642
+ /** Ms epoch when the slice was last updated. */
29643
+ lastChangedAt: number()
29644
+ });
29645
+ /**
29646
+ * Runtime-state slice owned by this cap (kernel-managed: validated, mirrored,
29647
+ * observable). Adds `lastFetchedAt` on top of the status shape per the
29648
+ * convention.
29649
+ */
29650
+ var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: number() });
29651
+ var navigationCapability = {
29652
+ name: "navigation",
29653
+ scope: "device",
29654
+ deviceNative: true,
29655
+ mode: "singleton",
29656
+ deviceTypes: [DeviceType.Camera],
29657
+ deviceConfig: { ui: {
29658
+ kind: "widget",
29659
+ widgetId: "host/navigation-panel",
29660
+ tab: "navigation",
29661
+ topTab: true,
29662
+ label: "Navigation",
29663
+ order: 0
29664
+ } },
29665
+ methods: {
29666
+ /**
29667
+ * Momentary drive nudge (the robot moves). `protected` — mirrors
29668
+ * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
29669
+ * path) works for any authenticated user, not admin-only. The UI sends
29670
+ * these at ~1 Hz while a control is held; the provider forwards each one to
29671
+ * a single drive write WITHOUT debouncing.
29672
+ */
29673
+ move: method(NavigationMoveCommandSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }),
29674
+ /** Halt all motion immediately (zero drive vector). */
29675
+ stop: method(object({ deviceId: number() }), _void(), { kind: "mutation" }),
29676
+ /** Send the robot to a point on its live map. */
29677
+ goToPoint: method(NavigationPointSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }),
29678
+ /**
29679
+ * Enumerate the discrete controls THIS device supports (data-driven UI +
29680
+ * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
29681
+ */
29682
+ listActions: method(object({ deviceId: number() }), array(NavigationActionEntrySchema)),
29683
+ /**
29684
+ * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
29685
+ * unsupported action ids are rejected by the provider.
29686
+ */
29687
+ runAction: method(object({
29688
+ deviceId: number(),
29689
+ actionId: NavigationActionIdSchema
29690
+ }), _void(), { kind: "mutation" }),
29691
+ /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
29692
+ playSound: method(object({
29693
+ deviceId: number(),
29694
+ soundId: number().int()
29695
+ }), _void(), { kind: "mutation" }),
29696
+ /**
29697
+ * Turn the on-camera fill light on / off (the `OpenFullLight` control —
29698
+ * works anytime, no active stream required).
29699
+ */
29700
+ setLightOn: method(object({
29701
+ deviceId: number(),
29702
+ on: boolean()
29703
+ }), _void(), { kind: "mutation" }),
29704
+ /**
29705
+ * Set the fill-light mode (auto vs manual). `manual` optionally carries the
29706
+ * initial `level`. The auto/manual + level control is a CAMERA-service
29707
+ * action that generally needs an active camera stream/monitor session — the
29708
+ * UI shows the manual level slider ONLY when `mode === 'manual'`.
29709
+ */
29710
+ setLightMode: method(object({
29711
+ deviceId: number(),
29712
+ mode: NavigationLightModeSchema,
29713
+ level: number().min(40).max(100).optional()
29714
+ }), _void(), { kind: "mutation" }),
29715
+ /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
29716
+ setLightLevel: method(object({
29717
+ deviceId: number(),
29718
+ level: number().min(40).max(100)
29719
+ }), _void(), { kind: "mutation" }),
29720
+ /**
29721
+ * Per-device FEATURE-FLAG report for the general primitives — drives which
29722
+ * controls the UI shows (the per-entry flags for the dictionary come back on
29723
+ * `listActions`).
29724
+ */
29725
+ getFeatures: method(object({ deviceId: number() }), NavigationFeaturesSchema)
29726
+ },
29727
+ events: { onStatusChanged: { data: object({
29728
+ deviceId: number(),
29729
+ status: NavigationStatusSchema
29730
+ }) } },
29731
+ status: {
29732
+ schema: NavigationStatusSchema,
29733
+ kind: "push"
29734
+ },
29735
+ /**
29736
+ * Runtime-state slice mirrored by the kernel. The navigation panel watches it
29737
+ * for live mode / follow / flash changes.
29738
+ */
29739
+ runtimeState: NavigationRuntimeStateSchema,
29740
+ /**
29741
+ * Runtime-state durability: **session** — like `vacuum-control`, a restored
29742
+ * `mode: cleaning` / `following: true` is a robot that is not actually doing
29743
+ * that. The live handle re-publishes on connect.
29744
+ *
29745
+ * See `RuntimeStateDurability`. Enforced by
29746
+ * `scripts/check-runtime-state-durability.ts`.
29747
+ */
29748
+ durability: "session"
29749
+ };
29750
+ /**
29381
29751
  * reboot — device-scoped capability for "soft" device reboots (firmware
29382
29752
  * reboot via vendor protocol; cameras, NVRs, doorbells). Surfaces a
29383
29753
  * single mutation so the UI can offer a confirm-and-reboot button for
@@ -32890,6 +33260,8 @@ var DEVICE_LOCAL_STATE_CAPS = {
32890
33260
  motionTrigger: motionTriggerCapability,
32891
33261
  motionZones: motionZonesCapability,
32892
33262
  nativeObjectDetection: nativeObjectDetectionCapability,
33263
+ navigation: navigationCapability,
33264
+ networkLink: networkLinkCapability,
32893
33265
  notifier: notifierCapability,
32894
33266
  numericSensor: numericSensorCapability,
32895
33267
  petFeeder: petFeederCapability,
@@ -36961,6 +37333,66 @@ Object.freeze({
36961
37333
  addonId: null,
36962
37334
  access: "create"
36963
37335
  },
37336
+ "navigation.getFeatures": {
37337
+ capName: "navigation",
37338
+ capScope: "device",
37339
+ addonId: null,
37340
+ access: "view"
37341
+ },
37342
+ "navigation.goToPoint": {
37343
+ capName: "navigation",
37344
+ capScope: "device",
37345
+ addonId: null,
37346
+ access: "create"
37347
+ },
37348
+ "navigation.listActions": {
37349
+ capName: "navigation",
37350
+ capScope: "device",
37351
+ addonId: null,
37352
+ access: "view"
37353
+ },
37354
+ "navigation.move": {
37355
+ capName: "navigation",
37356
+ capScope: "device",
37357
+ addonId: null,
37358
+ access: "create"
37359
+ },
37360
+ "navigation.playSound": {
37361
+ capName: "navigation",
37362
+ capScope: "device",
37363
+ addonId: null,
37364
+ access: "create"
37365
+ },
37366
+ "navigation.runAction": {
37367
+ capName: "navigation",
37368
+ capScope: "device",
37369
+ addonId: null,
37370
+ access: "create"
37371
+ },
37372
+ "navigation.setLightLevel": {
37373
+ capName: "navigation",
37374
+ capScope: "device",
37375
+ addonId: null,
37376
+ access: "create"
37377
+ },
37378
+ "navigation.setLightMode": {
37379
+ capName: "navigation",
37380
+ capScope: "device",
37381
+ addonId: null,
37382
+ access: "create"
37383
+ },
37384
+ "navigation.setLightOn": {
37385
+ capName: "navigation",
37386
+ capScope: "device",
37387
+ addonId: null,
37388
+ access: "create"
37389
+ },
37390
+ "navigation.stop": {
37391
+ capName: "navigation",
37392
+ capScope: "device",
37393
+ addonId: null,
37394
+ access: "create"
37395
+ },
36964
37396
  "networkAccess.getEndpoint": {
36965
37397
  capName: "network-access",
36966
37398
  capScope: "system",
@@ -41056,6 +41488,56 @@ Object.freeze({
41056
41488
  form: "single",
41057
41489
  optional: false
41058
41490
  }],
41491
+ "navigation.getFeatures": [{
41492
+ name: "deviceId",
41493
+ form: "single",
41494
+ optional: false
41495
+ }],
41496
+ "navigation.goToPoint": [{
41497
+ name: "deviceId",
41498
+ form: "single",
41499
+ optional: false
41500
+ }],
41501
+ "navigation.listActions": [{
41502
+ name: "deviceId",
41503
+ form: "single",
41504
+ optional: false
41505
+ }],
41506
+ "navigation.move": [{
41507
+ name: "deviceId",
41508
+ form: "single",
41509
+ optional: false
41510
+ }],
41511
+ "navigation.playSound": [{
41512
+ name: "deviceId",
41513
+ form: "single",
41514
+ optional: false
41515
+ }],
41516
+ "navigation.runAction": [{
41517
+ name: "deviceId",
41518
+ form: "single",
41519
+ optional: false
41520
+ }],
41521
+ "navigation.setLightLevel": [{
41522
+ name: "deviceId",
41523
+ form: "single",
41524
+ optional: false
41525
+ }],
41526
+ "navigation.setLightMode": [{
41527
+ name: "deviceId",
41528
+ form: "single",
41529
+ optional: false
41530
+ }],
41531
+ "navigation.setLightOn": [{
41532
+ name: "deviceId",
41533
+ form: "single",
41534
+ optional: false
41535
+ }],
41536
+ "navigation.stop": [{
41537
+ name: "deviceId",
41538
+ form: "single",
41539
+ optional: false
41540
+ }],
41059
41541
  "networkQuality.getDeviceStats": [{
41060
41542
  name: "deviceId",
41061
41543
  form: "single",
package/dist/addon.mjs CHANGED
@@ -7,7 +7,7 @@ import { networkInterfaces } from "node:os";
7
7
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
8
8
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
9
9
  //#endregion
10
- //#region ../types/dist/event-category-BZL-fdNj.mjs
10
+ //#region ../types/dist/event-category-zAv7pMUz.mjs
11
11
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
12
12
  EventCategory["SystemBoot"] = "system.boot";
13
13
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -379,6 +379,13 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
379
379
  */
380
380
  EventCategory["BatteryOnStatusChanged"] = "battery.onStatusChanged";
381
381
  /**
382
+ * Cap event fired by every device that registers the `network-link`
383
+ * capability. Mirrors the cap definition's `onStatusChanged`. Carries
384
+ * `{ deviceId, status: NetworkLinkStatus }` — a link switch or a signal
385
+ * reading that moved.
386
+ */
387
+ EventCategory["NetworkLinkOnStatusChanged"] = "network-link.onStatusChanged";
388
+ /**
382
389
  * Emitted by the battery cap provider WHEN `wakeForStream` enters the
383
390
  * "wake in progress" window — between the Baichuan wake-up issue and
384
391
  * the camera's first dialed-back RTP packet. The stream-broker
@@ -24161,6 +24168,88 @@ onStatusChanged: { data: object({
24161
24168
  volatileStateFields: ["lastUpdated"]
24162
24169
  };
24163
24170
  /**
24171
+ * Network-link snapshot. Same shape for every provider (a Reolink wifi
24172
+ * camera, a Home Assistant device with a signal-strength sensor, a Tapo
24173
+ * plug): one slice under `device.runtimeState['network-link']`, one badge,
24174
+ * one Home Assistant projection.
24175
+ */
24176
+ var NetworkLinkStatusSchema = object({
24177
+ /** The link the device is on. `'unknown'` = not read yet, not "no link". */
24178
+ type: _enum([
24179
+ "wifi",
24180
+ "ethernet",
24181
+ "cellular",
24182
+ "unknown"
24183
+ ]),
24184
+ /**
24185
+ * Link quality, 0..100 inclusive, normalised by the provider from whatever
24186
+ * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
24187
+ * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
24188
+ * one whose reading has not landed must not be drawn at 0 %. Consumers
24189
+ * SKIP a null rather than coerce it.
24190
+ */
24191
+ signalPercent: number().min(0).max(100).nullable(),
24192
+ /** Raw received signal strength in dBm, when the firmware reports one. */
24193
+ rssiDbm: number().optional(),
24194
+ /** Network name of a wireless link, when the firmware reports it. */
24195
+ ssid: string().optional(),
24196
+ /** Ms epoch of the last observation. Lets consumers reason about freshness. */
24197
+ lastUpdated: number()
24198
+ });
24199
+ var networkLinkCapability = {
24200
+ name: "network-link",
24201
+ scope: "device",
24202
+ deviceNative: true,
24203
+ mode: "singleton",
24204
+ deviceTypes: [
24205
+ DeviceType.Camera,
24206
+ DeviceType.Sensor,
24207
+ DeviceType.Button,
24208
+ DeviceType.Switch,
24209
+ DeviceType.Light,
24210
+ DeviceType.Lock,
24211
+ DeviceType.Siren
24212
+ ],
24213
+ methods: {},
24214
+ events: {
24215
+ /**
24216
+ * Emitted whenever the cached status changes (a link switch, a signal
24217
+ * reading that moved). Mirrored on the parent chain by the
24218
+ * DeviceEventPropagator like `battery.onStatusChanged`.
24219
+ */
24220
+ onStatusChanged: { data: object({
24221
+ deviceId: number(),
24222
+ status: NetworkLinkStatusSchema
24223
+ }) } },
24224
+ status: {
24225
+ schema: NetworkLinkStatusSchema,
24226
+ kind: "push",
24227
+ empty: {
24228
+ type: "unknown",
24229
+ signalPercent: null,
24230
+ lastUpdated: 0
24231
+ }
24232
+ },
24233
+ /**
24234
+ * Runtime-state slice — every provider stores the same shape under
24235
+ * `device.runtimeState['network-link']`, read once by the badge and the
24236
+ * Home Assistant projector regardless of the driver.
24237
+ */
24238
+ runtimeState: NetworkLinkStatusSchema,
24239
+ /**
24240
+ * Runtime-state durability: **restored** — a link reading is slow to
24241
+ * change and a sleeping battery camera may not report for hours; the
24242
+ * restored slice is what the badge shows until the next read.
24243
+ *
24244
+ * See `RuntimeStateDurability`. Enforced by
24245
+ * `scripts/check-runtime-state-durability.ts`.
24246
+ */
24247
+ durability: "restored",
24248
+ /** Clock fields: written, but excluded from the compare that decides
24249
+ * whether persisting is worth a SQLite commit. */
24250
+ volatileStateFields: ["lastUpdated"]
24251
+ };
24252
+ /**
24164
24253
  * Generic boolean sensor — last-resort fallback when no domain-
24165
24254
  * specific binary cap fits (Home Assistant `binary_sensor` without a
24166
24255
  * known `device_class`, or a domain we haven't typed yet). Pure
@@ -29379,6 +29468,287 @@ var ptzAutotrackCapability = {
29379
29468
  durability: "session"
29380
29469
  };
29381
29470
  /**
29471
+ * `navigation` — a device-scoped capability that natively expresses the FULL
29472
+ * navigation / action surface of a robot that DRIVES ITSELF and carries an
29473
+ * on-board camera (the Dreame robot-vacuum camera is the first provider).
29474
+ *
29475
+ * Why a NEW cap rather than overloading `ptz`:
29476
+ * - `ptz` moves a *gimbal* on a fixed camera (pan/tilt/zoom of the lens). A
29477
+ * robot vacuum has no gimbal — the whole chassis drives, turns and spins.
29478
+ * The two are different physical models: PTZ is absolute-position + presets,
29479
+ * navigation is momentary drive nudges + discrete robot ACTIONS
29480
+ * (dock / spot-clean / follow-pet / go-to-point / …).
29481
+ * - This cap is the SOURCE OF TRUTH. Two consumers adapt from it rather than
29482
+ * the reverse:
29483
+ * 1. a native CamStack navigation panel (data-driven from `listActions`
29484
+ * / `getOptions`), and
29485
+ * 2. the PTZ surface — a thin adapter mimics `ptz` from `navigation` so a
29486
+ * robot camera shows up in the existing PTZ control path without every
29487
+ * PTZ provider learning about robots. The mapping lives in the adapter,
29488
+ * not here (see the addon design note):
29489
+ * ptz.continuousMove({pan,tilt}) → navigation.move({pan,tilt})
29490
+ * ptz.stop() → navigation.stop()
29491
+ * ptz.goHome() → navigation.runAction('goHome')
29492
+ * ptz.getPresets() → navigation.listActions() (id→preset)
29493
+ * ptz.goToPreset(id) → navigation.runAction(id)
29494
+ *
29495
+ * ## Continuous drive
29496
+ *
29497
+ * `move` is MOMENTARY. Fluid navigation comes from the UI (or the PTZ adapter)
29498
+ * sending `move({pan,tilt})` REPEATEDLY at ~1 Hz while a direction is held, and
29499
+ * one `stop()` on release — exactly like the robot app's remote-drive joystick.
29500
+ * The provider forwards EACH `move` to one drive write; it must NOT debounce or
29501
+ * coalesce them. The UI owns the cadence.
29502
+ *
29503
+ * ## The action dictionary
29504
+ *
29505
+ * The discrete controls (dock / locate / spot-clean / follow / flash / sounds)
29506
+ * are a DATA-DRIVEN dictionary the cap exposes via `listActions()`. Each entry
29507
+ * carries `{ id, kind, label, icon }` (plus `soundId` for sound entries) so the
29508
+ * native panel AND the PTZ mimic render buttons WITHOUT hardcoding a
29509
+ * vendor-specific list. `kind: 'action'` entries are triggered with
29510
+ * `runAction({ actionId })`; `kind: 'sound'` entries with `playSound({ soundId })`
29511
+ * (the entry carries the `soundId` to pass). The general primitives — `move`,
29512
+ * `stop`, `goToPoint` — stay as first-class methods, not dictionary entries.
29513
+ *
29514
+ * NOTE (provider wiring): the first provider (Dreame) drives this with the RAW
29515
+ * `callAction(siid,aiid,in)` / `setProperty({siid,piid,value})` MIoT primitives
29516
+ * that the currently-published `@apocaliss92/nodedreame` already exposes on
29517
+ * every device handle. A future nodedreame publish adds a typed
29518
+ * `DreameCameraController` (drive / playPetSound / spotClean / findPet / …); the
29519
+ * provider can then swap the raw calls for the typed methods with no change to
29520
+ * THIS contract.
29521
+ */
29522
+ /**
29523
+ * A momentary drive nudge. The robot MOVES (no gimbal) for as long as the caller
29524
+ * keeps sending nudges (~1 Hz); an explicit `stop` (or letting the nudges lapse)
29525
+ * halts it.
29526
+ *
29527
+ * - `pan` — turn: negative = left, positive = right, 0 = straight.
29528
+ * - `tilt` — throttle: positive = forward, negative = spin / turn-around.
29529
+ * - `speed` — optional intensity hint [0, 1]; the provider may scale the drive
29530
+ * vector by it (drivers without proportional drive ignore it).
29531
+ *
29532
+ * `pan` / `tilt` are normalized [-1, 1]. Both optional so a caller can nudge one
29533
+ * axis alone; an all-undefined nudge is a no-op.
29534
+ */
29535
+ var NavigationMoveCommandSchema = object({
29536
+ pan: number().min(-1).max(1).optional(),
29537
+ tilt: number().min(-1).max(1).optional(),
29538
+ speed: number().min(0).max(1).optional()
29539
+ });
29540
+ /**
29541
+ * The enumerated discrete actions a navigation-capable robot can perform via
29542
+ * `runAction`. This is the CLOSED vocabulary; a given device advertises the
29543
+ * subset it supports through `listActions`. Sounds are NOT here — they go through
29544
+ * `playSound` (see the `sound` dictionary entries).
29545
+ */
29546
+ var NavigationActionIdSchema = _enum([
29547
+ "goHome",
29548
+ "locate",
29549
+ "spotClean",
29550
+ "findPet",
29551
+ "personFollow",
29552
+ "stop",
29553
+ "startClean",
29554
+ "pauseClean",
29555
+ "dockWash",
29556
+ "autoEmpty",
29557
+ "flashOn",
29558
+ "flashOff"
29559
+ ]);
29560
+ /** Whether a dictionary entry is a `runAction` action or a `playSound` sound. */
29561
+ var NavigationEntryKindSchema = _enum(["action", "sound"]);
29562
+ /**
29563
+ * One entry in the navigation action dictionary — the DATA-DRIVEN unit both the
29564
+ * native panel and the PTZ mimic render as a button.
29565
+ *
29566
+ * - `id` — stable id. For `kind:'action'` it is a {@link NavigationActionId}
29567
+ * (pass to `runAction`); for `kind:'sound'` it is a namespaced id
29568
+ * (`sound:meow`) whose `soundId` is passed to `playSound`.
29569
+ * - `icon` — icon HINT (lucide-style name; the UI maps it to its own set).
29570
+ * - `label` — operator-facing English label.
29571
+ * - `soundId` — wire sound id, present only on `kind:'sound'` entries.
29572
+ * - `enabled` — per-device FEATURE FLAG. `listActions` reports it so the UI /
29573
+ * PTZ render ONLY enabled entries. Data-driven: the provider
29574
+ * flips it from config, never by editing code.
29575
+ */
29576
+ var NavigationActionEntrySchema = object({
29577
+ id: string(),
29578
+ kind: NavigationEntryKindSchema,
29579
+ label: string(),
29580
+ icon: string(),
29581
+ /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
29582
+ soundId: number().int().optional(),
29583
+ /** Per-device feature flag — render this entry only when true. */
29584
+ enabled: boolean()
29585
+ });
29586
+ /** Coordinates for `goToPoint` — a point on the robot's live map. */
29587
+ var NavigationPointSchema = object({
29588
+ x: number(),
29589
+ y: number()
29590
+ });
29591
+ /**
29592
+ * Per-device FEATURE-FLAG report for the general (non-dictionary) primitives.
29593
+ * The cap reports which are enabled so the UI / PTZ render only the controls
29594
+ * that are turned on for THIS device. Data-driven: the provider derives these
29595
+ * from config + probe, never hardcoded in the UI. The per-DICTIONARY-entry flags
29596
+ * live on {@link NavigationActionEntrySchema.enabled}; these gate the primitives
29597
+ * that are not dictionary entries.
29598
+ *
29599
+ * - `move` / `stop` — the momentary drive joystick.
29600
+ * - `goToPoint` — send-to-map-coordinate. Ships OFF on Dreame until the
29601
+ * map-coordinate plumbing is wired.
29602
+ * - `runAction` — the discrete action buttons (dictionary `kind:'action'`).
29603
+ * - `playSound` — the sound buttons (dictionary `kind:'sound'`).
29604
+ * - `light` — the on/off fill-light toggle (works anytime).
29605
+ * - `lightMode` — the auto/manual selector + manual level slider (a
29606
+ * camera-service control; needs an active stream).
29607
+ */
29608
+ var NavigationFeaturesSchema = object({
29609
+ move: boolean(),
29610
+ stop: boolean(),
29611
+ goToPoint: boolean(),
29612
+ runAction: boolean(),
29613
+ playSound: boolean(),
29614
+ light: boolean(),
29615
+ lightMode: boolean()
29616
+ });
29617
+ /** Light mode: `auto` lets the camera choose brightness; `manual` uses `level`. */
29618
+ var NavigationLightModeSchema = _enum(["auto", "manual"]);
29619
+ /**
29620
+ * Live navigation state so the UI can reflect what the robot is doing:
29621
+ * - `mode` — coarse activity (idle / cleaning / following / …).
29622
+ * - `following` — person/pet follow is currently armed.
29623
+ * - `flash` — the on-camera fill light is on.
29624
+ * - `lightMode` — auto vs manual fill-light mode.
29625
+ * - `lightLevel` — manual fill-light level (40..100); meaningful when
29626
+ * `lightMode === 'manual'`.
29627
+ */
29628
+ var NavigationStatusSchema = object({
29629
+ mode: _enum([
29630
+ "idle",
29631
+ "cleaning",
29632
+ "spot",
29633
+ "following",
29634
+ "goto",
29635
+ "returning",
29636
+ "paused",
29637
+ "unknown"
29638
+ ]),
29639
+ following: boolean(),
29640
+ flash: boolean(),
29641
+ lightMode: NavigationLightModeSchema,
29642
+ lightLevel: number().min(40).max(100),
29643
+ /** Ms epoch when the slice was last updated. */
29644
+ lastChangedAt: number()
29645
+ });
29646
+ /**
29647
+ * Runtime-state slice owned by this cap (kernel-managed: validated, mirrored,
29648
+ * observable). Adds `lastFetchedAt` on top of the status shape per the
29649
+ * convention.
29650
+ */
29651
+ var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: number() });
29652
+ var navigationCapability = {
29653
+ name: "navigation",
29654
+ scope: "device",
29655
+ deviceNative: true,
29656
+ mode: "singleton",
29657
+ deviceTypes: [DeviceType.Camera],
29658
+ deviceConfig: { ui: {
29659
+ kind: "widget",
29660
+ widgetId: "host/navigation-panel",
29661
+ tab: "navigation",
29662
+ topTab: true,
29663
+ label: "Navigation",
29664
+ order: 0
29665
+ } },
29666
+ methods: {
29667
+ /**
29668
+ * Momentary drive nudge (the robot moves). `protected` — mirrors
29669
+ * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
29670
+ * path) works for any authenticated user, not admin-only. The UI sends
29671
+ * these at ~1 Hz while a control is held; the provider forwards each one to
29672
+ * a single drive write WITHOUT debouncing.
29673
+ */
29674
+ move: method(NavigationMoveCommandSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }),
29675
+ /** Halt all motion immediately (zero drive vector). */
29676
+ stop: method(object({ deviceId: number() }), _void(), { kind: "mutation" }),
29677
+ /** Send the robot to a point on its live map. */
29678
+ goToPoint: method(NavigationPointSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }),
29679
+ /**
29680
+ * Enumerate the discrete controls THIS device supports (data-driven UI +
29681
+ * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
29682
+ */
29683
+ listActions: method(object({ deviceId: number() }), array(NavigationActionEntrySchema)),
29684
+ /**
29685
+ * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
29686
+ * unsupported action ids are rejected by the provider.
29687
+ */
29688
+ runAction: method(object({
29689
+ deviceId: number(),
29690
+ actionId: NavigationActionIdSchema
29691
+ }), _void(), { kind: "mutation" }),
29692
+ /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
29693
+ playSound: method(object({
29694
+ deviceId: number(),
29695
+ soundId: number().int()
29696
+ }), _void(), { kind: "mutation" }),
29697
+ /**
29698
+ * Turn the on-camera fill light on / off (the `OpenFullLight` control —
29699
+ * works anytime, no active stream required).
29700
+ */
29701
+ setLightOn: method(object({
29702
+ deviceId: number(),
29703
+ on: boolean()
29704
+ }), _void(), { kind: "mutation" }),
29705
+ /**
29706
+ * Set the fill-light mode (auto vs manual). `manual` optionally carries the
29707
+ * initial `level`. The auto/manual + level control is a CAMERA-service
29708
+ * action that generally needs an active camera stream/monitor session — the
29709
+ * UI shows the manual level slider ONLY when `mode === 'manual'`.
29710
+ */
29711
+ setLightMode: method(object({
29712
+ deviceId: number(),
29713
+ mode: NavigationLightModeSchema,
29714
+ level: number().min(40).max(100).optional()
29715
+ }), _void(), { kind: "mutation" }),
29716
+ /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
29717
+ setLightLevel: method(object({
29718
+ deviceId: number(),
29719
+ level: number().min(40).max(100)
29720
+ }), _void(), { kind: "mutation" }),
29721
+ /**
29722
+ * Per-device FEATURE-FLAG report for the general primitives — drives which
29723
+ * controls the UI shows (the per-entry flags for the dictionary come back on
29724
+ * `listActions`).
29725
+ */
29726
+ getFeatures: method(object({ deviceId: number() }), NavigationFeaturesSchema)
29727
+ },
29728
+ events: { onStatusChanged: { data: object({
29729
+ deviceId: number(),
29730
+ status: NavigationStatusSchema
29731
+ }) } },
29732
+ status: {
29733
+ schema: NavigationStatusSchema,
29734
+ kind: "push"
29735
+ },
29736
+ /**
29737
+ * Runtime-state slice mirrored by the kernel. The navigation panel watches it
29738
+ * for live mode / follow / flash changes.
29739
+ */
29740
+ runtimeState: NavigationRuntimeStateSchema,
29741
+ /**
29742
+ * Runtime-state durability: **session** — like `vacuum-control`, a restored
29743
+ * `mode: cleaning` / `following: true` is a robot that is not actually doing
29744
+ * that. The live handle re-publishes on connect.
29745
+ *
29746
+ * See `RuntimeStateDurability`. Enforced by
29747
+ * `scripts/check-runtime-state-durability.ts`.
29748
+ */
29749
+ durability: "session"
29750
+ };
29751
+ /**
29382
29752
  * reboot — device-scoped capability for "soft" device reboots (firmware
29383
29753
  * reboot via vendor protocol; cameras, NVRs, doorbells). Surfaces a
29384
29754
  * single mutation so the UI can offer a confirm-and-reboot button for
@@ -32891,6 +33261,8 @@ var DEVICE_LOCAL_STATE_CAPS = {
32891
33261
  motionTrigger: motionTriggerCapability,
32892
33262
  motionZones: motionZonesCapability,
32893
33263
  nativeObjectDetection: nativeObjectDetectionCapability,
33264
+ navigation: navigationCapability,
33265
+ networkLink: networkLinkCapability,
32894
33266
  notifier: notifierCapability,
32895
33267
  numericSensor: numericSensorCapability,
32896
33268
  petFeeder: petFeederCapability,
@@ -36962,6 +37334,66 @@ Object.freeze({
36962
37334
  addonId: null,
36963
37335
  access: "create"
36964
37336
  },
37337
+ "navigation.getFeatures": {
37338
+ capName: "navigation",
37339
+ capScope: "device",
37340
+ addonId: null,
37341
+ access: "view"
37342
+ },
37343
+ "navigation.goToPoint": {
37344
+ capName: "navigation",
37345
+ capScope: "device",
37346
+ addonId: null,
37347
+ access: "create"
37348
+ },
37349
+ "navigation.listActions": {
37350
+ capName: "navigation",
37351
+ capScope: "device",
37352
+ addonId: null,
37353
+ access: "view"
37354
+ },
37355
+ "navigation.move": {
37356
+ capName: "navigation",
37357
+ capScope: "device",
37358
+ addonId: null,
37359
+ access: "create"
37360
+ },
37361
+ "navigation.playSound": {
37362
+ capName: "navigation",
37363
+ capScope: "device",
37364
+ addonId: null,
37365
+ access: "create"
37366
+ },
37367
+ "navigation.runAction": {
37368
+ capName: "navigation",
37369
+ capScope: "device",
37370
+ addonId: null,
37371
+ access: "create"
37372
+ },
37373
+ "navigation.setLightLevel": {
37374
+ capName: "navigation",
37375
+ capScope: "device",
37376
+ addonId: null,
37377
+ access: "create"
37378
+ },
37379
+ "navigation.setLightMode": {
37380
+ capName: "navigation",
37381
+ capScope: "device",
37382
+ addonId: null,
37383
+ access: "create"
37384
+ },
37385
+ "navigation.setLightOn": {
37386
+ capName: "navigation",
37387
+ capScope: "device",
37388
+ addonId: null,
37389
+ access: "create"
37390
+ },
37391
+ "navigation.stop": {
37392
+ capName: "navigation",
37393
+ capScope: "device",
37394
+ addonId: null,
37395
+ access: "create"
37396
+ },
36965
37397
  "networkAccess.getEndpoint": {
36966
37398
  capName: "network-access",
36967
37399
  capScope: "system",
@@ -41057,6 +41489,56 @@ Object.freeze({
41057
41489
  form: "single",
41058
41490
  optional: false
41059
41491
  }],
41492
+ "navigation.getFeatures": [{
41493
+ name: "deviceId",
41494
+ form: "single",
41495
+ optional: false
41496
+ }],
41497
+ "navigation.goToPoint": [{
41498
+ name: "deviceId",
41499
+ form: "single",
41500
+ optional: false
41501
+ }],
41502
+ "navigation.listActions": [{
41503
+ name: "deviceId",
41504
+ form: "single",
41505
+ optional: false
41506
+ }],
41507
+ "navigation.move": [{
41508
+ name: "deviceId",
41509
+ form: "single",
41510
+ optional: false
41511
+ }],
41512
+ "navigation.playSound": [{
41513
+ name: "deviceId",
41514
+ form: "single",
41515
+ optional: false
41516
+ }],
41517
+ "navigation.runAction": [{
41518
+ name: "deviceId",
41519
+ form: "single",
41520
+ optional: false
41521
+ }],
41522
+ "navigation.setLightLevel": [{
41523
+ name: "deviceId",
41524
+ form: "single",
41525
+ optional: false
41526
+ }],
41527
+ "navigation.setLightMode": [{
41528
+ name: "deviceId",
41529
+ form: "single",
41530
+ optional: false
41531
+ }],
41532
+ "navigation.setLightOn": [{
41533
+ name: "deviceId",
41534
+ form: "single",
41535
+ optional: false
41536
+ }],
41537
+ "navigation.stop": [{
41538
+ name: "deviceId",
41539
+ form: "single",
41540
+ optional: false
41541
+ }],
41060
41542
  "networkQuality.getDeviceStats": [{
41061
41543
  name: "deviceId",
41062
41544
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-hikvision",
3
- "version": "1.2.77",
3
+ "version": "1.2.79",
4
4
  "description": "Hikvision camera device provider addon for CamStack — ISAPI over HTTP(S) with digest auth (snapshot, alarm stream, RTSP discovery)",
5
5
  "keywords": [
6
6
  "camstack",