@camstack/addon-training 0.1.49 → 0.1.51

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 (21) hide show
  1. package/dist/{MotionZonesSettings-B1g6IUfQ.mjs → MotionZonesSettings-UIEvGQ7i.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-D4WRZZeD.mjs → PrivacyMaskSettings-8HsoAjc4.mjs} +2 -2
  3. package/dist/{SceneMonitorEditor-pYZfyXtn.mjs → SceneMonitorEditor-B-2phYr7.mjs} +1 -1
  4. package/dist/_stub.js +4583 -4578
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-OY71DavC.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-BitwJXj2.mjs} +3 -3
  6. package/dist/{_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BVsoOuTM.mjs → _virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B91cVqvt.mjs} +1 -1
  7. package/dist/{dist-DvgVhVUN.js → dist-1Yys6sQo.js} +177 -26
  8. package/dist/{dist-D-7oMcBM.mjs → dist-tkgio-iT.mjs} +177 -26
  9. package/dist/{hostInit-B3DR-ujG.mjs → hostInit-DdbXSPxl.mjs} +2 -2
  10. package/dist/{icons-BwkaMam3.mjs → icons-BnErSNU6.mjs} +1056 -1073
  11. package/dist/index.js +1 -1
  12. package/dist/orchestrator/training.addon.js +1 -1
  13. package/dist/orchestrator/training.addon.mjs +1 -1
  14. package/dist/{player-overlays-B4V_uIvb.mjs → player-overlays-C8W-hfE1.mjs} +1 -1
  15. package/dist/providers/modal/training-modal.addon.js +1 -1
  16. package/dist/providers/modal/training-modal.addon.mjs +1 -1
  17. package/dist/remoteEntry.js +1 -1
  18. package/dist/{responsive-1iwc-rj2.mjs → responsive-BkgEJLES.mjs} +1 -1
  19. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-Dk6Nf5b3.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js--X40FWTZ.mjs} +1 -1
  20. package/dist/{zoom-out-DDZCsqkQ.mjs → zoom-out-hR5SOKZW.mjs} +82 -65
  21. package/package.json +1 -1
@@ -5420,7 +5420,7 @@ var ZodIssueCode = {
5420
5420
  var ZodFirstPartyTypeKind;
5421
5421
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5422
5422
  //#endregion
5423
- //#region ../types/dist/sleep-DRqxtc0C.mjs
5423
+ //#region ../types/dist/sleep-bwsTj221.mjs
5424
5424
  /**
5425
5425
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5426
5426
  * window to float samples (D455).
@@ -6671,7 +6671,6 @@ function createHandle(raw, opts) {
6671
6671
  const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
6672
6672
  const readAddonStoreResult = raw.readAddonStoreResult;
6673
6673
  const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
6674
- const readDeviceStoreBatch = raw.readDeviceStoreBatch;
6675
6674
  const overrides = {
6676
6675
  readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
6677
6676
  ...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
@@ -6695,12 +6694,12 @@ function createHandle(raw, opts) {
6695
6694
  };
6696
6695
  } } : {},
6697
6696
  readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
6698
- ...readDeviceStoreBatch !== void 0 ? { readDeviceStoreBatch: async (deviceIds) => {
6699
- const rows = await readDeviceStoreBatch.call(raw, deviceIds);
6697
+ readDeviceStoreBatch: async (deviceIds) => {
6698
+ const rows = await raw.readDeviceStoreBatch(deviceIds);
6700
6699
  const out = /* @__PURE__ */ new Map();
6701
6700
  for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
6702
6701
  return out;
6703
- } } : {},
6702
+ },
6704
6703
  writeAddonStore: async (patch) => {
6705
6704
  await writeAddon(patch);
6706
6705
  },
@@ -7527,7 +7526,7 @@ var BaseAddon = class {
7527
7526
  return this._addonSecretTree;
7528
7527
  }
7529
7528
  deviceSecretTree() {
7530
- this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
7529
+ this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
7531
7530
  return this._deviceSecretTree;
7532
7531
  }
7533
7532
  /**
@@ -7603,42 +7602,135 @@ var BaseAddon = class {
7603
7602
  this[timerOf] = timer;
7604
7603
  }
7605
7604
  /**
7606
- * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
7607
- * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
7608
- * swap, or any race where the SqliteSettingsBackend is between shutdown and
7609
- * re-initialize) doesn't propagate up into `initialize()` and leave the
7605
+ * Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
7606
+ * settings-store outage doesn't propagate up into `initialize()` and leave the
7610
7607
  * addon permanently broken.
7611
7608
  *
7612
- * Retry only the two known infra fingerprints. Anything else propagates so
7613
- * real bugs surface immediately. After the budget expires we fall back to
7614
- * `{}` (defaults) — the addon's first successful patch will rehydrate.
7609
+ * Two kinds of outage, two budgets:
7610
+ * - the store's own restart fingerprints (`SqliteSettingsBackend not
7611
+ * initialized`, `provider not available`): a short budget, then the addon
7612
+ * falls back to `{}` (defaults) - its first successful patch rehydrates;
7613
+ * - a TRANSPORT failure (the hub is restarting while this runner boots:
7614
+ * `discovery deadline exceeded`, `parent could not route`, a refused or
7615
+ * timed-out connection). Since D870 a runner's settings read THROWS instead
7616
+ * of answering `{}`, so without a budget a hub restart ends in a respawn
7617
+ * loop. A long, bounded budget; only after it the error propagates - the
7618
+ * addon never runs on defaults for a store it could not ask.
7619
+ * Anything else propagates immediately so real bugs surface.
7615
7620
  */
7616
7621
  async readAddonStoreWithRetry() {
7617
7622
  const settings = this.rawSettings();
7618
7623
  if (!settings) return {};
7619
- const delaysMs = [
7620
- 150,
7621
- 350,
7622
- 600,
7623
- 900
7624
- ];
7625
7624
  let lastErr;
7626
- for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
7625
+ for (let attempt = 0;; attempt++) try {
7627
7626
  const stored = await settings.readAddonStore() ?? {};
7628
7627
  this.settingsStoreReady = true;
7629
7628
  return stored;
7630
7629
  } catch (err) {
7631
7630
  lastErr = err;
7632
- const msg = err instanceof Error ? err.message : String(err);
7633
- if (!(msg.includes("SqliteSettingsBackend not initialized") || msg.includes("provider not available"))) throw err;
7634
- if (attempt === delaysMs.length) break;
7635
- await new Promise((r) => setTimeout(r, delaysMs[attempt]));
7631
+ const kind = classifyStoreError(err);
7632
+ if (kind === "permanent") throw err;
7633
+ const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
7634
+ if (delay === void 0) {
7635
+ if (kind === "transport") throw err;
7636
+ break;
7637
+ }
7638
+ await new Promise((r) => setTimeout(r, delay));
7636
7639
  }
7637
7640
  this.settingsStoreReady = false;
7638
7641
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
7639
7642
  return {};
7640
7643
  }
7641
7644
  };
7645
+ /** The settings store's own restart fingerprints. */
7646
+ var RESTART_RETRY_DELAYS_MS = [
7647
+ 150,
7648
+ 350,
7649
+ 600,
7650
+ 900
7651
+ ];
7652
+ /** A hub restarting under a booting runner: ~100 s in total. */
7653
+ var TRANSPORT_RETRY_DELAYS_MS = [
7654
+ 150,
7655
+ 350,
7656
+ 600,
7657
+ 900,
7658
+ 2e3,
7659
+ 5e3,
7660
+ 1e4,
7661
+ 2e4,
7662
+ 3e4,
7663
+ 3e4
7664
+ ];
7665
+ var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
7666
+ /** Node / socket error codes of a connection that did not happen or broke. */
7667
+ var TRANSPORT_CODES = new Set([
7668
+ "ECONNREFUSED",
7669
+ "ECONNRESET",
7670
+ "ETIMEDOUT",
7671
+ "EPIPE",
7672
+ "EHOSTUNREACH",
7673
+ "ENETUNREACH"
7674
+ ]);
7675
+ /** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
7676
+ var UNAVAILABLE_TRPC_CODES = new Set([
7677
+ "TIMEOUT",
7678
+ "SERVICE_UNAVAILABLE",
7679
+ "BAD_GATEWAY",
7680
+ "GATEWAY_TIMEOUT"
7681
+ ]);
7682
+ /** The exact phrases OUR transport raises when it cannot reach the hub. */
7683
+ var TRANSPORT_PHRASES = [
7684
+ "discovery deadline exceeded",
7685
+ "parent could not route child unowned cap call",
7686
+ "UDS request timed out after",
7687
+ "channel closed"
7688
+ ];
7689
+ /** Moleculer error `type`s of a call that could not be served right now. */
7690
+ var MOLECULER_TRANSIENT_TYPES = new Set([
7691
+ "REQUEST_TIMEOUT",
7692
+ "SERVICE_NOT_FOUND",
7693
+ "SERVICE_NOT_AVAILABLE",
7694
+ "REQUEST_REJECTED",
7695
+ "BROKER_DISCONNECTED"
7696
+ ]);
7697
+ function stringProp(value, key) {
7698
+ if (typeof value !== "object" || value === null) return void 0;
7699
+ const found = Reflect.get(value, key);
7700
+ return typeof found === "string" ? found : void 0;
7701
+ }
7702
+ /** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
7703
+ function errorCode(err) {
7704
+ const out = {};
7705
+ const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
7706
+ const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
7707
+ const node = stringProp(err, "code") ?? stringProp(cause, "code");
7708
+ if (node !== void 0) out.node = node;
7709
+ const trpc = stringProp(data, "code");
7710
+ if (trpc !== void 0) out.trpc = trpc;
7711
+ return out;
7712
+ }
7713
+ /**
7714
+ * Is a settings read failure worth retrying, and with which budget? Only
7715
+ * EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
7716
+ * one of the exact phrases our own transport raises. A message that merely
7717
+ * contains "timed out" or "unreachable" is not evidence of anything, and
7718
+ * `settings store not wired` is a wiring bug that retrying cannot fix.
7719
+ */
7720
+ function classifyStoreError(err) {
7721
+ const message = err instanceof Error ? err.message : String(err);
7722
+ if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
7723
+ const code = errorCode(err);
7724
+ if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
7725
+ if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
7726
+ if (typeof err === "object" && err !== null) {
7727
+ if (Reflect.get(err, "retryable") === true) return "transport";
7728
+ const type = stringProp(err, "type");
7729
+ if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
7730
+ }
7731
+ if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
7732
+ return "permanent";
7733
+ }
7642
7734
  /**
7643
7735
  * Collect the keys of every field marked `perNode: true`, recursing into
7644
7736
  * layout containers (`group` fields and `sub-tabs` tabs) the same way
@@ -8193,7 +8285,7 @@ object({
8193
8285
  params: record(string(), union([string(), number()])).optional()
8194
8286
  });
8195
8287
  //#endregion
8196
- //#region ../types/dist/err-msg-DLerKiVO.mjs
8288
+ //#region ../types/dist/err-msg--2RrYiLO.mjs
8197
8289
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
8198
8290
  DeviceType["Camera"] = "camera";
8199
8291
  DeviceType["Hub"] = "hub";
@@ -8462,6 +8554,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8462
8554
  /** Virtual HA toggle (input_boolean.*) — distinguishable from a
8463
8555
  * real Switch device for UI rendering / export adapters. */
8464
8556
  DeviceRole["BinaryHelper"] = "binary-helper";
8557
+ /** A smart plug: a `switch` whose source says it is an outlet (HA
8558
+ * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
8559
+ DeviceRole["Outlet"] = "outlet";
8465
8560
  /** Generic motion / occupancy / moving event source. Distinct from
8466
8561
  * the camera accessory PirSensor role: that one is a camera child;
8467
8562
  * this is a standalone HA / 3rd-party motion sensor. */
@@ -8529,7 +8624,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8529
8624
  return DeviceRole;
8530
8625
  }({});
8531
8626
  //#endregion
8532
- //#region ../types/dist/composition-EQ9Ok0Ee.mjs
8627
+ //#region ../types/dist/composition-DOwQB_Ua.mjs
8533
8628
  /**
8534
8629
  * The ffmpeg build every CamStack node downloads, per platform and arch.
8535
8630
  *
@@ -15886,6 +15981,28 @@ id: union([string(), number()]) }), {
15886
15981
  kind: "mutation",
15887
15982
  auth: "admin"
15888
15983
  });
15984
+ /** One table of the settings database. */
15985
+ var StorageTableSchema = object({
15986
+ /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
15987
+ name: string(),
15988
+ /** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
15989
+ owner: string().nullable(),
15990
+ rows: number().int().nonnegative(),
15991
+ /** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
15992
+ bytes: number().int().nonnegative().nullable(),
15993
+ /** Declared by an addon since this engine process started (a running addon claims it). */
15994
+ declaredThisBoot: boolean()
15995
+ });
15996
+ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
15997
+ collection: string().min(1),
15998
+ expectRows: number().int().nonnegative()
15999
+ }), object({
16000
+ dropped: literal(true),
16001
+ rows: number().int().nonnegative()
16002
+ }), {
16003
+ kind: "mutation",
16004
+ auth: "admin"
16005
+ });
15889
16006
  /**
15890
16007
  * `keychain` — the hub's encrypted store of secret values (D787).
15891
16008
  *
@@ -16427,6 +16544,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
16427
16544
  kind: "mutation",
16428
16545
  auth: "admin",
16429
16546
  caller: "required"
16547
+ }), method(object({
16548
+ ref: string().min(1),
16549
+ value: string().min(1)
16550
+ }), object({
16551
+ ref: string(),
16552
+ version: number().int()
16553
+ }), {
16554
+ kind: "mutation",
16555
+ auth: "admin",
16556
+ caller: "required"
16430
16557
  }), method(object({ refs: array(string().min(1)).max(1e3) }), object({
16431
16558
  deleted: array(string()).readonly(),
16432
16559
  kept: array(object({
@@ -52872,6 +52999,12 @@ object({
52872
52999
  /** Horizontal louver swing on/off. Null when the device has no
52873
53000
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
52874
53001
  swingHorizontal: boolean().nullable(),
53002
+ /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
53003
+ minTemp: number().nullable().optional(),
53004
+ /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
53005
+ maxTemp: number().nullable().optional(),
53006
+ /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
53007
+ tempStep: number().positive().nullable().optional(),
52875
53008
  /** Ms epoch when the slice was last updated (push or command). */
52876
53009
  lastFetchedAt: number()
52877
53010
  });
@@ -63879,6 +64012,12 @@ Object.freeze({
63879
64012
  addonId: null,
63880
64013
  access: "create"
63881
64014
  },
64015
+ "keychainAdmin.importSecret": {
64016
+ capName: "keychain-admin",
64017
+ capScope: "system",
64018
+ addonId: null,
64019
+ access: "create"
64020
+ },
63882
64021
  "keychainAdmin.listAudit": {
63883
64022
  capName: "keychain-admin",
63884
64023
  capScope: "system",
@@ -67665,6 +67804,18 @@ Object.freeze({
67665
67804
  addonId: null,
67666
67805
  access: "create"
67667
67806
  },
67807
+ "storageAdmin.dropCollection": {
67808
+ capName: "storage-admin",
67809
+ capScope: "system",
67810
+ addonId: null,
67811
+ access: "delete"
67812
+ },
67813
+ "storageAdmin.listTables": {
67814
+ capName: "storage-admin",
67815
+ capScope: "system",
67816
+ addonId: null,
67817
+ access: "view"
67818
+ },
67668
67819
  "storageEvictable.evict": {
67669
67820
  capName: "storage-evictable",
67670
67821
  capScope: "system",
@@ -5387,7 +5387,7 @@ var ZodIssueCode = {
5387
5387
  var ZodFirstPartyTypeKind;
5388
5388
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5389
5389
  //#endregion
5390
- //#region ../types/dist/sleep-DRqxtc0C.mjs
5390
+ //#region ../types/dist/sleep-bwsTj221.mjs
5391
5391
  /**
5392
5392
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5393
5393
  * window to float samples (D455).
@@ -6638,7 +6638,6 @@ function createHandle(raw, opts) {
6638
6638
  const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
6639
6639
  const readAddonStoreResult = raw.readAddonStoreResult;
6640
6640
  const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
6641
- const readDeviceStoreBatch = raw.readDeviceStoreBatch;
6642
6641
  const overrides = {
6643
6642
  readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
6644
6643
  ...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
@@ -6662,12 +6661,12 @@ function createHandle(raw, opts) {
6662
6661
  };
6663
6662
  } } : {},
6664
6663
  readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
6665
- ...readDeviceStoreBatch !== void 0 ? { readDeviceStoreBatch: async (deviceIds) => {
6666
- const rows = await readDeviceStoreBatch.call(raw, deviceIds);
6664
+ readDeviceStoreBatch: async (deviceIds) => {
6665
+ const rows = await raw.readDeviceStoreBatch(deviceIds);
6667
6666
  const out = /* @__PURE__ */ new Map();
6668
6667
  for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
6669
6668
  return out;
6670
- } } : {},
6669
+ },
6671
6670
  writeAddonStore: async (patch) => {
6672
6671
  await writeAddon(patch);
6673
6672
  },
@@ -7494,7 +7493,7 @@ var BaseAddon = class {
7494
7493
  return this._addonSecretTree;
7495
7494
  }
7496
7495
  deviceSecretTree() {
7497
- this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
7496
+ this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
7498
7497
  return this._deviceSecretTree;
7499
7498
  }
7500
7499
  /**
@@ -7570,42 +7569,135 @@ var BaseAddon = class {
7570
7569
  this[timerOf] = timer;
7571
7570
  }
7572
7571
  /**
7573
- * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
7574
- * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
7575
- * swap, or any race where the SqliteSettingsBackend is between shutdown and
7576
- * re-initialize) doesn't propagate up into `initialize()` and leave the
7572
+ * Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
7573
+ * settings-store outage doesn't propagate up into `initialize()` and leave the
7577
7574
  * addon permanently broken.
7578
7575
  *
7579
- * Retry only the two known infra fingerprints. Anything else propagates so
7580
- * real bugs surface immediately. After the budget expires we fall back to
7581
- * `{}` (defaults) — the addon's first successful patch will rehydrate.
7576
+ * Two kinds of outage, two budgets:
7577
+ * - the store's own restart fingerprints (`SqliteSettingsBackend not
7578
+ * initialized`, `provider not available`): a short budget, then the addon
7579
+ * falls back to `{}` (defaults) - its first successful patch rehydrates;
7580
+ * - a TRANSPORT failure (the hub is restarting while this runner boots:
7581
+ * `discovery deadline exceeded`, `parent could not route`, a refused or
7582
+ * timed-out connection). Since D870 a runner's settings read THROWS instead
7583
+ * of answering `{}`, so without a budget a hub restart ends in a respawn
7584
+ * loop. A long, bounded budget; only after it the error propagates - the
7585
+ * addon never runs on defaults for a store it could not ask.
7586
+ * Anything else propagates immediately so real bugs surface.
7582
7587
  */
7583
7588
  async readAddonStoreWithRetry() {
7584
7589
  const settings = this.rawSettings();
7585
7590
  if (!settings) return {};
7586
- const delaysMs = [
7587
- 150,
7588
- 350,
7589
- 600,
7590
- 900
7591
- ];
7592
7591
  let lastErr;
7593
- for (let attempt = 0; attempt <= delaysMs.length; attempt++) try {
7592
+ for (let attempt = 0;; attempt++) try {
7594
7593
  const stored = await settings.readAddonStore() ?? {};
7595
7594
  this.settingsStoreReady = true;
7596
7595
  return stored;
7597
7596
  } catch (err) {
7598
7597
  lastErr = err;
7599
- const msg = err instanceof Error ? err.message : String(err);
7600
- if (!(msg.includes("SqliteSettingsBackend not initialized") || msg.includes("provider not available"))) throw err;
7601
- if (attempt === delaysMs.length) break;
7602
- await new Promise((r) => setTimeout(r, delaysMs[attempt]));
7598
+ const kind = classifyStoreError(err);
7599
+ if (kind === "permanent") throw err;
7600
+ const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
7601
+ if (delay === void 0) {
7602
+ if (kind === "transport") throw err;
7603
+ break;
7604
+ }
7605
+ await new Promise((r) => setTimeout(r, delay));
7603
7606
  }
7604
7607
  this.settingsStoreReady = false;
7605
7608
  this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
7606
7609
  return {};
7607
7610
  }
7608
7611
  };
7612
+ /** The settings store's own restart fingerprints. */
7613
+ var RESTART_RETRY_DELAYS_MS = [
7614
+ 150,
7615
+ 350,
7616
+ 600,
7617
+ 900
7618
+ ];
7619
+ /** A hub restarting under a booting runner: ~100 s in total. */
7620
+ var TRANSPORT_RETRY_DELAYS_MS = [
7621
+ 150,
7622
+ 350,
7623
+ 600,
7624
+ 900,
7625
+ 2e3,
7626
+ 5e3,
7627
+ 1e4,
7628
+ 2e4,
7629
+ 3e4,
7630
+ 3e4
7631
+ ];
7632
+ var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
7633
+ /** Node / socket error codes of a connection that did not happen or broke. */
7634
+ var TRANSPORT_CODES = new Set([
7635
+ "ECONNREFUSED",
7636
+ "ECONNRESET",
7637
+ "ETIMEDOUT",
7638
+ "EPIPE",
7639
+ "EHOSTUNREACH",
7640
+ "ENETUNREACH"
7641
+ ]);
7642
+ /** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
7643
+ var UNAVAILABLE_TRPC_CODES = new Set([
7644
+ "TIMEOUT",
7645
+ "SERVICE_UNAVAILABLE",
7646
+ "BAD_GATEWAY",
7647
+ "GATEWAY_TIMEOUT"
7648
+ ]);
7649
+ /** The exact phrases OUR transport raises when it cannot reach the hub. */
7650
+ var TRANSPORT_PHRASES = [
7651
+ "discovery deadline exceeded",
7652
+ "parent could not route child unowned cap call",
7653
+ "UDS request timed out after",
7654
+ "channel closed"
7655
+ ];
7656
+ /** Moleculer error `type`s of a call that could not be served right now. */
7657
+ var MOLECULER_TRANSIENT_TYPES = new Set([
7658
+ "REQUEST_TIMEOUT",
7659
+ "SERVICE_NOT_FOUND",
7660
+ "SERVICE_NOT_AVAILABLE",
7661
+ "REQUEST_REJECTED",
7662
+ "BROKER_DISCONNECTED"
7663
+ ]);
7664
+ function stringProp(value, key) {
7665
+ if (typeof value !== "object" || value === null) return void 0;
7666
+ const found = Reflect.get(value, key);
7667
+ return typeof found === "string" ? found : void 0;
7668
+ }
7669
+ /** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
7670
+ function errorCode(err) {
7671
+ const out = {};
7672
+ const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
7673
+ const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
7674
+ const node = stringProp(err, "code") ?? stringProp(cause, "code");
7675
+ if (node !== void 0) out.node = node;
7676
+ const trpc = stringProp(data, "code");
7677
+ if (trpc !== void 0) out.trpc = trpc;
7678
+ return out;
7679
+ }
7680
+ /**
7681
+ * Is a settings read failure worth retrying, and with which budget? Only
7682
+ * EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
7683
+ * one of the exact phrases our own transport raises. A message that merely
7684
+ * contains "timed out" or "unreachable" is not evidence of anything, and
7685
+ * `settings store not wired` is a wiring bug that retrying cannot fix.
7686
+ */
7687
+ function classifyStoreError(err) {
7688
+ const message = err instanceof Error ? err.message : String(err);
7689
+ if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
7690
+ const code = errorCode(err);
7691
+ if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
7692
+ if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
7693
+ if (typeof err === "object" && err !== null) {
7694
+ if (Reflect.get(err, "retryable") === true) return "transport";
7695
+ const type = stringProp(err, "type");
7696
+ if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
7697
+ }
7698
+ if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
7699
+ return "permanent";
7700
+ }
7609
7701
  /**
7610
7702
  * Collect the keys of every field marked `perNode: true`, recursing into
7611
7703
  * layout containers (`group` fields and `sub-tabs` tabs) the same way
@@ -8160,7 +8252,7 @@ object({
8160
8252
  params: record(string(), union([string(), number()])).optional()
8161
8253
  });
8162
8254
  //#endregion
8163
- //#region ../types/dist/err-msg-DLerKiVO.mjs
8255
+ //#region ../types/dist/err-msg--2RrYiLO.mjs
8164
8256
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
8165
8257
  DeviceType["Camera"] = "camera";
8166
8258
  DeviceType["Hub"] = "hub";
@@ -8429,6 +8521,9 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8429
8521
  /** Virtual HA toggle (input_boolean.*) — distinguishable from a
8430
8522
  * real Switch device for UI rendering / export adapters. */
8431
8523
  DeviceRole["BinaryHelper"] = "binary-helper";
8524
+ /** A smart plug: a `switch` whose source says it is an outlet (HA
8525
+ * `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
8526
+ DeviceRole["Outlet"] = "outlet";
8432
8527
  /** Generic motion / occupancy / moving event source. Distinct from
8433
8528
  * the camera accessory PirSensor role: that one is a camera child;
8434
8529
  * this is a standalone HA / 3rd-party motion sensor. */
@@ -8496,7 +8591,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
8496
8591
  return DeviceRole;
8497
8592
  }({});
8498
8593
  //#endregion
8499
- //#region ../types/dist/composition-EQ9Ok0Ee.mjs
8594
+ //#region ../types/dist/composition-DOwQB_Ua.mjs
8500
8595
  /**
8501
8596
  * The ffmpeg build every CamStack node downloads, per platform and arch.
8502
8597
  *
@@ -15853,6 +15948,28 @@ id: union([string(), number()]) }), {
15853
15948
  kind: "mutation",
15854
15949
  auth: "admin"
15855
15950
  });
15951
+ /** One table of the settings database. */
15952
+ var StorageTableSchema = object({
15953
+ /** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
15954
+ name: string(),
15955
+ /** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
15956
+ owner: string().nullable(),
15957
+ rows: number().int().nonnegative(),
15958
+ /** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
15959
+ bytes: number().int().nonnegative().nullable(),
15960
+ /** Declared by an addon since this engine process started (a running addon claims it). */
15961
+ declaredThisBoot: boolean()
15962
+ });
15963
+ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
15964
+ collection: string().min(1),
15965
+ expectRows: number().int().nonnegative()
15966
+ }), object({
15967
+ dropped: literal(true),
15968
+ rows: number().int().nonnegative()
15969
+ }), {
15970
+ kind: "mutation",
15971
+ auth: "admin"
15972
+ });
15856
15973
  /**
15857
15974
  * `keychain` — the hub's encrypted store of secret values (D787).
15858
15975
  *
@@ -16394,6 +16511,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
16394
16511
  kind: "mutation",
16395
16512
  auth: "admin",
16396
16513
  caller: "required"
16514
+ }), method(object({
16515
+ ref: string().min(1),
16516
+ value: string().min(1)
16517
+ }), object({
16518
+ ref: string(),
16519
+ version: number().int()
16520
+ }), {
16521
+ kind: "mutation",
16522
+ auth: "admin",
16523
+ caller: "required"
16397
16524
  }), method(object({ refs: array(string().min(1)).max(1e3) }), object({
16398
16525
  deleted: array(string()).readonly(),
16399
16526
  kept: array(object({
@@ -52839,6 +52966,12 @@ object({
52839
52966
  /** Horizontal louver swing on/off. Null when the device has no
52840
52967
  * horizontal swing surface (gate with `DeviceFeature.ClimateSwingHorizontal`). */
52841
52968
  swingHorizontal: boolean().nullable(),
52969
+ /** Lowest setpoint the device accepts, Celsius. Absent / null = not reported. */
52970
+ minTemp: number().nullable().optional(),
52971
+ /** Highest setpoint the device accepts, Celsius. Absent / null = not reported. */
52972
+ maxTemp: number().nullable().optional(),
52973
+ /** Setpoint granularity, Celsius (0.5, 1). Absent / null = not reported. */
52974
+ tempStep: number().positive().nullable().optional(),
52842
52975
  /** Ms epoch when the slice was last updated (push or command). */
52843
52976
  lastFetchedAt: number()
52844
52977
  });
@@ -63846,6 +63979,12 @@ Object.freeze({
63846
63979
  addonId: null,
63847
63980
  access: "create"
63848
63981
  },
63982
+ "keychainAdmin.importSecret": {
63983
+ capName: "keychain-admin",
63984
+ capScope: "system",
63985
+ addonId: null,
63986
+ access: "create"
63987
+ },
63849
63988
  "keychainAdmin.listAudit": {
63850
63989
  capName: "keychain-admin",
63851
63990
  capScope: "system",
@@ -67632,6 +67771,18 @@ Object.freeze({
67632
67771
  addonId: null,
67633
67772
  access: "create"
67634
67773
  },
67774
+ "storageAdmin.dropCollection": {
67775
+ capName: "storage-admin",
67776
+ capScope: "system",
67777
+ addonId: null,
67778
+ access: "delete"
67779
+ },
67780
+ "storageAdmin.listTables": {
67781
+ capName: "storage-admin",
67782
+ capScope: "system",
67783
+ addonId: null,
67784
+ access: "view"
67785
+ },
67635
67786
  "storageEvictable.evict": {
67636
67787
  capName: "storage-evictable",
67637
67788
  capScope: "system",
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.2.375",
39
+ version: "1.2.377",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -81,7 +81,7 @@ async function r() {
81
81
  }
82
82
  },
83
83
  "@camstack/ui-library": {
84
- version: "1.2.293",
84
+ version: "1.2.295",
85
85
  scope: "default",
86
86
  shareConfig: {
87
87
  singleton: !0,