@camstack/system 1.1.55 → 1.1.57

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 (50) hide show
  1. package/dist/addon-runner.js +2 -2
  2. package/dist/addon-runner.mjs +2 -2
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  15. package/dist/builtins/doorbell/trigger-engine.d.ts +18 -0
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.d.ts +8 -14
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.js +37 -30
  18. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +37 -30
  19. package/dist/builtins/hub-forwarder/index.js +1 -1
  20. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  21. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  22. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  23. package/dist/builtins/local-network/local-network.addon.js +1 -1
  24. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  25. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  26. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  27. package/dist/builtins/platform-probe/index.js +1 -1
  28. package/dist/builtins/platform-probe/index.mjs +1 -1
  29. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  30. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  31. package/dist/builtins/snapshot/index.js +1 -1
  32. package/dist/builtins/snapshot/index.mjs +1 -1
  33. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  34. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  35. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  36. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  37. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  38. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  39. package/dist/builtins/system-config/system-config.addon.js +1 -1
  40. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  41. package/dist/builtins/winston-logging/index.js +1 -1
  42. package/dist/builtins/winston-logging/index.mjs +1 -1
  43. package/dist/{dist-CD5HJkAg.js → dist-Bg795EQF.js} +367 -1
  44. package/dist/{dist-RlzjLezj.mjs → dist-JAF9YR1q.mjs} +367 -1
  45. package/dist/index.js +12 -4
  46. package/dist/index.mjs +12 -4
  47. package/dist/kernel/addon-loader.d.ts +7 -2
  48. package/dist/{manifest-python-deps-BMKlyabv.mjs → manifest-python-deps-9wuRyL-k.mjs} +1 -1
  49. package/dist/{manifest-python-deps-BpDOYWC2.js → manifest-python-deps-eI3-3FeW.js} +1 -1
  50. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_dist = require("./dist-CD5HJkAg.js");
3
- const require_manifest_python_deps = require("./manifest-python-deps-BpDOYWC2.js");
2
+ const require_dist = require("./dist-Bg795EQF.js");
3
+ const require_manifest_python_deps = require("./manifest-python-deps-eI3-3FeW.js");
4
4
  const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
5
5
  let node_fs = require("node:fs");
6
6
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,5 +1,5 @@
1
- import { j as isCollectionArrayMethod, t as ALL_CAPABILITY_DEFINITIONS } from "./dist-RlzjLezj.mjs";
2
- import { F as createUdsLoggerWithControl, I as LocalChildClient, bt as resolveAddonClass, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, vt as installManifestNativeDeps } from "./manifest-python-deps-BMKlyabv.mjs";
1
+ import { j as isCollectionArrayMethod, t as ALL_CAPABILITY_DEFINITIONS } from "./dist-JAF9YR1q.mjs";
2
+ import { F as createUdsLoggerWithControl, I as LocalChildClient, bt as resolveAddonClass, i as createUdsAddonContext, it as setWorkerNativeCapsChangeListener, nt as getWorkerNativeCapSnapshot, ot as validateProviderRegistrations, t as installManifestPythonDeps, tt as getWorkerNativeCapProvider, vt as installManifestNativeDeps } from "./manifest-python-deps-9wuRyL-k.mjs";
3
3
  import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
4
4
  import { register } from "node:module";
5
5
  import * as fs from "node:fs";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_fs = require("node:fs");
9
9
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { f as addonPagesCapability, it as BaseAddon, kt as EventCategory, rt as errMsg } from "../../dist-RlzjLezj.mjs";
1
+ import { f as addonPagesCapability, it as BaseAddon, kt as EventCategory, rt as errMsg } from "../../dist-JAF9YR1q.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_fs = require("node:fs");
9
9
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { it as BaseAddon, kt as EventCategory, m as addonWidgetsCapability, rt as errMsg } from "../../dist-RlzjLezj.mjs";
1
+ import { it as BaseAddon, kt as EventCategory, m as addonWidgetsCapability, rt as errMsg } from "../../dist-JAF9YR1q.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  //#region src/builtins/alerts/alerts.addon.ts
8
8
  var CATEGORY_MAPPINGS = {
9
9
  "addon.crashed": {
@@ -1,4 +1,4 @@
1
- import { _t as createEvent, h as alertsCapability, it as BaseAddon, kt as EventCategory, n as AlertSchema, rt as errMsg } from "../../dist-RlzjLezj.mjs";
1
+ import { _t as createEvent, h as alertsCapability, it as BaseAddon, kt as EventCategory, n as AlertSchema, rt as errMsg } from "../../dist-JAF9YR1q.mjs";
2
2
  //#region src/builtins/alerts/alerts.addon.ts
3
3
  var CATEGORY_MAPPINGS = {
4
4
  "addon.crashed": {
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_fs$1 = require_chunk.__toESM(node_fs, 1);
9
9
  node_fs = require_chunk.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { it as BaseAddon, kt as EventCategory, v as backupCapability } from "../../dist-RlzjLezj.mjs";
1
+ import { it as BaseAddon, kt as EventCategory, v as backupCapability } from "../../dist-JAF9YR1q.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import Kt from "node:fs";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { L as logDestinationCapability, it as BaseAddon } from "../../dist-RlzjLezj.mjs";
1
+ import { L as logDestinationCapability, it as BaseAddon } from "../../dist-JAF9YR1q.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  let _camstack_types_node = require("@camstack/types/node");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { E as evaluateLinkExpression, J as setByPath, Ot as sleep, S as deviceStatusCapability, St as isDeviceConfigCap, T as enumerateSchemaFields, U as parseStreamParamsFormPatch, V as normalizeUnit, a as DeviceStatusSchema, b as deviceManagerCapability, c as STREAM_PROFILE_META, ct as DeviceFeature, et as toExpressionValue, g as applyTransform, i as CAP_NAMES_WITH_STATUS, it as BaseAddon, k as getByPath, kt as EventCategory, lt as DeviceRole, nt as validateExpressionSource, pt as WELL_KNOWN_TAB_MAP, rt as errMsg, t as ALL_CAPABILITY_DEFINITIONS, ut as DeviceType, w as enumerateItemArrayFields, x as deviceStateCapability, y as buildStreamParamsConfigSchema } from "../../dist-RlzjLezj.mjs";
1
+ import { E as evaluateLinkExpression, J as setByPath, Ot as sleep, S as deviceStatusCapability, St as isDeviceConfigCap, T as enumerateSchemaFields, U as parseStreamParamsFormPatch, V as normalizeUnit, a as DeviceStatusSchema, b as deviceManagerCapability, c as STREAM_PROFILE_META, ct as DeviceFeature, et as toExpressionValue, g as applyTransform, i as CAP_NAMES_WITH_STATUS, it as BaseAddon, k as getByPath, kt as EventCategory, lt as DeviceRole, nt as validateExpressionSource, pt as WELL_KNOWN_TAB_MAP, rt as errMsg, t as ALL_CAPABILITY_DEFINITIONS, ut as DeviceType, w as enumerateItemArrayFields, x as deviceStateCapability, y as buildStreamParamsConfigSchema } from "../../dist-JAF9YR1q.mjs";
2
2
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -21,6 +21,24 @@
21
21
  export declare const SOURCE_CAP_ACTIVE_FIELD: Readonly<Record<string, string>>;
22
22
  /** Cap names whose presence in a device's bindings qualify it as a source. */
23
23
  export declare const SOURCE_CAPS: readonly string[];
24
+ /**
25
+ * Device `type` values (from `DeviceType`) that can host a binary/switch
26
+ * source cap. Used by the camera's `device-multiselect` picker as the
27
+ * CLIENT-SIDE filter, alongside `SOURCE_CAPS`.
28
+ *
29
+ * Why types and not caps alone: the shared picker filters
30
+ * `deviceManager.listAll` rows client-side, and those rows carry only the
31
+ * device's advertised `features` — NOT its registered cap list. On the live
32
+ * cluster binary sensors and switches advertise EMPTY features (features
33
+ * mirror only a handful of caps like `motion-trigger`), so a caps-only
34
+ * filter matched against `features` would list nothing (the very bug this
35
+ * replaced, which relied on the now-empty `getAllBindings`). Matching by
36
+ * `type` is the reliable client-side signal; the union with `SOURCE_CAPS`
37
+ * still captures any device that DOES advertise a source-cap feature.
38
+ * `sensor` covers contact/motion/flood/gas/smoke/CO/vibration/tamper,
39
+ * `switch` covers switches, `control` covers generic binary actuators.
40
+ */
41
+ export declare const SOURCE_DEVICE_TYPES: readonly string[];
24
42
  /** True when a cap is a recognised binary/switch doorbell source. */
25
43
  export declare function isSourceCap(capName: string): boolean;
26
44
  /**
@@ -83,22 +83,16 @@ export declare class VirtualDoorbellAddon extends BaseAddon<VirtualDoorbellAddon
83
83
  * cap also applies to Button accessories (native providers) where a
84
84
  * virtual-source picker is meaningless.
85
85
  *
86
- * A SINGLE `multiselect` field lists every eligible binary/switch
87
- * device; at ≥8 options the admin-ui form-builder auto-swaps it for the
88
- * searchable multi-select (same idiom as "Linked devices"). The reaction
89
- * is derived automatically per device type — no trigger/eventType
90
- * controls.
86
+ * A SINGLE `device-multiselect` field carries only a declarative
87
+ * `deviceFilter` the admin-ui renders the SHARED searchable device
88
+ * picker (same idiom as "Linked devices") whose options come from
89
+ * `deviceManager.listAll` filtered CLIENT-SIDE. The addon NO LONGER
90
+ * computes the option list server-side: the old path enumerated
91
+ * `deviceManager.getAllBindings`, which returns an empty set on the live
92
+ * cluster and left the picker blank. The reaction is still derived
93
+ * automatically per device type — no trigger/eventType controls.
91
94
  */
92
95
  private buildDeviceSettingsContribution;
93
- /**
94
- * Cluster-wide picker options: every device whose BINDINGS expose one of
95
- * the recognised binary/switch source caps. Binding-sourced (not
96
- * feature-flag sourced) because cap names are exactly what the runtime
97
- * consumes. Labelled `Name (Location)` (id carried in the option value)
98
- * so the searchable multi-select matches on name, location, or id — the
99
- * same shape "Linked devices" uses.
100
- */
101
- private listSourceDeviceOptions;
102
96
  private saveDeviceSettingsPatch;
103
97
  }
104
98
  export default VirtualDoorbellAddon;
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  //#region src/builtins/doorbell/trigger-engine.ts
8
8
  /**
9
9
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -39,6 +39,28 @@ var SOURCE_CAP_ACTIVE_FIELD = {
39
39
  };
40
40
  /** Cap names whose presence in a device's bindings qualify it as a source. */
41
41
  var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
42
+ /**
43
+ * Device `type` values (from `DeviceType`) that can host a binary/switch
44
+ * source cap. Used by the camera's `device-multiselect` picker as the
45
+ * CLIENT-SIDE filter, alongside `SOURCE_CAPS`.
46
+ *
47
+ * Why types and not caps alone: the shared picker filters
48
+ * `deviceManager.listAll` rows client-side, and those rows carry only the
49
+ * device's advertised `features` — NOT its registered cap list. On the live
50
+ * cluster binary sensors and switches advertise EMPTY features (features
51
+ * mirror only a handful of caps like `motion-trigger`), so a caps-only
52
+ * filter matched against `features` would list nothing (the very bug this
53
+ * replaced, which relied on the now-empty `getAllBindings`). Matching by
54
+ * `type` is the reliable client-side signal; the union with `SOURCE_CAPS`
55
+ * still captures any device that DOES advertise a source-cap feature.
56
+ * `sensor` covers contact/motion/flood/gas/smoke/CO/vibration/tamper,
57
+ * `switch` covers switches, `control` covers generic binary actuators.
58
+ */
59
+ var SOURCE_DEVICE_TYPES = [
60
+ "sensor",
61
+ "switch",
62
+ "control"
63
+ ];
42
64
  /** True when a cap is a recognised binary/switch doorbell source. */
43
65
  function isSourceCap(capName) {
44
66
  return Object.prototype.hasOwnProperty.call(SOURCE_CAP_ACTIVE_FIELD, capName);
@@ -443,11 +465,14 @@ var VirtualDoorbellAddon = class extends require_dist.BaseAddon {
443
465
  * cap also applies to Button accessories (native providers) where a
444
466
  * virtual-source picker is meaningless.
445
467
  *
446
- * A SINGLE `multiselect` field lists every eligible binary/switch
447
- * device; at ≥8 options the admin-ui form-builder auto-swaps it for the
448
- * searchable multi-select (same idiom as "Linked devices"). The reaction
449
- * is derived automatically per device type — no trigger/eventType
450
- * controls.
468
+ * A SINGLE `device-multiselect` field carries only a declarative
469
+ * `deviceFilter` the admin-ui renders the SHARED searchable device
470
+ * picker (same idiom as "Linked devices") whose options come from
471
+ * `deviceManager.listAll` filtered CLIENT-SIDE. The addon NO LONGER
472
+ * computes the option list server-side: the old path enumerated
473
+ * `deviceManager.getAllBindings`, which returns an empty set on the live
474
+ * cluster and left the picker blank. The reaction is still derived
475
+ * automatically per device type — no trigger/eventType controls.
451
476
  */
452
477
  async buildDeviceSettingsContribution(deviceId) {
453
478
  const device = await this.lookupDevice(deviceId);
@@ -459,37 +484,19 @@ var VirtualDoorbellAddon = class extends require_dist.BaseAddon {
459
484
  description: "Ring this camera’s doorbell when a chosen device becomes active — a contact opening, a switch turning on, or a sensor firing. Pick any binary sensor or switch below; the reaction is automatic per device type.",
460
485
  order: 71,
461
486
  fields: [{
462
- type: "multiselect",
487
+ type: "device-multiselect",
463
488
  key: KEY_SOURCES,
464
489
  label: "Trigger devices",
465
490
  description: "Any of these becoming active rings this camera. Binary sensors ring when they go active; switches ring when they turn on.",
466
- options: await this.listSourceDeviceOptions(),
491
+ deviceFilter: {
492
+ caps: SOURCE_CAPS,
493
+ types: SOURCE_DEVICE_TYPES,
494
+ excludeDeviceId: deviceId
495
+ },
467
496
  value: sources.map((s) => String(s.deviceId))
468
497
  }]
469
498
  }] };
470
499
  }
471
- /**
472
- * Cluster-wide picker options: every device whose BINDINGS expose one of
473
- * the recognised binary/switch source caps. Binding-sourced (not
474
- * feature-flag sourced) because cap names are exactly what the runtime
475
- * consumes. Labelled `Name (Location)` (id carried in the option value)
476
- * so the searchable multi-select matches on name, location, or id — the
477
- * same shape "Linked devices" uses.
478
- */
479
- async listSourceDeviceOptions() {
480
- try {
481
- const [devices, allBindings] = await Promise.all([this.ctx.api.deviceManager.listAll.query({}), this.ctx.api.deviceManager.getAllBindings.query({})]);
482
- const eligibleIds = /* @__PURE__ */ new Set();
483
- for (const deviceBindings of allBindings) if (deviceBindings.entries.some((e) => SOURCE_CAPS.includes(e.capName))) eligibleIds.add(deviceBindings.deviceId);
484
- return devices.filter((d) => eligibleIds.has(d.id)).map((d) => ({
485
- value: String(d.id),
486
- label: typeof d.location === "string" && d.location.length > 0 ? `${d.name} (${d.location})` : d.name
487
- })).toSorted((a, b) => a.label.localeCompare(b.label));
488
- } catch (err) {
489
- this.ctx.logger.warn("virtual-doorbell: source-device enumeration failed", { meta: { error: require_dist.errMsg(err) } });
490
- return [];
491
- }
492
- }
493
500
  async saveDeviceSettingsPatch(deviceId, patch) {
494
501
  if (!this.ctx.settings) throw new Error("[virtual-doorbell] settings store unavailable — cannot persist per-device settings");
495
502
  if ("doorbellSources" in patch) {
@@ -1,4 +1,4 @@
1
- import { C as doorbellCapability, _t as createEvent, it as BaseAddon, kt as EventCategory, rt as errMsg, ut as DeviceType } from "../../dist-RlzjLezj.mjs";
1
+ import { C as doorbellCapability, _t as createEvent, it as BaseAddon, kt as EventCategory, rt as errMsg, ut as DeviceType } from "../../dist-JAF9YR1q.mjs";
2
2
  //#region src/builtins/doorbell/trigger-engine.ts
3
3
  /**
4
4
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -34,6 +34,28 @@ var SOURCE_CAP_ACTIVE_FIELD = {
34
34
  };
35
35
  /** Cap names whose presence in a device's bindings qualify it as a source. */
36
36
  var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
37
+ /**
38
+ * Device `type` values (from `DeviceType`) that can host a binary/switch
39
+ * source cap. Used by the camera's `device-multiselect` picker as the
40
+ * CLIENT-SIDE filter, alongside `SOURCE_CAPS`.
41
+ *
42
+ * Why types and not caps alone: the shared picker filters
43
+ * `deviceManager.listAll` rows client-side, and those rows carry only the
44
+ * device's advertised `features` — NOT its registered cap list. On the live
45
+ * cluster binary sensors and switches advertise EMPTY features (features
46
+ * mirror only a handful of caps like `motion-trigger`), so a caps-only
47
+ * filter matched against `features` would list nothing (the very bug this
48
+ * replaced, which relied on the now-empty `getAllBindings`). Matching by
49
+ * `type` is the reliable client-side signal; the union with `SOURCE_CAPS`
50
+ * still captures any device that DOES advertise a source-cap feature.
51
+ * `sensor` covers contact/motion/flood/gas/smoke/CO/vibration/tamper,
52
+ * `switch` covers switches, `control` covers generic binary actuators.
53
+ */
54
+ var SOURCE_DEVICE_TYPES = [
55
+ "sensor",
56
+ "switch",
57
+ "control"
58
+ ];
37
59
  /** True when a cap is a recognised binary/switch doorbell source. */
38
60
  function isSourceCap(capName) {
39
61
  return Object.prototype.hasOwnProperty.call(SOURCE_CAP_ACTIVE_FIELD, capName);
@@ -438,11 +460,14 @@ var VirtualDoorbellAddon = class extends BaseAddon {
438
460
  * cap also applies to Button accessories (native providers) where a
439
461
  * virtual-source picker is meaningless.
440
462
  *
441
- * A SINGLE `multiselect` field lists every eligible binary/switch
442
- * device; at ≥8 options the admin-ui form-builder auto-swaps it for the
443
- * searchable multi-select (same idiom as "Linked devices"). The reaction
444
- * is derived automatically per device type — no trigger/eventType
445
- * controls.
463
+ * A SINGLE `device-multiselect` field carries only a declarative
464
+ * `deviceFilter` the admin-ui renders the SHARED searchable device
465
+ * picker (same idiom as "Linked devices") whose options come from
466
+ * `deviceManager.listAll` filtered CLIENT-SIDE. The addon NO LONGER
467
+ * computes the option list server-side: the old path enumerated
468
+ * `deviceManager.getAllBindings`, which returns an empty set on the live
469
+ * cluster and left the picker blank. The reaction is still derived
470
+ * automatically per device type — no trigger/eventType controls.
446
471
  */
447
472
  async buildDeviceSettingsContribution(deviceId) {
448
473
  const device = await this.lookupDevice(deviceId);
@@ -454,37 +479,19 @@ var VirtualDoorbellAddon = class extends BaseAddon {
454
479
  description: "Ring this camera’s doorbell when a chosen device becomes active — a contact opening, a switch turning on, or a sensor firing. Pick any binary sensor or switch below; the reaction is automatic per device type.",
455
480
  order: 71,
456
481
  fields: [{
457
- type: "multiselect",
482
+ type: "device-multiselect",
458
483
  key: KEY_SOURCES,
459
484
  label: "Trigger devices",
460
485
  description: "Any of these becoming active rings this camera. Binary sensors ring when they go active; switches ring when they turn on.",
461
- options: await this.listSourceDeviceOptions(),
486
+ deviceFilter: {
487
+ caps: SOURCE_CAPS,
488
+ types: SOURCE_DEVICE_TYPES,
489
+ excludeDeviceId: deviceId
490
+ },
462
491
  value: sources.map((s) => String(s.deviceId))
463
492
  }]
464
493
  }] };
465
494
  }
466
- /**
467
- * Cluster-wide picker options: every device whose BINDINGS expose one of
468
- * the recognised binary/switch source caps. Binding-sourced (not
469
- * feature-flag sourced) because cap names are exactly what the runtime
470
- * consumes. Labelled `Name (Location)` (id carried in the option value)
471
- * so the searchable multi-select matches on name, location, or id — the
472
- * same shape "Linked devices" uses.
473
- */
474
- async listSourceDeviceOptions() {
475
- try {
476
- const [devices, allBindings] = await Promise.all([this.ctx.api.deviceManager.listAll.query({}), this.ctx.api.deviceManager.getAllBindings.query({})]);
477
- const eligibleIds = /* @__PURE__ */ new Set();
478
- for (const deviceBindings of allBindings) if (deviceBindings.entries.some((e) => SOURCE_CAPS.includes(e.capName))) eligibleIds.add(deviceBindings.deviceId);
479
- return devices.filter((d) => eligibleIds.has(d.id)).map((d) => ({
480
- value: String(d.id),
481
- label: typeof d.location === "string" && d.location.length > 0 ? `${d.name} (${d.location})` : d.name
482
- })).toSorted((a, b) => a.label.localeCompare(b.label));
483
- } catch (err) {
484
- this.ctx.logger.warn("virtual-doorbell: source-device enumeration failed", { meta: { error: errMsg(err) } });
485
- return [];
486
- }
487
- }
488
495
  async saveDeviceSettingsPatch(deviceId, patch) {
489
496
  if (!this.ctx.settings) throw new Error("[virtual-doorbell] settings store unavailable — cannot persist per-device settings");
490
497
  if ("doorbellSources" in patch) {
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { L as logDestinationCapability, it as BaseAddon } from "../../dist-RlzjLezj.mjs";
1
+ import { L as logDestinationCapability, it as BaseAddon } from "../../dist-JAF9YR1q.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_chunk.__toESM(node_crypto);
9
9
  let crypto$1 = require("crypto");
@@ -1,4 +1,4 @@
1
- import { _ as authProviderCapability, d as UserRecordSchema, it as BaseAddon, l as ScopedTokenSchema, r as ApiKeyRecordSchema, tt as userManagementCapability } from "../../dist-RlzjLezj.mjs";
1
+ import { _ as authProviderCapability, d as UserRecordSchema, it as BaseAddon, l as ScopedTokenSchema, r as ApiKeyRecordSchema, tt as userManagementCapability } from "../../dist-JAF9YR1q.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import * as crypto$1 from "node:crypto";
4
4
  import { randomUUID } from "node:crypto";
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-CD5HJkAg.js");
3
+ const require_dist = require("../../dist-Bg795EQF.js");
4
4
  let node_os = require("node:os");
5
5
  node_os = require_chunk.__toESM(node_os);
6
6
  //#region src/builtins/local-network/local-network.addon.ts
@@ -1,4 +1,4 @@
1
- import { I as localNetworkCapability, it as BaseAddon, kt as EventCategory } from "../../dist-RlzjLezj.mjs";
1
+ import { I as localNetworkCapability, it as BaseAddon, kt as EventCategory } from "../../dist-JAF9YR1q.mjs";
2
2
  import * as os from "node:os";
3
3
  //#region src/builtins/local-network/local-network.addon.ts
4
4
  /**
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { _t as createEvent, it as BaseAddon, kt as EventCategory, z as metricsProviderCapability } from "../../dist-RlzjLezj.mjs";
1
+ import { _t as createEvent, it as BaseAddon, kt as EventCategory, z as metricsProviderCapability } from "../../dist-JAF9YR1q.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile, execFileSync } from "node:child_process";
4
4
  import { promisify } from "node:util";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { W as platformProbeCapability, it as BaseAddon, kt as EventCategory, q as scoreRuntimes, rt as errMsg, yt as emitReadiness } from "../../dist-RlzjLezj.mjs";
1
+ import { W as platformProbeCapability, it as BaseAddon, kt as EventCategory, q as scoreRuntimes, rt as errMsg, yt as emitReadiness } from "../../dist-JAF9YR1q.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  import { promisify } from "node:util";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
8
8
  /**
9
9
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -1,4 +1,4 @@
1
- import { it as BaseAddon, kt as EventCategory } from "../../dist-RlzjLezj.mjs";
1
+ import { it as BaseAddon, kt as EventCategory } from "../../dist-JAF9YR1q.mjs";
2
2
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
3
3
  /**
4
4
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  //#region src/builtins/snapshot/snapshot-coalescing.ts
9
9
  /**
@@ -1,4 +1,4 @@
1
- import { $ as streamQualityLabel, B as nodePin, X as snapshotCapability, ct as DeviceFeature, it as BaseAddon, rt as errMsg, ut as DeviceType } from "../../dist-RlzjLezj.mjs";
1
+ import { $ as streamQualityLabel, B as nodePin, X as snapshotCapability, ct as DeviceFeature, it as BaseAddon, rt as errMsg, ut as DeviceType } from "../../dist-JAF9YR1q.mjs";
2
2
  import { execFile } from "node:child_process";
3
3
  //#region src/builtins/snapshot/snapshot-coalescing.ts
4
4
  /**
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { O as filesystemBrowseCapability, Q as storageProviderCapability, it as BaseAddon } from "../../dist-RlzjLezj.mjs";
1
+ import { O as filesystemBrowseCapability, Q as storageProviderCapability, it as BaseAddon } from "../../dist-JAF9YR1q.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import * as path$1 from "node:path";
4
4
  import { basename, dirname, join, resolve, sep } from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let better_sqlite3 = require("better-sqlite3");
9
9
  better_sqlite3 = require_chunk.__toESM(better_sqlite3);
@@ -1,4 +1,4 @@
1
- import { Y as settingsStoreCapability, it as BaseAddon, mt as asJsonObject, rt as errMsg, s as RUNTIME_DEFAULTS, wt as parseJsonUnknown } from "../../dist-RlzjLezj.mjs";
1
+ import { Y as settingsStoreCapability, it as BaseAddon, mt as asJsonObject, rt as errMsg, s as RUNTIME_DEFAULTS, wt as parseJsonUnknown } from "../../dist-JAF9YR1q.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import Database from "better-sqlite3";
4
4
  //#region src/builtins/sqlite-storage/sqlite-settings-backend.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_chunk.__toESM(node_path);
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -1,4 +1,4 @@
1
- import { Ct as parseJsonObject, Z as storageCapability, it as BaseAddon, u as StorageLocationTypeSchema } from "../../dist-RlzjLezj.mjs";
1
+ import { Ct as parseJsonObject, Z as storageCapability, it as BaseAddon, u as StorageLocationTypeSchema } from "../../dist-JAF9YR1q.mjs";
2
2
  import * as path$1 from "node:path";
3
3
  import * as fs from "node:fs/promises";
4
4
  import { buildStorageLocationRegistry } from "@camstack/system";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { it as BaseAddon, rt as errMsg, xt as hydrateSchema } from "../../dist-RlzjLezj.mjs";
1
+ import { it as BaseAddon, rt as errMsg, xt as hydrateSchema } from "../../dist-JAF9YR1q.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CD5HJkAg.js");
6
+ const require_dist = require("../../dist-Bg795EQF.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { L as logDestinationCapability, it as BaseAddon } from "../../dist-RlzjLezj.mjs";
1
+ import { L as logDestinationCapability, it as BaseAddon } from "../../dist-JAF9YR1q.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";