@camstack/addon-admin-ui 1.2.525 → 1.2.527

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.
Files changed (153) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/assets/{AddonAssetIcon-DwYI7EBR.js → AddonAssetIcon-BZ0hQcho.js} +1 -1
  3. package/dist/assets/Addons-BAqAfY0r.js +5 -0
  4. package/dist/assets/{AdminPage-BVK_kyJp.js → AdminPage-7avIuDBh.js} +1 -1
  5. package/dist/assets/{AdminSectionedPage-Bu-9skyU.js → AdminSectionedPage-Bd2owMGm.js} +1 -1
  6. package/dist/assets/AiProfiles-aCDtxD2T.js +3 -0
  7. package/dist/assets/BrokerEditorDialog-Cp7neyJC.js +1 -0
  8. package/dist/assets/Build-SObpGxas.js +40 -0
  9. package/dist/assets/{Calendars-NM9x_lTF.js → Calendars-B2TORAnq.js} +1 -1
  10. package/dist/assets/Cluster-CD4DYic_.js +1 -0
  11. package/dist/assets/{ComposedDeviceEditDialog-3vtEogq6.js → ComposedDeviceEditDialog-BiIiBFG-.js} +1 -1
  12. package/dist/assets/CompositionBlockEditor-BXYXXUEU.js +2 -0
  13. package/dist/assets/{CompositionEditorPage-D1auHftj.js → CompositionEditorPage-Cm4C-7Z-.js} +1 -1
  14. package/dist/assets/Data-CRPVPhFT.js +2 -0
  15. package/dist/assets/DeviceDetail-DNsWQGiy.js +2 -0
  16. package/dist/assets/{DeviceDoActionFields-CLlHGMoV.js → DeviceDoActionFields-BEhy6_KW.js} +1 -1
  17. package/dist/assets/{Devices-mrHggHWa.js → Devices-CS5P4d9n.js} +1 -1
  18. package/dist/assets/{EmbedPlayerPage-HIM7BXTf.js → EmbedPlayerPage-B7MIw7cv.js} +1 -1
  19. package/dist/assets/History-BbOfWf-p.js +1 -0
  20. package/dist/assets/Identity-Df140-fX.js +1 -0
  21. package/dist/assets/IntegrationDetail-B7sqZ94w.js +1 -0
  22. package/dist/assets/{IntegrationHub-BLWPwX2F.js → IntegrationHub-DndCuqMe.js} +1 -1
  23. package/dist/assets/Integrations-BJh_WZAT.js +1 -0
  24. package/dist/assets/Integrations-DRCpstaG.js +1 -0
  25. package/dist/assets/Library-Cx17_SRL.js +1 -0
  26. package/dist/assets/Locations-Dt9LOu5v.js +3 -0
  27. package/dist/assets/{Logs-BNUawZoE.js → Logs-CrxQn3Uq.js} +1 -1
  28. package/dist/assets/{MaskShapeCanvas-BxH91O-B.js → MaskShapeCanvas-tJyuoOTy.js} +1 -1
  29. package/dist/assets/{MotionZonesSettings-DZKfaecb.js → MotionZonesSettings-DaGyceJa.js} +1 -1
  30. package/dist/assets/{MyAccess-BxcRYFsj.js → MyAccess-Bp2dsON4.js} +1 -1
  31. package/dist/assets/Network-DftlNAIH.js +1 -0
  32. package/dist/assets/{NodeAddonsSettingsPanel-BcI9AigA.js → NodeAddonsSettingsPanel-JIXh-CLX.js} +1 -1
  33. package/dist/assets/Notifications-2tj0VaIS.js +1 -0
  34. package/dist/assets/Pipeline-CqZqLWQZ.js +1 -0
  35. package/dist/assets/{PrivacyMaskSettings-C1-GOEuN.js → PrivacyMaskSettings-zzQcpeb0.js} +1 -1
  36. package/dist/assets/Recordings-DoAJqZqc.js +1 -0
  37. package/dist/assets/{RemoteGrid-BZbW8PeH.js → RemoteGrid-Bz_u9tq6.js} +1 -1
  38. package/dist/assets/ScanNetworkModal-DL-mWRtG.js +1 -0
  39. package/dist/assets/{SceneMonitorEditor-C71RV5fL.js → SceneMonitorEditor-B0ICySEi.js} +1 -1
  40. package/dist/assets/SessionsList-QvqpXCDN.js +2 -0
  41. package/dist/assets/{Showroom-Pcaz2yKE.js → Showroom-B6CDELO1.js} +1 -1
  42. package/dist/assets/System-C0KUcOaJ.js +1 -0
  43. package/dist/assets/{Templates-ABoqOu9o.js → Templates-uAAxbV9V.js} +1 -1
  44. package/dist/assets/{Terminal-uL0eTf8i.js → Terminal-D1fhPY66.js} +5 -5
  45. package/dist/assets/{Viewer-BzqHL8lA.js → Viewer-BWN8qfBa.js} +1 -1
  46. package/dist/assets/{_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-Dbqn5c5X.js → _virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-sshFQYnL.js} +1 -1
  47. package/dist/assets/{addon-icons-context-CzyObK2t.js → addon-icons-context-BHF-RdY-.js} +1 -1
  48. package/dist/assets/{archive-CDziWnvV.js → archive-2V-x_y9T.js} +1 -1
  49. package/dist/assets/{assignment-candidates-a6kHcvux.js → assignment-candidates-BpC51VXR.js} +1 -1
  50. package/dist/assets/{authed-media-BcO83va1.js → authed-media-CdbbdgDZ.js} +1 -1
  51. package/dist/assets/{bell-off-O7zrX-Mc.js → bell-off-DDQ8FbF1.js} +1 -1
  52. package/dist/assets/{board-model-sARab1AV.js → board-model-D3wYwuI4.js} +1 -1
  53. package/dist/assets/{boxes-svitF8K8.js → boxes-BwEV9Act.js} +1 -1
  54. package/dist/assets/circle-slash-BjzSCz4-.js +1 -0
  55. package/dist/assets/client-r4i0EpF6.js +711 -0
  56. package/dist/assets/{cloud-upload-B_uHg9Qk.js → cloud-upload-BII8r4p9.js} +1 -1
  57. package/dist/assets/crop-D9cpl5_W.js +1 -0
  58. package/dist/assets/{device-integration-icon-Bi5qhaK0.js → device-integration-icon-Cf2JTj5q.js} +1 -1
  59. package/dist/assets/device-picker-model-B_Zjyqyk.js +1 -0
  60. package/dist/assets/{dist-C5kry-nn.js → dist-B0LFF94V.js} +1 -1
  61. package/dist/assets/dist-BOdjk8IV.js +711 -0
  62. package/dist/assets/{file-up-BnEcmi9q.js → file-up-Bizrs9Ca.js} +1 -1
  63. package/dist/assets/{format-DGQA45Ye.js → format-Bqo-j708.js} +1 -1
  64. package/dist/assets/{grip-vertical-DcUn4qSJ.js → grip-vertical-C-qhB3oM.js} +1 -1
  65. package/dist/assets/{hard-drive-download-CkT0au9p.js → hard-drive-download-Dym9jihx.js} +1 -1
  66. package/dist/assets/{hostInit-Dc7Z_glr.js → hostInit-bmlgmSTk.js} +1 -1
  67. package/dist/assets/i18n-BE1bPZOs.js +1 -0
  68. package/dist/assets/icons-BZ8D05w2.js +1 -0
  69. package/dist/assets/index-DimGtYV1.css +2 -0
  70. package/dist/assets/index-Ohn-2iW_.js +4 -0
  71. package/dist/assets/{key-vQtBGf1l.js → key-DddJ6bil.js} +1 -1
  72. package/dist/assets/{key-round-CFwVAamk.js → key-round-CbCqycf3.js} +1 -1
  73. package/dist/assets/{layers-C-B3EC65.js → layers-C-ZqYk3U.js} +1 -1
  74. package/dist/assets/{linked-devices-model-DUYPy_9l.js → linked-devices-model-BPUD1khJ.js} +1 -1
  75. package/dist/assets/{list-ordered-C_tb_5Gb.js → list-ordered-Cxznlat2.js} +1 -1
  76. package/dist/assets/{location-owned-usage-caVqSKq8.js → location-owned-usage-DOmOvdiG.js} +1 -1
  77. package/dist/assets/{log-in-D1R8-3xg.js → log-in-9Uirjetj.js} +1 -1
  78. package/dist/assets/{memory-stick-DTKZNNzT.js → memory-stick-CLL3KiND.js} +1 -1
  79. package/dist/assets/{message-square-plus-_8_Z4yYf.js → message-square-plus-C2nVnMG1.js} +1 -1
  80. package/dist/assets/{mf-entry-bootstrap-0-c9e86d7a.js → mf-entry-bootstrap-0-edf9b20a.js} +2 -2
  81. package/dist/assets/{mic-off-S8b79RhQ.js → mic-off-CcHQBwDt.js} +1 -1
  82. package/dist/assets/{monitor-off-DktfIub5.js → monitor-off-DUE1gt1S.js} +1 -1
  83. package/dist/assets/{monitor-smartphone-DiJ0euAC.js → monitor-smartphone-CwOoXB0L.js} +1 -1
  84. package/dist/assets/mount-B_Zjyqyk.js +1 -0
  85. package/dist/assets/{player-overlays-DyWZd37T.js → player-overlays-Cw_gjG75.js} +1 -1
  86. package/dist/assets/{recycle-uEKrRpl_.js → recycle-hW2Os1lc.js} +1 -1
  87. package/dist/assets/remoteEntry-CHuAXwj-.js +1 -0
  88. package/dist/assets/{ruler-Cf92fzAd.js → ruler-SYjqj9n_.js} +1 -1
  89. package/dist/assets/{scan-eye-_8TdtNzb.js → scan-eye-BZlKp5Ev.js} +1 -1
  90. package/dist/assets/{scissors-B9AK1nLy.js → scissors-Prn6Kbb7.js} +1 -1
  91. package/dist/assets/{search-x-CVEVpLBV.js → search-x-CCA8373c.js} +1 -1
  92. package/dist/assets/{send-CBO9g-_U.js → send-C5rCH5_0.js} +1 -1
  93. package/dist/assets/{settings-2-BSjF3kN4.js → settings-2-wlw_-rxi.js} +1 -1
  94. package/dist/assets/{shapes-BsX-8xUL.js → shapes-Kj0bK1D8.js} +1 -1
  95. package/dist/assets/{sigma-veVH6AL2.js → sigma-NPxR3lj1.js} +1 -1
  96. package/dist/assets/speaker-CIKbprnA.js +1 -0
  97. package/dist/assets/src-nnHXOip7.js +67 -0
  98. package/dist/assets/{step-inspector-model-BW-W62sj.js → step-inspector-model-BKKc_lkM.js} +1 -1
  99. package/dist/assets/{table-2-DfmXeNvT.js → table-2-Sw__YAPa.js} +1 -1
  100. package/dist/assets/{timer-reset-D9i5HPty.js → timer-reset-BhPZYoCT.js} +1 -1
  101. package/dist/assets/{unlink-Dsy3yrQn.js → unlink-DNy8sNAV.js} +1 -1
  102. package/dist/assets/{usb-taNjM8Y4.js → usb-CyQsTtlO.js} +1 -1
  103. package/dist/assets/{use-device-bound-caps-Dy9z5ssE.js → use-device-bound-caps-BQo_OD1O.js} +1 -1
  104. package/dist/assets/{use-scenes-feed-BMJYnt2p.js → use-scenes-feed-BCDAv1nN.js} +1 -1
  105. package/dist/assets/{use-stationary-objects-DKZYbXwz.js → use-stationary-objects-BnIfyZw5.js} +1 -1
  106. package/dist/assets/useEventInvalidation-B_Zjyqyk.js +1 -0
  107. package/dist/assets/{useNodeUrlSelection-BbrmxDID.js → useNodeUrlSelection-CR_BnlEg.js} +1 -1
  108. package/dist/assets/user-round-BY4YxbLZ.js +1 -0
  109. package/dist/assets/{user-x-LhbJK13A.js → user-x-CUkBojXx.js} +1 -1
  110. package/dist/assets/variable-CpgCMY_Q.js +1 -0
  111. package/dist/assets/{video-BiqduAcp.js → video-DXegVNA2.js} +1 -1
  112. package/dist/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-_PsSzHuE.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__admin_ui_host__remoteEntry-_hash_-2LL0aB_0.js} +2 -2
  113. package/dist/index.html +10 -10
  114. package/dist/server/addon.js +928 -123
  115. package/package.json +1 -1
  116. package/dist/assets/Addons-D5S6sTN7.js +0 -5
  117. package/dist/assets/AiProfiles-BJhboAC6.js +0 -3
  118. package/dist/assets/BrokerEditorDialog-DsKGCg9y.js +0 -1
  119. package/dist/assets/Build-5sZQPjWb.js +0 -40
  120. package/dist/assets/Cluster-C104ZPJk.js +0 -1
  121. package/dist/assets/CompositionBlockEditor-DYg72AlP.js +0 -2
  122. package/dist/assets/Data-D4jDqmcs.js +0 -2
  123. package/dist/assets/DeviceDetail-8sreVV-i.js +0 -2
  124. package/dist/assets/History-9T3cw528.js +0 -1
  125. package/dist/assets/Identity-vSPkTQS9.js +0 -1
  126. package/dist/assets/IntegrationDetail-DwipvGcz.js +0 -1
  127. package/dist/assets/Integrations-9y5oqDxP.js +0 -1
  128. package/dist/assets/Integrations-ZJ4_tnkn.js +0 -1
  129. package/dist/assets/Library-Cdfp5QPG.js +0 -1
  130. package/dist/assets/Locations-BNiiWGXW.js +0 -3
  131. package/dist/assets/Network-7hIzFAXX.js +0 -1
  132. package/dist/assets/Notifications-dUvsPqSL.js +0 -1
  133. package/dist/assets/Pipeline-Bu7x636X.js +0 -1
  134. package/dist/assets/Recordings-BPIJtEGz.js +0 -1
  135. package/dist/assets/ScanNetworkModal-BQ5OlzZX.js +0 -1
  136. package/dist/assets/SessionsList-BF7BLjjI.js +0 -2
  137. package/dist/assets/System-BVsN5xi0.js +0 -1
  138. package/dist/assets/circle-slash-CORBdt2Z.js +0 -1
  139. package/dist/assets/client-DdGu3rlb.js +0 -674
  140. package/dist/assets/crop-AXhcNsdd.js +0 -1
  141. package/dist/assets/device-picker-model-DhYiPHjb.js +0 -1
  142. package/dist/assets/dist-CE4Thp2G.js +0 -674
  143. package/dist/assets/i18n-C-H4BlYV.js +0 -1
  144. package/dist/assets/icons-C7_2EHkF.js +0 -1
  145. package/dist/assets/index-CyYfrxyQ.js +0 -4
  146. package/dist/assets/index-DjwiZ7qT.css +0 -2
  147. package/dist/assets/mount-DhYiPHjb.js +0 -1
  148. package/dist/assets/remoteEntry-C0WZ_Eg2.js +0 -1
  149. package/dist/assets/speaker-JfimpXfG.js +0 -1
  150. package/dist/assets/src-DF4f-P3R.js +0 -67
  151. package/dist/assets/useEventInvalidation-DhYiPHjb.js +0 -1
  152. package/dist/assets/user-round-BioYT2Ju.js +0 -1
  153. package/dist/assets/variable-BnqMYwWo.js +0 -1
@@ -5456,7 +5456,7 @@ var ZodIssueCode = {
5456
5456
  var ZodFirstPartyTypeKind;
5457
5457
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5458
5458
  //#endregion
5459
- //#region ../types/dist/sleep-63gM_1JR.mjs
5459
+ //#region ../types/dist/sleep-CeyhrcOk.mjs
5460
5460
  /**
5461
5461
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5462
5462
  * window to float samples (D455).
@@ -7523,6 +7523,7 @@ var BaseAddon = class {
7523
7523
  * a warning and falls back rather than crashing boot.
7524
7524
  */
7525
7525
  state(key, schema, fallback) {
7526
+ this._declaredStateKeys.add(key);
7526
7527
  return createDurableState({
7527
7528
  key,
7528
7529
  schema,
@@ -7538,6 +7539,96 @@ var BaseAddon = class {
7538
7539
  } })
7539
7540
  });
7540
7541
  }
7542
+ /** Durable-state keys this process registered through {@link state}. */
7543
+ _declaredStateKeys = /* @__PURE__ */ new Set();
7544
+ /**
7545
+ * Stale-data audit: every global-settings key this addon still declares —
7546
+ * constructor defaults, settings-schema fields (layout containers included),
7547
+ * durable-state keys and the root of each `secretStatePaths` entry. Answered
7548
+ * here, in the addon's own process, so the hub keeps no copy of it.
7549
+ */
7550
+ async getDeclaredSettingsKeys() {
7551
+ return { keys: [...this.declaredKeySet()].toSorted() };
7552
+ }
7553
+ declaredKeySet() {
7554
+ const keys = new Set(Object.keys(this.defaults));
7555
+ const schema = this.globalSettingsSchema();
7556
+ if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
7557
+ for (const key of this._declaredStateKeys) keys.add(key);
7558
+ for (const path of this.secretStatePaths) {
7559
+ const root = path.split(/[.[]/, 1)[0];
7560
+ if (root) keys.add(root);
7561
+ }
7562
+ return keys;
7563
+ }
7564
+ /**
7565
+ * Stale-data audit: remove stored global-settings keys the addon no longer
7566
+ * declares, and clear the keychain values their markers held — except a value
7567
+ * a surviving key still points at. A declared or absent key is refused.
7568
+ */
7569
+ async unsetStoredSettingsKeys(keys) {
7570
+ const settings = this._ctx?.settings;
7571
+ const raw = this.rawSettings();
7572
+ if (!settings || !raw) return {
7573
+ removed: [],
7574
+ refused: keys.map((key) => ({
7575
+ key,
7576
+ reason: "no settings store"
7577
+ })),
7578
+ secretsCleared: 0
7579
+ };
7580
+ let stored;
7581
+ try {
7582
+ stored = await raw.readAddonStore();
7583
+ } catch (error) {
7584
+ const reason = `store unreadable: ${error instanceof Error ? error.message : "unknown"}`;
7585
+ return {
7586
+ removed: [],
7587
+ refused: keys.map((key) => ({
7588
+ key,
7589
+ reason
7590
+ })),
7591
+ secretsCleared: 0
7592
+ };
7593
+ }
7594
+ const declared = this.declaredKeySet();
7595
+ const removed = [];
7596
+ const refused = [];
7597
+ for (const key of keys) {
7598
+ const base = key.split("@", 1)[0] ?? key;
7599
+ if (declared.has(base)) refused.push({
7600
+ key,
7601
+ reason: "still declared by the addon"
7602
+ });
7603
+ else if (!(key in stored)) refused.push({
7604
+ key,
7605
+ reason: "not stored"
7606
+ });
7607
+ else removed.push(key);
7608
+ }
7609
+ if (removed.length === 0) return {
7610
+ removed,
7611
+ refused,
7612
+ secretsCleared: 0
7613
+ };
7614
+ const kept = /* @__PURE__ */ new Set();
7615
+ const held = /* @__PURE__ */ new Set();
7616
+ for (const [key, value] of Object.entries(stored)) {
7617
+ const target = removed.includes(key) ? held : kept;
7618
+ for (const ref of collectSecretMarkerRefs(value)) target.add(ref);
7619
+ }
7620
+ const refsToClear = [...held].filter((ref) => !kept.has(ref));
7621
+ const patch = {};
7622
+ for (const key of removed) patch[key] = void 0;
7623
+ await settings.writeAddonStore(patch);
7624
+ const secretsCleared = refsToClear.length > 0 && this._ctx?.secrets ? await this._ctx.secrets.delete(refsToClear) : 0;
7625
+ await this.resolveConfig();
7626
+ return {
7627
+ removed,
7628
+ refused,
7629
+ secretsCleared
7630
+ };
7631
+ }
7541
7632
  /** Per-device variant of {@link state}, backed by the per-device store. */
7542
7633
  deviceState(deviceId, key, schema, fallback) {
7543
7634
  return createDurableState({
@@ -7808,6 +7899,21 @@ function dropPerNodeFields(fields) {
7808
7899
  }
7809
7900
  return kept;
7810
7901
  }
7902
+ function collectFieldKeys(fields) {
7903
+ const collected = [];
7904
+ for (const field of fields) {
7905
+ if (field.type === "group") {
7906
+ collected.push(...collectFieldKeys(field.fields));
7907
+ continue;
7908
+ }
7909
+ if (field.type === "sub-tabs") {
7910
+ for (const tab of field.tabs) collected.push(...collectFieldKeys(tab.fields));
7911
+ continue;
7912
+ }
7913
+ if ("key" in field && typeof field.key === "string") collected.push(field.key);
7914
+ }
7915
+ return collected;
7916
+ }
7811
7917
  function collectPerNodeFieldKeys(fields) {
7812
7918
  const collected = [];
7813
7919
  for (const field of fields) {
@@ -10583,7 +10689,7 @@ object({
10583
10689
  params: record(string(), union([string(), number()])).optional()
10584
10690
  });
10585
10691
  //#endregion
10586
- //#region ../types/dist/err-msg-DWXWc0Di.mjs
10692
+ //#region ../types/dist/err-msg-BD5buxvT.mjs
10587
10693
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
10588
10694
  DeviceType["Camera"] = "camera";
10589
10695
  DeviceType["Hub"] = "hub";
@@ -10689,6 +10795,12 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
10689
10795
  DeviceType["Washer"] = "washer";
10690
10796
  /** Tumble dryer. Same plug-built running surface as {@link Washer}. */
10691
10797
  DeviceType["Dryer"] = "dryer";
10798
+ /** The physical bridge box of a vendor ecosystem (Velux KLF-200, and later
10799
+ * other radio-to-IP bridges). It carries the box's own facts (firmware,
10800
+ * address, connection state) and the actions the box really supports
10801
+ * (`reboot`, `device-admin-link`). Products are NOT its children — they are
10802
+ * adopted beside it — and its connection settings stay on the integration. */
10803
+ DeviceType["Gateway"] = "gateway";
10692
10804
  return DeviceType;
10693
10805
  }({});
10694
10806
  var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
@@ -10939,7 +11051,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
10939
11051
  return DeviceRole;
10940
11052
  }({});
10941
11053
  //#endregion
10942
- //#region ../types/dist/composition-kSuOifuG.mjs
11054
+ //#region ../types/dist/composition-C7WdThmP.mjs
10943
11055
  /**
10944
11056
  * The ffmpeg build every CamStack node downloads, per platform and arch.
10945
11057
  *
@@ -15928,8 +16040,172 @@ object({
15928
16040
  quantity: ReadingQuantitySchema,
15929
16041
  unit: string().min(1)
15930
16042
  });
15931
- /** How a leaf compares a device field to a value. Derived from the field's
15932
- * `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
16043
+ new Set([
16044
+ {
16045
+ id: "person",
16046
+ name: "Person"
16047
+ },
16048
+ {
16049
+ id: "vehicle",
16050
+ name: "Vehicle"
16051
+ },
16052
+ {
16053
+ id: "animal",
16054
+ name: "Animal"
16055
+ },
16056
+ {
16057
+ id: "package",
16058
+ name: "Package"
16059
+ }
16060
+ ].map((l) => l.id));
16061
+ var COCO_TO_MACRO = {
16062
+ mapping: {
16063
+ person: "person",
16064
+ bicycle: "vehicle",
16065
+ car: "vehicle",
16066
+ motorcycle: "vehicle",
16067
+ airplane: "vehicle",
16068
+ bus: "vehicle",
16069
+ train: "vehicle",
16070
+ truck: "vehicle",
16071
+ boat: "vehicle",
16072
+ bird: "animal",
16073
+ cat: "animal",
16074
+ dog: "animal",
16075
+ horse: "animal",
16076
+ sheep: "animal",
16077
+ cow: "animal",
16078
+ elephant: "animal",
16079
+ bear: "animal",
16080
+ zebra: "animal",
16081
+ giraffe: "animal",
16082
+ suitcase: "package",
16083
+ backpack: "package",
16084
+ handbag: "package"
16085
+ },
16086
+ preserveOriginal: false
16087
+ };
16088
+ /**
16089
+ * WHAT a detection-shaped leaf is about: one MACRO class (person, vehicle,
16090
+ * animal, package) plus an OPTIONAL label that narrows it.
16091
+ *
16092
+ * The taxonomy is `COCO_TO_MACRO` (and `CLASS_MAP_MACRO_TARGETS`) — never a
16093
+ * second list. The label that fits depends on the macro class:
16094
+ *
16095
+ * - `animal` → a sub-class (`dog`, `cat`, …);
16096
+ * - `person` → a known face (an identity of the `face-gallery`);
16097
+ * - `vehicle` → a plate (its text) or a sub-class (`car`, `truck`, …);
16098
+ * - `package` → a sub-class.
16099
+ *
16100
+ * `other` exists only so a class the taxonomy does not know (a custom model's
16101
+ * `forklift`) survives the conversion of a stored recipe without widening.
16102
+ *
16103
+ * A label is decided LATE (D683): face recognition and LPR land after the
16104
+ * detection starts. The track-lifecycle `update` event carries the newly
16105
+ * resolved `label` / `plateText`, which is why a leaf with a label fires when
16106
+ * the subject GETS it — not at the first detection.
16107
+ */
16108
+ var SubClassLabelSchema = object({
16109
+ kind: literal("sub-class"),
16110
+ className: string().min(1)
16111
+ });
16112
+ var IdentityLabelSchema = object({
16113
+ kind: literal("identity"),
16114
+ /** The `face-gallery` identity id (stable across a rename). */
16115
+ identityId: string().min(1),
16116
+ /** The identity's name — what a track-lifecycle event carries as `label`. */
16117
+ name: string().min(1)
16118
+ });
16119
+ var PlateLabelSchema = object({
16120
+ kind: literal("plate"),
16121
+ text: string().min(1)
16122
+ });
16123
+ function macroOf$1(className) {
16124
+ return COCO_TO_MACRO.mapping[className.toLowerCase()] ?? className.toLowerCase();
16125
+ }
16126
+ /** A sub-class label must belong to the macro it is under. */
16127
+ function subClassBelongs(macro, label) {
16128
+ return label === void 0 || macroOf$1(label.className) === macro;
16129
+ }
16130
+ var subClassOf = (macro) => SubClassLabelSchema.refine((l) => subClassBelongs(macro, l), { message: `that class is not a kind of ${macro} in the detection taxonomy` });
16131
+ var DetectionSubjectSchema = discriminatedUnion("macro", [
16132
+ object({
16133
+ macro: literal("animal"),
16134
+ label: subClassOf("animal").optional()
16135
+ }),
16136
+ object({
16137
+ macro: literal("person"),
16138
+ label: IdentityLabelSchema.optional()
16139
+ }),
16140
+ object({
16141
+ macro: literal("vehicle"),
16142
+ label: discriminatedUnion("kind", [SubClassLabelSchema.refine((l) => subClassBelongs("vehicle", l), { message: "that class is not a kind of vehicle in the detection taxonomy" }), PlateLabelSchema]).optional()
16143
+ }),
16144
+ object({
16145
+ macro: literal("package"),
16146
+ label: subClassOf("package").optional()
16147
+ }),
16148
+ object({
16149
+ macro: literal("other"),
16150
+ className: string().min(1)
16151
+ })
16152
+ ]);
16153
+ /**
16154
+ * The subject of a PARKED object. The stationary registry's event carries a
16155
+ * class and a zone, no face and no plate — an identity or plate label there
16156
+ * could never match, so the schema refuses it instead of letting it sit dead.
16157
+ */
16158
+ var StationarySubjectSchema = DetectionSubjectSchema.refine((s) => s.macro === "other" || s.label === void 0 || s.label.kind === "sub-class", { message: "a parked object carries no face or plate — only a class can narrow it" });
16159
+ /**
16160
+ * A stored class string as a subject: a macro name stays a macro; a raw class
16161
+ * the taxonomy files under a macro becomes that macro plus the sub-class label
16162
+ * (`dog` → animal / dog); anything else is `other`, kept verbatim.
16163
+ */
16164
+ function subjectFromClassName(className) {
16165
+ const name = className.trim().toLowerCase();
16166
+ const macro = COCO_TO_MACRO.mapping[name];
16167
+ if (new Set(Object.values(COCO_TO_MACRO.mapping)).has(name)) return macroOnly(name);
16168
+ if (macro === void 0) return {
16169
+ macro: "other",
16170
+ className
16171
+ };
16172
+ const label = {
16173
+ kind: "sub-class",
16174
+ className: name
16175
+ };
16176
+ switch (macro) {
16177
+ case "animal": return {
16178
+ macro: "animal",
16179
+ label
16180
+ };
16181
+ case "vehicle": return {
16182
+ macro: "vehicle",
16183
+ label
16184
+ };
16185
+ case "package": return {
16186
+ macro: "package",
16187
+ label
16188
+ };
16189
+ default: return {
16190
+ macro: "other",
16191
+ className
16192
+ };
16193
+ }
16194
+ }
16195
+ function macroOnly(macro) {
16196
+ switch (macro) {
16197
+ case "animal": return { macro: "animal" };
16198
+ case "person": return { macro: "person" };
16199
+ case "vehicle": return { macro: "vehicle" };
16200
+ case "package": return { macro: "package" };
16201
+ default: return {
16202
+ macro: "other",
16203
+ className: macro
16204
+ };
16205
+ }
16206
+ }
16207
+ /** How a state leaf compares a device field to a value. Derived from the
16208
+ * field's `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
15933
16209
  var AutomationConditionOperatorSchema = _enum([
15934
16210
  "eq",
15935
16211
  "ne",
@@ -15940,12 +16216,76 @@ var AutomationConditionOperatorSchema = _enum([
15940
16216
  "contains",
15941
16217
  "in"
15942
16218
  ]);
15943
- var AutomationConditionLeafSchema = object({
15944
- kind: literal("condition"),
16219
+ /**
16220
+ * D8 compliance, and it is the reason a "becomes" leaf is not merely an event
16221
+ * subscription: it evaluates against the **state mirror**, which is reconciled,
16222
+ * and an event only WAKES the evaluation. A dropped event therefore DELAYS a
16223
+ * fire; it does not lose it.
16224
+ */
16225
+ var DeviceBecomesLeafSchema = object({
16226
+ kind: literal("device-becomes"),
16227
+ deviceId: number().int().nonnegative(),
16228
+ cap: string().min(1),
16229
+ fieldPath: string().min(1),
16230
+ /** Fire when the field takes this value. Omit to fire on any change. */
16231
+ becomes: union([
16232
+ string(),
16233
+ number(),
16234
+ boolean()
16235
+ ]).optional(),
16236
+ /** Only on a CHANGE of value, not on every re-report. */
16237
+ edge: boolean().optional(),
16238
+ /** The new value must hold this long before it counts. */
16239
+ forMs: number().int().min(0).max(864e5).optional(),
16240
+ /** Collapse a burst into one occurrence. */
16241
+ debounceMs: number().int().min(0).max(6e5).optional()
16242
+ });
16243
+ var DeviceEventLeafSchema = object({
16244
+ kind: literal("device-event"),
16245
+ /** An `EventCategory` value. */
16246
+ category: string().min(1),
16247
+ deviceId: number().int().nonnegative().optional()
16248
+ });
16249
+ var ScheduleLeafSchema = object({
16250
+ kind: literal("schedule"),
16251
+ cron: string().min(1).max(120)
16252
+ });
16253
+ var ManualLeafSchema = object({ kind: literal("manual") });
16254
+ var IntervalLeafSchema = object({
16255
+ kind: literal("interval"),
16256
+ minutes: number().int().min(1).max(1440)
16257
+ });
16258
+ var DetectionLeafSchema = object({
16259
+ kind: literal("detection"),
16260
+ /** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
16261
+ subjects: array(DetectionSubjectSchema).min(1),
16262
+ deviceIds: array(number().int().nonnegative()).min(1),
16263
+ /** The track must have visited at least one of these zones. */
16264
+ zones: array(string().min(1)).min(1).optional(),
16265
+ /** Best-so-far confidence the track must reach. */
16266
+ minScore: number().min(0).max(1).optional()
16267
+ });
16268
+ var StationaryLeafSchema = object({
16269
+ kind: literal("stationary"),
16270
+ deviceIds: array(number().int().nonnegative()).min(1),
16271
+ subjects: array(StationarySubjectSchema).min(1).optional(),
16272
+ zones: array(string().min(1)).min(1).optional(),
16273
+ /** `appeared`: it became parked. `departed`: the registry retired it. */
16274
+ phase: _enum(["appeared", "departed"])
16275
+ });
16276
+ /** "is": one device field compared to one value, NOW. */
16277
+ var DeviceIsLeafSchema = object({
16278
+ kind: literal("device-is"),
15945
16279
  deviceId: number().int().nonnegative(),
15946
16280
  cap: string().min(1),
15947
16281
  fieldPath: string().min(1),
15948
16282
  operator: AutomationConditionOperatorSchema,
16283
+ /**
16284
+ * The value. A number on a quantity field is stored in the field's base unit
16285
+ * (D944); a quantity value is the WRITE form, which the save boundary
16286
+ * replaces with the converted number and `unit`. A leaf that still carries
16287
+ * one is never evaluated (it fails).
16288
+ */
15949
16289
  value: union([
15950
16290
  string(),
15951
16291
  number(),
@@ -15953,24 +16293,26 @@ var AutomationConditionLeafSchema = object({
15953
16293
  array(union([string(), number()])),
15954
16294
  QuantityValueSchema
15955
16295
  ]),
16296
+ /** The unit the person picked for `value` (display only; `value` is in the base unit). */
15956
16297
  unit: string().min(1).optional()
15957
16298
  });
15958
- var AutomationConditionStationaryPresentSchema = object({
16299
+ /**
16300
+ * True when the camera currently has a PARKED object matching `subjects` in at
16301
+ * least one of `zones`. Reads the zone-analytics snapshot through the same
16302
+ * mirror as every other state leaf, so a dropped event never decides it (D49).
16303
+ */
16304
+ var StationaryPresentLeafSchema = object({
15959
16305
  kind: literal("stationary-present"),
15960
16306
  deviceId: number().int().nonnegative(),
15961
- /** Class names; a macro class matches every raw class that maps to it. */
15962
- classes: array(string().min(1)).min(1).optional(),
15963
- /** The parked object must be in at least one of these zones. */
16307
+ subjects: array(StationarySubjectSchema).min(1).optional(),
15964
16308
  zones: array(string().min(1)).min(1).optional()
15965
16309
  });
15966
16310
  /**
15967
- * The expression leaf, declared as a plain object rather than an intersection
15968
- * with {@link ExpressionSourceSchema}: a discriminated union has to be able to
15969
- * read `kind` off each option, and an intersection hides it. The author-time
15970
- * validation is the SAME function `ExpressionSourceSchema` runs, so the two
15971
- * cannot drift — an expression that one accepts, the other accepts.
16311
+ * The escape hatch: the bounded expression engine over named device bindings.
16312
+ * Declared as a plain object so a discriminated union can read `kind`; the
16313
+ * author-time validation is the SAME function `ExpressionSourceSchema` runs.
15972
16314
  */
15973
- var AutomationConditionExpressionSchema = object({
16315
+ var ExpressionLeafSchema = object({
15974
16316
  kind: literal("expression"),
15975
16317
  expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
15976
16318
  bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
@@ -15982,55 +16324,62 @@ var AutomationConditionExpressionSchema = object({
15982
16324
  path: ["expr"]
15983
16325
  });
15984
16326
  });
15985
- var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
16327
+ var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
15986
16328
  object({
15987
16329
  kind: literal("all"),
15988
- children: array(AutomationConditionSchema)
16330
+ children: array(AutomationWhenSchema)
15989
16331
  }),
15990
16332
  object({
15991
16333
  kind: literal("any"),
15992
- children: array(AutomationConditionSchema)
16334
+ children: array(AutomationWhenSchema)
15993
16335
  }),
15994
16336
  object({
15995
16337
  kind: literal("not"),
15996
- child: AutomationConditionSchema
16338
+ child: AutomationWhenSchema
15997
16339
  }),
15998
- AutomationConditionLeafSchema,
15999
- AutomationConditionExpressionSchema,
16000
- AutomationConditionStationaryPresentSchema
16340
+ DeviceBecomesLeafSchema,
16341
+ DeviceEventLeafSchema,
16342
+ ScheduleLeafSchema,
16343
+ ManualLeafSchema,
16344
+ IntervalLeafSchema,
16345
+ DetectionLeafSchema,
16346
+ StationaryLeafSchema,
16347
+ DeviceIsLeafSchema,
16348
+ StationaryPresentLeafSchema,
16349
+ ExpressionLeafSchema
16001
16350
  ]));
16002
16351
  /**
16003
- * What starts a run.
16004
- *
16005
- * D8 compliance, and it is the reason `device-state` is not merely an event
16006
- * subscription: the trigger evaluates against the **state mirror**, which is
16007
- * reconciled, and an event only WAKES the evaluation. A dropped event therefore
16008
- * DELAYS a trigger; it does not lose it. `schedule` uses `croner` — the one
16009
- * already in the repo — because `setInterval(24h)` drifts and "at 23:30" does
16010
- * not.
16011
- */
16012
- var AutomationTriggerSchema = discriminatedUnion("kind", [
16352
+ * The recipe shape before the single `when` tree: a flat list of triggers
16353
+ * (OR'd, each an instant) and an optional condition tree. Stored recipes —
16354
+ * the `AUTOMATION_RECIPE` literal of every block saved so far, and whatever an
16355
+ * older client still posts — are in this shape, so it stays READABLE here, and
16356
+ * {@link convertLegacyRecipe} turns it into the new one, losslessly and one
16357
+ * way. Nothing writes this shape any more.
16358
+ *
16359
+ * `{ triggers: [t1…tn], conditions?: C }`
16360
+ * → `when = all(any(t1…tn), C)`, or `any(t1…tn)` without `C`;
16361
+ * a single trigger is not wrapped in `any`.
16362
+ * a `condition` leaf → a `device-is` leaf (the "is" verb);
16363
+ * a `device-state` trigger → a `device-becomes` leaf (the "becomes" verb);
16364
+ * `classes: ['dog']` → the macro `animal` with the label `dog`.
16365
+ */
16366
+ var LegacyTriggerSchema = discriminatedUnion("kind", [
16013
16367
  object({
16014
16368
  kind: literal("device-state"),
16015
16369
  deviceId: number().int().nonnegative(),
16016
16370
  cap: string().min(1),
16017
16371
  fieldPath: string().min(1),
16018
- /** Fire when the field takes this value. Omit to fire on any change. */
16019
16372
  becomes: union([
16020
16373
  string(),
16021
16374
  number(),
16022
16375
  boolean()
16023
16376
  ]).optional(),
16024
- /** Only on a CHANGE of value, not on every re-report. */
16025
16377
  edge: boolean().optional(),
16026
- /** The condition must hold this long before the run starts. */
16027
16378
  forMs: number().int().min(0).max(864e5).optional(),
16028
- /** Collapse a burst into one run. */
16029
16379
  debounceMs: number().int().min(0).max(6e5).optional()
16030
16380
  }),
16031
16381
  object({
16032
16382
  kind: literal("device-event"),
16033
- /** An `EventCategory` value. */
16034
16383
  category: string().min(1),
16035
16384
  deviceId: number().int().nonnegative().optional()
16036
16385
  }),
@@ -16041,25 +16390,16 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16041
16390
  object({ kind: literal("manual") }),
16042
16391
  object({
16043
16392
  kind: literal("detection"),
16044
- /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
16045
- * matches every raw class that maps to it; any other name matches
16046
- * exactly. */
16047
16393
  classes: array(string().min(1)).min(1),
16048
16394
  deviceIds: array(number().int().nonnegative()).min(1),
16049
- /** The track must have visited at least one of these zones. */
16050
16395
  zones: array(string().min(1)).min(1).optional(),
16051
- /** Best-so-far confidence the track must reach. */
16052
16396
  minScore: number().min(0).max(1).optional()
16053
16397
  }),
16054
16398
  object({
16055
16399
  kind: literal("stationary"),
16056
16400
  deviceIds: array(number().int().nonnegative()).min(1),
16057
- /** Class names; a macro class matches like the detection trigger. */
16058
16401
  classes: array(string().min(1)).min(1).optional(),
16059
- /** The parked object must be in at least one of these zones. */
16060
16402
  zones: array(string().min(1)).min(1).optional(),
16061
- /** `appeared`: it became parked. `departed`: the registry retired it.
16062
- * The vocabulary of `PipelineAnalyticsStationaryChanged`. */
16063
16403
  phase: _enum(["appeared", "departed"])
16064
16404
  }),
16065
16405
  object({
@@ -16067,6 +16407,143 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16067
16407
  minutes: number().int().min(1).max(1440)
16068
16408
  })
16069
16409
  ]);
16410
+ var LegacyConditionLeafSchema = object({
16411
+ kind: literal("condition"),
16412
+ deviceId: number().int().nonnegative(),
16413
+ cap: string().min(1),
16414
+ fieldPath: string().min(1),
16415
+ operator: AutomationConditionOperatorSchema,
16416
+ value: union([
16417
+ string(),
16418
+ number(),
16419
+ boolean(),
16420
+ array(union([string(), number()])),
16421
+ QuantityValueSchema
16422
+ ]),
16423
+ unit: string().min(1).optional()
16424
+ });
16425
+ var LegacyStationaryPresentSchema = object({
16426
+ kind: literal("stationary-present"),
16427
+ deviceId: number().int().nonnegative(),
16428
+ classes: array(string().min(1)).min(1).optional(),
16429
+ zones: array(string().min(1)).min(1).optional()
16430
+ });
16431
+ var LegacyExpressionSchema = object({
16432
+ kind: literal("expression"),
16433
+ expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
16434
+ bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
16435
+ }).superRefine((src, ctx) => {
16436
+ const err = validateExpressionSource(src);
16437
+ if (err !== null) ctx.addIssue({
16438
+ code: "custom",
16439
+ message: err,
16440
+ path: ["expr"]
16441
+ });
16442
+ });
16443
+ var LegacyConditionSchema = lazy(() => discriminatedUnion("kind", [
16444
+ object({
16445
+ kind: literal("all"),
16446
+ children: array(LegacyConditionSchema)
16447
+ }),
16448
+ object({
16449
+ kind: literal("any"),
16450
+ children: array(LegacyConditionSchema)
16451
+ }),
16452
+ object({
16453
+ kind: literal("not"),
16454
+ child: LegacyConditionSchema
16455
+ }),
16456
+ LegacyConditionLeafSchema,
16457
+ LegacyExpressionSchema,
16458
+ LegacyStationaryPresentSchema
16459
+ ]));
16460
+ function convertTrigger(trigger) {
16461
+ switch (trigger.kind) {
16462
+ case "device-state": return {
16463
+ ...trigger,
16464
+ kind: "device-becomes"
16465
+ };
16466
+ case "detection": {
16467
+ const { classes, ...rest } = trigger;
16468
+ return {
16469
+ ...rest,
16470
+ subjects: classes.map(subjectFromClassName)
16471
+ };
16472
+ }
16473
+ case "stationary": {
16474
+ const { classes, ...rest } = trigger;
16475
+ return {
16476
+ ...rest,
16477
+ ...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
16478
+ };
16479
+ }
16480
+ default: return trigger;
16481
+ }
16482
+ }
16483
+ /** An old condition tree as a `when` subtree: `condition` → "is", classes → subjects. */
16484
+ function convertLegacyCondition(condition) {
16485
+ switch (condition.kind) {
16486
+ case "condition": return {
16487
+ ...condition,
16488
+ kind: "device-is"
16489
+ };
16490
+ case "stationary-present": {
16491
+ const { classes, ...rest } = condition;
16492
+ return {
16493
+ ...rest,
16494
+ ...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
16495
+ };
16496
+ }
16497
+ case "expression": return condition;
16498
+ case "not": return {
16499
+ kind: "not",
16500
+ child: convertLegacyCondition(condition.child)
16501
+ };
16502
+ case "all":
16503
+ case "any": return {
16504
+ kind: condition.kind,
16505
+ children: condition.children.map(convertLegacyCondition)
16506
+ };
16507
+ }
16508
+ }
16509
+ /** `all(any(t1…tn), C)`, or `any(t1…tn)`; a single trigger is not wrapped in `any`. */
16510
+ function convertLegacyWhen(half) {
16511
+ const leaves = half.triggers.map(convertTrigger);
16512
+ const first = leaves[0];
16513
+ const triggers = leaves.length === 1 && first !== void 0 ? first : {
16514
+ kind: "any",
16515
+ children: leaves
16516
+ };
16517
+ if (half.conditions === void 0) return triggers;
16518
+ return {
16519
+ kind: "all",
16520
+ children: [triggers, convertLegacyCondition(half.conditions)]
16521
+ };
16522
+ }
16523
+ /**
16524
+ * The automation RECIPE — what a visually-authored automation is, before it
16525
+ * becomes code.
16526
+ *
16527
+ * This is the data half of `docs/design/2026-08-08-automation-scripts-custom-devices.md`
16528
+ * Phase 6. A recipe is not an engine and never evaluates anything: it is a
16529
+ * declarative description that {@link generateAutomationBlock} compiles to the
16530
+ * TypeScript of an ordinary core block. There is therefore no second rule
16531
+ * engine, no second store and no second runtime — the block runner is the
16532
+ * runtime, `core_blocks` is the store (§2.1).
16533
+ *
16534
+ * Two parts:
16535
+ *
16536
+ * - **`when`** — ONE tree (`./when.ts`) of all/any/not over event leaves
16537
+ * (instants) and state leaves (durations). It replaces the flat trigger list
16538
+ * plus the separate condition tree; the firing rule is in that file and in
16539
+ * ADR "one tree".
16540
+ * - **Actions** — `NcRuleActionSchema`'s two steps, plus the `{kind:'code'}`
16541
+ * fragment of §3.2.2. See {@link AutomationActionSchema} for the ONE place
16542
+ * this had to diverge from "verbatim", and why.
16543
+ *
16544
+ * A recipe stored in the old shape (`triggers` + `conditions`) still LOADS:
16545
+ * {@link AutomationRecipeSchema} (the parse-anywhere schema) converts it on parse. Nothing writes it.
16546
+ */
16070
16547
  /**
16071
16548
  * One action step.
16072
16549
  *
@@ -16169,69 +16646,49 @@ var AutomationRunModeSchema = discriminatedUnion("mode", [
16169
16646
  minutes: number().int().min(1).max(1440)
16170
16647
  })
16171
16648
  ]);
16172
- var AutomationRecipeSchema = object({
16173
- triggers: array(AutomationTriggerSchema),
16174
- conditions: AutomationConditionSchema.optional(),
16649
+ var CurrentAutomationRecipeSchema = object({
16650
+ when: AutomationWhenSchema,
16175
16651
  actions: array(AutomationActionSchema),
16176
16652
  runMode: AutomationRunModeSchema.optional(),
16177
16653
  /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
16178
16654
  keepTraces: number().int().min(1).max(100).optional()
16179
16655
  });
16180
- new Set([
16181
- {
16182
- id: "person",
16183
- name: "Person"
16184
- },
16185
- {
16186
- id: "vehicle",
16187
- name: "Vehicle"
16188
- },
16189
- {
16190
- id: "animal",
16191
- name: "Animal"
16192
- },
16193
- {
16194
- id: "package",
16195
- name: "Package"
16196
- }
16197
- ].map((l) => l.id));
16198
- var COCO_TO_MACRO = {
16199
- mapping: {
16200
- person: "person",
16201
- bicycle: "vehicle",
16202
- car: "vehicle",
16203
- motorcycle: "vehicle",
16204
- airplane: "vehicle",
16205
- bus: "vehicle",
16206
- train: "vehicle",
16207
- truck: "vehicle",
16208
- boat: "vehicle",
16209
- bird: "animal",
16210
- cat: "animal",
16211
- dog: "animal",
16212
- horse: "animal",
16213
- sheep: "animal",
16214
- cow: "animal",
16215
- elephant: "animal",
16216
- bear: "animal",
16217
- zebra: "animal",
16218
- giraffe: "animal",
16219
- suitcase: "package",
16220
- backpack: "package",
16221
- handbag: "package"
16222
- },
16223
- preserveOriginal: false
16224
- };
16656
+ /** A recipe as it was stored before the single tree. Read-only: nothing writes this. */
16657
+ var LegacyAutomationRecipeSchema = object({
16658
+ triggers: array(LegacyTriggerSchema),
16659
+ conditions: LegacyConditionSchema.optional(),
16660
+ actions: array(AutomationActionSchema),
16661
+ runMode: AutomationRunModeSchema.optional(),
16662
+ keepTraces: number().int().min(1).max(100).optional()
16663
+ });
16664
+ /** The old shape as the new one, lossless. */
16665
+ function convertLegacyRecipe(legacy) {
16666
+ const { triggers, conditions, ...rest } = legacy;
16667
+ return {
16668
+ when: convertLegacyWhen({
16669
+ triggers,
16670
+ conditions
16671
+ }),
16672
+ ...rest
16673
+ };
16674
+ }
16675
+ /**
16676
+ * Where recipes are PARSED (the core-blocks cap, the MCP tool, the literal a
16677
+ * saved block carries): the new shape as it is, or the old shape converted to
16678
+ * it. The output type is always the new one.
16679
+ */
16680
+ var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
16225
16681
  new Set(Object.values(COCO_TO_MACRO.mapping));
16226
16682
  /**
16227
16683
  * Run traces — what an automation (or, later, an Action run) DID, node by node,
16228
16684
  * so the editor can paint every block green or red with its reason.
16229
16685
  *
16230
- * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
16231
- * every condition node (containers and leaves, including the ones a
16232
- * short-circuit never reached), the run-mode verdict and every action step —
16233
- * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
16234
- * for an Action run). Skipped
16686
+ * A trace is data, not a log: it covers the WHOLE tree — the event leaf that
16687
+ * fired, every node of the `when` tree (containers and leaves, including the
16688
+ * ones a short-circuit never reached), the run-mode verdict and every action
16689
+ * step — each addressed by a stable path (`when.0.1`, `actions.2`, or `steps.N`
16690
+ * for an Action run). Traces written before the single tree carry
16691
+ * `conditions.*` paths and still parse. Skipped
16235
16692
  * evaluations are traced too, and identical consecutive skips collapse into a
16236
16693
  * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
16237
16694
  *
@@ -16263,6 +16720,8 @@ var TraceNodeOutcomeSchema = _enum([
16263
16720
  ]);
16264
16721
  var AutomationTriggerTraceSchema = object({
16265
16722
  kind: string().min(1),
16723
+ /** The event leaf that fired (`when`, `when.0.1`); absent on traces written before the single tree. */
16724
+ path: string().min(1).optional(),
16266
16725
  outcome: _enum(["pass", "fail"]),
16267
16726
  /** Why an event was rejected ("class person not in [dog]") or what fired. */
16268
16727
  reason: string(),
@@ -16270,15 +16729,23 @@ var AutomationTriggerTraceSchema = object({
16270
16729
  vars: record(string(), TraceValueSchema)
16271
16730
  });
16272
16731
  var AutomationConditionTraceNodeSchema = object({
16273
- /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
16732
+ /** `when` for the root, `when.<i>` for its i-th child, … (`conditions…` on an old trace). */
16274
16733
  path: string().min(1),
16275
16734
  kind: _enum([
16276
16735
  "all",
16277
16736
  "any",
16278
16737
  "not",
16279
16738
  "condition",
16739
+ "device-is",
16280
16740
  "expression",
16281
- "stationary-present"
16741
+ "stationary-present",
16742
+ "device-becomes",
16743
+ "device-event",
16744
+ "schedule",
16745
+ "interval",
16746
+ "manual",
16747
+ "detection",
16748
+ "stationary"
16282
16749
  ]),
16283
16750
  outcome: TraceNodeOutcomeSchema,
16284
16751
  reason: string(),
@@ -16312,6 +16779,12 @@ var AutomationStepTraceSchema = object({
16312
16779
  deviceId: number().int().optional(),
16313
16780
  startedAt: number().optional(),
16314
16781
  endedAt: number().optional(),
16782
+ /**
16783
+ * The parameters the step SENT, after `{{vars.x}}` substitution — what the
16784
+ * device was actually asked. Bounded like trigger `vars`; a secret parameter
16785
+ * is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
16786
+ */
16787
+ args: record(string(), TraceValueSchema).optional(),
16315
16788
  outcome: _enum([
16316
16789
  "ok",
16317
16790
  "error",
@@ -17061,6 +17534,23 @@ method(object({
17061
17534
  addonId: string(),
17062
17535
  nodeId: string().optional()
17063
17536
  }), SettingsSchemaWithValuesSchema.nullable()), method(object({
17537
+ addonId: string(),
17538
+ nodeId: string().optional()
17539
+ }), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
17540
+ addonId: string(),
17541
+ nodeId: string().optional(),
17542
+ keys: array(string())
17543
+ }), object({
17544
+ removed: array(string()),
17545
+ refused: array(object({
17546
+ key: string(),
17547
+ reason: string()
17548
+ })),
17549
+ secretsCleared: number()
17550
+ }), {
17551
+ kind: "mutation",
17552
+ auth: "admin"
17553
+ }), method(object({
17064
17554
  addonId: string(),
17065
17555
  deviceId: number(),
17066
17556
  nodeId: string().optional()
@@ -18496,8 +18986,21 @@ var BrokerProviderInfoSchema = object({
18496
18986
  * (`{ sessionId }`) drops the session. The dialog that edits a broker
18497
18987
  * offers it as the default path; the fields the session fills are
18498
18988
  * marked `filledBySignIn` in the kind's settings schema.
18989
+ *
18990
+ * `methods` lists the ways the session can sign in, recommended first:
18991
+ * `'device-code'` adds `POST {basePath}/code/start` (body: the form's
18992
+ * settings) answering `{ sessionId, userCode, verificationUrl,
18993
+ * expiresAt }` — the operator types `userCode` at `verificationUrl` on
18994
+ * any device, and the same `status` / `commit` / `cancel` routes follow
18995
+ * (status then carries `method`, `userCode`, `verificationUrl`,
18996
+ * `expiresAt` and the phases `'awaiting-code'` / `'expired'`);
18997
+ * `'proxy'` is the `{basePath}/start` flow above. Absent means
18998
+ * `['proxy']`.
18499
18999
  */
18500
- signIn: object({ basePath: string().min(1) }).optional()
19000
+ signIn: object({
19001
+ basePath: string().min(1),
19002
+ methods: array(_enum(["device-code", "proxy"])).optional()
19003
+ }).optional()
18501
19004
  }))
18502
19005
  });
18503
19006
  var ListInputSchema = object({
@@ -20971,6 +21474,8 @@ var StorageKindSchema = object({
20971
21474
  "orphaned",
20972
21475
  "dropped"
20973
21476
  ]),
21477
+ /** An owner declared this kind since the engine started. */
21478
+ declaredThisBoot: boolean(),
20974
21479
  /** `null` when the caller asked for no counts (`counts: false`). */
20975
21480
  rows: number().int().nonnegative().nullable(),
20976
21481
  /** Rows x the average size of a sample; null for an empty kind. */
@@ -20982,6 +21487,51 @@ var StorageKindSchema = object({
20982
21487
  /** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
20983
21488
  declaration: RecordKindDeclarationSchema
20984
21489
  });
21490
+ _enum([
21491
+ "orphaned",
21492
+ "stale",
21493
+ "unknown"
21494
+ ]);
21495
+ /** A record kind nobody declared since its owner started, or whose owner is gone. */
21496
+ var StaleKindFindingSchema = object({
21497
+ owner: string(),
21498
+ kind: string(),
21499
+ /** `unknown`: the owner could not be judged (not running, or its state unreadable) - never dropped. */
21500
+ verdict: _enum(["orphaned", "unknown"]),
21501
+ reason: string(),
21502
+ rows: number().int().nonnegative().nullable(),
21503
+ /** local-auth's tables are never dropped by this audit. */
21504
+ protected: boolean()
21505
+ });
21506
+ /** A stored global-settings key the running addon no longer declares. */
21507
+ var StaleSettingsKeyFindingSchema = object({
21508
+ addonId: string(),
21509
+ /** The stored key as written, a per-node `<key>@<nodeId>` suffix included. */
21510
+ key: string(),
21511
+ /**
21512
+ * Keychain refs whose ONLY marker sits under this key: in use by stale data,
21513
+ * not orphans yet. Removing the key releases them.
21514
+ */
21515
+ secretRefs: array(string())
21516
+ });
21517
+ /** An addon the audit could not judge, with the reason. Never reported as stale. */
21518
+ var StaleUnknownSchema = object({
21519
+ subject: string(),
21520
+ reason: string()
21521
+ });
21522
+ var StaleDataReportSchema = object({
21523
+ /** Epoch ms of the last finished scan; `null` before the first. */
21524
+ scannedAt: number().nullable(),
21525
+ scanning: boolean(),
21526
+ trigger: _enum([
21527
+ "boot",
21528
+ "interval",
21529
+ "manual"
21530
+ ]).nullable(),
21531
+ kinds: array(StaleKindFindingSchema),
21532
+ settingsKeys: array(StaleSettingsKeyFindingSchema),
21533
+ unknown: array(StaleUnknownSchema)
21534
+ });
20985
21535
  method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
20986
21536
  owner: string().min(1).optional(),
20987
21537
  /** `false` skips the per-kind row count (a catalog read only). Default true. */
@@ -20989,7 +21539,30 @@ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: arr
20989
21539
  }), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
20990
21540
  owner: string().min(1),
20991
21541
  kind: RecordKindNameSchema
20992
- }), RecordQueryResultSchema, { auth: "admin" });
21542
+ }), RecordQueryResultSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, {
21543
+ kind: "mutation",
21544
+ auth: "admin"
21545
+ }), method(object({
21546
+ owner: string().min(1),
21547
+ kind: RecordKindNameSchema
21548
+ }), object({
21549
+ deleted: number().int().nonnegative(),
21550
+ secretsReleased: number().int().nonnegative()
21551
+ }), {
21552
+ kind: "mutation",
21553
+ auth: "admin",
21554
+ caller: "required"
21555
+ }), method(object({
21556
+ addonId: string().min(1),
21557
+ key: string().min(1)
21558
+ }), object({
21559
+ removed: boolean(),
21560
+ secretsCleared: number().int().nonnegative()
21561
+ }), {
21562
+ kind: "mutation",
21563
+ auth: "admin",
21564
+ caller: "required"
21565
+ });
20993
21566
  /**
20994
21567
  * record-entries - the ONE admin surface for every list of user-created entries
20995
21568
  * (D881): notification targets, brokers, LLM profiles, anything an addon
@@ -21128,6 +21701,12 @@ method(object({
21128
21701
  }), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
21129
21702
  kind: "mutation",
21130
21703
  auth: "admin"
21704
+ }), method(object({
21705
+ refs: RefListSchema,
21706
+ onBehalfOf: string().min(1)
21707
+ }), object({ released: number().int() }), {
21708
+ kind: "mutation",
21709
+ auth: "admin"
21131
21710
  }), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
21132
21711
  /**
21133
21712
  * device-history — the ONE mechanism for numeric history (D850).
@@ -21917,7 +22496,9 @@ var DiscoveredChildDeviceSchema = lazy(() => object({
21917
22496
  /** Upstream-system identity + rendering hints for the candidate. */
21918
22497
  sourceInfo: SourceInfoSchema.optional(),
21919
22498
  /** Nested entity-children for accordion preview — integrations populate, Hubs omit. */
21920
- children: array(DiscoveredChildDeviceSchema).readonly().optional()
22499
+ children: array(DiscoveredChildDeviceSchema).readonly().optional(),
22500
+ /** `childNativeId` of the gateway candidate in the same list this device sits behind. */
22501
+ parentStableId: string().optional()
21921
22502
  }));
21922
22503
  /**
21923
22504
  * Status block — populated by the integration after each
@@ -42232,6 +42813,79 @@ object({
42232
42813
  metadata: record(string(), unknown()).optional()
42233
42814
  });
42234
42815
  /**
42816
+ * HOW a provider can be found on the network. The provider only DECLARES the
42817
+ * mode; the `local-network` scan engine runs it once for every provider that
42818
+ * asked (one multicast browse, one host sweep) and hands the raw hits back to
42819
+ * `classifyDiscoveryHits`. A provider never opens a discovery socket itself,
42820
+ * except through a `custom` mode.
42821
+ */
42822
+ var DiscoveryModeSchema = discriminatedUnion("kind", [
42823
+ object({
42824
+ kind: literal("mdns"),
42825
+ /** DNS-SD service type, e.g. `_esphomelib._tcp`. */
42826
+ serviceType: string().min(1),
42827
+ /** Regular expression an instance name must match (case-insensitive). */
42828
+ instancePattern: string().optional()
42829
+ }),
42830
+ object({
42831
+ kind: literal("ws-discovery"),
42832
+ /** Probe types (e.g. `dn:NetworkVideoTransmitter`); omitted = any device. */
42833
+ types: array(string()).optional()
42834
+ }),
42835
+ object({
42836
+ kind: literal("ssdp"),
42837
+ searchTarget: string().min(1)
42838
+ }),
42839
+ object({
42840
+ kind: literal("udp-broadcast"),
42841
+ port: number().int().min(1).max(65535),
42842
+ payloadHex: string().regex(/^([0-9a-fA-F]{2})*$/),
42843
+ /** Port replies arrive on, when the device answers somewhere other than the source port. */
42844
+ listenPort: number().int().min(1).max(65535).optional()
42845
+ }),
42846
+ object({
42847
+ kind: literal("http-probe"),
42848
+ port: number().int().min(1).max(65535),
42849
+ path: string().startsWith("/"),
42850
+ method: _enum(["GET", "POST"]).default("GET"),
42851
+ bodyText: string().optional(),
42852
+ contentType: string().optional(),
42853
+ tls: boolean().default(false),
42854
+ /** Status codes that count as a hit. Omitted = any HTTP answer. */
42855
+ acceptStatus: array(number().int()).optional()
42856
+ }),
42857
+ object({
42858
+ kind: literal("custom"),
42859
+ /** Why the engine cannot do it (an allowlisted exception, e.g. a vendor UDP protocol). */
42860
+ reason: string().min(1)
42861
+ })
42862
+ ]);
42863
+ /** What the engine saw on the wire for ONE declared mode; the provider classifies it. */
42864
+ var DiscoveryHitSchema = object({
42865
+ /** Index into the provider's own `getDiscoveryModes()` array. */
42866
+ modeIndex: number().int().min(0),
42867
+ host: string(),
42868
+ port: number().int(),
42869
+ http: object({
42870
+ status: number().int(),
42871
+ headers: record(string(), string()),
42872
+ /** At most 16 KiB. */
42873
+ bodyText: string()
42874
+ }).optional(),
42875
+ mdns: object({
42876
+ instance: string(),
42877
+ host: string(),
42878
+ port: number().int(),
42879
+ txt: record(string(), string())
42880
+ }).optional(),
42881
+ udp: object({ payloadHex: string() }).optional(),
42882
+ wsd: object({
42883
+ xaddrs: array(string()),
42884
+ types: array(string()),
42885
+ scopes: array(string())
42886
+ }).optional()
42887
+ });
42888
+ /**
42235
42889
  * Candidate handed back from discovery and accepted by
42236
42890
  * `adoptDiscoveredDevice`. Shape mirrors the in-process
42237
42891
  * `DiscoveredDevice` interface used by `DeviceDiscovery`.
@@ -42266,7 +42920,17 @@ var DiscoveryCandidateSchema = object({
42266
42920
  /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
42267
42921
  onboardedDeviceId: number().optional(),
42268
42922
  /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
42269
- onboardedName: string().optional()
42923
+ onboardedName: string().optional(),
42924
+ /**
42925
+ * This candidate is an INTEGRATION to set up (a gateway, a hub, a controller),
42926
+ * not a device to adopt. Replaces the `prefilledConfig.setupIntegration` marker,
42927
+ * which is still read for one release.
42928
+ */
42929
+ setupIntegration: boolean().optional(),
42930
+ /** `stableId` of the gateway candidate this device was found BEHIND. */
42931
+ parentStableId: string().optional(),
42932
+ /** `false` = shown for context but not pickable. Default true. */
42933
+ selectable: boolean().optional()
42270
42934
  });
42271
42935
  /**
42272
42936
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -42319,6 +42983,12 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
42319
42983
  }), method(object({}), boolean()), method(object({ params: record(string(), unknown()).optional() }), array(DiscoveryCandidateSchema), {
42320
42984
  kind: "mutation",
42321
42985
  auth: "admin"
42986
+ }), method(object({}), array(DiscoveryModeSchema)), method(object({
42987
+ hits: array(DiscoveryHitSchema),
42988
+ params: record(string(), unknown()).optional()
42989
+ }), array(DiscoveryCandidateSchema), {
42990
+ kind: "mutation",
42991
+ auth: "admin"
42322
42992
  }), method(object({}), CreationSchemaOutputSchema), method(object({}), object({ deviceType: _enum(DeviceType).nullable() })), method(object({ candidate: DiscoveryCandidateSchema }), DeviceSummarySchema, {
42323
42993
  kind: "mutation",
42324
42994
  auth: "admin"
@@ -43046,17 +43716,23 @@ method(object({
43046
43716
  }), method(object({}), object({ providers: array(object({
43047
43717
  addonId: string(),
43048
43718
  label: string()
43049
- })).readonly() }), { auth: "admin" }), method(object({}), object({ groups: array(object({
43050
- addonId: string(),
43051
- label: string(),
43052
- candidates: array(DiscoveryCandidateSchema).readonly(),
43053
- error: string().nullable()
43054
- })).readonly() }), {
43055
- kind: "mutation",
43056
- auth: "admin"
43057
- }), method(object({
43719
+ })).readonly() }), { auth: "admin" }), method(
43720
+ /** `subnets`: extra CIDRs for THIS scan, on top of the hub's own and the saved list. */
43721
+ object({ subnets: array(string()).optional() }),
43722
+ object({ groups: array(object({
43723
+ addonId: string(),
43724
+ label: string(),
43725
+ candidates: array(DiscoveryCandidateSchema).readonly(),
43726
+ error: string().nullable()
43727
+ })).readonly() }),
43728
+ {
43729
+ kind: "mutation",
43730
+ auth: "admin"
43731
+ }
43732
+ ), method(object({
43058
43733
  addonId: string(),
43059
- params: record(string(), unknown()).optional()
43734
+ params: record(string(), unknown()).optional(),
43735
+ subnets: array(string()).optional()
43060
43736
  }), object({ candidates: array(DiscoveryCandidateSchema).readonly() }), {
43061
43737
  kind: "mutation",
43062
43738
  auth: "admin"
@@ -60676,7 +61352,9 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
60676
61352
  id: string(),
60677
61353
  settings: record(string(), unknown()),
60678
61354
  /** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
60679
- secretKeys: array(string()).optional()
61355
+ secretKeys: array(string()).optional(),
61356
+ /** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
61357
+ skipRestart: boolean().optional()
60680
61358
  }), object({ success: boolean() }), {
60681
61359
  kind: "mutation",
60682
61360
  auth: "admin"
@@ -61153,6 +61831,39 @@ var TlsStatusSchema = object({
61153
61831
  hasCa: boolean()
61154
61832
  }).nullable()
61155
61833
  });
61834
+ /** One provider's ask of the scan engine: the modes it declared. */
61835
+ var ScanRequestSchema = object({
61836
+ providerId: string().min(1),
61837
+ modes: array(DiscoveryModeSchema)
61838
+ });
61839
+ var ScanErrorSchema = object({
61840
+ /** Absent for a failure that is the scan's own (a bad subnet, a socket that would not open). */
61841
+ providerId: string().optional(),
61842
+ /** The mode family that failed (`mdns`, `http-probe`, …). */
61843
+ mode: string().optional(),
61844
+ message: string()
61845
+ });
61846
+ /** What one scan did, per mode family; the engine logs the same numbers once. */
61847
+ var ScanStatsSchema = object({
61848
+ hostsProbed: number().int(),
61849
+ hits: number().int(),
61850
+ timeouts: number().int(),
61851
+ byMode: record(string(), object({
61852
+ probed: number().int(),
61853
+ hits: number().int(),
61854
+ timeouts: number().int()
61855
+ }))
61856
+ });
61857
+ var ScanNetworkResultSchema = object({
61858
+ hits: array(object({
61859
+ providerId: string(),
61860
+ hit: DiscoveryHitSchema
61861
+ })),
61862
+ /** CIDRs actually covered: the hub's own /24s, the saved list and the per-call list. */
61863
+ subnetsScanned: array(string()),
61864
+ errors: array(ScanErrorSchema),
61865
+ stats: ScanStatsSchema
61866
+ });
61156
61867
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
61157
61868
  /**
61158
61869
  * LEGACY HINT — do not send from new code. Kept optional so clients
@@ -61207,6 +61918,16 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
61207
61918
  }), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
61208
61919
  kind: "mutation",
61209
61920
  auth: "admin"
61921
+ }), method(object({
61922
+ requests: array(ScanRequestSchema),
61923
+ subnets: array(string()).optional()
61924
+ }), ScanNetworkResultSchema, {
61925
+ kind: "mutation",
61926
+ auth: "admin",
61927
+ timeoutMs: 9e4
61928
+ }), method(_void(), object({ subnets: array(string()) }), { auth: "admin" }), method(object({ subnets: array(string()) }), object({ subnets: array(string()) }), {
61929
+ kind: "mutation",
61930
+ auth: "admin"
61210
61931
  }), method(_void(), TlsStatusSchema, {
61211
61932
  kind: "mutation",
61212
61933
  auth: "admin"
@@ -63749,7 +64470,7 @@ object({
63749
64470
  });
63750
64471
  Object.values(DeviceType);
63751
64472
  DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
63752
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
64473
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
63753
64474
  kind: "mutation",
63754
64475
  auth: "admin"
63755
64476
  });
@@ -67607,7 +68328,7 @@ discriminatedUnion("kind", [object({
67607
68328
  })]);
67608
68329
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
67609
68330
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
67610
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
68331
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
67611
68332
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
67612
68333
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
67613
68334
  new Set(CAP_NAMES);
@@ -67660,7 +68381,7 @@ var AccessoryKind = {
67660
68381
  PrivacyMask: DeviceRole.PrivacyMask
67661
68382
  };
67662
68383
  AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
67663
- DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
68384
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
67664
68385
  new Set(Object.values(DeviceType));
67665
68386
  Object.freeze({
67666
68387
  "accessories.getStatus": {
@@ -68048,6 +68769,13 @@ Object.freeze({
68048
68769
  access: "create",
68049
68770
  role: "admin"
68050
68771
  },
68772
+ "addonSettings.getDeclaredSettingsKeys": {
68773
+ capName: "addon-settings",
68774
+ capScope: "system",
68775
+ addonId: null,
68776
+ access: "view",
68777
+ role: "admin"
68778
+ },
68051
68779
  "addonSettings.getDeviceSettings": {
68052
68780
  capName: "addon-settings",
68053
68781
  capScope: "system",
@@ -68069,6 +68797,13 @@ Object.freeze({
68069
68797
  access: "view",
68070
68798
  role: "admin"
68071
68799
  },
68800
+ "addonSettings.unsetStoredSettingsKeys": {
68801
+ capName: "addon-settings",
68802
+ capScope: "system",
68803
+ addonId: null,
68804
+ access: "create",
68805
+ role: "admin"
68806
+ },
68072
68807
  "addonSettings.updateDeviceSettings": {
68073
68808
  capName: "addon-settings",
68074
68809
  capScope: "system",
@@ -70484,6 +71219,13 @@ Object.freeze({
70484
71219
  access: "create",
70485
71220
  role: "admin"
70486
71221
  },
71222
+ "deviceProvider.classifyDiscoveryHits": {
71223
+ capName: "device-provider",
71224
+ capScope: "system",
71225
+ addonId: null,
71226
+ access: "create",
71227
+ role: "admin"
71228
+ },
70487
71229
  "deviceProvider.createDevice": {
70488
71230
  capName: "device-provider",
70489
71231
  capScope: "system",
@@ -70512,6 +71254,13 @@ Object.freeze({
70512
71254
  access: "view",
70513
71255
  role: "admin"
70514
71256
  },
71257
+ "deviceProvider.getDiscoveryModes": {
71258
+ capName: "device-provider",
71259
+ capScope: "system",
71260
+ addonId: null,
71261
+ access: "view",
71262
+ role: "admin"
71263
+ },
70515
71264
  "deviceProvider.getDiscoveryParamsSchema": {
70516
71265
  capName: "device-provider",
70517
71266
  capScope: "system",
@@ -71289,6 +72038,13 @@ Object.freeze({
71289
72038
  access: "create",
71290
72039
  role: "admin"
71291
72040
  },
72041
+ "keychain.releaseRecordRefs": {
72042
+ capName: "keychain",
72043
+ capScope: "system",
72044
+ addonId: null,
72045
+ access: "create",
72046
+ role: "admin"
72047
+ },
71292
72048
  "keychain.resolve": {
71293
72049
  capName: "keychain",
71294
72050
  capScope: "system",
@@ -71709,6 +72465,13 @@ Object.freeze({
71709
72465
  access: "view",
71710
72466
  role: "admin"
71711
72467
  },
72468
+ "localNetwork.getScanSubnets": {
72469
+ capName: "local-network",
72470
+ capScope: "system",
72471
+ addonId: null,
72472
+ access: "view",
72473
+ role: "admin"
72474
+ },
71712
72475
  "localNetwork.getTlsStatus": {
71713
72476
  capName: "local-network",
71714
72477
  capScope: "system",
@@ -71758,6 +72521,13 @@ Object.freeze({
71758
72521
  access: "create",
71759
72522
  role: "admin"
71760
72523
  },
72524
+ "localNetwork.scanNetwork": {
72525
+ capName: "local-network",
72526
+ capScope: "system",
72527
+ addonId: null,
72528
+ access: "create",
72529
+ role: "admin"
72530
+ },
71761
72531
  "localNetwork.setAddressRole": {
71762
72532
  capName: "local-network",
71763
72533
  capScope: "system",
@@ -71779,6 +72549,13 @@ Object.freeze({
71779
72549
  access: "create",
71780
72550
  role: "admin"
71781
72551
  },
72552
+ "localNetwork.setScanSubnets": {
72553
+ capName: "local-network",
72554
+ capScope: "system",
72555
+ addonId: null,
72556
+ access: "create",
72557
+ role: "admin"
72558
+ },
71782
72559
  "localNetwork.setViewerEndpoints": {
71783
72560
  capName: "local-network",
71784
72561
  capScope: "system",
@@ -76000,6 +76777,13 @@ Object.freeze({
76000
76777
  access: "create",
76001
76778
  role: "admin"
76002
76779
  },
76780
+ "storageAdmin.dropOrphanedKind": {
76781
+ capName: "storage-admin",
76782
+ capScope: "system",
76783
+ addonId: null,
76784
+ access: "delete",
76785
+ role: "admin"
76786
+ },
76003
76787
  "storageAdmin.listKinds": {
76004
76788
  capName: "storage-admin",
76005
76789
  capScope: "system",
@@ -76021,6 +76805,27 @@ Object.freeze({
76021
76805
  access: "view",
76022
76806
  role: "admin"
76023
76807
  },
76808
+ "storageAdmin.scanStaleData": {
76809
+ capName: "storage-admin",
76810
+ capScope: "system",
76811
+ addonId: null,
76812
+ access: "create",
76813
+ role: "admin"
76814
+ },
76815
+ "storageAdmin.staleDataReport": {
76816
+ capName: "storage-admin",
76817
+ capScope: "system",
76818
+ addonId: null,
76819
+ access: "view",
76820
+ role: "admin"
76821
+ },
76822
+ "storageAdmin.unsetStaleSettingsKey": {
76823
+ capName: "storage-admin",
76824
+ capScope: "system",
76825
+ addonId: null,
76826
+ access: "create",
76827
+ role: "admin"
76828
+ },
76024
76829
  "storageEvictable.evict": {
76025
76830
  capName: "storage-evictable",
76026
76831
  capScope: "system",
@@ -81717,7 +82522,7 @@ function projectAction(cap, action) {
81717
82522
  };
81718
82523
  }
81719
82524
  Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
81720
- DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
82525
+ DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
81721
82526
  DeviceFeature.BatteryOperated;
81722
82527
  DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
81723
82528
  var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
@@ -85974,7 +86779,7 @@ var AdminUIAddon = class extends BaseAddon {
85974
86779
  capability: adminUiCapability,
85975
86780
  provider: {
85976
86781
  getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
85977
- getVersion: async () => ({ version: "1.2.525" })
86782
+ getVersion: async () => ({ version: "1.2.527" })
85978
86783
  }
85979
86784
  }];
85980
86785
  }