@camstack/addon-agent-ui 1.2.313 → 1.2.315

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.315] - 2026-11-02
11
+
12
+ ### Changed
13
+
14
+ - Rebuilt against updated dependencies.
15
+
16
+ ## [1.2.314] - 2026-11-02
17
+
18
+ ### Changed
19
+
20
+ - Rebuilt against updated dependencies.
21
+
10
22
  ## [1.2.313] - 2026-11-02
11
23
 
12
24
  ### Changed
@@ -362,13 +362,13 @@
362
362
  "server/server/packages/addon-agent-ui/src/updates-panel.tsx": "ec6b5b3701322bbb68dbe581703409cd373418cf9227ce829ea0945e70e60d54",
363
363
  "server/server/packages/addon-agent-ui/src/use-pairing-status.ts": "46193a1e83a64250dfff75d417448d74ba0159465e710861b996e903b03e427a",
364
364
  "server/server/packages/sdk/dist/index.js": "89409bfb9632c46ed63ee4882c70b7994c1ff90c67d27a5d4b86b0990c41a131",
365
- "server/server/packages/types/dist/client.mjs": "8d2722a6a47194ba322fc16d1536e6ba4e5fef7739789a08c90c6c4d2c7aa30a",
366
- "server/server/packages/types/dist/composition-kKKOdFIG.mjs": "8884b661df300b88fba5ad861a6cb0096cee1a1c1ca8058bea94907caaff28e6",
367
- "server/server/packages/types/dist/device-type-BOWLtoOt.mjs": "cc8447229f79743a89db50f17befd0674934b4c79bc905aa1a7f4e8667453b2c",
365
+ "server/server/packages/types/dist/client.mjs": "50205f786fd40359dcdedc87bbcb4cb8ecc6f9872c722457c9a44337b381545b",
366
+ "server/server/packages/types/dist/composition-Dh3RfMx5.mjs": "3bd5440c77222424fd992b3c3c5c68953abfccce331a639c311c36dd9d523e26",
367
+ "server/server/packages/types/dist/device-type-CfHw_quR.mjs": "a925875f57dabf69331e51c8d28fbade95a6a0ba92ab5bfe362f49c212290846",
368
368
  "server/server/packages/types/dist/event-category-BmPm98n0.mjs": "3617fc3941b34f1dcc8ebd3c1360e8c302747400d43e3ab694388496c2bb568d",
369
- "server/server/packages/types/dist/index.mjs": "b4c9c734fcd0204d4ddf52cb2495840dcc2552d850f0cdea1120a2d93422782c",
369
+ "server/server/packages/types/dist/index.mjs": "bcab27d609d10b949f60c65ab290ecd970628ce66b45c5a0982b3802d65299c4",
370
370
  "server/server/packages/types/dist/node-color.mjs": "942e663074f0a4ed2f8643848ec139eba4b5fd16d0d01c8a23e08a4819a8843b",
371
- "server/server/packages/types/dist/sleep-B-vp7x15.mjs": "26814958a629c9c04f1b47301254025974b7b9ad9d093523348469d4cd03d2ac",
371
+ "server/server/packages/types/dist/sleep-QsoHs66J.mjs": "dd8a98c45a8cd12e44f1bd0ffba0c461c8d3c4e471225acdd8647926843668dd",
372
372
  "server/server/packages/ui-library/src/composites/adaptive-downlink.ts": "6e9698e97721461b72a3750f930dd7214d049be865491bfb5a64b87c032b0e22",
373
373
  "server/server/packages/ui-library/src/composites/camera-stream-player.tsx": "c04809229b4e091fed09e2bcfe643d6b36cb3a39e294229b49f8a095e4e6ebd3",
374
374
  "server/server/packages/ui-library/src/composites/cap-settings/CameraGridLayoutEditor.tsx": "bfe36ccad931ef0981fe24fa315f136d35dbc3164df221346806173447178324",
@@ -435,7 +435,7 @@
435
435
  "server/server/packages/ui-library/src/composites/device-controls/valve-control.tsx": "373f34532ac55a02db9163927a7e63be9cdbba52b5d16e9e10b71bb7802e0131",
436
436
  "server/server/packages/ui-library/src/composites/device-controls/water-heater-control.tsx": "4a2bad97090f2d115973c68eb60a2973c8edbb02d40a58daecdaffcd5a49b971",
437
437
  "server/server/packages/ui-library/src/composites/device-controls/weather-control.tsx": "46cab536e8388072e0123f7b84b1b66d837791c4216b4df0df47a7a34fbca75b",
438
- "server/server/packages/ui-library/src/composites/device-export-panel.tsx": "3b1a5f7278bad3885d37c087ca6daedb04e152b25fbbf7ddb525e050c5c3f229",
438
+ "server/server/packages/ui-library/src/composites/device-export-panel.tsx": "168098ec1ecd6dc75515f9788ef829ae1ce00cd27ce4c72cadf8b686ae5784ba",
439
439
  "server/server/packages/ui-library/src/composites/device-history/history-selection.ts": "472d8a88d824218318707fbc017f5bcce3a777e88c2d7c59c0563ae88530aa8b",
440
440
  "server/server/packages/ui-library/src/composites/device-history/history-window.ts": "8b34ddb19c75be312a4efda5bc4ce3a02c53b391dfbf7226f0491c87612bd951",
441
441
  "server/server/packages/ui-library/src/composites/device-item/actions.tsx": "8d56da1265444e7e49a17acb33e498b9ca47a4921e56c18216ac0a1095bb4693",
@@ -479,7 +479,7 @@
479
479
  "server/server/packages/ui-library/src/contexts/vod-playback.tsx": "c07bc3ecc786182ef2f27d13bec3f520288e0b47367edd7ddf1c2e7b4a2bbaee",
480
480
  "server/server/packages/ui-library/src/contexts/widget-registry.tsx": "71a2815c645abe96821a9e9e5668e321187527d569414f5ca197d615856cc17a",
481
481
  "server/server/packages/ui-library/src/contexts/zone-editing.tsx": "275f680304ab09dd747fd9e859dc5aa0535df33eb2d33eb669a6de66bdd848d0",
482
- "server/server/packages/ui-library/src/generated/system-hooks.ts": "0c3e5118d73f77cc0f49c451fb38f6530ddb2e9c4af0d4a4c201320d447c0074",
482
+ "server/server/packages/ui-library/src/generated/system-hooks.ts": "cf09d4a765ce36b589615ca6954d1a01adca02906c950ae03fed218e7ce5c5b7",
483
483
  "server/server/packages/ui-library/src/hooks/turn-server-cache.ts": "fab22f069c3761e0d268449e056d9c499b6ea63b879684a40023735b34bad061",
484
484
  "server/server/packages/ui-library/src/hooks/use-device-capability.ts": "b74ede60be8039c45f6c712a407ee838ab208610435870ddabaa8598cb7cd7f3",
485
485
  "server/server/packages/ui-library/src/hooks/use-device-detections.ts": "61cc54cf2e7f53864e5aa533c05679e4c204c4004a0296f63ab169b003888611",
package/dist/addon.js CHANGED
@@ -786,7 +786,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
786
786
  return EventCategory;
787
787
  }({});
788
788
  //#endregion
789
- //#region ../types/dist/device-type-BOWLtoOt.mjs
789
+ //#region ../types/dist/device-type-CfHw_quR.mjs
790
790
  /**
791
791
  import { errMsg } from '@camstack/types'
792
792
  * Extract a human-readable message from an unknown error value.
@@ -975,6 +975,9 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
975
975
  DeviceFeature["CoverPositionable"] = "cover-positionable";
976
976
  /** Cover exposes slat-tilt control. Gates the tilt slider UI. */
977
977
  DeviceFeature["CoverTilt"] = "cover-tilt";
978
+ /** Cover has a real stop: `stop` halts the travel in place. Gates the stop
979
+ * control in exporters; a provider whose `stop` is a no-op never declares it. */
980
+ DeviceFeature["CoverStoppable"] = "cover-stoppable";
978
981
  /** Valve exposes intermediate position control (0..100). Gates the
979
982
  * position slider / drag surface UI. */
980
983
  DeviceFeature["ValvePositionable"] = "valve-positionable";
@@ -5840,7 +5843,7 @@ var ZodIssueCode = {
5840
5843
  var ZodFirstPartyTypeKind;
5841
5844
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5842
5845
  //#endregion
5843
- //#region ../types/dist/sleep-B-vp7x15.mjs
5846
+ //#region ../types/dist/sleep-QsoHs66J.mjs
5844
5847
  /**
5845
5848
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5846
5849
  * window to float samples (D455).
@@ -5957,7 +5960,7 @@ function isKeychainStoredField(field) {
5957
5960
  var SECRET_CLEAR = { $secretClear: true };
5958
5961
  /** The `'***'` token D634 redaction writes for non-admins. */
5959
5962
  var LEGACY_DEVICE_REDACTION = "***";
5960
- function isRecord$6(value) {
5963
+ function isRecord$7(value) {
5961
5964
  return typeof value === "object" && value !== null && !Array.isArray(value);
5962
5965
  }
5963
5966
  function makeSecretMarker(ref) {
@@ -5968,12 +5971,12 @@ function makeSecretMarker(ref) {
5968
5971
  }
5969
5972
  /** A stored marker: an object whose `$secret` is a non-empty string. */
5970
5973
  function isSecretMarker(value) {
5971
- if (!isRecord$6(value)) return false;
5974
+ if (!isRecord$7(value)) return false;
5972
5975
  const ref = value["$secret"];
5973
5976
  return typeof ref === "string" && ref.length > 0;
5974
5977
  }
5975
5978
  function isSecretClear(value) {
5976
- return isRecord$6(value) && value["$secretClear"] === true;
5979
+ return isRecord$7(value) && value["$secretClear"] === true;
5977
5980
  }
5978
5981
  /**
5979
5982
  * The UI view of a stored secret field value: a marker becomes "set", inline
@@ -6679,7 +6682,7 @@ var KeychainWriteRefusedError = class extends Error {
6679
6682
  }
6680
6683
  };
6681
6684
  var KEEP = { kind: "keep" };
6682
- function isRecord$5(value) {
6685
+ function isRecord$6(value) {
6683
6686
  return typeof value === "object" && value !== null && !Array.isArray(value);
6684
6687
  }
6685
6688
  function defaultMintId() {
@@ -6692,7 +6695,7 @@ function collectSecretMarkerRefs(value, out = []) {
6692
6695
  return out;
6693
6696
  }
6694
6697
  if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
6695
- else if (isRecord$5(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6698
+ else if (isRecord$6(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6696
6699
  return out;
6697
6700
  }
6698
6701
  async function storedValueEquals(secrets, marker, candidate) {
@@ -6747,7 +6750,7 @@ function rowId(row, idKey) {
6747
6750
  return null;
6748
6751
  }
6749
6752
  async function writeArray(ctx, node, incoming, stored, fieldPath) {
6750
- const storedRows = Array.isArray(stored) ? stored.filter(isRecord$5) : [];
6753
+ const storedRows = Array.isArray(stored) ? stored.filter(isRecord$6) : [];
6751
6754
  if (!Array.isArray(incoming)) {
6752
6755
  ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
6753
6756
  return {
@@ -6763,7 +6766,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
6763
6766
  const kept = /* @__PURE__ */ new Set();
6764
6767
  const rows = [];
6765
6768
  for (const row of incoming) {
6766
- if (!isRecord$5(row)) {
6769
+ if (!isRecord$6(row)) {
6767
6770
  rows.push(row);
6768
6771
  continue;
6769
6772
  }
@@ -6788,7 +6791,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
6788
6791
  case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
6789
6792
  case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
6790
6793
  case "object": {
6791
- if (!isRecord$5(incoming)) {
6794
+ if (!isRecord$6(incoming)) {
6792
6795
  if (incoming === void 0) return KEEP;
6793
6796
  ctx.deletes.push(...collectSecretMarkerRefs(stored));
6794
6797
  return {
@@ -6796,7 +6799,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
6796
6799
  value: incoming
6797
6800
  };
6798
6801
  }
6799
- const storedObj = isRecord$5(stored) ? stored : {};
6802
+ const storedObj = isRecord$6(stored) ? stored : {};
6800
6803
  return {
6801
6804
  kind: "value",
6802
6805
  value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
@@ -6919,7 +6922,7 @@ async function resolveSecretMarkers(value, secrets, options = {}) {
6919
6922
  value: items
6920
6923
  };
6921
6924
  }
6922
- if (isRecord$5(v)) {
6925
+ if (isRecord$6(v)) {
6923
6926
  const out = {};
6924
6927
  for (const [k, child] of Object.entries(v)) {
6925
6928
  const s = substitute(child);
@@ -6951,7 +6954,7 @@ var SecretOverwriteRefusedError = class extends Error {
6951
6954
  this.name = "SecretOverwriteRefusedError";
6952
6955
  }
6953
6956
  };
6954
- function isRecord$4(value) {
6957
+ function isRecord$5(value) {
6955
6958
  return typeof value === "object" && value !== null && !Array.isArray(value);
6956
6959
  }
6957
6960
  function resolvedValue(pass, marker) {
@@ -6980,7 +6983,7 @@ function inArray(pass, incoming, stored) {
6980
6983
  return incoming.map((item, index) => {
6981
6984
  const twin = stored[index];
6982
6985
  if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
6983
- if (isRecord$4(item) && isRecord$4(twin)) return inArrayRow(pass, item, twin);
6986
+ if (isRecord$5(item) && isRecord$5(twin)) return inArrayRow(pass, item, twin);
6984
6987
  if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
6985
6988
  return item;
6986
6989
  });
@@ -6991,7 +6994,7 @@ function inArrayRow(pass, incoming, stored) {
6991
6994
  const twin = stored[key];
6992
6995
  if (isSecretMarker(twin)) {
6993
6996
  if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
6994
- } else if (isRecord$4(value) && isRecord$4(twin)) out[key] = inArrayRow(pass, value, twin);
6997
+ } else if (isRecord$5(value) && isRecord$5(twin)) out[key] = inArrayRow(pass, value, twin);
6995
6998
  else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
6996
6999
  }
6997
7000
  return out;
@@ -6999,7 +7002,7 @@ function inArrayRow(pass, incoming, stored) {
6999
7002
  async function walk(pass, incoming, stored) {
7000
7003
  if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
7001
7004
  if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
7002
- if (!isRecord$4(incoming) || !isRecord$4(stored)) return incoming;
7005
+ if (!isRecord$5(incoming) || !isRecord$5(stored)) return incoming;
7003
7006
  const out = { ...incoming };
7004
7007
  for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
7005
7008
  return out;
@@ -7030,7 +7033,7 @@ async function restoreUnchangedSecrets(input) {
7030
7033
  function isJsonEqual(a, b) {
7031
7034
  if (Object.is(a, b)) return true;
7032
7035
  if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
7033
- if (isRecord$4(a) && isRecord$4(b)) {
7036
+ if (isRecord$5(a) && isRecord$5(b)) {
7034
7037
  const keys = Object.keys(a);
7035
7038
  if (keys.length !== Object.keys(b).length) return false;
7036
7039
  return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
@@ -7054,7 +7057,7 @@ function markerKeysOf(blob, owner) {
7054
7057
  }
7055
7058
  function nodeFromStored(value, owner) {
7056
7059
  if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
7057
- if (!isRecord$4(value)) return null;
7060
+ if (!isRecord$5(value)) return null;
7058
7061
  const children = /* @__PURE__ */ new Map();
7059
7062
  for (const [key, child] of Object.entries(value)) {
7060
7063
  const node = nodeFromStored(child, owner);
@@ -7673,6 +7676,44 @@ var BaseAddon = class {
7673
7676
  return null;
7674
7677
  }
7675
7678
  /**
7679
+ * D986 scoped settings: the form shape of the CLUSTER-wide settings one capability this addon
7680
+ * provides owns, or `null` when it has none. `cap` is a capability name the addon's manifest
7681
+ * declares. Stored as `addon-settings` rows `<addonId>.<field>`. The keys of this schema, of
7682
+ * {@link nodeSettingsSchema} and of {@link deviceDefaultsSchema} are disjoint, and a key never
7683
+ * contains `@` (guard `check-scoped-settings-declared`). SHAPE only: values are read by scope.
7684
+ */
7685
+ async clusterSettingsSchema(_cap) {
7686
+ return null;
7687
+ }
7688
+ /**
7689
+ * D986: the form shape of the settings one capability owns PER NODE, or `null`. Stored as
7690
+ * `addon-settings` rows `<addonId>.<field>@<nodeId>` carrying `nodeId` in the row data, so a
7691
+ * forgotten node's rows go in one central delete.
7692
+ */
7693
+ async nodeSettingsSchema(_cap) {
7694
+ return null;
7695
+ }
7696
+ /**
7697
+ * D986: the form shape of the FLEET DEFAULTS of the per-device settings one capability owns,
7698
+ * or `null`. Stored as `addon-settings` rows `<addonId>.<field>`; a device resolves its value
7699
+ * as device row, then this default, then the schema default (`resolveDeviceSettings`).
7700
+ */
7701
+ async deviceDefaultsSchema(_cap) {
7702
+ return null;
7703
+ }
7704
+ /**
7705
+ * The host's read of the scoped declarations: the schema one capability declares for `scope`,
7706
+ * or `null`. Beside {@link getIntegrationFormSchema}, the same shape: the hook is protected, the
7707
+ * host asks this method.
7708
+ */
7709
+ async getScopedSettingsSchema(scope, cap) {
7710
+ switch (scope) {
7711
+ case "cluster": return this.clusterSettingsSchema(cap);
7712
+ case "node": return this.nodeSettingsSchema(cap);
7713
+ case "device-defaults": return this.deviceDefaultsSchema(cap);
7714
+ }
7715
+ }
7716
+ /**
7676
7717
  * What the create wizard and the integration edit page render for one instance: the addon's
7677
7718
  * per-instance schema when it declares one, else its global schema (what they always showed).
7678
7719
  */
@@ -8466,6 +8507,21 @@ function normalizeAddonInitResult(result) {
8466
8507
  if (Array.isArray(result)) return { providers: result };
8467
8508
  return result;
8468
8509
  }
8510
+ /** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
8511
+ /** The files a kind may live in. `history` is device-history's (D850), never records. */
8512
+ var RECORD_DATABASES = ["settings", "events"];
8513
+ /** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
8514
+ var RECORD_CLASSES = [
8515
+ "config",
8516
+ "ledger",
8517
+ "audit",
8518
+ "mirror",
8519
+ "ephemeral"
8520
+ ];
8521
+ var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
8522
+ var RECORD_MAX_DATA_BYTES = 1024 * 1024;
8523
+ var RECORD_BATCH_MAX = 1e3;
8524
+ var RECORD_QUERY_MAX = 1e3;
8469
8525
  /**
8470
8526
  * The share-view scope kinds a share-link token can carry. The hub mints
8471
8527
  * and validates them (`ShareViewGrantSchema` builds its
@@ -8490,21 +8546,6 @@ var PeerBytesTicketSchema = object({
8490
8546
  */
8491
8547
  declaredBytes: number().int().nonnegative().nullable()
8492
8548
  });
8493
- /** Limits and names of `ctx.records` (D881). One authority; the engine and the facade read it. */
8494
- /** The files a kind may live in. `history` is device-history's (D850), never records. */
8495
- var RECORD_DATABASES = ["settings", "events"];
8496
- /** The classes of a kind: the `@durable` vocabulary, one authority (replaces the annotation). */
8497
- var RECORD_CLASSES = [
8498
- "config",
8499
- "ledger",
8500
- "audit",
8501
- "mirror",
8502
- "ephemeral"
8503
- ];
8504
- var RECORD_DEFAULT_MAX_DATA_BYTES = 64 * 1024;
8505
- var RECORD_MAX_DATA_BYTES = 1024 * 1024;
8506
- var RECORD_BATCH_MAX = 1e3;
8507
- var RECORD_QUERY_MAX = 1e3;
8508
8549
  /**
8509
8550
  * Record kinds (D881): the declaration an addon makes of one kind of fact, the
8510
8551
  * Zod row schema that describes it, and the helper that turns the two into the
@@ -9304,6 +9345,7 @@ var CAP_NAMES = Object.freeze([
9304
9345
  "humidity-sensor",
9305
9346
  "image",
9306
9347
  "image-settings",
9348
+ "integration-instances",
9307
9349
  "integrations",
9308
9350
  "intercom",
9309
9351
  "ir-transmitter",
@@ -11237,7 +11279,7 @@ object({
11237
11279
  params: record(string(), union([string(), number()])).optional()
11238
11280
  });
11239
11281
  //#endregion
11240
- //#region ../types/dist/composition-kKKOdFIG.mjs
11282
+ //#region ../types/dist/composition-Dh3RfMx5.mjs
11241
11283
  /**
11242
11284
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11243
11285
  *
@@ -20720,6 +20762,7 @@ var SYSTEM_CAP_VOCABULARY = {
20720
20762
  "load-contribution": system(INTERNAL),
20721
20763
  "local-network": system(NETWORK),
20722
20764
  "log-channels": system(INTERNAL),
20765
+ "integration-instances": system(INTERNAL),
20723
20766
  "log-destination": system(INTERNAL),
20724
20767
  "login-method": system(IDENTITY),
20725
20768
  "mesh-network": system(NETWORK),
@@ -42866,7 +42909,7 @@ var TargetKindCapsSchema = object({
42866
42909
  * the union is large and not meant for runtime validation here; the exported
42867
42910
  * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
42868
42911
  */
42869
- var ConfigSchemaPassthrough$3 = unknown();
42912
+ var ConfigSchemaPassthrough$4 = unknown();
42870
42913
  var TargetKindSchema = object({
42871
42914
  kind: string(),
42872
42915
  label: string(),
@@ -42903,7 +42946,7 @@ var TargetKindSchema = object({
42903
42946
  * its raster path, which is the safe default for an unknown image.
42904
42947
  */
42905
42948
  iconMediaType: string().optional(),
42906
- configSchema: ConfigSchemaPassthrough$3,
42949
+ configSchema: ConfigSchemaPassthrough$4,
42907
42950
  /**
42908
42951
  * The config keys that decide WHERE this kind's secrets are sent (server,
42909
42952
  * base URL, instance, chat, recipient). A save that changes any of them
@@ -52623,6 +52666,78 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
52623
52666
  auth: "admin"
52624
52667
  });
52625
52668
  /**
52669
+ * `integration-instances` - the connection of an addon that holds ONE or SEVERAL
52670
+ * (an account, a relay, a destination) as typed records (D985, D986). The addon's
52671
+ * own function page reads and writes them (Logging for Loki); the Integrations
52672
+ * page lists only the integrations that bring devices (D996).
52673
+ *
52674
+ * A collection of providers: each addon that holds connections registers one and a
52675
+ * caller reaches a provider with the pinned `addonId`.
52676
+ *
52677
+ * Identity: the instance id is immutable and opaque to the caller. A record the
52678
+ * Loki migration made keeps the `integrations` registry id it was created under
52679
+ * (`int_NNNN`); a single instance made by its function page takes the addon id.
52680
+ * `name` is the editable label.
52681
+ *
52682
+ * Secrets: every field the instance kind marks `.meta({ secret: true })` reaches
52683
+ * the form as a `SecretFieldView`, never text (D789). A patch value omitted, `''`
52684
+ * or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
52685
+ * clears it.
52686
+ */
52687
+ var ConfigSchemaPassthrough$3 = custom();
52688
+ var IntegrationInstanceIdSchema = string().min(1);
52689
+ var IntegrationInstanceDescriptionSchema = object({
52690
+ /** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
52691
+ instanceMode: _enum(["single", "multiple"]),
52692
+ /** What the instance is, for the UI (`destination`, `relay`, `account`). */
52693
+ kind: string(),
52694
+ /** True when `testInstance` really probes the remote. */
52695
+ canTest: boolean()
52696
+ });
52697
+ var IntegrationInstanceStatusSchema = object({
52698
+ state: _enum([
52699
+ "ok",
52700
+ "off",
52701
+ "error",
52702
+ "unknown"
52703
+ ]),
52704
+ detail: string().optional()
52705
+ });
52706
+ var IntegrationInstanceSchema = object({
52707
+ id: IntegrationInstanceIdSchema,
52708
+ name: string(),
52709
+ /** The addon's own rule (an URL is present, a client id is present...). */
52710
+ configured: boolean(),
52711
+ /** Values for the instance form; a secret is a `SecretFieldView`, never text. */
52712
+ formValues: record(string(), unknown()),
52713
+ status: IntegrationInstanceStatusSchema.optional()
52714
+ });
52715
+ var IntegrationInstancePatchSchema = object({
52716
+ id: IntegrationInstanceIdSchema,
52717
+ name: string().min(1).optional(),
52718
+ /** Omitted / '' / redaction token keeps a secret; `{ $secretClear: true }` clears it. */
52719
+ values: record(string(), unknown())
52720
+ });
52721
+ method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3, { auth: "admin" }), method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }), method(object({
52722
+ id: IntegrationInstanceIdSchema,
52723
+ name: string().min(1)
52724
+ }), object({ id: string() }), {
52725
+ kind: "mutation",
52726
+ auth: "admin"
52727
+ }), method(IntegrationInstancePatchSchema, object({ ok: literal(true) }), {
52728
+ kind: "mutation",
52729
+ auth: "admin"
52730
+ }), method(object({ id: IntegrationInstanceIdSchema }), object({ ok: literal(true) }), {
52731
+ kind: "mutation",
52732
+ auth: "admin"
52733
+ }), optionalMethod(object({
52734
+ id: IntegrationInstanceIdSchema.nullable(),
52735
+ values: record(string(), unknown())
52736
+ }), TestConnectionResultSchema$1, {
52737
+ kind: "mutation",
52738
+ auth: "admin"
52739
+ });
52740
+ /**
52626
52741
  * Two-way audio / talk-back cap. Clients open a WebRTC audio-only
52627
52742
  * session (server-initiated offer pattern, identical to the main
52628
52743
  * video WebRTC flow) and the browser captures the mic; the server
@@ -62655,7 +62770,14 @@ var NcMediaPolicySchema = object({
62655
62770
  */
62656
62771
  profile: CamProfileSchema.optional(),
62657
62772
  /** See {@link NcLinkedCameraModeSchema}. */
62658
- linkedCameras: NcLinkedCameraModeSchema.optional()
62773
+ linkedCameras: NcLinkedCameraModeSchema.optional(),
62774
+ /**
62775
+ * Sensor rules only: may this rule wake a SLEEPING battery camera to take
62776
+ * the linked still it attaches (D998)? Absent means yes, an explicit `false`
62777
+ * opts out. Deliberately not a Zod default: a rule that never chose keeps
62778
+ * following the hub's. One bounded wake per event; nothing is attached later.
62779
+ */
62780
+ wakeSleepingCamera: boolean().optional()
62659
62781
  });
62660
62782
  /**
62661
62783
  * Cooldown GRANULARITY over the subject's class — how much a fired
@@ -70323,7 +70445,16 @@ DeviceType.Sensor;
70323
70445
  * future fields without breaking the existing call sites.
70324
70446
  */
70325
70447
  var EmailAddressSchema = email();
70448
+ /**
70449
+ * Which relay of a multi-relay provider answers (D996). Absent = the provider's first / default
70450
+ * relay, so a caller written before relays existed keeps working unchanged. A provider with one
70451
+ * fixed relay ignores it. This is the integration instance id (`int_NNNN`), NOT the addon id: the
70452
+ * collection pin `addonId` still picks the provider, `instanceId` picks the relay inside it.
70453
+ */
70454
+ var InstanceIdSchema = string().min(1).optional();
70455
+ var RelayPinSchema = object({ instanceId: InstanceIdSchema });
70326
70456
  var SendEmailInputSchema = object({
70457
+ instanceId: InstanceIdSchema,
70327
70458
  to: union([EmailAddressSchema, array(EmailAddressSchema).min(1)]),
70328
70459
  cc: array(EmailAddressSchema).optional(),
70329
70460
  bcc: array(EmailAddressSchema).optional(),
@@ -70359,11 +70490,11 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
70359
70490
  kind: "mutation",
70360
70491
  auth: "admin",
70361
70492
  access: "create"
70362
- }), method(_void(), SmtpStatusSchema, {
70493
+ }), method(RelayPinSchema, SmtpStatusSchema, {
70363
70494
  kind: "mutation",
70364
70495
  auth: "admin",
70365
70496
  access: "view"
70366
- }), method(_void(), SmtpStatusSchema, { auth: "admin" });
70497
+ }), method(RelayPinSchema, SmtpStatusSchema, { auth: "admin" });
70367
70498
  /**
70368
70499
  * A single device snapshot returned as base64 JPEG/PNG.
70369
70500
  *
@@ -70395,10 +70526,12 @@ DeviceType.Camera, method(object({
70395
70526
  * even when the cache is well within the device's
70396
70527
  * `snapshotMaxAgeS` window.
70397
70528
  *
70398
- * **`force` is an OPERATOR signal, not a freshness preference.** On a
70529
+ * **`force` is a demand with a named principal, not a freshness
70530
+ * preference** — an operator button, the scheduled refresh (D847), or a
70531
+ * notification rule that left the camera's wake allowed (D998). On a
70399
70532
  * battery camera it is the one thing that walks past the wrapper's
70400
- * sleep gate and wakes the camera, so a background caller — a poller,
70401
- * an event handler, a thumbnail — must NEVER set it. Every such caller
70533
+ * sleep gate and wakes the camera, so a poller, a thumbnail or a
70534
+ * prestream must NEVER set it. Every such caller
70402
70535
  * gets the cached frame, which on a sleeping battery camera is the
70403
70536
  * correct answer: stale but honest beats woken.
70404
70537
  */
@@ -74356,7 +74489,7 @@ discriminatedUnion("kind", [object({
74356
74489
  kind: literal("pronto"),
74357
74490
  code: string()
74358
74491
  })]);
74359
- DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
74492
+ DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
74360
74493
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
74361
74494
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
74362
74495
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
@@ -77976,6 +78109,62 @@ Object.freeze({
77976
78109
  access: "create",
77977
78110
  role: "admin"
77978
78111
  },
78112
+ "integrationInstances.createInstance": {
78113
+ capName: "integration-instances",
78114
+ capScope: "system",
78115
+ addonId: null,
78116
+ access: "create",
78117
+ role: "admin"
78118
+ },
78119
+ "integrationInstances.deleteInstance": {
78120
+ capName: "integration-instances",
78121
+ capScope: "system",
78122
+ addonId: null,
78123
+ access: "delete",
78124
+ role: "admin"
78125
+ },
78126
+ "integrationInstances.describe": {
78127
+ capName: "integration-instances",
78128
+ capScope: "system",
78129
+ addonId: null,
78130
+ access: "view",
78131
+ role: "admin"
78132
+ },
78133
+ "integrationInstances.getInstance": {
78134
+ capName: "integration-instances",
78135
+ capScope: "system",
78136
+ addonId: null,
78137
+ access: "view",
78138
+ role: "admin"
78139
+ },
78140
+ "integrationInstances.instanceFormSchema": {
78141
+ capName: "integration-instances",
78142
+ capScope: "system",
78143
+ addonId: null,
78144
+ access: "view",
78145
+ role: "admin"
78146
+ },
78147
+ "integrationInstances.listInstances": {
78148
+ capName: "integration-instances",
78149
+ capScope: "system",
78150
+ addonId: null,
78151
+ access: "view",
78152
+ role: "admin"
78153
+ },
78154
+ "integrationInstances.testInstance": {
78155
+ capName: "integration-instances",
78156
+ capScope: "system",
78157
+ addonId: null,
78158
+ access: "create",
78159
+ role: "admin"
78160
+ },
78161
+ "integrationInstances.updateInstance": {
78162
+ capName: "integration-instances",
78163
+ capScope: "system",
78164
+ addonId: null,
78165
+ access: "create",
78166
+ role: "admin"
78167
+ },
77979
78168
  "integrations.create": {
77980
78169
  capName: "integrations",
77981
78170
  capScope: "system",
@@ -89281,7 +89470,7 @@ var AgentUIAddon = class extends BaseAddon {
89281
89470
  capability: adminUiCapability,
89282
89471
  provider: {
89283
89472
  getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
89284
- getVersion: async () => ({ version: "1.2.313" })
89473
+ getVersion: async () => ({ version: "1.2.315" })
89285
89474
  }
89286
89475
  }];
89287
89476
  }
@@ -1 +1 @@
1
- import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{c as a,d as o,g as s,l as c,p as l,t as u,w as d}from"./index-D5vO_ttL.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=d(`grid-2x2`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`M3 12h18`,key:`1i2n21`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),m=n(i(),1),h=n(e(),1),g=110,_=`motion-zones`,v=0,y=[1,2,3],b=1,x=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,S=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function C(e,t,n){let r=t*n,i=Array.from({length:r});for(let t=0;t<r;t+=1)i[t]=e[t]===!0;return i}function w(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(e[n]!==t[n])return!1;return!0}function T(e,t){return Math.ceil(e/t)}function E(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function D(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+E(n,t,i)]=!0)}return o}function O(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+E(n,t,i)]===!0}return o}function k({deviceId:e}){let n=t(),i=o(s().trpcClient,e),[d,E]=(0,m.useState)(null),[k,A]=(0,m.useState)(!1),[j,M]=(0,m.useState)(null),[N,P]=(0,m.useState)(null),[F,I]=(0,m.useState)(b),[L,R]=(0,m.useState)(!1),[z,B]=(0,m.useState)(!1),V=(0,m.useRef)(!1);(0,m.useEffect)(()=>{if(!i)return;let e=!1;return V.current=!1,E(null),A(!1),M(null),P(null),I(b),B(!1),(async()=>{try{let t=await i.motionZones?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(E(t),V.current)return;let n=await i.motionZones?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);V.current=!0;let r=n.regions.find(e=>e.shape.kind===`grid`),a=C(r?r.shape.cells:[],t.grid.width,t.grid.height);P(a),M(D(a,t.grid.width,t.grid.height,b))}catch(t){if(e)return;c(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=d?d.grid.width:0,U=d?d.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&d?O(j,H,U,F):null,[j,d,H,U,F]),q=(0,m.useMemo)(()=>K!==null&&N!==null&&!w(K,N),[K,N]),J=(0,m.useMemo)(()=>K?K.reduce((e,t)=>t?e+1:e,0):0,[K]),Y=H*U,X=W*G,Z=(0,m.useCallback)(e=>{X>0&&M(Array.from({length:X},()=>e))},[X]),ee=(0,m.useCallback)(()=>{M(e=>e&&e.map(e=>!e))},[]),te=(0,m.useCallback)(()=>{N&&d&&M(D(N,H,U,F))},[N,d,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!d||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,d,H,U]),Q=(0,m.useMemo)(()=>d&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[d,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!d)){R(!0);try{let e=O(j,d.grid.width,d.grid.height,F),t={kind:`grid`,gridWidth:d.grid.width,gridHeight:d.grid.height,cells:e};await i.motionZones?.setZone({patch:{regions:[{id:v,enabled:!0,shape:t}]}});let n=await i.motionZones?.getStatus({});if(n){let e=n.regions.find(e=>e.shape.kind===`grid`),t=C(e?e.shape.cells:[],d.grid.width,d.grid.height);P(t),M(D(t,d.grid.width,d.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,d,F]);u((0,m.useMemo)(()=>z&&!k&&d&&j?{id:_,order:g,node:(0,h.jsx)(f,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:W,height:G},selectedId:v,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[z,k,d,j,Q,$,W,G]));let ie=!k&&d!==null&&j!==null;return i?(0,h.jsx)(l,{title:n(`uiLibrary.motionZones.title`,`Motion Zones`),icon:(0,h.jsx)(p,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsx)(`div`,{className:`flex flex-col gap-3`,children:k?(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:n(`uiLibrary.motionZones.unsupported`,`This camera doesn't expose an on-board motion zones grid.`)}):ie?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.motionZones.intro`,fallback:`Toggle <0>Edit grid</0> to paint the region directly on the live frame, then <1>Save</1> to push the mask to the camera. Pick a bigger <2>Cell size</2> for quicker, broad-stroke painting — it's resampled to the camera's native {{width}}×{{height}} grid on save (×1 is the finest).`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`e`),(0,h.jsx)(`strong`,{className:`text-foreground`},`s`),(0,h.jsx)(`strong`,{className:`text-foreground`},`c`)],values:{width:d.grid.width,height:d.grid.height}})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:()=>B(e=>!e),disabled:L,"aria-pressed":z,className:z?S:x,children:z?n(`uiLibrary.motionZones.doneEditing`,`Done editing`):n(`uiLibrary.motionZones.editGrid`,`Edit grid`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!0),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOn`,`All on`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!1),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOff`,`All off`)}),(0,h.jsx)(`button`,{type:`button`,onClick:ee,disabled:L,className:x,children:n(`uiLibrary.motionZones.invert`,`Invert`)}),(0,h.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":n(`uiLibrary.motionZones.cellSize`,`Cell size`),children:[(0,h.jsx)(`span`,{className:`${a} mr-0.5`,children:n(`uiLibrary.motionZones.cellSize`,`Cell size`)}),y.map(e=>(0,h.jsxs)(`button`,{type:`button`,onClick:()=>ne(e),disabled:L,"aria-pressed":F===e,title:e===1?n(`uiLibrary.motionZones.cameraGrid`,`Camera grid {{width}}×{{height}} (finest)`,{width:H,height:U}):n(`uiLibrary.motionZones.paintingGrid`,`{{width}}×{{height}} painting grid · cells ×{{factor}} bigger`,{width:T(H,e),height:T(U,e),factor:e}),className:F===e?S:x,children:[`×`,e]},e))]}),(0,h.jsxs)(`span`,{className:`${a} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:d.grid.width,height:d.grid.height}),F===1?``:n(`uiLibrary.motionZones.paintSuffix`,` · paint {{width}}×{{height}}`,{width:W,height:G})]}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:te,disabled:L||!q,className:`rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,children:n(`uiLibrary.motionZones.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void re(),disabled:L||!q,className:S,children:L?n(`uiLibrary.motionZones.saving`,`Saving…`):n(`uiLibrary.motionZones.save`,`Save`)})]})]}):(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:n(`uiLibrary.motionZones.loading`,`Loading the camera's grid…`)})})}):null}export{k as MotionZonesSettings};
1
+ import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{c as a,d as o,g as s,l as c,p as l,t as u,w as d}from"./index-MaQ1EcJ3.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=d(`grid-2x2`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`M3 12h18`,key:`1i2n21`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),m=n(i(),1),h=n(e(),1),g=110,_=`motion-zones`,v=0,y=[1,2,3],b=1,x=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,S=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function C(e,t,n){let r=t*n,i=Array.from({length:r});for(let t=0;t<r;t+=1)i[t]=e[t]===!0;return i}function w(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(e[n]!==t[n])return!1;return!0}function T(e,t){return Math.ceil(e/t)}function E(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function D(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+E(n,t,i)]=!0)}return o}function O(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+E(n,t,i)]===!0}return o}function k({deviceId:e}){let n=t(),i=o(s().trpcClient,e),[d,E]=(0,m.useState)(null),[k,A]=(0,m.useState)(!1),[j,M]=(0,m.useState)(null),[N,P]=(0,m.useState)(null),[F,I]=(0,m.useState)(b),[L,R]=(0,m.useState)(!1),[z,B]=(0,m.useState)(!1),V=(0,m.useRef)(!1);(0,m.useEffect)(()=>{if(!i)return;let e=!1;return V.current=!1,E(null),A(!1),M(null),P(null),I(b),B(!1),(async()=>{try{let t=await i.motionZones?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(E(t),V.current)return;let n=await i.motionZones?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);V.current=!0;let r=n.regions.find(e=>e.shape.kind===`grid`),a=C(r?r.shape.cells:[],t.grid.width,t.grid.height);P(a),M(D(a,t.grid.width,t.grid.height,b))}catch(t){if(e)return;c(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=d?d.grid.width:0,U=d?d.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&d?O(j,H,U,F):null,[j,d,H,U,F]),q=(0,m.useMemo)(()=>K!==null&&N!==null&&!w(K,N),[K,N]),J=(0,m.useMemo)(()=>K?K.reduce((e,t)=>t?e+1:e,0):0,[K]),Y=H*U,X=W*G,Z=(0,m.useCallback)(e=>{X>0&&M(Array.from({length:X},()=>e))},[X]),ee=(0,m.useCallback)(()=>{M(e=>e&&e.map(e=>!e))},[]),te=(0,m.useCallback)(()=>{N&&d&&M(D(N,H,U,F))},[N,d,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!d||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,d,H,U]),Q=(0,m.useMemo)(()=>d&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[d,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!d)){R(!0);try{let e=O(j,d.grid.width,d.grid.height,F),t={kind:`grid`,gridWidth:d.grid.width,gridHeight:d.grid.height,cells:e};await i.motionZones?.setZone({patch:{regions:[{id:v,enabled:!0,shape:t}]}});let n=await i.motionZones?.getStatus({});if(n){let e=n.regions.find(e=>e.shape.kind===`grid`),t=C(e?e.shape.cells:[],d.grid.width,d.grid.height);P(t),M(D(t,d.grid.width,d.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,d,F]);u((0,m.useMemo)(()=>z&&!k&&d&&j?{id:_,order:g,node:(0,h.jsx)(f,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:W,height:G},selectedId:v,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[z,k,d,j,Q,$,W,G]));let ie=!k&&d!==null&&j!==null;return i?(0,h.jsx)(l,{title:n(`uiLibrary.motionZones.title`,`Motion Zones`),icon:(0,h.jsx)(p,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsx)(`div`,{className:`flex flex-col gap-3`,children:k?(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:n(`uiLibrary.motionZones.unsupported`,`This camera doesn't expose an on-board motion zones grid.`)}):ie?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.motionZones.intro`,fallback:`Toggle <0>Edit grid</0> to paint the region directly on the live frame, then <1>Save</1> to push the mask to the camera. Pick a bigger <2>Cell size</2> for quicker, broad-stroke painting — it's resampled to the camera's native {{width}}×{{height}} grid on save (×1 is the finest).`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`e`),(0,h.jsx)(`strong`,{className:`text-foreground`},`s`),(0,h.jsx)(`strong`,{className:`text-foreground`},`c`)],values:{width:d.grid.width,height:d.grid.height}})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:()=>B(e=>!e),disabled:L,"aria-pressed":z,className:z?S:x,children:z?n(`uiLibrary.motionZones.doneEditing`,`Done editing`):n(`uiLibrary.motionZones.editGrid`,`Edit grid`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!0),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOn`,`All on`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!1),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOff`,`All off`)}),(0,h.jsx)(`button`,{type:`button`,onClick:ee,disabled:L,className:x,children:n(`uiLibrary.motionZones.invert`,`Invert`)}),(0,h.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":n(`uiLibrary.motionZones.cellSize`,`Cell size`),children:[(0,h.jsx)(`span`,{className:`${a} mr-0.5`,children:n(`uiLibrary.motionZones.cellSize`,`Cell size`)}),y.map(e=>(0,h.jsxs)(`button`,{type:`button`,onClick:()=>ne(e),disabled:L,"aria-pressed":F===e,title:e===1?n(`uiLibrary.motionZones.cameraGrid`,`Camera grid {{width}}×{{height}} (finest)`,{width:H,height:U}):n(`uiLibrary.motionZones.paintingGrid`,`{{width}}×{{height}} painting grid · cells ×{{factor}} bigger`,{width:T(H,e),height:T(U,e),factor:e}),className:F===e?S:x,children:[`×`,e]},e))]}),(0,h.jsxs)(`span`,{className:`${a} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:d.grid.width,height:d.grid.height}),F===1?``:n(`uiLibrary.motionZones.paintSuffix`,` · paint {{width}}×{{height}}`,{width:W,height:G})]}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:te,disabled:L||!q,className:`rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,children:n(`uiLibrary.motionZones.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void re(),disabled:L||!q,className:S,children:L?n(`uiLibrary.motionZones.saving`,`Saving…`):n(`uiLibrary.motionZones.save`,`Save`)})]})]}):(0,h.jsx)(`p`,{className:`${a} leading-relaxed`,children:n(`uiLibrary.motionZones.loading`,`Loading the camera's grid…`)})})}):null}export{k as MotionZonesSettings};