@camstack/addon-provider-dreame 0.2.147 → 0.2.149

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.mjs CHANGED
@@ -41,7 +41,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
41
41
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
42
42
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
43
43
  //#endregion
44
- //#region ../types/dist/event-category-DOl51-Ij.mjs
44
+ //#region ../types/dist/event-category-BfJwUd2S.mjs
45
45
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
46
46
  EventCategory["SystemBoot"] = "system.boot";
47
47
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -358,6 +358,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
358
358
  */
359
359
  EventCategory["PipelineDetectionBlind"] = "pipeline.detection-blind";
360
360
  /**
361
+ * A camera's motion source has fired constantly with no subject for an hour
362
+ * (motion duty or session count over the bar, zero detections) — it is not
363
+ * blind, its motion zones or sensitivity want tuning (D907). Paired with
364
+ * {@link PipelineMotionCalm}.
365
+ */
366
+ EventCategory["PipelineMotionNoisy"] = "pipeline.motion-noisy";
367
+ /** The recovery edge of {@link PipelineMotionNoisy}: the motion fell back, or a detection arrived. */
368
+ EventCategory["PipelineMotionCalm"] = "pipeline.motion-calm";
369
+ /**
361
370
  * Per-camera pipeline config was mutated by the orchestrator
362
371
  * (3-level settings change via `setAgentAddonDefaults` /
363
372
  * `setCameraStepToggle` / `setCameraPipelineForAgent` or a
@@ -729,6 +738,14 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
729
738
  EventCategory["BackupRestored"] = "backup.restored";
730
739
  EventCategory["NotificationDispatched"] = "notification.dispatched";
731
740
  EventCategory["NotificationFailed"] = "notification.failed";
741
+ /**
742
+ * A notification rule that cannot work (D905): its stored row does not load,
743
+ * or it still names a device / delivery target the prune CONFIRMED deleted.
744
+ * One edge per rule per distinct set of reasons; never on an unanswered read.
745
+ */
746
+ EventCategory["NotificationRuleBroken"] = "notification.rule-broken";
747
+ /** The clear of {@link NotificationRuleBroken}: the rule loads and names nothing deleted. */
748
+ EventCategory["NotificationRuleRepaired"] = "notification.rule-repaired";
732
749
  EventCategory["DeviceUpdated"] = "device.updated";
733
750
  /**
734
751
  * Transport-level connectivity. Emitted by the device driver when
@@ -5456,7 +5473,7 @@ var ZodIssueCode$1 = {
5456
5473
  var ZodFirstPartyTypeKind$1;
5457
5474
  ZodFirstPartyTypeKind$1 || (ZodFirstPartyTypeKind$1 = {});
5458
5475
  //#endregion
5459
- //#region ../types/dist/sleep-_sV1BE2f.mjs
5476
+ //#region ../types/dist/sleep-DJFUfKvA.mjs
5460
5477
  /**
5461
5478
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5462
5479
  * window to float samples (D455).
@@ -7985,7 +8002,7 @@ var EntryDescriptorSchema = object({
7985
8002
  /** JSON Schema of the entry body; `secret: true` marks a password field. */
7986
8003
  fields: record(string(), unknown())
7987
8004
  }).strict();
7988
- /** What crosses the wire and sits in the engine's catalog: JSON only, no Zod. */
8005
+ /** What crosses the wire from an addon: JSON only, no Zod. An unknown key is refused. */
7989
8006
  var RecordKindDeclarationSchema = object({
7990
8007
  database: _enum(RECORD_DATABASES).default("settings"),
7991
8008
  class: _enum(RECORD_CLASSES),
@@ -8004,14 +8021,6 @@ var RecordKindDeclarationSchema = object({
8004
8021
  secretPaths: array(string().min(1)).default([]),
8005
8022
  /** The id key of each array that carries a secret (`{ headers: 'name' }`). */
8006
8023
  secretArrayKeys: record(string(), string()).default({}),
8007
- /**
8008
- * RETIRED (D900): the legacy table a kind adopted from during the records
8009
- * migration. Nothing declares it any more and nothing reads it; the schema
8010
- * still ACCEPTS it because the catalog rows the migration wrote carry it
8011
- * (an orphaned kind is never redeclared) and an addon built before the
8012
- * removal still sends it. Never set it.
8013
- */
8014
- adoptsFrom: string().min(1).optional(),
8015
8024
  /** Kinds that must agree through `transact` sit in one file under one family name. */
8016
8025
  transactFamily: string().min(1).optional(),
8017
8026
  entry: EntryDescriptorSchema.optional()
@@ -12703,17 +12712,6 @@ object({
12703
12712
  createdAt: number(),
12704
12713
  updatedAt: number()
12705
12714
  });
12706
- object({
12707
- id: string(),
12708
- label: string(),
12709
- isAdmin: boolean().default(false),
12710
- allowedProviders: AllowedProviderSchema,
12711
- allowedDevices: AllowedDevicesSchema,
12712
- tokenHash: string(),
12713
- tokenPrefix: string(),
12714
- createdAt: number(),
12715
- lastUsedAt: number().optional()
12716
- });
12717
12715
  object({
12718
12716
  id: string(),
12719
12717
  userId: string(),
@@ -16695,16 +16693,36 @@ var RecordKindStateSchema = _enum([
16695
16693
  var DeclareKindResultSchema = object({
16696
16694
  kindId: number().int(),
16697
16695
  state: RecordKindStateSchema,
16698
- createdIndexes: array(string()),
16699
- /**
16700
- * RETIRED (D900): always `null`. It carried the records-migration boot guard;
16701
- * it stays on the wire, null, because an addon built before the removal reads
16702
- * an ABSENT field as "adoption pending" and refuses to start.
16703
- */
16704
- adoptionPending: _null()
16696
+ createdIndexes: array(string())
16705
16697
  });
16706
16698
  /** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
16707
16699
  var SecretRefsList = array(string());
16700
+ /**
16701
+ * One op of a `transact`: the single-row writes, each on a kind of the caller.
16702
+ * All ops of one call share ONE database file and ONE `transactFamily`.
16703
+ */
16704
+ var RecordTransactOpSchema = discriminatedUnion("op", [
16705
+ object({
16706
+ op: literal("put"),
16707
+ kind: RecordKindNameSchema,
16708
+ record: RecordInputSchema,
16709
+ ifRev: number().int().min(0).optional()
16710
+ }),
16711
+ object({
16712
+ op: literal("patch"),
16713
+ kind: RecordKindNameSchema,
16714
+ id: RecordIdSchema,
16715
+ data: RecordBodySchema,
16716
+ at: number().int().nullable().optional(),
16717
+ expiresAt: number().int().nullable().optional(),
16718
+ ifRev: number().int().min(1).optional()
16719
+ }),
16720
+ object({
16721
+ op: literal("delete"),
16722
+ kind: RecordKindNameSchema,
16723
+ id: RecordIdSchema
16724
+ })
16725
+ ]);
16708
16726
  var RecordSecretRefsSchema = record(string(), record(string(), string()));
16709
16727
  method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
16710
16728
  kind: "mutation",
@@ -16811,6 +16829,16 @@ method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema)
16811
16829
  }), {
16812
16830
  kind: "mutation",
16813
16831
  auth: "admin"
16832
+ }), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
16833
+ results: array(object({
16834
+ rev: number().int().nullable(),
16835
+ deleted: boolean()
16836
+ })),
16837
+ releasedRefs: SecretRefsList,
16838
+ secretRefs: SecretRefsList
16839
+ }), {
16840
+ kind: "mutation",
16841
+ auth: "admin"
16814
16842
  }), method(object({
16815
16843
  deviceId: number().int(),
16816
16844
  kinds: array(RecordKindNameSchema).optional()
@@ -42434,9 +42462,13 @@ var NcSystemEventKindSchema = _enum([
42434
42462
  "node-updating",
42435
42463
  "node-inference-unavailable",
42436
42464
  "detection-blind",
42465
+ "motion-noisy",
42466
+ "motion-calm",
42437
42467
  "recording-stalled",
42438
42468
  "recording-healthy",
42439
42469
  "recording-degraded",
42470
+ "device-attention-required",
42471
+ "device-attention-resolved",
42440
42472
  "storage-location-low",
42441
42473
  "storage-location-critical",
42442
42474
  "storage-location-ok",
@@ -42460,6 +42492,8 @@ var NcSystemEventKindSchema = _enum([
42460
42492
  "alert-critical",
42461
42493
  "alert-error",
42462
42494
  "alert-critical-resolved",
42495
+ "rule-broken",
42496
+ "rule-repaired",
42463
42497
  "camera-online",
42464
42498
  "camera-offline",
42465
42499
  "camera-disabled",
@@ -55783,6 +55817,16 @@ var DishwasherStatusSchema = object({
55783
55817
  powerState: string().nullable(),
55784
55818
  saltLevel: DishwasherConsumableLevelSchema.nullable(),
55785
55819
  rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
55820
+ /**
55821
+ * True while the appliance asks for its filter to be cleaned (smart-filter, filter-system or
55822
+ * combined care-and-filter reminder raised); false when every such reminder reads clear; null
55823
+ * when none is reported. A cleared reminder is not an unknown one.
55824
+ */
55825
+ filterCleaningDue: boolean().nullable(),
55826
+ /** True while the machine-care (descaling / cleaning-program) reminder is raised; false when clear; null when not reported. */
55827
+ machineCareDue: boolean().nullable(),
55828
+ /** Program runs left before the machine-care reminder is due; null when the appliance does not say. */
55829
+ machineCareRemainingRuns: number().int().nonnegative().nullable(),
55786
55830
  /** Option states: null when the appliance does not expose the option or has not said. */
55787
55831
  halfLoad: boolean().nullable(),
55788
55832
  hygienePlus: boolean().nullable(),
@@ -64581,16 +64625,6 @@ var UpdateUserInputSchema = object({
64581
64625
  allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
64582
64626
  scopes: array(TokenScopeSchema).optional()
64583
64627
  });
64584
- var ApiKeySummarySchema = object({
64585
- id: string(),
64586
- label: string(),
64587
- isAdmin: boolean().default(false),
64588
- allowedProviders: union([array(string()), literal("*")]).optional(),
64589
- allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
64590
- tokenPrefix: string(),
64591
- createdAt: number(),
64592
- lastUsedAt: number().optional()
64593
- });
64594
64628
  var OauthSessionSummarySchema = object({
64595
64629
  id: string(),
64596
64630
  userId: string(),
@@ -64612,8 +64646,7 @@ var TotpStatusSchema = object({
64612
64646
  confirmedAt: number().nullable()
64613
64647
  });
64614
64648
  /**
64615
- * user-management — singleton capability for user accounts and the
64616
- * legacy API-key rows.
64649
+ * user-management — singleton capability for user accounts.
64617
64650
  *
64618
64651
  * v2 model: every method gated by `auth: 'admin'`. Admin-only delegation
64619
64652
  * — the subset check on `setUserScopes` is moot
@@ -64669,10 +64702,6 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
64669
64702
  }), UserSummarySchema.extend({ passwordHash: string() }).nullable(), {
64670
64703
  kind: "mutation",
64671
64704
  access: "view"
64672
- }), method(_void(), array(ApiKeySummarySchema), { auth: "admin" }), method(object({ id: string() }), object({ success: literal(true) }), {
64673
- kind: "mutation",
64674
- auth: "admin",
64675
- access: "delete"
64676
64705
  }), method(object({ userId: string() }), TotpSetupResultSchema, {
64677
64706
  kind: "mutation",
64678
64707
  auth: "admin",
@@ -74144,6 +74173,12 @@ Object.freeze({
74144
74173
  addonId: null,
74145
74174
  access: "view"
74146
74175
  },
74176
+ "records.transact": {
74177
+ capName: "records",
74178
+ capScope: "system",
74179
+ addonId: null,
74180
+ access: "create"
74181
+ },
74147
74182
  "sceneMonitor.captureReference": {
74148
74183
  capName: "scene-monitor",
74149
74184
  capScope: "device",
@@ -75476,12 +75511,6 @@ Object.freeze({
75476
75511
  addonId: null,
75477
75512
  access: "view"
75478
75513
  },
75479
- "userManagement.listApiKeys": {
75480
- capName: "user-management",
75481
- capScope: "system",
75482
- addonId: null,
75483
- access: "view"
75484
- },
75485
75514
  "userManagement.listExternalIdentities": {
75486
75515
  capName: "user-management",
75487
75516
  capScope: "system",
@@ -75536,12 +75565,6 @@ Object.freeze({
75536
75565
  addonId: null,
75537
75566
  access: "create"
75538
75567
  },
75539
- "userManagement.revokeApiKey": {
75540
- capName: "user-management",
75541
- capScope: "system",
75542
- addonId: null,
75543
- access: "delete"
75544
- },
75545
75568
  "userManagement.revokeOauthSession": {
75546
75569
  capName: "user-management",
75547
75570
  capScope: "system",
@@ -78906,9 +78929,13 @@ new Map(Object.entries({
78906
78929
  "stream-online": "device-events",
78907
78930
  "stream-offline": "device-events",
78908
78931
  "detection-blind": "device-events",
78932
+ "motion-noisy": "device-events",
78933
+ "motion-calm": "device-events",
78909
78934
  "recording-stalled": "device-events",
78910
78935
  "recording-healthy": "device-events",
78911
78936
  "recording-degraded": "device-events",
78937
+ "device-attention-required": "device-events",
78938
+ "device-attention-resolved": "device-events",
78912
78939
  "node-online": "system",
78913
78940
  "node-offline": "system",
78914
78941
  "node-restarting": "system",
@@ -78925,6 +78952,8 @@ new Map(Object.entries({
78925
78952
  "alert-critical": "system",
78926
78953
  "alert-error": "system",
78927
78954
  "alert-critical-resolved": "system",
78955
+ "rule-broken": "system",
78956
+ "rule-repaired": "system",
78928
78957
  "storage-location-low": "system",
78929
78958
  "storage-location-critical": "system",
78930
78959
  "storage-location-ok": "system",
@@ -78989,6 +79018,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
78989
79018
  label: "Detecting nothing",
78990
79019
  kinds: ["detection-blind"]
78991
79020
  },
79021
+ {
79022
+ id: "motion-noise",
79023
+ family: "device-events",
79024
+ label: "Motion without a subject",
79025
+ kinds: ["motion-noisy", "motion-calm"]
79026
+ },
78992
79027
  {
78993
79028
  id: "recording",
78994
79029
  family: "device-events",
@@ -78999,6 +79034,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
78999
79034
  "recording-degraded"
79000
79035
  ]
79001
79036
  },
79037
+ {
79038
+ id: "device-attention",
79039
+ family: "device-events",
79040
+ label: "Needs attention",
79041
+ kinds: ["device-attention-required", "device-attention-resolved"]
79042
+ },
79002
79043
  {
79003
79044
  id: "nodes",
79004
79045
  family: "system",
@@ -79059,6 +79100,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
79059
79100
  "alert-critical-resolved"
79060
79101
  ]
79061
79102
  },
79103
+ {
79104
+ id: "rule-health",
79105
+ family: "system",
79106
+ label: "Notification rules",
79107
+ kinds: ["rule-broken", "rule-repaired"]
79108
+ },
79062
79109
  {
79063
79110
  id: "alarm",
79064
79111
  family: "alarm",
@@ -79132,6 +79179,7 @@ var ALERT_EPISODE = [
79132
79179
  "alert-error",
79133
79180
  "alert-critical-resolved"
79134
79181
  ];
79182
+ var DEVICE_ATTENTION = ["device-attention-required", "device-attention-resolved"];
79135
79183
  var UPDATES = [
79136
79184
  "addon-update-available",
79137
79185
  "server-update-available",
@@ -79161,9 +79209,12 @@ var DEVICE_EVENTS = [
79161
79209
  "stream-online",
79162
79210
  "stream-offline",
79163
79211
  "detection-blind",
79212
+ "motion-noisy",
79213
+ "motion-calm",
79164
79214
  "recording-stalled",
79165
79215
  "recording-healthy",
79166
79216
  "recording-degraded",
79217
+ ...DEVICE_ATTENTION,
79167
79218
  ...ALARM
79168
79219
  ];
79169
79220
  /** Every kind a summary may speak about — the two summarisable families. */
@@ -79331,7 +79382,13 @@ var STATE_SUMMARY_VARS = [
79331
79382
  [...RULE_TEXT],
79332
79383
  [...RULE_TEXT],
79333
79384
  [...RULE_TEXT],
79334
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79385
+ [...RULE_TEXT],
79386
+ [...RULE_TEXT],
79387
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79388
+ [...RULE_TEXT],
79389
+ [...RULE_TEXT],
79390
+ [...RULE_TEXT],
79391
+ [...RULE_TEXT],
79335
79392
  [...RULE_TEXT],
79336
79393
  [...RULE_TEXT],
79337
79394
  [...RULE_TEXT],
@@ -79349,11 +79406,11 @@ var STATE_SUMMARY_VARS = [
79349
79406
  [...RULE_TEXT],
79350
79407
  [...RULE_TEXT],
79351
79408
  [...RULE_TEXT],
79409
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79352
79410
  ([...RULE_TEXT], [...ALERT_EPISODE]),
79411
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79353
79412
  ([...RULE_TEXT], [...ALERT_EPISODE]),
79354
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79355
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79356
- ([...RULE_TEXT], [...ALERT_EPISODE]),
79413
+ ([...RULE_TEXT], [...DEVICE_ATTENTION, ...ALERT_EPISODE]),
79357
79414
  [...RULE_TEXT],
79358
79415
  [...RULE_TEXT],
79359
79416
  [...RULE_TEXT],
@@ -79374,6 +79431,125 @@ var STATE_SUMMARY_VARS = [
79374
79431
  [...RULE_TEXT],
79375
79432
  ...STATE_SUMMARY_VARS
79376
79433
  ];
79434
+ new Map([
79435
+ {
79436
+ id: "device-connectivity",
79437
+ family: "device-events",
79438
+ label: "Device connectivity (offline / back online)",
79439
+ bad: ["device-offline"],
79440
+ recovery: ["device-online"],
79441
+ snapshotLabel: "Devices offline now",
79442
+ snapshotSection: "device-state"
79443
+ },
79444
+ {
79445
+ id: "device-switch",
79446
+ family: "device-events",
79447
+ label: "Device switched off / back on",
79448
+ bad: ["device-disabled"],
79449
+ recovery: ["device-enabled"],
79450
+ snapshotLabel: "Devices switched off now",
79451
+ snapshotSection: "device-state"
79452
+ },
79453
+ {
79454
+ id: "stream-connectivity",
79455
+ family: "device-events",
79456
+ label: "Stream connectivity (offline / back online)",
79457
+ bad: ["stream-offline"],
79458
+ recovery: ["stream-online"],
79459
+ snapshotLabel: "Streams offline now",
79460
+ snapshotSection: "streams"
79461
+ },
79462
+ {
79463
+ id: "battery",
79464
+ family: "device-events",
79465
+ label: "Battery (low / back to normal)",
79466
+ bad: ["device-battery-low"],
79467
+ recovery: ["device-battery-normal"],
79468
+ snapshotLabel: "Batteries low now",
79469
+ snapshotSection: "battery"
79470
+ },
79471
+ {
79472
+ id: "consumables",
79473
+ family: "device-events",
79474
+ label: "Consumables (low / replaced)",
79475
+ bad: ["device-consumable-low"],
79476
+ recovery: ["device-consumable-normal"],
79477
+ snapshotLabel: "Consumables low now",
79478
+ snapshotSection: "consumables"
79479
+ },
79480
+ {
79481
+ id: "tanks",
79482
+ family: "device-events",
79483
+ label: "Tanks (low or full / back to normal)",
79484
+ bad: ["device-tank-low", "device-tank-full"],
79485
+ recovery: ["device-tank-normal"],
79486
+ snapshotLabel: "Tanks low or full now",
79487
+ snapshotSection: "tanks"
79488
+ },
79489
+ {
79490
+ id: "motion-noise",
79491
+ family: "device-events",
79492
+ label: "Motion without a subject (constant / calm again)",
79493
+ bad: ["motion-noisy"],
79494
+ recovery: ["motion-calm"],
79495
+ snapshotLabel: "Cameras with constant motion now",
79496
+ snapshotSection: null
79497
+ },
79498
+ {
79499
+ id: "recording-health",
79500
+ family: "device-events",
79501
+ label: "Recording health (stalled or degraded / recording again)",
79502
+ bad: ["recording-stalled", "recording-degraded"],
79503
+ recovery: ["recording-healthy"],
79504
+ snapshotLabel: "Recordings stalled now",
79505
+ snapshotSection: null
79506
+ },
79507
+ {
79508
+ id: "device-attention",
79509
+ family: "device-events",
79510
+ label: "Device needs attention (raised / resolved)",
79511
+ bad: ["device-attention-required"],
79512
+ recovery: ["device-attention-resolved"],
79513
+ snapshotLabel: "Devices needing attention now",
79514
+ snapshotSection: null
79515
+ },
79516
+ {
79517
+ id: "node-connectivity",
79518
+ family: "system",
79519
+ label: "Node connectivity (offline / back online)",
79520
+ bad: ["node-offline"],
79521
+ recovery: ["node-online"],
79522
+ snapshotLabel: "Nodes offline now",
79523
+ snapshotSection: "nodes"
79524
+ },
79525
+ {
79526
+ id: "storage-space",
79527
+ family: "system",
79528
+ label: "Storage space (low or critical / ok again)",
79529
+ bad: ["storage-location-low", "storage-location-critical"],
79530
+ recovery: ["storage-location-ok"],
79531
+ snapshotLabel: "Storage locations low now",
79532
+ snapshotSection: null
79533
+ },
79534
+ {
79535
+ id: "critical-alerts",
79536
+ family: "system",
79537
+ label: "Critical alerts (raised / resolved)",
79538
+ bad: ["alert-critical"],
79539
+ recovery: ["alert-critical-resolved"],
79540
+ snapshotLabel: "Critical alerts open now",
79541
+ snapshotSection: null
79542
+ },
79543
+ {
79544
+ id: "rule-health",
79545
+ family: "system",
79546
+ label: "Rule health (broken / repaired)",
79547
+ bad: ["rule-broken"],
79548
+ recovery: ["rule-repaired"],
79549
+ snapshotLabel: "Notification rules broken now",
79550
+ snapshotSection: null
79551
+ }
79552
+ ].flatMap((spec) => [...spec.bad, ...spec.recovery].map((kind) => [kind, spec])));
79377
79553
  new Set([
79378
79554
  "disarmed",
79379
79555
  "armed_home",
@@ -132823,8 +132999,6 @@ function buildConnectionFormSchema() {
132823
132999
  id: "credentials",
132824
133000
  title: "Dreamehome account",
132825
133001
  titleKey: "provider-dreame:connection.s_credentials.title",
132826
- description: "Sign in with your Dreamehome (Dreame app) account credentials.",
132827
- descriptionKey: "provider-dreame:connection.s_credentials.description",
132828
133002
  columns: 1,
132829
133003
  fields: [{
132830
133004
  type: "text",
@@ -132846,8 +133020,6 @@ function buildConnectionFormSchema() {
132846
133020
  id: "region",
132847
133021
  title: "Region",
132848
133022
  titleKey: "provider-dreame:connection.s_region.title",
132849
- description: "Select the Dreame cloud region your account is registered in.",
132850
- descriptionKey: "provider-dreame:connection.s_region.description",
132851
133023
  columns: 1,
132852
133024
  fields: [{
132853
133025
  type: "select",
@@ -135266,6 +135438,81 @@ function rfcUrl(host, port) {
135266
135438
  return `tcp://${host}:${String(port)}`;
135267
135439
  }
135268
135440
  //#endregion
135441
+ //#region src/devices/dreame-pin-attention.ts
135442
+ var REFUSED_CODE_MINUS_ONE = /startMonitor refused \(code -1\)/;
135443
+ /** The refusal an error carries, or `null` when it is some other failure. */
135444
+ function classifyPinRefusal(err, accessCodeSet) {
135445
+ const message = err instanceof Error ? err.message : String(err);
135446
+ if (!REFUSED_CODE_MINUS_ONE.test(message)) return null;
135447
+ return accessCodeSet ? "rejected" : "missing";
135448
+ }
135449
+ function pinAlertId(deviceId) {
135450
+ return `dreame.pin-required:${String(deviceId)}`;
135451
+ }
135452
+ function buildPinAlert(deviceId, deviceName, reason) {
135453
+ const now = Date.now();
135454
+ return {
135455
+ id: pinAlertId(deviceId),
135456
+ category: "device.credentials-required",
135457
+ severity: "critical",
135458
+ title: reason === "missing" ? "Camera PIN required" : "Camera PIN rejected",
135459
+ message: reason === "missing" ? `${deviceName} needs its camera PIN before its video can open. Enter it in the device settings.` : `${deviceName} refused the camera PIN that is set. Enter the correct PIN in the device settings.`,
135460
+ action: {
135461
+ label: "Enter PIN",
135462
+ href: `/devices/${String(deviceId)}`,
135463
+ navigation: "app"
135464
+ },
135465
+ status: "active",
135466
+ read: false,
135467
+ createdAt: now,
135468
+ updatedAt: now,
135469
+ source: {
135470
+ type: "device",
135471
+ id: String(deviceId)
135472
+ },
135473
+ metadata: { episodeKey: reason }
135474
+ };
135475
+ }
135476
+ /**
135477
+ * One device's PIN episode. It raises the alert ONCE per episode, and while the
135478
+ * episode is open it tells the wake path to stand down: a refusal that needs the
135479
+ * operator is terminal until the PIN setting changes, so the broker's 30 s
135480
+ * re-dial must not re-open the monitor and fail again.
135481
+ */
135482
+ var PinAttention = class {
135483
+ ports;
135484
+ open = null;
135485
+ constructor(ports) {
135486
+ this.ports = ports;
135487
+ }
135488
+ /** True while the PIN is known to be unusable — the wake must not try. */
135489
+ get blocked() {
135490
+ return this.open !== null;
135491
+ }
135492
+ /** A wake failed. Raises the alert on the first refusal of an episode. */
135493
+ async refused(reason) {
135494
+ if (this.open !== null) return "already-open";
135495
+ this.open = reason;
135496
+ await this.ports.emit(buildPinAlert(this.ports.deviceId, this.ports.deviceName(), reason));
135497
+ return "raised";
135498
+ }
135499
+ /** A monitor opened: the PIN works, the episode is over. */
135500
+ async succeeded() {
135501
+ return this.settingChanged();
135502
+ }
135503
+ /**
135504
+ * The PIN setting changed: the episode is no longer known to be terminal, so
135505
+ * the next wake tries again. The alert is resolved here — a still-wrong PIN
135506
+ * raises a fresh episode on that wake.
135507
+ */
135508
+ async settingChanged() {
135509
+ if (this.open === null) return false;
135510
+ this.open = null;
135511
+ await this.ports.resolve(pinAlertId(this.ports.deviceId));
135512
+ return true;
135513
+ }
135514
+ };
135515
+ //#endregion
135269
135516
  //#region src/devices/dreame-navigation-actions.ts
135270
135517
  /** Forward throttle magnitude for a full-forward nudge (device speed units). */
135271
135518
  var FORWARD_SPEED = 200;
@@ -181457,15 +181704,13 @@ var CAMERA_SETTINGS_SCHEMA = { sections: [{
181457
181704
  id: "camera-backend",
181458
181705
  title: "Video backend",
181459
181706
  titleKey: "provider-dreame:camera_settings.s_camera-backend.title",
181460
- description: "Which cloud video service this robot streams through. The two are not interchangeable: only the Aliyun path is implemented here — a robot on Tencent can be seen in the Dreame app but not by CamStack, because Tencent offers it a proprietary P2P plane we do not speak. Changing this asks the ROBOT to switch and waits until its SDK reports ready; the robot is the authority on where it actually is.",
181707
+ description: "Only the Aliyun path is implemented; a robot on Tencent cannot be viewed by CamStack. Changing this asks the robot to switch.",
181461
181708
  descriptionKey: "provider-dreame:camera_settings.s_camera-backend.description",
181462
181709
  fields: [{
181463
181710
  type: "select",
181464
181711
  key: "videoVendor",
181465
181712
  label: "Backend",
181466
181713
  labelKey: "provider-dreame:camera_settings.s_camera-backend.videoVendor.label",
181467
- description: "Aliyun streams through a plain relay and works today. Tencent is offered so the other path can be tested once it exists.",
181468
- descriptionKey: "provider-dreame:camera_settings.s_camera-backend.videoVendor.description",
181469
181714
  options: [{
181470
181715
  value: "ali",
181471
181716
  label: "Aliyun LinkVisual (supported)",
@@ -181651,6 +181896,10 @@ var DreameCameraDevice = class extends DreameChildDevice {
181651
181896
  const data = event.data;
181652
181897
  if ((typeof data === "object" && data !== null && "deviceId" in data ? data.deviceId : null) !== this.id) return;
181653
181898
  if (this.session === null) {
181899
+ if (this.pinAttention.blocked) {
181900
+ this.ctx.logger.debug("robot camera: wake skipped — the camera PIN needs the operator", { tags: { deviceId: this.id } });
181901
+ return;
181902
+ }
181654
181903
  this.openStream().catch(() => void 0);
181655
181904
  return;
181656
181905
  }
@@ -181737,8 +181986,65 @@ var DreameCameraDevice = class extends DreameChildDevice {
181737
181986
  durationMs: Date.now() - startedAt
181738
181987
  };
181739
181988
  }
181989
+ /**
181990
+ * The PIN episode (D911): a monitor refused for want of the camera PIN needs
181991
+ * the operator, so it raises ONE critical alert and the wake stands down
181992
+ * until the PIN setting changes.
181993
+ */
181994
+ pinAttention = new PinAttention({
181995
+ deviceId: this.id,
181996
+ deviceName: () => this.name,
181997
+ emit: async (alert) => {
181998
+ const emit = this.ctx.api.alerts?.emit;
181999
+ if (emit === void 0) {
182000
+ this.ctx.logger.warn("robot camera: PIN alert skipped — alerts capability missing", { tags: { deviceId: this.id } });
182001
+ return;
182002
+ }
182003
+ try {
182004
+ await emit.mutate(alert);
182005
+ this.ctx.logger.warn("robot camera: PIN alert raised — the wake stops until the PIN changes", {
182006
+ tags: { deviceId: this.id },
182007
+ meta: {
182008
+ alertId: alert.id,
182009
+ episodeKey: alert.metadata?.["episodeKey"]
182010
+ }
182011
+ });
182012
+ } catch (err) {
182013
+ this.ctx.logger.warn("robot camera: PIN alert emit failed", {
182014
+ tags: { deviceId: this.id },
182015
+ meta: { error: errMsg(err) }
182016
+ });
182017
+ }
182018
+ },
182019
+ resolve: async (alertId) => {
182020
+ const update = this.ctx.api.alerts?.update;
182021
+ if (update === void 0) return;
182022
+ try {
182023
+ await update.mutate({
182024
+ alertId,
182025
+ patch: {
182026
+ status: "completed",
182027
+ read: true
182028
+ }
182029
+ });
182030
+ this.ctx.logger.info("robot camera: PIN alert resolved", {
182031
+ tags: { deviceId: this.id },
182032
+ meta: { alertId }
182033
+ });
182034
+ } catch (err) {
182035
+ this.ctx.logger.warn("robot camera: PIN alert resolve failed", {
182036
+ tags: { deviceId: this.id },
182037
+ meta: {
182038
+ alertId,
182039
+ error: errMsg(err)
182040
+ }
182041
+ });
182042
+ }
182043
+ }
182044
+ });
181740
182045
  openStream() {
181741
182046
  if (this.opening !== null) return this.opening;
182047
+ if (this.pinAttention.blocked) return Promise.reject(/* @__PURE__ */ new Error("camera PIN unusable — waiting for the operator"));
181742
182048
  const run = (async () => {
181743
182049
  const startedAt = Date.now();
181744
182050
  try {
@@ -181770,6 +182076,7 @@ var DreameCameraDevice = class extends DreameChildDevice {
181770
182076
  this.publishBattery();
181771
182077
  this.publishCatalog();
181772
182078
  await this.publishRelay(this.session);
182079
+ this.pinAttention.succeeded();
181773
182080
  this.ctx.logger.info("robot camera: monitor open, RTP server published", {
181774
182081
  tags: { deviceId: this.id },
181775
182082
  meta: {
@@ -181786,6 +182093,9 @@ var DreameCameraDevice = class extends DreameChildDevice {
181786
182093
  error: errMsg(err)
181787
182094
  }
181788
182095
  });
182096
+ const code = this.config.get("navigationAccessCode");
182097
+ const refusal = classifyPinRefusal(err, code !== void 0 && code !== "");
182098
+ if (refusal !== null) await this.pinAttention.refused(refusal);
181789
182099
  throw err;
181790
182100
  }
181791
182101
  })();
@@ -182003,6 +182313,7 @@ var DreameCameraDevice = class extends DreameChildDevice {
182003
182313
  }
182004
182314
  }
182005
182315
  if (!("navigationAccessCode" in patch)) return;
182316
+ await this.pinAttention.settingChanged();
182006
182317
  await this.closeStream("removed");
182007
182318
  this.controller = null;
182008
182319
  this.controllerHandle = null;
@@ -183822,8 +184133,6 @@ var DreameVacuumMapDevice = class extends DreameChildDevice {
183822
184133
  id: "map-style",
183823
184134
  title: "Map Style",
183824
184135
  titleKey: "provider-dreame:schema.s_map-style.title",
183825
- description: "Colour scheme and overlays for the rendered cleaning map.",
183826
- descriptionKey: "provider-dreame:schema.s_map-style.description",
183827
184136
  columns: 2,
183828
184137
  fields: [
183829
184138
  {