@camstack/addon-osd-manager 0.1.239 → 0.1.241

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 (17) hide show
  1. package/dist/{MotionZonesSettings-BU-acK9Z.mjs → MotionZonesSettings-G8UCebu1.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-DMXiC1dM.mjs → PrivacyMaskSettings-2NTC9B3W.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-ePI2t_wT.mjs → SceneMonitorEditor-ByMmaROK.mjs} +3 -3
  4. package/dist/_stub.js +2763 -2729
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-CNlBultD.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-BBDNfcPI.mjs} +3 -3
  6. package/dist/{_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DkfPNoLU.mjs → _virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BFkoKPX3.mjs} +1 -1
  7. package/dist/{hostInit-CTiW6F1s.mjs → hostInit-DCTyM4Bh.mjs} +2 -2
  8. package/dist/index.js +189 -26
  9. package/dist/index.mjs +189 -26
  10. package/dist/{player-overlays-B4efNR1A.mjs → player-overlays-B9Leo3bK.mjs} +1 -1
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-CvSyX7h3.mjs → responsive-DCFnF6WM.mjs} +1 -1
  13. package/dist/{scene-monitor-copy-QAemI7ef.mjs → scene-monitor-copy-B3PBzmS-.mjs} +1 -1
  14. package/dist/{square-BG-oZkZc.mjs → square-CvNDzXRw.mjs} +1 -1
  15. package/dist/{trash-2-DvemWMy7.mjs → trash-2-DHcGMoei.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-CbjSvUsn.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-qRoXJuGX.mjs} +1 -1
  17. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5390,7 +5390,7 @@ var ZodIssueCode = {
5390
5390
  var ZodFirstPartyTypeKind;
5391
5391
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5392
5392
  //#endregion
5393
- //#region ../types/dist/sleep-DRqxtc0C.mjs
5393
+ //#region ../types/dist/sleep-bwsTj221.mjs
5394
5394
  /**
5395
5395
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5396
5396
  * window to float samples (D455).
@@ -6641,7 +6641,6 @@ function createHandle(raw, opts) {
6641
6641
  const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
6642
6642
  const readAddonStoreResult = raw.readAddonStoreResult;
6643
6643
  const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
6644
- const readDeviceStoreBatch = raw.readDeviceStoreBatch;
6645
6644
  const overrides = {
6646
6645
  readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
6647
6646
  ...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
@@ -6665,12 +6664,12 @@ function createHandle(raw, opts) {
6665
6664
  };
6666
6665
  } } : {},
6667
6666
  readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
6668
- ...readDeviceStoreBatch !== void 0 ? { readDeviceStoreBatch: async (deviceIds) => {
6669
- const rows = await readDeviceStoreBatch.call(raw, deviceIds);
6667
+ readDeviceStoreBatch: async (deviceIds) => {
6668
+ const rows = await raw.readDeviceStoreBatch(deviceIds);
6670
6669
  const out = /* @__PURE__ */ new Map();
6671
6670
  for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
6672
6671
  return out;
6673
- } } : {},
6672
+ },
6674
6673
  writeAddonStore: async (patch) => {
6675
6674
  await writeAddon(patch);
6676
6675
  },
@@ -7497,7 +7496,7 @@ var BaseAddon = class {
7497
7496
  return this._addonSecretTree;
7498
7497
  }
7499
7498
  deviceSecretTree() {
7500
- this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
7499
+ this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
7501
7500
  return this._deviceSecretTree;
7502
7501
  }
7503
7502
  /**
@@ -7573,42 +7572,135 @@ var BaseAddon = class {
7573
7572
  this[timerOf] = timer;
7574
7573
  }
7575
7574
  /**
7576
- * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
7577
- * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
7578
- * swap, or any race where the SqliteSettingsBackend is between shutdown and
7579
- * re-initialize) doesn't propagate up into `initialize()` and leave the
7575
+ * Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
7576
+ * settings-store outage doesn't propagate up into `initialize()` and leave the
7580
7577
  * addon permanently broken.
7581
7578
  *
7582
- * Retry only the two known infra fingerprints. Anything else propagates so
7583
- * real bugs surface immediately. After the budget expires we fall back to
7584
- * `{}` (defaults) — the addon's first successful patch will rehydrate.
7579
+ * Two kinds of outage, two budgets:
7580
+ * - the store's own restart fingerprints (`SqliteSettingsBackend not
7581
+ * initialized`, `provider not available`): a short budget, then the addon
7582
+ * falls back to `{}` (defaults) - its first successful patch rehydrates;
7583
+ * - a TRANSPORT failure (the hub is restarting while this runner boots:
7584
+ * `discovery deadline exceeded`, `parent could not route`, a refused or
7585
+ * timed-out connection). Since D870 a runner's settings read THROWS instead
7586
+ * of answering `{}`, so without a budget a hub restart ends in a respawn
7587
+ * loop. A long, bounded budget; only after it the error propagates - the
7588
+ * addon never runs on defaults for a store it could not ask.
7589
+ * Anything else propagates immediately so real bugs surface.
7585
7590
  */
7586
7591
  async readAddonStoreWithRetry() {
7587
7592
  const settings = this.rawSettings();
7588
7593
  if (!settings) return {};
7589
- const delaysMs = [
7590
- 150,
7591
- 350,
7592
- 600,
7593
- 900
7594
- ];
7595
7594
  let lastErr;
7596
- for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
7595
+ for (let attempt = 0;; attempt++) try {
7597
7596
  const stored = await settings.readAddonStore() ?? {};
7598
7597
  this.settingsStoreReady = true;
7599
7598
  return stored;
7600
7599
  } catch (err) {
7601
7600
  lastErr = err;
7602
- const msg = err instanceof Error ? err.message : String(err);
7603
- if (!(msg.includes("SqliteSettingsBackend not initialized") || msg.includes("provider not available"))) throw err;
7604
- if (attempt === delaysMs.length) break;
7605
- await new Promise((r) => setTimeout(r, delaysMs[attempt]));
7601
+ const kind = classifyStoreError(err);
7602
+ if (kind === "permanent") throw err;
7603
+ const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
7604
+ if (delay === void 0) {
7605
+ if (kind === "transport") throw err;
7606
+ break;
7607
+ }
7608
+ await new Promise((r) => setTimeout(r, delay));
7606
7609
  }
7607
7610
  this.settingsStoreReady = false;
7608
7611
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
7609
7612
  return {};
7610
7613
  }
7611
7614
  };
7615
+ /** The settings store's own restart fingerprints. */
7616
+ var RESTART_RETRY_DELAYS_MS = [
7617
+ 150,
7618
+ 350,
7619
+ 600,
7620
+ 900
7621
+ ];
7622
+ /** A hub restarting under a booting runner: ~100 s in total. */
7623
+ var TRANSPORT_RETRY_DELAYS_MS = [
7624
+ 150,
7625
+ 350,
7626
+ 600,
7627
+ 900,
7628
+ 2e3,
7629
+ 5e3,
7630
+ 1e4,
7631
+ 2e4,
7632
+ 3e4,
7633
+ 3e4
7634
+ ];
7635
+ var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
7636
+ /** Node / socket error codes of a connection that did not happen or broke. */
7637
+ var TRANSPORT_CODES = new Set([
7638
+ "ECONNREFUSED",
7639
+ "ECONNRESET",
7640
+ "ETIMEDOUT",
7641
+ "EPIPE",
7642
+ "EHOSTUNREACH",
7643
+ "ENETUNREACH"
7644
+ ]);
7645
+ /** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
7646
+ var UNAVAILABLE_TRPC_CODES = new Set([
7647
+ "TIMEOUT",
7648
+ "SERVICE_UNAVAILABLE",
7649
+ "BAD_GATEWAY",
7650
+ "GATEWAY_TIMEOUT"
7651
+ ]);
7652
+ /** The exact phrases OUR transport raises when it cannot reach the hub. */
7653
+ var TRANSPORT_PHRASES = [
7654
+ "discovery deadline exceeded",
7655
+ "parent could not route child unowned cap call",
7656
+ "UDS request timed out after",
7657
+ "channel closed"
7658
+ ];
7659
+ /** Moleculer error `type`s of a call that could not be served right now. */
7660
+ var MOLECULER_TRANSIENT_TYPES = new Set([
7661
+ "REQUEST_TIMEOUT",
7662
+ "SERVICE_NOT_FOUND",
7663
+ "SERVICE_NOT_AVAILABLE",
7664
+ "REQUEST_REJECTED",
7665
+ "BROKER_DISCONNECTED"
7666
+ ]);
7667
+ function stringProp(value, key) {
7668
+ if (typeof value !== "object" || value === null) return void 0;
7669
+ const found = Reflect.get(value, key);
7670
+ return typeof found === "string" ? found : void 0;
7671
+ }
7672
+ /** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
7673
+ function errorCode(err) {
7674
+ const out = {};
7675
+ const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
7676
+ const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
7677
+ const node = stringProp(err, "code") ?? stringProp(cause, "code");
7678
+ if (node !== void 0) out.node = node;
7679
+ const trpc = stringProp(data, "code");
7680
+ if (trpc !== void 0) out.trpc = trpc;
7681
+ return out;
7682
+ }
7683
+ /**
7684
+ * Is a settings read failure worth retrying, and with which budget? Only
7685
+ * EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
7686
+ * one of the exact phrases our own transport raises. A message that merely
7687
+ * contains "timed out" or "unreachable" is not evidence of anything, and
7688
+ * `settings store not wired` is a wiring bug that retrying cannot fix.
7689
+ */
7690
+ function classifyStoreError(err) {
7691
+ const message = err instanceof Error ? err.message : String(err);
7692
+ if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
7693
+ const code = errorCode(err);
7694
+ if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
7695
+ if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
7696
+ if (typeof err === "object" && err !== null) {
7697
+ if (Reflect.get(err, "retryable") === true) return "transport";
7698
+ const type = stringProp(err, "type");
7699
+ if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
7700
+ }
7701
+ if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
7702
+ return "permanent";
7703
+ }
7612
7704
  /**
7613
7705
  * Collect the keys of every field marked `perNode: true`, recursing into
7614
7706
  * layout containers (`group` fields and `sub-tabs` tabs) the same way
@@ -8163,7 +8255,7 @@ object({
8163
8255
  params: record(string(), union([string(), number()])).optional()
8164
8256
  });
8165
8257
  //#endregion
8166
- //#region ../types/dist/err-msg-DLerKiVO.mjs
8258
+ //#region ../types/dist/err-msg--2RrYiLO.mjs
8167
8259
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
8168
8260
  DeviceType["Camera"] = "camera";
8169
8261
  DeviceType["Hub"] = "hub";
@@ -8432,6 +8524,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8432
8524
  /** Virtual HA toggle (input_boolean.*) — distinguishable from a
8433
8525
  * real Switch device for UI rendering / export adapters. */
8434
8526
  DeviceRole["BinaryHelper"] = "binary-helper";
8527
+ /** A smart plug: a `switch` whose source says it is an outlet (HA
8528
+ * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
8529
+ DeviceRole["Outlet"] = "outlet";
8435
8530
  /** Generic motion / occupancy / moving event source. Distinct from
8436
8531
  * the camera accessory PirSensor role: that one is a camera child;
8437
8532
  * this is a standalone HA / 3rd-party motion sensor. */
@@ -8499,7 +8594,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8499
8594
  return DeviceRole;
8500
8595
  }({});
8501
8596
  //#endregion
8502
- //#region ../types/dist/composition-EQ9Ok0Ee.mjs
8597
+ //#region ../types/dist/composition-DOwQB_Ua.mjs
8503
8598
  /**
8504
8599
  * The ffmpeg build every CamStack node downloads, per platform and arch.
8505
8600
  *
@@ -15838,6 +15933,28 @@ id: union([string(), number()]) }), {
15838
15933
  kind: "mutation",
15839
15934
  auth: "admin"
15840
15935
  });
15936
+ /** One table of the settings database. */
15937
+ var StorageTableSchema = object({
15938
+ /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
15939
+ name: string(),
15940
+ /** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
15941
+ owner: string().nullable(),
15942
+ rows: number().int().nonnegative(),
15943
+ /** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
15944
+ bytes: number().int().nonnegative().nullable(),
15945
+ /** Declared by an addon since this engine process started (a running addon claims it). */
15946
+ declaredThisBoot: boolean()
15947
+ });
15948
+ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
15949
+ collection: string().min(1),
15950
+ expectRows: number().int().nonnegative()
15951
+ }), object({
15952
+ dropped: literal(true),
15953
+ rows: number().int().nonnegative()
15954
+ }), {
15955
+ kind: "mutation",
15956
+ auth: "admin"
15957
+ });
15841
15958
  /**
15842
15959
  * `keychain` — the hub's encrypted store of secret values (D787).
15843
15960
  *
@@ -16379,6 +16496,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
16379
16496
  kind: "mutation",
16380
16497
  auth: "admin",
16381
16498
  caller: "required"
16499
+ }), method(object({
16500
+ ref: string().min(1),
16501
+ value: string().min(1)
16502
+ }), object({
16503
+ ref: string(),
16504
+ version: number().int()
16505
+ }), {
16506
+ kind: "mutation",
16507
+ auth: "admin",
16508
+ caller: "required"
16382
16509
  }), method(object({ refs: array(string().min(1)).max(1e3) }), object({
16383
16510
  deleted: array(string()).readonly(),
16384
16511
  kept: array(object({
@@ -53437,6 +53564,12 @@ object({
53437
53564
  /** Horizontal louver swing on/off. Null when the device has no
53438
53565
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
53439
53566
  swingHorizontal: boolean().nullable(),
53567
+ /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
53568
+ minTemp: number().nullable().optional(),
53569
+ /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
53570
+ maxTemp: number().nullable().optional(),
53571
+ /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
53572
+ tempStep: number().positive().nullable().optional(),
53440
53573
  /** Ms epoch when the slice was last updated (push or command). */
53441
53574
  lastFetchedAt: number()
53442
53575
  });
@@ -64722,6 +64855,12 @@ Object.freeze({
64722
64855
  addonId: null,
64723
64856
  access: "create"
64724
64857
  },
64858
+ "keychainAdmin.importSecret": {
64859
+ capName: "keychain-admin",
64860
+ capScope: "system",
64861
+ addonId: null,
64862
+ access: "create"
64863
+ },
64725
64864
  "keychainAdmin.listAudit": {
64726
64865
  capName: "keychain-admin",
64727
64866
  capScope: "system",
@@ -68508,6 +68647,18 @@ Object.freeze({
68508
68647
  addonId: null,
68509
68648
  access: "create"
68510
68649
  },
68650
+ "storageAdmin.dropCollection": {
68651
+ capName: "storage-admin",
68652
+ capScope: "system",
68653
+ addonId: null,
68654
+ access: "delete"
68655
+ },
68656
+ "storageAdmin.listTables": {
68657
+ capName: "storage-admin",
68658
+ capScope: "system",
68659
+ addonId: null,
68660
+ access: "view"
68661
+ },
68511
68662
  "storageEvictable.evict": {
68512
68663
  capName: "storage-evictable",
68513
68664
  capScope: "system",
@@ -75073,6 +75224,18 @@ var CAP_STATE_LEAVES = Object.freeze({
75073
75224
  {
75074
75225
  "path": "swingHorizontal",
75075
75226
  "valueType": "boolean"
75227
+ },
75228
+ {
75229
+ "path": "minTemp",
75230
+ "valueType": "number"
75231
+ },
75232
+ {
75233
+ "path": "maxTemp",
75234
+ "valueType": "number"
75235
+ },
75236
+ {
75237
+ "path": "tempStep",
75238
+ "valueType": "number"
75076
75239
  }
75077
75240
  ],
75078
75241
  "color": [
package/dist/index.mjs CHANGED
@@ -5386,7 +5386,7 @@ var ZodIssueCode = {
5386
5386
  var ZodFirstPartyTypeKind;
5387
5387
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5388
5388
  //#endregion
5389
- //#region ../types/dist/sleep-DRqxtc0C.mjs
5389
+ //#region ../types/dist/sleep-bwsTj221.mjs
5390
5390
  /**
5391
5391
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5392
5392
  * window to float samples (D455).
@@ -6637,7 +6637,6 @@ function createHandle(raw, opts) {
6637
6637
  const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
6638
6638
  const readAddonStoreResult = raw.readAddonStoreResult;
6639
6639
  const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
6640
- const readDeviceStoreBatch = raw.readDeviceStoreBatch;
6641
6640
  const overrides = {
6642
6641
  readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
6643
6642
  ...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
@@ -6661,12 +6660,12 @@ function createHandle(raw, opts) {
6661
6660
  };
6662
6661
  } } : {},
6663
6662
  readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
6664
- ...readDeviceStoreBatch !== void 0 ? { readDeviceStoreBatch: async (deviceIds) => {
6665
- const rows = await readDeviceStoreBatch.call(raw, deviceIds);
6663
+ readDeviceStoreBatch: async (deviceIds) => {
6664
+ const rows = await raw.readDeviceStoreBatch(deviceIds);
6666
6665
  const out = /* @__PURE__ */ new Map();
6667
6666
  for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
6668
6667
  return out;
6669
- } } : {},
6668
+ },
6670
6669
  writeAddonStore: async (patch) => {
6671
6670
  await writeAddon(patch);
6672
6671
  },
@@ -7493,7 +7492,7 @@ var BaseAddon = class {
7493
7492
  return this._addonSecretTree;
7494
7493
  }
7495
7494
  deviceSecretTree() {
7496
- this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
7495
+ this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
7497
7496
  return this._deviceSecretTree;
7498
7497
  }
7499
7498
  /**
@@ -7569,42 +7568,135 @@ var BaseAddon = class {
7569
7568
  this[timerOf] = timer;
7570
7569
  }
7571
7570
  /**
7572
- * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
7573
- * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
7574
- * swap, or any race where the SqliteSettingsBackend is between shutdown and
7575
- * re-initialize) doesn't propagate up into `initialize()` and leave the
7571
+ * Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
7572
+ * settings-store outage doesn't propagate up into `initialize()` and leave the
7576
7573
  * addon permanently broken.
7577
7574
  *
7578
- * Retry only the two known infra fingerprints. Anything else propagates so
7579
- * real bugs surface immediately. After the budget expires we fall back to
7580
- * `{}` (defaults) — the addon's first successful patch will rehydrate.
7575
+ * Two kinds of outage, two budgets:
7576
+ * - the store's own restart fingerprints (`SqliteSettingsBackend not
7577
+ * initialized`, `provider not available`): a short budget, then the addon
7578
+ * falls back to `{}` (defaults) - its first successful patch rehydrates;
7579
+ * - a TRANSPORT failure (the hub is restarting while this runner boots:
7580
+ * `discovery deadline exceeded`, `parent could not route`, a refused or
7581
+ * timed-out connection). Since D870 a runner's settings read THROWS instead
7582
+ * of answering `{}`, so without a budget a hub restart ends in a respawn
7583
+ * loop. A long, bounded budget; only after it the error propagates - the
7584
+ * addon never runs on defaults for a store it could not ask.
7585
+ * Anything else propagates immediately so real bugs surface.
7581
7586
  */
7582
7587
  async readAddonStoreWithRetry() {
7583
7588
  const settings = this.rawSettings();
7584
7589
  if (!settings) return {};
7585
- const delaysMs = [
7586
- 150,
7587
- 350,
7588
- 600,
7589
- 900
7590
- ];
7591
7590
  let lastErr;
7592
- for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
7591
+ for (let attempt = 0;; attempt++) try {
7593
7592
  const stored = await settings.readAddonStore() ?? {};
7594
7593
  this.settingsStoreReady = true;
7595
7594
  return stored;
7596
7595
  } catch (err) {
7597
7596
  lastErr = err;
7598
- const msg = err instanceof Error ? err.message : String(err);
7599
- if (!(msg.includes("SqliteSettingsBackend not initialized") || msg.includes("provider not available"))) throw err;
7600
- if (attempt === delaysMs.length) break;
7601
- await new Promise((r) => setTimeout(r, delaysMs[attempt]));
7597
+ const kind = classifyStoreError(err);
7598
+ if (kind === "permanent") throw err;
7599
+ const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
7600
+ if (delay === void 0) {
7601
+ if (kind === "transport") throw err;
7602
+ break;
7603
+ }
7604
+ await new Promise((r) => setTimeout(r, delay));
7602
7605
  }
7603
7606
  this.settingsStoreReady = false;
7604
7607
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
7605
7608
  return {};
7606
7609
  }
7607
7610
  };
7611
+ /** The settings store's own restart fingerprints. */
7612
+ var RESTART_RETRY_DELAYS_MS = [
7613
+ 150,
7614
+ 350,
7615
+ 600,
7616
+ 900
7617
+ ];
7618
+ /** A hub restarting under a booting runner: ~100 s in total. */
7619
+ var TRANSPORT_RETRY_DELAYS_MS = [
7620
+ 150,
7621
+ 350,
7622
+ 600,
7623
+ 900,
7624
+ 2e3,
7625
+ 5e3,
7626
+ 1e4,
7627
+ 2e4,
7628
+ 3e4,
7629
+ 3e4
7630
+ ];
7631
+ var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
7632
+ /** Node / socket error codes of a connection that did not happen or broke. */
7633
+ var TRANSPORT_CODES = new Set([
7634
+ "ECONNREFUSED",
7635
+ "ECONNRESET",
7636
+ "ETIMEDOUT",
7637
+ "EPIPE",
7638
+ "EHOSTUNREACH",
7639
+ "ENETUNREACH"
7640
+ ]);
7641
+ /** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
7642
+ var UNAVAILABLE_TRPC_CODES = new Set([
7643
+ "TIMEOUT",
7644
+ "SERVICE_UNAVAILABLE",
7645
+ "BAD_GATEWAY",
7646
+ "GATEWAY_TIMEOUT"
7647
+ ]);
7648
+ /** The exact phrases OUR transport raises when it cannot reach the hub. */
7649
+ var TRANSPORT_PHRASES = [
7650
+ "discovery deadline exceeded",
7651
+ "parent could not route child unowned cap call",
7652
+ "UDS request timed out after",
7653
+ "channel closed"
7654
+ ];
7655
+ /** Moleculer error `type`s of a call that could not be served right now. */
7656
+ var MOLECULER_TRANSIENT_TYPES = new Set([
7657
+ "REQUEST_TIMEOUT",
7658
+ "SERVICE_NOT_FOUND",
7659
+ "SERVICE_NOT_AVAILABLE",
7660
+ "REQUEST_REJECTED",
7661
+ "BROKER_DISCONNECTED"
7662
+ ]);
7663
+ function stringProp(value, key) {
7664
+ if (typeof value !== "object" || value === null) return void 0;
7665
+ const found = Reflect.get(value, key);
7666
+ return typeof found === "string" ? found : void 0;
7667
+ }
7668
+ /** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
7669
+ function errorCode(err) {
7670
+ const out = {};
7671
+ const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
7672
+ const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
7673
+ const node = stringProp(err, "code") ?? stringProp(cause, "code");
7674
+ if (node !== void 0) out.node = node;
7675
+ const trpc = stringProp(data, "code");
7676
+ if (trpc !== void 0) out.trpc = trpc;
7677
+ return out;
7678
+ }
7679
+ /**
7680
+ * Is a settings read failure worth retrying, and with which budget? Only
7681
+ * EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
7682
+ * one of the exact phrases our own transport raises. A message that merely
7683
+ * contains "timed out" or "unreachable" is not evidence of anything, and
7684
+ * `settings store not wired` is a wiring bug that retrying cannot fix.
7685
+ */
7686
+ function classifyStoreError(err) {
7687
+ const message = err instanceof Error ? err.message : String(err);
7688
+ if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
7689
+ const code = errorCode(err);
7690
+ if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
7691
+ if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
7692
+ if (typeof err === "object" && err !== null) {
7693
+ if (Reflect.get(err, "retryable") === true) return "transport";
7694
+ const type = stringProp(err, "type");
7695
+ if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
7696
+ }
7697
+ if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
7698
+ return "permanent";
7699
+ }
7608
7700
  /**
7609
7701
  * Collect the keys of every field marked `perNode: true`, recursing into
7610
7702
  * layout containers (`group` fields and `sub-tabs` tabs) the same way
@@ -8159,7 +8251,7 @@ object({
8159
8251
  params: record(string(), union([string(), number()])).optional()
8160
8252
  });
8161
8253
  //#endregion
8162
- //#region ../types/dist/err-msg-DLerKiVO.mjs
8254
+ //#region ../types/dist/err-msg--2RrYiLO.mjs
8163
8255
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
8164
8256
  DeviceType["Camera"] = "camera";
8165
8257
  DeviceType["Hub"] = "hub";
@@ -8428,6 +8520,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8428
8520
  /** Virtual HA toggle (input_boolean.*) — distinguishable from a
8429
8521
  * real Switch device for UI rendering / export adapters. */
8430
8522
  DeviceRole["BinaryHelper"] = "binary-helper";
8523
+ /** A smart plug: a `switch` whose source says it is an outlet (HA
8524
+ * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
8525
+ DeviceRole["Outlet"] = "outlet";
8431
8526
  /** Generic motion / occupancy / moving event source. Distinct from
8432
8527
  * the camera accessory PirSensor role: that one is a camera child;
8433
8528
  * this is a standalone HA / 3rd-party motion sensor. */
@@ -8495,7 +8590,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8495
8590
  return DeviceRole;
8496
8591
  }({});
8497
8592
  //#endregion
8498
- //#region ../types/dist/composition-EQ9Ok0Ee.mjs
8593
+ //#region ../types/dist/composition-DOwQB_Ua.mjs
8499
8594
  /**
8500
8595
  * The ffmpeg build every CamStack node downloads, per platform and arch.
8501
8596
  *
@@ -15834,6 +15929,28 @@ id: union([string(), number()]) }), {
15834
15929
  kind: "mutation",
15835
15930
  auth: "admin"
15836
15931
  });
15932
+ /** One table of the settings database. */
15933
+ var StorageTableSchema = object({
15934
+ /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
15935
+ name: string(),
15936
+ /** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
15937
+ owner: string().nullable(),
15938
+ rows: number().int().nonnegative(),
15939
+ /** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
15940
+ bytes: number().int().nonnegative().nullable(),
15941
+ /** Declared by an addon since this engine process started (a running addon claims it). */
15942
+ declaredThisBoot: boolean()
15943
+ });
15944
+ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
15945
+ collection: string().min(1),
15946
+ expectRows: number().int().nonnegative()
15947
+ }), object({
15948
+ dropped: literal(true),
15949
+ rows: number().int().nonnegative()
15950
+ }), {
15951
+ kind: "mutation",
15952
+ auth: "admin"
15953
+ });
15837
15954
  /**
15838
15955
  * `keychain` — the hub's encrypted store of secret values (D787).
15839
15956
  *
@@ -16375,6 +16492,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
16375
16492
  kind: "mutation",
16376
16493
  auth: "admin",
16377
16494
  caller: "required"
16495
+ }), method(object({
16496
+ ref: string().min(1),
16497
+ value: string().min(1)
16498
+ }), object({
16499
+ ref: string(),
16500
+ version: number().int()
16501
+ }), {
16502
+ kind: "mutation",
16503
+ auth: "admin",
16504
+ caller: "required"
16378
16505
  }), method(object({ refs: array(string().min(1)).max(1e3) }), object({
16379
16506
  deleted: array(string()).readonly(),
16380
16507
  kept: array(object({
@@ -53433,6 +53560,12 @@ object({
53433
53560
  /** Horizontal louver swing on/off. Null when the device has no
53434
53561
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
53435
53562
  swingHorizontal: boolean().nullable(),
53563
+ /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
53564
+ minTemp: number().nullable().optional(),
53565
+ /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
53566
+ maxTemp: number().nullable().optional(),
53567
+ /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
53568
+ tempStep: number().positive().nullable().optional(),
53436
53569
  /** Ms epoch when the slice was last updated (push or command). */
53437
53570
  lastFetchedAt: number()
53438
53571
  });
@@ -64718,6 +64851,12 @@ Object.freeze({
64718
64851
  addonId: null,
64719
64852
  access: "create"
64720
64853
  },
64854
+ "keychainAdmin.importSecret": {
64855
+ capName: "keychain-admin",
64856
+ capScope: "system",
64857
+ addonId: null,
64858
+ access: "create"
64859
+ },
64721
64860
  "keychainAdmin.listAudit": {
64722
64861
  capName: "keychain-admin",
64723
64862
  capScope: "system",
@@ -68504,6 +68643,18 @@ Object.freeze({
68504
68643
  addonId: null,
68505
68644
  access: "create"
68506
68645
  },
68646
+ "storageAdmin.dropCollection": {
68647
+ capName: "storage-admin",
68648
+ capScope: "system",
68649
+ addonId: null,
68650
+ access: "delete"
68651
+ },
68652
+ "storageAdmin.listTables": {
68653
+ capName: "storage-admin",
68654
+ capScope: "system",
68655
+ addonId: null,
68656
+ access: "view"
68657
+ },
68507
68658
  "storageEvictable.evict": {
68508
68659
  capName: "storage-evictable",
68509
68660
  capScope: "system",
@@ -75069,6 +75220,18 @@ var CAP_STATE_LEAVES = Object.freeze({
75069
75220
  {
75070
75221
  "path": "swingHorizontal",
75071
75222
  "valueType": "boolean"
75223
+ },
75224
+ {
75225
+ "path": "minTemp",
75226
+ "valueType": "number"
75227
+ },
75228
+ {
75229
+ "path": "maxTemp",
75230
+ "valueType": "number"
75231
+ },
75232
+ {
75233
+ "path": "tempStep",
75234
+ "valueType": "number"
75072
75235
  }
75073
75236
  ],
75074
75237
  "color": [
@@ -1,6 +1,6 @@
1
1
  import { S as e, d as t, u as n, v as r } from "./_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react__loadShare__.js-C-pbjC7c.mjs";
2
2
  import { n as i, r as a, t as o } from "./_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-DalfdIDw.mjs";
3
- import { c as s, l as c } from "./responsive-CvSyX7h3.mjs";
3
+ import { c as s, l as c } from "./responsive-DCFnF6WM.mjs";
4
4
  var l = c("chevron-down", [["path", {
5
5
  d: "m6 9 6 6 6-6",
6
6
  key: "qrunsl"
@@ -1,2 +1,2 @@
1
- import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-CbjSvUsn.mjs";
1
+ import { n as e, t } from "./virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-qRoXJuGX.mjs";
2
2
  export { t as get, e as init };