@camstack/addon-provider-wyze 0.2.73 → 0.2.75

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
@@ -31,7 +31,7 @@ let net = require("net");
31
31
  net = __toESM(net, 1);
32
32
  let node_child_process = require("node:child_process");
33
33
  let node_tls = require("node:tls");
34
- //#region ../types/dist/event-category-BZL-fdNj.mjs
34
+ //#region ../types/dist/event-category-zAv7pMUz.mjs
35
35
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
36
36
  EventCategory["SystemBoot"] = "system.boot";
37
37
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -403,6 +403,13 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
403
403
  */
404
404
  EventCategory["BatteryOnStatusChanged"] = "battery.onStatusChanged";
405
405
  /**
406
+ * Cap event fired by every device that registers the `network-link`
407
+ * capability. Mirrors the cap definition's `onStatusChanged`. Carries
408
+ * `{ deviceId, status: NetworkLinkStatus }` — a link switch or a signal
409
+ * reading that moved.
410
+ */
411
+ EventCategory["NetworkLinkOnStatusChanged"] = "network-link.onStatusChanged";
412
+ /**
406
413
  * Emitted by the battery cap provider WHEN `wakeForStream` enters the
407
414
  * "wake in progress" window — between the Baichuan wake-up issue and
408
415
  * the camera's first dialed-back RTP packet. The stream-broker
@@ -23902,6 +23909,88 @@ onStatusChanged: { data: object({
23902
23909
  volatileStateFields: ["lastUpdated"]
23903
23910
  };
23904
23911
  /**
23912
+ * Network-link snapshot. Same shape for every provider (a Reolink wifi
23913
+ * camera, a Home Assistant device with a signal-strength sensor, a Tapo
23914
+ * plug): one slice under `device.runtimeState['network-link']`, one badge,
23915
+ * one Home Assistant projection.
23916
+ */
23917
+ var NetworkLinkStatusSchema = object({
23918
+ /** The link the device is on. `'unknown'` = not read yet, not "no link". */
23919
+ type: _enum([
23920
+ "wifi",
23921
+ "ethernet",
23922
+ "cellular",
23923
+ "unknown"
23924
+ ]),
23925
+ /**
23926
+ * Link quality, 0..100 inclusive, normalised by the provider from whatever
23927
+ * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
23928
+ * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
23929
+ * one whose reading has not landed must not be drawn at 0 %. Consumers
23930
+ * SKIP a null rather than coerce it.
23931
+ */
23932
+ signalPercent: number().min(0).max(100).nullable(),
23933
+ /** Raw received signal strength in dBm, when the firmware reports one. */
23934
+ rssiDbm: number().optional(),
23935
+ /** Network name of a wireless link, when the firmware reports it. */
23936
+ ssid: string().optional(),
23937
+ /** Ms epoch of the last observation. Lets consumers reason about freshness. */
23938
+ lastUpdated: number()
23939
+ });
23940
+ var networkLinkCapability = {
23941
+ name: "network-link",
23942
+ scope: "device",
23943
+ deviceNative: true,
23944
+ mode: "singleton",
23945
+ deviceTypes: [
23946
+ DeviceType.Camera,
23947
+ DeviceType.Sensor,
23948
+ DeviceType.Button,
23949
+ DeviceType.Switch,
23950
+ DeviceType.Light,
23951
+ DeviceType.Lock,
23952
+ DeviceType.Siren
23953
+ ],
23954
+ methods: {},
23955
+ events: {
23956
+ /**
23957
+ * Emitted whenever the cached status changes (a link switch, a signal
23958
+ * reading that moved). Mirrored on the parent chain by the
23959
+ * DeviceEventPropagator like `battery.onStatusChanged`.
23960
+ */
23961
+ onStatusChanged: { data: object({
23962
+ deviceId: number(),
23963
+ status: NetworkLinkStatusSchema
23964
+ }) } },
23965
+ status: {
23966
+ schema: NetworkLinkStatusSchema,
23967
+ kind: "push",
23968
+ empty: {
23969
+ type: "unknown",
23970
+ signalPercent: null,
23971
+ lastUpdated: 0
23972
+ }
23973
+ },
23974
+ /**
23975
+ * Runtime-state slice — every provider stores the same shape under
23976
+ * `device.runtimeState['network-link']`, read once by the badge and the
23977
+ * Home Assistant projector regardless of the driver.
23978
+ */
23979
+ runtimeState: NetworkLinkStatusSchema,
23980
+ /**
23981
+ * Runtime-state durability: **restored** — a link reading is slow to
23982
+ * change and a sleeping battery camera may not report for hours; the
23983
+ * restored slice is what the badge shows until the next read.
23984
+ *
23985
+ * See `RuntimeStateDurability`. Enforced by
23986
+ * `scripts/check-runtime-state-durability.ts`.
23987
+ */
23988
+ durability: "restored",
23989
+ /** Clock fields: written, but excluded from the compare that decides
23990
+ * whether persisting is worth a SQLite commit. */
23991
+ volatileStateFields: ["lastUpdated"]
23992
+ };
23993
+ /**
23905
23994
  * Generic boolean sensor — last-resort fallback when no domain-
23906
23995
  * specific binary cap fits (Home Assistant `binary_sensor` without a
23907
23996
  * known `device_class`, or a domain we haven't typed yet). Pure
@@ -29019,6 +29108,287 @@ var ptzAutotrackCapability = {
29019
29108
  */
29020
29109
  durability: "session"
29021
29110
  };
29111
+ /**
29112
+ * `navigation` — a device-scoped capability that natively expresses the FULL
29113
+ * navigation / action surface of a robot that DRIVES ITSELF and carries an
29114
+ * on-board camera (the Dreame robot-vacuum camera is the first provider).
29115
+ *
29116
+ * Why a NEW cap rather than overloading `ptz`:
29117
+ * - `ptz` moves a *gimbal* on a fixed camera (pan/tilt/zoom of the lens). A
29118
+ * robot vacuum has no gimbal — the whole chassis drives, turns and spins.
29119
+ * The two are different physical models: PTZ is absolute-position + presets,
29120
+ * navigation is momentary drive nudges + discrete robot ACTIONS
29121
+ * (dock / spot-clean / follow-pet / go-to-point / …).
29122
+ * - This cap is the SOURCE OF TRUTH. Two consumers adapt from it rather than
29123
+ * the reverse:
29124
+ * 1. a native CamStack navigation panel (data-driven from `listActions`
29125
+ * / `getOptions`), and
29126
+ * 2. the PTZ surface — a thin adapter mimics `ptz` from `navigation` so a
29127
+ * robot camera shows up in the existing PTZ control path without every
29128
+ * PTZ provider learning about robots. The mapping lives in the adapter,
29129
+ * not here (see the addon design note):
29130
+ * ptz.continuousMove({pan,tilt}) → navigation.move({pan,tilt})
29131
+ * ptz.stop() → navigation.stop()
29132
+ * ptz.goHome() → navigation.runAction('goHome')
29133
+ * ptz.getPresets() → navigation.listActions() (id→preset)
29134
+ * ptz.goToPreset(id) → navigation.runAction(id)
29135
+ *
29136
+ * ## Continuous drive
29137
+ *
29138
+ * `move` is MOMENTARY. Fluid navigation comes from the UI (or the PTZ adapter)
29139
+ * sending `move({pan,tilt})` REPEATEDLY at ~1 Hz while a direction is held, and
29140
+ * one `stop()` on release — exactly like the robot app's remote-drive joystick.
29141
+ * The provider forwards EACH `move` to one drive write; it must NOT debounce or
29142
+ * coalesce them. The UI owns the cadence.
29143
+ *
29144
+ * ## The action dictionary
29145
+ *
29146
+ * The discrete controls (dock / locate / spot-clean / follow / flash / sounds)
29147
+ * are a DATA-DRIVEN dictionary the cap exposes via `listActions()`. Each entry
29148
+ * carries `{ id, kind, label, icon }` (plus `soundId` for sound entries) so the
29149
+ * native panel AND the PTZ mimic render buttons WITHOUT hardcoding a
29150
+ * vendor-specific list. `kind: 'action'` entries are triggered with
29151
+ * `runAction({ actionId })`; `kind: 'sound'` entries with `playSound({ soundId })`
29152
+ * (the entry carries the `soundId` to pass). The general primitives — `move`,
29153
+ * `stop`, `goToPoint` — stay as first-class methods, not dictionary entries.
29154
+ *
29155
+ * NOTE (provider wiring): the first provider (Dreame) drives this with the RAW
29156
+ * `callAction(siid,aiid,in)` / `setProperty({siid,piid,value})` MIoT primitives
29157
+ * that the currently-published `@apocaliss92/nodedreame` already exposes on
29158
+ * every device handle. A future nodedreame publish adds a typed
29159
+ * `DreameCameraController` (drive / playPetSound / spotClean / findPet / …); the
29160
+ * provider can then swap the raw calls for the typed methods with no change to
29161
+ * THIS contract.
29162
+ */
29163
+ /**
29164
+ * A momentary drive nudge. The robot MOVES (no gimbal) for as long as the caller
29165
+ * keeps sending nudges (~1 Hz); an explicit `stop` (or letting the nudges lapse)
29166
+ * halts it.
29167
+ *
29168
+ * - `pan` — turn: negative = left, positive = right, 0 = straight.
29169
+ * - `tilt` — throttle: positive = forward, negative = spin / turn-around.
29170
+ * - `speed` — optional intensity hint [0, 1]; the provider may scale the drive
29171
+ * vector by it (drivers without proportional drive ignore it).
29172
+ *
29173
+ * `pan` / `tilt` are normalized [-1, 1]. Both optional so a caller can nudge one
29174
+ * axis alone; an all-undefined nudge is a no-op.
29175
+ */
29176
+ var NavigationMoveCommandSchema = object({
29177
+ pan: number().min(-1).max(1).optional(),
29178
+ tilt: number().min(-1).max(1).optional(),
29179
+ speed: number().min(0).max(1).optional()
29180
+ });
29181
+ /**
29182
+ * The enumerated discrete actions a navigation-capable robot can perform via
29183
+ * `runAction`. This is the CLOSED vocabulary; a given device advertises the
29184
+ * subset it supports through `listActions`. Sounds are NOT here — they go through
29185
+ * `playSound` (see the `sound` dictionary entries).
29186
+ */
29187
+ var NavigationActionIdSchema = _enum([
29188
+ "goHome",
29189
+ "locate",
29190
+ "spotClean",
29191
+ "findPet",
29192
+ "personFollow",
29193
+ "stop",
29194
+ "startClean",
29195
+ "pauseClean",
29196
+ "dockWash",
29197
+ "autoEmpty",
29198
+ "flashOn",
29199
+ "flashOff"
29200
+ ]);
29201
+ /** Whether a dictionary entry is a `runAction` action or a `playSound` sound. */
29202
+ var NavigationEntryKindSchema = _enum(["action", "sound"]);
29203
+ /**
29204
+ * One entry in the navigation action dictionary — the DATA-DRIVEN unit both the
29205
+ * native panel and the PTZ mimic render as a button.
29206
+ *
29207
+ * - `id` — stable id. For `kind:'action'` it is a {@link NavigationActionId}
29208
+ * (pass to `runAction`); for `kind:'sound'` it is a namespaced id
29209
+ * (`sound:meow`) whose `soundId` is passed to `playSound`.
29210
+ * - `icon` — icon HINT (lucide-style name; the UI maps it to its own set).
29211
+ * - `label` — operator-facing English label.
29212
+ * - `soundId` — wire sound id, present only on `kind:'sound'` entries.
29213
+ * - `enabled` — per-device FEATURE FLAG. `listActions` reports it so the UI /
29214
+ * PTZ render ONLY enabled entries. Data-driven: the provider
29215
+ * flips it from config, never by editing code.
29216
+ */
29217
+ var NavigationActionEntrySchema = object({
29218
+ id: string(),
29219
+ kind: NavigationEntryKindSchema,
29220
+ label: string(),
29221
+ icon: string(),
29222
+ /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
29223
+ soundId: number().int().optional(),
29224
+ /** Per-device feature flag — render this entry only when true. */
29225
+ enabled: boolean()
29226
+ });
29227
+ /** Coordinates for `goToPoint` — a point on the robot's live map. */
29228
+ var NavigationPointSchema = object({
29229
+ x: number(),
29230
+ y: number()
29231
+ });
29232
+ /**
29233
+ * Per-device FEATURE-FLAG report for the general (non-dictionary) primitives.
29234
+ * The cap reports which are enabled so the UI / PTZ render only the controls
29235
+ * that are turned on for THIS device. Data-driven: the provider derives these
29236
+ * from config + probe, never hardcoded in the UI. The per-DICTIONARY-entry flags
29237
+ * live on {@link NavigationActionEntrySchema.enabled}; these gate the primitives
29238
+ * that are not dictionary entries.
29239
+ *
29240
+ * - `move` / `stop` — the momentary drive joystick.
29241
+ * - `goToPoint` — send-to-map-coordinate. Ships OFF on Dreame until the
29242
+ * map-coordinate plumbing is wired.
29243
+ * - `runAction` — the discrete action buttons (dictionary `kind:'action'`).
29244
+ * - `playSound` — the sound buttons (dictionary `kind:'sound'`).
29245
+ * - `light` — the on/off fill-light toggle (works anytime).
29246
+ * - `lightMode` — the auto/manual selector + manual level slider (a
29247
+ * camera-service control; needs an active stream).
29248
+ */
29249
+ var NavigationFeaturesSchema = object({
29250
+ move: boolean(),
29251
+ stop: boolean(),
29252
+ goToPoint: boolean(),
29253
+ runAction: boolean(),
29254
+ playSound: boolean(),
29255
+ light: boolean(),
29256
+ lightMode: boolean()
29257
+ });
29258
+ /** Light mode: `auto` lets the camera choose brightness; `manual` uses `level`. */
29259
+ var NavigationLightModeSchema = _enum(["auto", "manual"]);
29260
+ /**
29261
+ * Live navigation state so the UI can reflect what the robot is doing:
29262
+ * - `mode` — coarse activity (idle / cleaning / following / …).
29263
+ * - `following` — person/pet follow is currently armed.
29264
+ * - `flash` — the on-camera fill light is on.
29265
+ * - `lightMode` — auto vs manual fill-light mode.
29266
+ * - `lightLevel` — manual fill-light level (40..100); meaningful when
29267
+ * `lightMode === 'manual'`.
29268
+ */
29269
+ var NavigationStatusSchema = object({
29270
+ mode: _enum([
29271
+ "idle",
29272
+ "cleaning",
29273
+ "spot",
29274
+ "following",
29275
+ "goto",
29276
+ "returning",
29277
+ "paused",
29278
+ "unknown"
29279
+ ]),
29280
+ following: boolean(),
29281
+ flash: boolean(),
29282
+ lightMode: NavigationLightModeSchema,
29283
+ lightLevel: number().min(40).max(100),
29284
+ /** Ms epoch when the slice was last updated. */
29285
+ lastChangedAt: number()
29286
+ });
29287
+ /**
29288
+ * Runtime-state slice owned by this cap (kernel-managed: validated, mirrored,
29289
+ * observable). Adds `lastFetchedAt` on top of the status shape per the
29290
+ * convention.
29291
+ */
29292
+ var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: number() });
29293
+ var navigationCapability = {
29294
+ name: "navigation",
29295
+ scope: "device",
29296
+ deviceNative: true,
29297
+ mode: "singleton",
29298
+ deviceTypes: [DeviceType.Camera],
29299
+ deviceConfig: { ui: {
29300
+ kind: "widget",
29301
+ widgetId: "host/navigation-panel",
29302
+ tab: "navigation",
29303
+ topTab: true,
29304
+ label: "Navigation",
29305
+ order: 0
29306
+ } },
29307
+ methods: {
29308
+ /**
29309
+ * Momentary drive nudge (the robot moves). `protected` — mirrors
29310
+ * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
29311
+ * path) works for any authenticated user, not admin-only. The UI sends
29312
+ * these at ~1 Hz while a control is held; the provider forwards each one to
29313
+ * a single drive write WITHOUT debouncing.
29314
+ */
29315
+ move: method(NavigationMoveCommandSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }),
29316
+ /** Halt all motion immediately (zero drive vector). */
29317
+ stop: method(object({ deviceId: number() }), _void(), { kind: "mutation" }),
29318
+ /** Send the robot to a point on its live map. */
29319
+ goToPoint: method(NavigationPointSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }),
29320
+ /**
29321
+ * Enumerate the discrete controls THIS device supports (data-driven UI +
29322
+ * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
29323
+ */
29324
+ listActions: method(object({ deviceId: number() }), array(NavigationActionEntrySchema)),
29325
+ /**
29326
+ * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
29327
+ * unsupported action ids are rejected by the provider.
29328
+ */
29329
+ runAction: method(object({
29330
+ deviceId: number(),
29331
+ actionId: NavigationActionIdSchema
29332
+ }), _void(), { kind: "mutation" }),
29333
+ /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
29334
+ playSound: method(object({
29335
+ deviceId: number(),
29336
+ soundId: number().int()
29337
+ }), _void(), { kind: "mutation" }),
29338
+ /**
29339
+ * Turn the on-camera fill light on / off (the `OpenFullLight` control —
29340
+ * works anytime, no active stream required).
29341
+ */
29342
+ setLightOn: method(object({
29343
+ deviceId: number(),
29344
+ on: boolean()
29345
+ }), _void(), { kind: "mutation" }),
29346
+ /**
29347
+ * Set the fill-light mode (auto vs manual). `manual` optionally carries the
29348
+ * initial `level`. The auto/manual + level control is a CAMERA-service
29349
+ * action that generally needs an active camera stream/monitor session — the
29350
+ * UI shows the manual level slider ONLY when `mode === 'manual'`.
29351
+ */
29352
+ setLightMode: method(object({
29353
+ deviceId: number(),
29354
+ mode: NavigationLightModeSchema,
29355
+ level: number().min(40).max(100).optional()
29356
+ }), _void(), { kind: "mutation" }),
29357
+ /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
29358
+ setLightLevel: method(object({
29359
+ deviceId: number(),
29360
+ level: number().min(40).max(100)
29361
+ }), _void(), { kind: "mutation" }),
29362
+ /**
29363
+ * Per-device FEATURE-FLAG report for the general primitives — drives which
29364
+ * controls the UI shows (the per-entry flags for the dictionary come back on
29365
+ * `listActions`).
29366
+ */
29367
+ getFeatures: method(object({ deviceId: number() }), NavigationFeaturesSchema)
29368
+ },
29369
+ events: { onStatusChanged: { data: object({
29370
+ deviceId: number(),
29371
+ status: NavigationStatusSchema
29372
+ }) } },
29373
+ status: {
29374
+ schema: NavigationStatusSchema,
29375
+ kind: "push"
29376
+ },
29377
+ /**
29378
+ * Runtime-state slice mirrored by the kernel. The navigation panel watches it
29379
+ * for live mode / follow / flash changes.
29380
+ */
29381
+ runtimeState: NavigationRuntimeStateSchema,
29382
+ /**
29383
+ * Runtime-state durability: **session** — like `vacuum-control`, a restored
29384
+ * `mode: cleaning` / `following: true` is a robot that is not actually doing
29385
+ * that. The live handle re-publishes on connect.
29386
+ *
29387
+ * See `RuntimeStateDurability`. Enforced by
29388
+ * `scripts/check-runtime-state-durability.ts`.
29389
+ */
29390
+ durability: "session"
29391
+ };
29022
29392
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
29023
29393
  kind: "mutation",
29024
29394
  auth: "admin"
@@ -32294,6 +32664,8 @@ var DEVICE_LOCAL_STATE_CAPS = {
32294
32664
  motionTrigger: motionTriggerCapability,
32295
32665
  motionZones: motionZonesCapability,
32296
32666
  nativeObjectDetection: nativeObjectDetectionCapability,
32667
+ navigation: navigationCapability,
32668
+ networkLink: networkLinkCapability,
32297
32669
  notifier: notifierCapability,
32298
32670
  numericSensor: numericSensorCapability,
32299
32671
  petFeeder: petFeederCapability,
@@ -36147,6 +36519,66 @@ Object.freeze({
36147
36519
  addonId: null,
36148
36520
  access: "create"
36149
36521
  },
36522
+ "navigation.getFeatures": {
36523
+ capName: "navigation",
36524
+ capScope: "device",
36525
+ addonId: null,
36526
+ access: "view"
36527
+ },
36528
+ "navigation.goToPoint": {
36529
+ capName: "navigation",
36530
+ capScope: "device",
36531
+ addonId: null,
36532
+ access: "create"
36533
+ },
36534
+ "navigation.listActions": {
36535
+ capName: "navigation",
36536
+ capScope: "device",
36537
+ addonId: null,
36538
+ access: "view"
36539
+ },
36540
+ "navigation.move": {
36541
+ capName: "navigation",
36542
+ capScope: "device",
36543
+ addonId: null,
36544
+ access: "create"
36545
+ },
36546
+ "navigation.playSound": {
36547
+ capName: "navigation",
36548
+ capScope: "device",
36549
+ addonId: null,
36550
+ access: "create"
36551
+ },
36552
+ "navigation.runAction": {
36553
+ capName: "navigation",
36554
+ capScope: "device",
36555
+ addonId: null,
36556
+ access: "create"
36557
+ },
36558
+ "navigation.setLightLevel": {
36559
+ capName: "navigation",
36560
+ capScope: "device",
36561
+ addonId: null,
36562
+ access: "create"
36563
+ },
36564
+ "navigation.setLightMode": {
36565
+ capName: "navigation",
36566
+ capScope: "device",
36567
+ addonId: null,
36568
+ access: "create"
36569
+ },
36570
+ "navigation.setLightOn": {
36571
+ capName: "navigation",
36572
+ capScope: "device",
36573
+ addonId: null,
36574
+ access: "create"
36575
+ },
36576
+ "navigation.stop": {
36577
+ capName: "navigation",
36578
+ capScope: "device",
36579
+ addonId: null,
36580
+ access: "create"
36581
+ },
36150
36582
  "networkAccess.getEndpoint": {
36151
36583
  capName: "network-access",
36152
36584
  capScope: "system",
@@ -40242,6 +40674,56 @@ Object.freeze({
40242
40674
  form: "single",
40243
40675
  optional: false
40244
40676
  }],
40677
+ "navigation.getFeatures": [{
40678
+ name: "deviceId",
40679
+ form: "single",
40680
+ optional: false
40681
+ }],
40682
+ "navigation.goToPoint": [{
40683
+ name: "deviceId",
40684
+ form: "single",
40685
+ optional: false
40686
+ }],
40687
+ "navigation.listActions": [{
40688
+ name: "deviceId",
40689
+ form: "single",
40690
+ optional: false
40691
+ }],
40692
+ "navigation.move": [{
40693
+ name: "deviceId",
40694
+ form: "single",
40695
+ optional: false
40696
+ }],
40697
+ "navigation.playSound": [{
40698
+ name: "deviceId",
40699
+ form: "single",
40700
+ optional: false
40701
+ }],
40702
+ "navigation.runAction": [{
40703
+ name: "deviceId",
40704
+ form: "single",
40705
+ optional: false
40706
+ }],
40707
+ "navigation.setLightLevel": [{
40708
+ name: "deviceId",
40709
+ form: "single",
40710
+ optional: false
40711
+ }],
40712
+ "navigation.setLightMode": [{
40713
+ name: "deviceId",
40714
+ form: "single",
40715
+ optional: false
40716
+ }],
40717
+ "navigation.setLightOn": [{
40718
+ name: "deviceId",
40719
+ form: "single",
40720
+ optional: false
40721
+ }],
40722
+ "navigation.stop": [{
40723
+ name: "deviceId",
40724
+ form: "single",
40725
+ optional: false
40726
+ }],
40245
40727
  "networkQuality.getDeviceStats": [{
40246
40728
  name: "deviceId",
40247
40729
  form: "single",
package/dist/addon.mjs CHANGED
@@ -10,7 +10,7 @@ import { getCACertificates, setDefaultCACertificates } from "node:tls";
10
10
  //#region \0rolldown/runtime.js
11
11
  var __require$1 = /* @__PURE__ */ createRequire(import.meta.url);
12
12
  //#endregion
13
- //#region ../types/dist/event-category-BZL-fdNj.mjs
13
+ //#region ../types/dist/event-category-zAv7pMUz.mjs
14
14
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
15
15
  EventCategory["SystemBoot"] = "system.boot";
16
16
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -382,6 +382,13 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
382
382
  */
383
383
  EventCategory["BatteryOnStatusChanged"] = "battery.onStatusChanged";
384
384
  /**
385
+ * Cap event fired by every device that registers the `network-link`
386
+ * capability. Mirrors the cap definition's `onStatusChanged`. Carries
387
+ * `{ deviceId, status: NetworkLinkStatus }` — a link switch or a signal
388
+ * reading that moved.
389
+ */
390
+ EventCategory["NetworkLinkOnStatusChanged"] = "network-link.onStatusChanged";
391
+ /**
385
392
  * Emitted by the battery cap provider WHEN `wakeForStream` enters the
386
393
  * "wake in progress" window — between the Baichuan wake-up issue and
387
394
  * the camera's first dialed-back RTP packet. The stream-broker
@@ -23881,6 +23888,88 @@ onStatusChanged: { data: object({
23881
23888
  volatileStateFields: ["lastUpdated"]
23882
23889
  };
23883
23890
  /**
23891
+ * Network-link snapshot. Same shape for every provider (a Reolink wifi
23892
+ * camera, a Home Assistant device with a signal-strength sensor, a Tapo
23893
+ * plug): one slice under `device.runtimeState['network-link']`, one badge,
23894
+ * one Home Assistant projection.
23895
+ */
23896
+ var NetworkLinkStatusSchema = object({
23897
+ /** The link the device is on. `'unknown'` = not read yet, not "no link". */
23898
+ type: _enum([
23899
+ "wifi",
23900
+ "ethernet",
23901
+ "cellular",
23902
+ "unknown"
23903
+ ]),
23904
+ /**
23905
+ * Link quality, 0..100 inclusive, normalised by the provider from whatever
23906
+ * the firmware reports (bars, RSSI, a vendor scale). **`null` means NOT
23907
+ * KNOWN or NOT APPLICABLE** — a wired link has no signal, and a wireless
23908
+ * one whose reading has not landed must not be drawn at 0 %. Consumers
23909
+ * SKIP a null rather than coerce it.
23910
+ */
23911
+ signalPercent: number().min(0).max(100).nullable(),
23912
+ /** Raw received signal strength in dBm, when the firmware reports one. */
23913
+ rssiDbm: number().optional(),
23914
+ /** Network name of a wireless link, when the firmware reports it. */
23915
+ ssid: string().optional(),
23916
+ /** Ms epoch of the last observation. Lets consumers reason about freshness. */
23917
+ lastUpdated: number()
23918
+ });
23919
+ var networkLinkCapability = {
23920
+ name: "network-link",
23921
+ scope: "device",
23922
+ deviceNative: true,
23923
+ mode: "singleton",
23924
+ deviceTypes: [
23925
+ DeviceType.Camera,
23926
+ DeviceType.Sensor,
23927
+ DeviceType.Button,
23928
+ DeviceType.Switch,
23929
+ DeviceType.Light,
23930
+ DeviceType.Lock,
23931
+ DeviceType.Siren
23932
+ ],
23933
+ methods: {},
23934
+ events: {
23935
+ /**
23936
+ * Emitted whenever the cached status changes (a link switch, a signal
23937
+ * reading that moved). Mirrored on the parent chain by the
23938
+ * DeviceEventPropagator like `battery.onStatusChanged`.
23939
+ */
23940
+ onStatusChanged: { data: object({
23941
+ deviceId: number(),
23942
+ status: NetworkLinkStatusSchema
23943
+ }) } },
23944
+ status: {
23945
+ schema: NetworkLinkStatusSchema,
23946
+ kind: "push",
23947
+ empty: {
23948
+ type: "unknown",
23949
+ signalPercent: null,
23950
+ lastUpdated: 0
23951
+ }
23952
+ },
23953
+ /**
23954
+ * Runtime-state slice — every provider stores the same shape under
23955
+ * `device.runtimeState['network-link']`, read once by the badge and the
23956
+ * Home Assistant projector regardless of the driver.
23957
+ */
23958
+ runtimeState: NetworkLinkStatusSchema,
23959
+ /**
23960
+ * Runtime-state durability: **restored** — a link reading is slow to
23961
+ * change and a sleeping battery camera may not report for hours; the
23962
+ * restored slice is what the badge shows until the next read.
23963
+ *
23964
+ * See `RuntimeStateDurability`. Enforced by
23965
+ * `scripts/check-runtime-state-durability.ts`.
23966
+ */
23967
+ durability: "restored",
23968
+ /** Clock fields: written, but excluded from the compare that decides
23969
+ * whether persisting is worth a SQLite commit. */
23970
+ volatileStateFields: ["lastUpdated"]
23971
+ };
23972
+ /**
23884
23973
  * Generic boolean sensor — last-resort fallback when no domain-
23885
23974
  * specific binary cap fits (Home Assistant `binary_sensor` without a
23886
23975
  * known `device_class`, or a domain we haven't typed yet). Pure
@@ -28998,6 +29087,287 @@ var ptzAutotrackCapability = {
28998
29087
  */
28999
29088
  durability: "session"
29000
29089
  };
29090
+ /**
29091
+ * `navigation` — a device-scoped capability that natively expresses the FULL
29092
+ * navigation / action surface of a robot that DRIVES ITSELF and carries an
29093
+ * on-board camera (the Dreame robot-vacuum camera is the first provider).
29094
+ *
29095
+ * Why a NEW cap rather than overloading `ptz`:
29096
+ * - `ptz` moves a *gimbal* on a fixed camera (pan/tilt/zoom of the lens). A
29097
+ * robot vacuum has no gimbal — the whole chassis drives, turns and spins.
29098
+ * The two are different physical models: PTZ is absolute-position + presets,
29099
+ * navigation is momentary drive nudges + discrete robot ACTIONS
29100
+ * (dock / spot-clean / follow-pet / go-to-point / …).
29101
+ * - This cap is the SOURCE OF TRUTH. Two consumers adapt from it rather than
29102
+ * the reverse:
29103
+ * 1. a native CamStack navigation panel (data-driven from `listActions`
29104
+ * / `getOptions`), and
29105
+ * 2. the PTZ surface — a thin adapter mimics `ptz` from `navigation` so a
29106
+ * robot camera shows up in the existing PTZ control path without every
29107
+ * PTZ provider learning about robots. The mapping lives in the adapter,
29108
+ * not here (see the addon design note):
29109
+ * ptz.continuousMove({pan,tilt}) → navigation.move({pan,tilt})
29110
+ * ptz.stop() → navigation.stop()
29111
+ * ptz.goHome() → navigation.runAction('goHome')
29112
+ * ptz.getPresets() → navigation.listActions() (id→preset)
29113
+ * ptz.goToPreset(id) → navigation.runAction(id)
29114
+ *
29115
+ * ## Continuous drive
29116
+ *
29117
+ * `move` is MOMENTARY. Fluid navigation comes from the UI (or the PTZ adapter)
29118
+ * sending `move({pan,tilt})` REPEATEDLY at ~1 Hz while a direction is held, and
29119
+ * one `stop()` on release — exactly like the robot app's remote-drive joystick.
29120
+ * The provider forwards EACH `move` to one drive write; it must NOT debounce or
29121
+ * coalesce them. The UI owns the cadence.
29122
+ *
29123
+ * ## The action dictionary
29124
+ *
29125
+ * The discrete controls (dock / locate / spot-clean / follow / flash / sounds)
29126
+ * are a DATA-DRIVEN dictionary the cap exposes via `listActions()`. Each entry
29127
+ * carries `{ id, kind, label, icon }` (plus `soundId` for sound entries) so the
29128
+ * native panel AND the PTZ mimic render buttons WITHOUT hardcoding a
29129
+ * vendor-specific list. `kind: 'action'` entries are triggered with
29130
+ * `runAction({ actionId })`; `kind: 'sound'` entries with `playSound({ soundId })`
29131
+ * (the entry carries the `soundId` to pass). The general primitives — `move`,
29132
+ * `stop`, `goToPoint` — stay as first-class methods, not dictionary entries.
29133
+ *
29134
+ * NOTE (provider wiring): the first provider (Dreame) drives this with the RAW
29135
+ * `callAction(siid,aiid,in)` / `setProperty({siid,piid,value})` MIoT primitives
29136
+ * that the currently-published `@apocaliss92/nodedreame` already exposes on
29137
+ * every device handle. A future nodedreame publish adds a typed
29138
+ * `DreameCameraController` (drive / playPetSound / spotClean / findPet / …); the
29139
+ * provider can then swap the raw calls for the typed methods with no change to
29140
+ * THIS contract.
29141
+ */
29142
+ /**
29143
+ * A momentary drive nudge. The robot MOVES (no gimbal) for as long as the caller
29144
+ * keeps sending nudges (~1 Hz); an explicit `stop` (or letting the nudges lapse)
29145
+ * halts it.
29146
+ *
29147
+ * - `pan` — turn: negative = left, positive = right, 0 = straight.
29148
+ * - `tilt` — throttle: positive = forward, negative = spin / turn-around.
29149
+ * - `speed` — optional intensity hint [0, 1]; the provider may scale the drive
29150
+ * vector by it (drivers without proportional drive ignore it).
29151
+ *
29152
+ * `pan` / `tilt` are normalized [-1, 1]. Both optional so a caller can nudge one
29153
+ * axis alone; an all-undefined nudge is a no-op.
29154
+ */
29155
+ var NavigationMoveCommandSchema = object({
29156
+ pan: number().min(-1).max(1).optional(),
29157
+ tilt: number().min(-1).max(1).optional(),
29158
+ speed: number().min(0).max(1).optional()
29159
+ });
29160
+ /**
29161
+ * The enumerated discrete actions a navigation-capable robot can perform via
29162
+ * `runAction`. This is the CLOSED vocabulary; a given device advertises the
29163
+ * subset it supports through `listActions`. Sounds are NOT here — they go through
29164
+ * `playSound` (see the `sound` dictionary entries).
29165
+ */
29166
+ var NavigationActionIdSchema = _enum([
29167
+ "goHome",
29168
+ "locate",
29169
+ "spotClean",
29170
+ "findPet",
29171
+ "personFollow",
29172
+ "stop",
29173
+ "startClean",
29174
+ "pauseClean",
29175
+ "dockWash",
29176
+ "autoEmpty",
29177
+ "flashOn",
29178
+ "flashOff"
29179
+ ]);
29180
+ /** Whether a dictionary entry is a `runAction` action or a `playSound` sound. */
29181
+ var NavigationEntryKindSchema = _enum(["action", "sound"]);
29182
+ /**
29183
+ * One entry in the navigation action dictionary — the DATA-DRIVEN unit both the
29184
+ * native panel and the PTZ mimic render as a button.
29185
+ *
29186
+ * - `id` — stable id. For `kind:'action'` it is a {@link NavigationActionId}
29187
+ * (pass to `runAction`); for `kind:'sound'` it is a namespaced id
29188
+ * (`sound:meow`) whose `soundId` is passed to `playSound`.
29189
+ * - `icon` — icon HINT (lucide-style name; the UI maps it to its own set).
29190
+ * - `label` — operator-facing English label.
29191
+ * - `soundId` — wire sound id, present only on `kind:'sound'` entries.
29192
+ * - `enabled` — per-device FEATURE FLAG. `listActions` reports it so the UI /
29193
+ * PTZ render ONLY enabled entries. Data-driven: the provider
29194
+ * flips it from config, never by editing code.
29195
+ */
29196
+ var NavigationActionEntrySchema = object({
29197
+ id: string(),
29198
+ kind: NavigationEntryKindSchema,
29199
+ label: string(),
29200
+ icon: string(),
29201
+ /** Present only on `kind:'sound'` entries — the id to pass to `playSound`. */
29202
+ soundId: number().int().optional(),
29203
+ /** Per-device feature flag — render this entry only when true. */
29204
+ enabled: boolean()
29205
+ });
29206
+ /** Coordinates for `goToPoint` — a point on the robot's live map. */
29207
+ var NavigationPointSchema = object({
29208
+ x: number(),
29209
+ y: number()
29210
+ });
29211
+ /**
29212
+ * Per-device FEATURE-FLAG report for the general (non-dictionary) primitives.
29213
+ * The cap reports which are enabled so the UI / PTZ render only the controls
29214
+ * that are turned on for THIS device. Data-driven: the provider derives these
29215
+ * from config + probe, never hardcoded in the UI. The per-DICTIONARY-entry flags
29216
+ * live on {@link NavigationActionEntrySchema.enabled}; these gate the primitives
29217
+ * that are not dictionary entries.
29218
+ *
29219
+ * - `move` / `stop` — the momentary drive joystick.
29220
+ * - `goToPoint` — send-to-map-coordinate. Ships OFF on Dreame until the
29221
+ * map-coordinate plumbing is wired.
29222
+ * - `runAction` — the discrete action buttons (dictionary `kind:'action'`).
29223
+ * - `playSound` — the sound buttons (dictionary `kind:'sound'`).
29224
+ * - `light` — the on/off fill-light toggle (works anytime).
29225
+ * - `lightMode` — the auto/manual selector + manual level slider (a
29226
+ * camera-service control; needs an active stream).
29227
+ */
29228
+ var NavigationFeaturesSchema = object({
29229
+ move: boolean(),
29230
+ stop: boolean(),
29231
+ goToPoint: boolean(),
29232
+ runAction: boolean(),
29233
+ playSound: boolean(),
29234
+ light: boolean(),
29235
+ lightMode: boolean()
29236
+ });
29237
+ /** Light mode: `auto` lets the camera choose brightness; `manual` uses `level`. */
29238
+ var NavigationLightModeSchema = _enum(["auto", "manual"]);
29239
+ /**
29240
+ * Live navigation state so the UI can reflect what the robot is doing:
29241
+ * - `mode` — coarse activity (idle / cleaning / following / …).
29242
+ * - `following` — person/pet follow is currently armed.
29243
+ * - `flash` — the on-camera fill light is on.
29244
+ * - `lightMode` — auto vs manual fill-light mode.
29245
+ * - `lightLevel` — manual fill-light level (40..100); meaningful when
29246
+ * `lightMode === 'manual'`.
29247
+ */
29248
+ var NavigationStatusSchema = object({
29249
+ mode: _enum([
29250
+ "idle",
29251
+ "cleaning",
29252
+ "spot",
29253
+ "following",
29254
+ "goto",
29255
+ "returning",
29256
+ "paused",
29257
+ "unknown"
29258
+ ]),
29259
+ following: boolean(),
29260
+ flash: boolean(),
29261
+ lightMode: NavigationLightModeSchema,
29262
+ lightLevel: number().min(40).max(100),
29263
+ /** Ms epoch when the slice was last updated. */
29264
+ lastChangedAt: number()
29265
+ });
29266
+ /**
29267
+ * Runtime-state slice owned by this cap (kernel-managed: validated, mirrored,
29268
+ * observable). Adds `lastFetchedAt` on top of the status shape per the
29269
+ * convention.
29270
+ */
29271
+ var NavigationRuntimeStateSchema = NavigationStatusSchema.extend({ lastFetchedAt: number() });
29272
+ var navigationCapability = {
29273
+ name: "navigation",
29274
+ scope: "device",
29275
+ deviceNative: true,
29276
+ mode: "singleton",
29277
+ deviceTypes: [DeviceType.Camera],
29278
+ deviceConfig: { ui: {
29279
+ kind: "widget",
29280
+ widgetId: "host/navigation-panel",
29281
+ tab: "navigation",
29282
+ topTab: true,
29283
+ label: "Navigation",
29284
+ order: 0
29285
+ } },
29286
+ methods: {
29287
+ /**
29288
+ * Momentary drive nudge (the robot moves). `protected` — mirrors
29289
+ * `ptz.continuousMove` so the Viewer navigation panel (and the PTZ-mimic
29290
+ * path) works for any authenticated user, not admin-only. The UI sends
29291
+ * these at ~1 Hz while a control is held; the provider forwards each one to
29292
+ * a single drive write WITHOUT debouncing.
29293
+ */
29294
+ move: method(NavigationMoveCommandSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }),
29295
+ /** Halt all motion immediately (zero drive vector). */
29296
+ stop: method(object({ deviceId: number() }), _void(), { kind: "mutation" }),
29297
+ /** Send the robot to a point on its live map. */
29298
+ goToPoint: method(NavigationPointSchema.extend({ deviceId: number() }), _void(), { kind: "mutation" }),
29299
+ /**
29300
+ * Enumerate the discrete controls THIS device supports (data-driven UI +
29301
+ * PTZ mimic). Camera-probed subset of {@link NAVIGATION_ACTION_CATALOG}.
29302
+ */
29303
+ listActions: method(object({ deviceId: number() }), array(NavigationActionEntrySchema)),
29304
+ /**
29305
+ * Run one discrete action (a `kind:'action'` dictionary entry). Invalid /
29306
+ * unsupported action ids are rejected by the provider.
29307
+ */
29308
+ runAction: method(object({
29309
+ deviceId: number(),
29310
+ actionId: NavigationActionIdSchema
29311
+ }), _void(), { kind: "mutation" }),
29312
+ /** Play a sound by its wire id (the `soundId` of a `kind:'sound'` entry). */
29313
+ playSound: method(object({
29314
+ deviceId: number(),
29315
+ soundId: number().int()
29316
+ }), _void(), { kind: "mutation" }),
29317
+ /**
29318
+ * Turn the on-camera fill light on / off (the `OpenFullLight` control —
29319
+ * works anytime, no active stream required).
29320
+ */
29321
+ setLightOn: method(object({
29322
+ deviceId: number(),
29323
+ on: boolean()
29324
+ }), _void(), { kind: "mutation" }),
29325
+ /**
29326
+ * Set the fill-light mode (auto vs manual). `manual` optionally carries the
29327
+ * initial `level`. The auto/manual + level control is a CAMERA-service
29328
+ * action that generally needs an active camera stream/monitor session — the
29329
+ * UI shows the manual level slider ONLY when `mode === 'manual'`.
29330
+ */
29331
+ setLightMode: method(object({
29332
+ deviceId: number(),
29333
+ mode: NavigationLightModeSchema,
29334
+ level: number().min(40).max(100).optional()
29335
+ }), _void(), { kind: "mutation" }),
29336
+ /** Set the MANUAL fill-light level (40..100). Implies `manual` mode. */
29337
+ setLightLevel: method(object({
29338
+ deviceId: number(),
29339
+ level: number().min(40).max(100)
29340
+ }), _void(), { kind: "mutation" }),
29341
+ /**
29342
+ * Per-device FEATURE-FLAG report for the general primitives — drives which
29343
+ * controls the UI shows (the per-entry flags for the dictionary come back on
29344
+ * `listActions`).
29345
+ */
29346
+ getFeatures: method(object({ deviceId: number() }), NavigationFeaturesSchema)
29347
+ },
29348
+ events: { onStatusChanged: { data: object({
29349
+ deviceId: number(),
29350
+ status: NavigationStatusSchema
29351
+ }) } },
29352
+ status: {
29353
+ schema: NavigationStatusSchema,
29354
+ kind: "push"
29355
+ },
29356
+ /**
29357
+ * Runtime-state slice mirrored by the kernel. The navigation panel watches it
29358
+ * for live mode / follow / flash changes.
29359
+ */
29360
+ runtimeState: NavigationRuntimeStateSchema,
29361
+ /**
29362
+ * Runtime-state durability: **session** — like `vacuum-control`, a restored
29363
+ * `mode: cleaning` / `following: true` is a robot that is not actually doing
29364
+ * that. The live handle re-publishes on connect.
29365
+ *
29366
+ * See `RuntimeStateDurability`. Enforced by
29367
+ * `scripts/check-runtime-state-durability.ts`.
29368
+ */
29369
+ durability: "session"
29370
+ };
29001
29371
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
29002
29372
  kind: "mutation",
29003
29373
  auth: "admin"
@@ -32273,6 +32643,8 @@ var DEVICE_LOCAL_STATE_CAPS = {
32273
32643
  motionTrigger: motionTriggerCapability,
32274
32644
  motionZones: motionZonesCapability,
32275
32645
  nativeObjectDetection: nativeObjectDetectionCapability,
32646
+ navigation: navigationCapability,
32647
+ networkLink: networkLinkCapability,
32276
32648
  notifier: notifierCapability,
32277
32649
  numericSensor: numericSensorCapability,
32278
32650
  petFeeder: petFeederCapability,
@@ -36126,6 +36498,66 @@ Object.freeze({
36126
36498
  addonId: null,
36127
36499
  access: "create"
36128
36500
  },
36501
+ "navigation.getFeatures": {
36502
+ capName: "navigation",
36503
+ capScope: "device",
36504
+ addonId: null,
36505
+ access: "view"
36506
+ },
36507
+ "navigation.goToPoint": {
36508
+ capName: "navigation",
36509
+ capScope: "device",
36510
+ addonId: null,
36511
+ access: "create"
36512
+ },
36513
+ "navigation.listActions": {
36514
+ capName: "navigation",
36515
+ capScope: "device",
36516
+ addonId: null,
36517
+ access: "view"
36518
+ },
36519
+ "navigation.move": {
36520
+ capName: "navigation",
36521
+ capScope: "device",
36522
+ addonId: null,
36523
+ access: "create"
36524
+ },
36525
+ "navigation.playSound": {
36526
+ capName: "navigation",
36527
+ capScope: "device",
36528
+ addonId: null,
36529
+ access: "create"
36530
+ },
36531
+ "navigation.runAction": {
36532
+ capName: "navigation",
36533
+ capScope: "device",
36534
+ addonId: null,
36535
+ access: "create"
36536
+ },
36537
+ "navigation.setLightLevel": {
36538
+ capName: "navigation",
36539
+ capScope: "device",
36540
+ addonId: null,
36541
+ access: "create"
36542
+ },
36543
+ "navigation.setLightMode": {
36544
+ capName: "navigation",
36545
+ capScope: "device",
36546
+ addonId: null,
36547
+ access: "create"
36548
+ },
36549
+ "navigation.setLightOn": {
36550
+ capName: "navigation",
36551
+ capScope: "device",
36552
+ addonId: null,
36553
+ access: "create"
36554
+ },
36555
+ "navigation.stop": {
36556
+ capName: "navigation",
36557
+ capScope: "device",
36558
+ addonId: null,
36559
+ access: "create"
36560
+ },
36129
36561
  "networkAccess.getEndpoint": {
36130
36562
  capName: "network-access",
36131
36563
  capScope: "system",
@@ -40221,6 +40653,56 @@ Object.freeze({
40221
40653
  form: "single",
40222
40654
  optional: false
40223
40655
  }],
40656
+ "navigation.getFeatures": [{
40657
+ name: "deviceId",
40658
+ form: "single",
40659
+ optional: false
40660
+ }],
40661
+ "navigation.goToPoint": [{
40662
+ name: "deviceId",
40663
+ form: "single",
40664
+ optional: false
40665
+ }],
40666
+ "navigation.listActions": [{
40667
+ name: "deviceId",
40668
+ form: "single",
40669
+ optional: false
40670
+ }],
40671
+ "navigation.move": [{
40672
+ name: "deviceId",
40673
+ form: "single",
40674
+ optional: false
40675
+ }],
40676
+ "navigation.playSound": [{
40677
+ name: "deviceId",
40678
+ form: "single",
40679
+ optional: false
40680
+ }],
40681
+ "navigation.runAction": [{
40682
+ name: "deviceId",
40683
+ form: "single",
40684
+ optional: false
40685
+ }],
40686
+ "navigation.setLightLevel": [{
40687
+ name: "deviceId",
40688
+ form: "single",
40689
+ optional: false
40690
+ }],
40691
+ "navigation.setLightMode": [{
40692
+ name: "deviceId",
40693
+ form: "single",
40694
+ optional: false
40695
+ }],
40696
+ "navigation.setLightOn": [{
40697
+ name: "deviceId",
40698
+ form: "single",
40699
+ optional: false
40700
+ }],
40701
+ "navigation.stop": [{
40702
+ name: "deviceId",
40703
+ form: "single",
40704
+ optional: false
40705
+ }],
40224
40706
  "networkQuality.getDeviceStats": [{
40225
40707
  name: "deviceId",
40226
40708
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-wyze",
3
- "version": "0.2.73",
3
+ "version": "0.2.75",
4
4
  "description": "Wyze camera device-provider addon for CamStack — wraps the @apocaliss92/wyze-bridge-js P2P/DTLS client, feeding the stream-broker via the pull-rfc4571 lazy-publish path (a structural twin of addon-provider-reolink)",
5
5
  "keywords": [
6
6
  "camstack",