@camstack/addon-provider-homeassistant 1.2.172 → 1.2.601

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/dist/addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as connectivityCapability, At as record, B as floodCapability, C as buildAddonRouteProvider, Ct as _enum, D as carbonMonoxideCapability, Dt as literal, E as calendarSourceCapability, Et as discriminatedUnion, F as deviceAdoptionCapability, Ft as EventCategory, G as lawnMowerControlCapability, H as humidifierCapability, J as motionCapability, K as lockControlCapability, L as enumSensorCapability, M as controlCapability, Mt as union, N as coverCapability, Nt as unknown, O as climateControlCapability, Ot as number, P as deviceAdminLinkCapability, Pt as url, Q as numericSensorCapability, R as eventEmitterCapability, S as brokerCapability, T as buttonCapability, Tt as boolean, U as humiditySensorCapability, V as gasCapability, W as imageCapability, X as notificationOutputCapability, Y as normalizeUnit, Z as notifierCapability, _ as automationControlCapability, _t as DeviceRole, at as scriptRunnerCapability, b as binaryCapability, ct as tamperCapability, d as TargetSchema, dt as vacuumControlCapability, et as parseAdminBase, f as accessoriesCapability, ft as valveCapability, g as ambientLightSensorCapability, gt as DeviceFeature, h as alarmPanelCapability, ht as weatherCapability, it as pressureSensorCapability, j as contactCapability, jt as string, k as colorCapability, kt as object, lt as temperatureSensorCapability, m as airQualitySensorCapability, mt as waterHeaterCapability, n as BaseDevice, nt as prepareNotification, ot as smokeCapability, p as addonRoutesCapability, pt as vibrationCapability, q as mediaPlayerCapability, r as BaseDeviceProvider, rt as presenceCapability, s as EnumSensorDateTimeFormatSchema, st as switchCapability, tt as powerMeterCapability, ut as updateCapability, v as batteryCapability, vt as DeviceType, w as buildDeviceAdminUrl, wt as array, x as brightnessCapability, xt as createEvent, y as bestLocationMatch, yt as errMsg, z as fanControlCapability } from "./dist-B9xf8_Ix.mjs";
1
+ import { A as connectivityCapability, At as boolean, B as floodCapability, Bt as EventCategory, C as buildAddonRouteProvider, Ct as defineEntryKind, D as carbonMonoxideCapability, Dt as settingsBrokersAdoptsFrom, E as calendarSourceCapability, F as deviceAdoptionCapability, Ft as record, G as lawnMowerControlCapability, H as humidifierCapability, It as string, J as motionCapability, K as lockControlCapability, L as enumSensorCapability, Lt as union, M as controlCapability, Mt as literal, N as coverCapability, Nt as number, O as climateControlCapability, Ot as _enum, P as deviceAdminLinkCapability, Pt as object, Q as numericSensorCapability, R as eventEmitterCapability, Rt as unknown, S as brokerCapability, St as createEvent, T as buttonCapability, Tt as entryBody, U as humiditySensorCapability, V as gasCapability, W as imageCapability, X as notificationOutputCapability, Y as normalizeUnit, Z as notifierCapability, _ as automationControlCapability, _t as DeviceRole, at as scriptRunnerCapability, b as binaryCapability, ct as tamperCapability, d as TargetSchema, dt as vacuumControlCapability, et as parseAdminBase, f as accessoriesCapability, ft as valveCapability, g as ambientLightSensorCapability, gt as DeviceFeature, h as alarmPanelCapability, ht as weatherCapability, it as pressureSensorCapability, j as contactCapability, jt as discriminatedUnion, k as colorCapability, kt as array, lt as temperatureSensorCapability, m as airQualitySensorCapability, mt as waterHeaterCapability, n as BaseDevice, nt as prepareNotification, ot as smokeCapability, p as addonRoutesCapability, pt as vibrationCapability, q as mediaPlayerCapability, r as BaseDeviceProvider, rt as presenceCapability, s as EnumSensorDateTimeFormatSchema, st as switchCapability, tt as powerMeterCapability, ut as updateCapability, v as batteryCapability, vt as DeviceType, w as buildDeviceAdminUrl, wt as defineRecordKinds, x as brightnessCapability, xt as RecordSecretUnavailableError, y as bestLocationMatch, yt as errMsg, z as fanControlCapability, zt as url } from "./dist-BQLjEZxA.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
4
4
  //#region src/ha-admin-link.ts
@@ -1591,6 +1591,7 @@ var COLD_START$15 = {
1591
1591
  * - `target_temp_high` / `target_temp_low` → `targetHigh` / `targetLow`
1592
1592
  * - `target_humidity` → `targetHumidity`
1593
1593
  * - `current_temperature` / `current_humidity` → read-only
1594
+ * - `min_temp` / `max_temp` / `target_temp_step` → setpoint bounds
1594
1595
  *
1595
1596
  * Mutations route to:
1596
1597
  * - `climate.set_hvac_mode({ hvac_mode })`
@@ -1690,6 +1691,9 @@ function climateSliceFromState(state) {
1690
1691
  currentTemp: readNumberAttr$5(attrs, "current_temperature"),
1691
1692
  currentHumidity: readNumberAttr$5(attrs, "current_humidity"),
1692
1693
  ...readSwing(attrs),
1694
+ minTemp: readNumberAttr$5(attrs, "min_temp"),
1695
+ maxTemp: readNumberAttr$5(attrs, "max_temp"),
1696
+ tempStep: readNumberAttr$5(attrs, "target_temp_step"),
1693
1697
  lastFetchedAt
1694
1698
  };
1695
1699
  }
@@ -5804,7 +5808,7 @@ function mapHaEntityToCamStack(input) {
5804
5808
  switch (domain) {
5805
5809
  case "switch": return {
5806
5810
  deviceType: DeviceType.Switch,
5807
- role: null,
5811
+ role: deviceClass === "outlet" ? DeviceRole.Outlet : null,
5808
5812
  capabilities: ["switch"]
5809
5813
  };
5810
5814
  case "input_boolean": return {
@@ -6777,6 +6781,36 @@ function createHaIconRouteProvider() {
6777
6781
  }
6778
6782
  }]);
6779
6783
  }
6784
+ var haBrokersEntryKind = defineEntryKind("brokers", {
6785
+ title: "Home Assistant brokers",
6786
+ schema: entryBody({
6787
+ auth: HaBrokerEntrySchema.shape.auth,
6788
+ integrationId: string().optional()
6789
+ }),
6790
+ writeTrigger: "an operator adds, edits or removes a Home Assistant broker connection",
6791
+ adoptsFrom: settingsBrokersAdoptsFrom("provider-homeassistant")
6792
+ });
6793
+ var haRecordKinds = defineRecordKinds({
6794
+ /**
6795
+ * One row per configured Home Assistant push target. Operator-managed via the
6796
+ * notification-output cap CRUD; no TTL.
6797
+ * The legacy collection name is `provider-homeassistant:ha-notification-targets`.
6798
+ */
6799
+ "ha-notification-targets": {
6800
+ schema: TargetSchema.omit({ id: true }),
6801
+ class: "config",
6802
+ writeTrigger: "an operator creates, updates, enables/disables or deletes a HA notification target",
6803
+ device: "none",
6804
+ at: "none",
6805
+ retention: { none: "a row is removed only on an explicit operator delete; bounded by the number of HA notify targets the operator configures" },
6806
+ adoptsFrom: "provider-homeassistant:ha-notification-targets",
6807
+ entry: { title: "Home Assistant notification targets" }
6808
+ } });
6809
+ /** All record kinds this addon declares (targets + B2 broker entries). */
6810
+ var haAllRecordKinds = {
6811
+ ...haRecordKinds,
6812
+ [haBrokersEntryKind.name]: haBrokersEntryKind
6813
+ };
6780
6814
  //#endregion
6781
6815
  //#region src/ha-notification-output.ts
6782
6816
  /**
@@ -6811,8 +6845,6 @@ function createHaIconRouteProvider() {
6811
6845
  */
6812
6846
  /** The single kind this provider contributes to the shared catalog. */
6813
6847
  var HA_NOTIFICATION_KIND = "homeassistant";
6814
- /** addonId-namespaced settings-store collection holding HA notify targets. */
6815
- var HA_TARGETS_COLLECTION = "provider-homeassistant:ha-notification-targets";
6816
6848
  function haConfigSchema() {
6817
6849
  return { sections: [{
6818
6850
  id: "connection",
@@ -6992,71 +7024,59 @@ function parseNotifyServices(result, brokerId) {
6992
7024
  }
6993
7025
  }));
6994
7026
  }
6995
- /** KV JSON-blob shape — a single `data` column routes the row through the
6996
- * settings backend's canonical key/value path (id TEXT PK, data TEXT). */
6997
- var KV_BLOB_COLUMNS = [{
6998
- name: "data",
6999
- type: "TEXT",
7000
- notNull: true
7001
- }];
7002
- function rowToTarget(row) {
7003
- const parsed = TargetSchema.safeParse({
7004
- id: row.id,
7005
- ...row.data
7006
- });
7007
- return parsed.success ? parsed.data : null;
7008
- }
7009
- function targetToData(target) {
7010
- return {
7011
- name: target.name,
7012
- kind: target.kind,
7013
- addonId: target.addonId,
7014
- enabled: target.enabled,
7015
- config: target.config
7016
- };
7017
- }
7018
- /** CRUD over one settings-store collection of `homeassistant` notify targets. */
7019
- var HaTargetStore = class {
7020
- store;
7021
- collection;
7022
- constructor(store, collection = HA_TARGETS_COLLECTION) {
7023
- this.store = store;
7024
- this.collection = collection;
7027
+ /**
7028
+ * CRUD over the `ha-notification-targets` record kind.
7029
+ *
7030
+ * `HaTargetStore.of(records)` is the production factory; the test helper
7031
+ * (`makeFakeHaRecords`) supplies a fake transport so nothing hits SQLite.
7032
+ * No `init()` method: kinds are declared at addon boot via
7033
+ * `ctx.records.declare(haRecordKinds)`.
7034
+ */
7035
+ var HaTargetStore = class HaTargetStore {
7036
+ kind;
7037
+ constructor(kind) {
7038
+ this.kind = kind;
7025
7039
  }
7026
- /** Declare the backing collection before any read/write. The SQLite settings
7027
- * backend rejects undeclared collections (fail-fast), so this MUST run before
7028
- * `list`/`getById`/`upsert`/`delete` — mirrors addon-ai's ProfileStore.init. */
7029
- async init() {
7030
- await this.store.declareCollection(this.collection, KV_BLOB_COLUMNS);
7040
+ /** Factory: build from a declared `RecordsAccess` (after `ctx.records.declare`). */
7041
+ static of(records) {
7042
+ return new HaTargetStore(records.kind(haRecordKinds["ha-notification-targets"]));
7031
7043
  }
7032
7044
  async list() {
7033
- const rows = await this.store.query(this.collection);
7045
+ const page = await this.kind.query({ limit: 1e3 });
7034
7046
  const out = [];
7035
- for (const row of rows) {
7036
- const target = rowToTarget(row);
7037
- if (target) out.push(target);
7047
+ for (const record of page.records) {
7048
+ const parsed = TargetSchema.safeParse({
7049
+ id: record.id,
7050
+ ...record.data
7051
+ });
7052
+ if (parsed.success) out.push(parsed.data);
7038
7053
  }
7039
7054
  return out;
7040
7055
  }
7041
7056
  async getById(id) {
7042
- const row = (await this.store.query(this.collection)).find((r) => r.id === id);
7043
- return row ? rowToTarget(row) : null;
7057
+ const row = await this.kind.get(id);
7058
+ if (row === null) return null;
7059
+ const parsed = TargetSchema.safeParse({
7060
+ id: row.id,
7061
+ ...row.data
7062
+ });
7063
+ return parsed.success ? parsed.data : null;
7044
7064
  }
7045
7065
  async upsert(target) {
7046
- const exists = (await this.store.query(this.collection)).some((r) => r.id === target.id);
7047
- const data = targetToData(target);
7048
- if (exists) await this.store.update(this.collection, target.id, data);
7049
- else await this.store.insert(this.collection, {
7050
- id: target.id,
7051
- data
7066
+ const { id, ...rest } = target;
7067
+ const parsed = haRecordKinds["ha-notification-targets"].schema.safeParse(rest);
7068
+ if (!parsed.success) throw new Error(`invalid HA target '${id}': ${parsed.error.message}`);
7069
+ await this.kind.put({
7070
+ id,
7071
+ data: parsed.data
7052
7072
  });
7053
7073
  }
7054
7074
  async delete(id) {
7055
- await this.store.remove(this.collection, id);
7075
+ await this.kind.delete(id);
7056
7076
  }
7057
7077
  async setEnabled(id, enabled) {
7058
7078
  const target = await this.getById(id);
7059
- if (!target) return null;
7079
+ if (target === null) return null;
7060
7080
  const next = {
7061
7081
  ...target,
7062
7082
  enabled
@@ -7065,78 +7085,6 @@ var HaTargetStore = class {
7065
7085
  return next;
7066
7086
  }
7067
7087
  };
7068
- /** Adapt the tRPC-shaped `settings-store` cap client to the narrow port. */
7069
- function createApiHaSettingsStorePort(api) {
7070
- return {
7071
- query: async (collection) => {
7072
- return (await api.settingsStore.query.query({ collection })).map((r) => ({
7073
- id: r.id,
7074
- data: r.data
7075
- }));
7076
- },
7077
- insert: async (collection, record) => {
7078
- await api.settingsStore.insert.mutate({
7079
- collection,
7080
- record: {
7081
- id: record.id,
7082
- data: { ...record.data }
7083
- }
7084
- });
7085
- },
7086
- update: async (collection, id, data) => {
7087
- await api.settingsStore.update.mutate({
7088
- collection,
7089
- id,
7090
- data
7091
- });
7092
- },
7093
- remove: async (collection, id) => {
7094
- await api.settingsStore.delete.mutate({
7095
- collection,
7096
- key: id
7097
- });
7098
- },
7099
- declareCollection: async (collection, columns) => {
7100
- await api.settingsStore.declareCollection.mutate({
7101
- collection,
7102
- columns: columns.map((c) => ({ ...c }))
7103
- });
7104
- }
7105
- };
7106
- }
7107
- /** In-memory port — a resilient fallback when no `api` is wired. */
7108
- function createMemoryHaSettingsStorePort() {
7109
- const collections = /* @__PURE__ */ new Map();
7110
- const bucket = (collection) => {
7111
- let map = collections.get(collection);
7112
- if (!map) {
7113
- map = /* @__PURE__ */ new Map();
7114
- collections.set(collection, map);
7115
- }
7116
- return map;
7117
- };
7118
- return {
7119
- query: async (collection) => [...bucket(collection).values()],
7120
- insert: async (collection, record) => {
7121
- bucket(collection).set(record.id, {
7122
- id: record.id,
7123
- data: { ...record.data }
7124
- });
7125
- },
7126
- update: async (collection, id, data) => {
7127
- bucket(collection).set(id, {
7128
- id,
7129
- data: { ...data }
7130
- });
7131
- },
7132
- remove: async (collection, id) => {
7133
- bucket(collection).delete(id);
7134
- },
7135
- declareCollection: async (collection) => {
7136
- bucket(collection);
7137
- }
7138
- };
7139
- }
7140
7088
  /** The canned notification `testTarget` sends when the caller omits a sample. */
7141
7089
  function cannedNotification() {
7142
7090
  return {
@@ -7286,6 +7234,78 @@ function createHaNotificationOutputProvider(deps) {
7286
7234
  };
7287
7235
  }
7288
7236
  //#endregion
7237
+ //#region src/ha-broker-store.ts
7238
+ /**
7239
+ * CRUD for HA brokers over the `brokers` entry record kind (B2).
7240
+ */
7241
+ function rowToEntry(id, data) {
7242
+ const parsed = HaBrokerEntrySchema.safeParse({
7243
+ id,
7244
+ name: data.name,
7245
+ auth: data.auth,
7246
+ integrationId: data.integrationId
7247
+ });
7248
+ return parsed.success ? parsed.data : null;
7249
+ }
7250
+ var HaBrokerStore = class HaBrokerStore {
7251
+ kind;
7252
+ constructor(kind) {
7253
+ this.kind = kind;
7254
+ }
7255
+ static of(records) {
7256
+ return new HaBrokerStore(records.kind(haBrokersEntryKind));
7257
+ }
7258
+ async listResolved() {
7259
+ const page = await this.kind.query({ limit: 1e3 });
7260
+ const out = [];
7261
+ for (const record of page.records) {
7262
+ let data = record.data;
7263
+ try {
7264
+ const resolved = await this.kind.getResolved(record.id);
7265
+ if (resolved !== null) data = resolved.data;
7266
+ } catch (err) {
7267
+ if (!(err instanceof RecordSecretUnavailableError)) throw err;
7268
+ }
7269
+ const entry = rowToEntry(record.id, data);
7270
+ if (entry !== null) out.push(entry);
7271
+ }
7272
+ return out;
7273
+ }
7274
+ async getById(id) {
7275
+ try {
7276
+ const row = await this.kind.getResolved(id);
7277
+ if (row === null) return null;
7278
+ return rowToEntry(row.id, row.data);
7279
+ } catch (err) {
7280
+ if (err instanceof RecordSecretUnavailableError) return null;
7281
+ throw err;
7282
+ }
7283
+ }
7284
+ async upsert(entry) {
7285
+ const { id, name, auth, integrationId } = entry;
7286
+ const body = {
7287
+ name,
7288
+ auth,
7289
+ ...integrationId === void 0 ? {} : { integrationId }
7290
+ };
7291
+ const parsed = haBrokersEntryKind.schema.safeParse(body);
7292
+ if (!parsed.success) throw new Error(`invalid HA broker '${id}': ${parsed.error.message}`);
7293
+ await this.kind.put({
7294
+ id,
7295
+ data: parsed.data
7296
+ });
7297
+ }
7298
+ async delete(id) {
7299
+ await this.kind.delete(id);
7300
+ }
7301
+ async replaceAll(entries) {
7302
+ const page = await this.kind.query({ limit: 1e3 });
7303
+ const nextIds = new Set(entries.map((e) => e.id));
7304
+ for (const row of page.records) if (!nextIds.has(row.id)) await this.kind.delete(row.id);
7305
+ for (const entry of entries) await this.upsert(entry);
7306
+ }
7307
+ };
7308
+ //#endregion
7289
7309
  //#region src/ha-calendar-source.ts
7290
7310
  function isCalendarEntity(entityId) {
7291
7311
  return entityId.startsWith("calendar.");
@@ -7946,6 +7966,9 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
7946
7966
  return `ha:${String(config?.["brokerId"] ?? "unknown")}:dev:${String(config?.["haDeviceId"] ?? Date.now())}`;
7947
7967
  }
7948
7968
  registry = null;
7969
+ brokerStore = null;
7970
+ /** Runtime broker rows (records when B2 is live, else settings). */
7971
+ activeBrokers = [];
7949
7972
  /** Periodic re-sync sweep handle (cleared on shutdown). */
7950
7973
  resyncTimer = null;
7951
7974
  /** `brokerId → aggregator deviceId` cache for the per-broker generic
@@ -7959,6 +7982,11 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
7959
7982
  super({ ...DEFAULT_CONFIG });
7960
7983
  }
7961
7984
  async onInitialize() {
7985
+ const records = this.ctx.records;
7986
+ if (records === void 0) throw new Error("provider-homeassistant: ctx.records is required (hub-only addon)");
7987
+ await records.declare(haAllRecordKinds);
7988
+ this.brokerStore = HaBrokerStore.of(records);
7989
+ this.activeBrokers = await this.loadPersistedBrokers();
7962
7990
  this.registry = new HaBrokerRegistry(this.ctx.logger, this.ctx.eventBus);
7963
7991
  this.registry.setOnBrokerConnected((brokerId) => {
7964
7992
  this.reconcileBroker(brokerId);
@@ -7969,11 +7997,11 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
7969
7997
  this.registry.setOnBusEvent((brokerId, ev) => {
7970
7998
  this.routeBusEvent(brokerId, ev);
7971
7999
  });
7972
- await this.registry.restore(this.config.brokers);
8000
+ await this.registry.restore(this.activeBrokers);
7973
8001
  this.resyncTimer = setInterval(() => {
7974
8002
  this.reconcileAllConnectedBrokers();
7975
8003
  }, HaProviderAddon.RESYNC_INTERVAL_MS);
7976
- this.ctx.logger.info("ha provider initialised", { meta: { brokerCount: this.config.brokers.length } });
8004
+ this.ctx.logger.info("ha provider initialised", { meta: { brokerCount: this.activeBrokers.length } });
7977
8005
  await this.reconcileIntegrationsToBrokers();
7978
8006
  this.subscribeIntegrationLifecycle();
7979
8007
  return [
@@ -8004,11 +8032,9 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8004
8032
  ];
8005
8033
  }
8006
8034
  async buildNotificationOutputProvider() {
8007
- const api = this.ctx.api;
8008
- const port = api ? createApiHaSettingsStorePort(api) : createMemoryHaSettingsStorePort();
8009
- if (!api) this.ctx.logger.warn("ha notification-output: no ctx.api — targets persist in-memory only");
8010
- const store = new HaTargetStore(port);
8011
- await store.init();
8035
+ const records = this.ctx.records;
8036
+ if (records === void 0) throw new Error("ha notification-output: ctx.records is required (hub-only addon)");
8037
+ const store = HaTargetStore.of(records);
8012
8038
  return createHaNotificationOutputProvider({
8013
8039
  addonId: this.ctx.id,
8014
8040
  iconUrl: HA_NOTIFICATION_ICON_URL,
@@ -8025,7 +8051,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8025
8051
  if (!manager) throw new Error(`ha-broker '${brokerId}' not found`);
8026
8052
  return manager.sendMessage({ type: "get_services" });
8027
8053
  },
8028
- listBrokerIds: () => this.config.brokers.map((b) => b.id),
8054
+ listBrokerIds: () => this.activeBrokers.map((b) => b.id),
8029
8055
  logger: this.ctx.logger.child("notification-output")
8030
8056
  });
8031
8057
  }
@@ -8086,7 +8112,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8086
8112
  const settings = await api.integrations.getSettings.query({ id: integration.id });
8087
8113
  const brokerId = typeof settings["brokerId"] === "string" ? settings["brokerId"] : null;
8088
8114
  if (!brokerId) continue;
8089
- if (!this.config.brokers.find((b) => b.id === brokerId)) {
8115
+ if (!this.activeBrokers.find((b) => b.id === brokerId)) {
8090
8116
  this.ctx.logger.warn("integration→broker: linked broker not found", { meta: {
8091
8117
  integrationId: integration.id,
8092
8118
  brokerId
@@ -8099,11 +8125,11 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8099
8125
  brokerId
8100
8126
  });
8101
8127
  }
8102
- const linkedBrokers = linkBrokersToIntegrations(this.config.brokers, integrationsWithBrokerId);
8103
- if (linkedBrokers.some((b, i) => b !== this.config.brokers[i])) await this.updateGlobalSettings({ brokers: linkedBrokers });
8104
- const orphans = computeBrokerCleanup(this.config.brokers, survivingIntegrationIds);
8128
+ const linkedBrokers = linkBrokersToIntegrations(this.activeBrokers, integrationsWithBrokerId);
8129
+ if (linkedBrokers.some((b, i) => b !== this.activeBrokers[i])) await this.persistBrokerEntries(linkedBrokers);
8130
+ const orphans = computeBrokerCleanup(this.activeBrokers, survivingIntegrationIds);
8105
8131
  this.ctx.logger.info("integration→broker reconcile", { meta: {
8106
- brokerCount: this.config.brokers.length,
8132
+ brokerCount: this.activeBrokers.length,
8107
8133
  integrationCount: haIntegrations.length,
8108
8134
  orphans: orphans.length
8109
8135
  } });
@@ -8200,7 +8226,8 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8200
8226
  async onConfigChanged() {
8201
8227
  const reg = this.registry;
8202
8228
  if (!reg) return;
8203
- const persisted = this.config.brokers;
8229
+ this.activeBrokers = await this.loadPersistedBrokers();
8230
+ const persisted = this.activeBrokers;
8204
8231
  const liveIds = new Set(reg.list().map((b) => b.id));
8205
8232
  const persistedIds = new Set(persisted.map((e) => e.id));
8206
8233
  for (const liveId of liveIds) if (!persistedIds.has(liveId)) try {
@@ -8238,7 +8265,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8238
8265
  ...b,
8239
8266
  id: created.id
8240
8267
  } : b);
8241
- await this.updateGlobalSettings({ brokers: next });
8268
+ await this.persistBrokerEntries(next);
8242
8269
  }
8243
8270
  } catch (err) {
8244
8271
  this.ctx.logger.warn("ha onConfigChanged: failed to start new broker", { meta: {
@@ -8248,9 +8275,26 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8248
8275
  }
8249
8276
  }
8250
8277
  }
8278
+ async loadPersistedBrokers() {
8279
+ const store = this.brokerStore;
8280
+ if (store !== null) {
8281
+ const fromRecords = await store.listResolved();
8282
+ if (fromRecords.length > 0) return fromRecords;
8283
+ }
8284
+ return this.config.brokers;
8285
+ }
8286
+ async persistBrokerEntries(entries) {
8287
+ this.activeBrokers = [...entries];
8288
+ const store = this.brokerStore;
8289
+ if (store !== null) {
8290
+ await store.replaceAll(entries);
8291
+ return;
8292
+ }
8293
+ await this.updateGlobalSettings({ brokers: entries });
8294
+ }
8251
8295
  buildBrokerProvider() {
8252
8296
  const ownerAddonId = this.ctx.id;
8253
- const owns = (id) => this.config.brokers.some((b) => b.id === id);
8297
+ const owns = (id) => this.activeBrokers.some((b) => b.id === id);
8254
8298
  return {
8255
8299
  list: async ({ kind }) => {
8256
8300
  if (kind && kind !== HA_KIND) return [];
@@ -8280,7 +8324,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8280
8324
  brokerId: entry.id,
8281
8325
  name
8282
8326
  } });
8283
- await this.updateGlobalSettings({ brokers: [...this.config.brokers, entry] });
8327
+ await this.persistBrokerEntries([...this.activeBrokers, entry]);
8284
8328
  return { id: entry.id };
8285
8329
  },
8286
8330
  remove: async ({ id }) => {
@@ -8295,8 +8339,8 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8295
8339
  brokerId: id,
8296
8340
  logger: this.ctx.logger
8297
8341
  });
8298
- const nextBrokers = this.config.brokers.filter((b) => b.id !== id);
8299
- await this.updateGlobalSettings({ brokers: nextBrokers });
8342
+ const nextBrokers = this.activeBrokers.filter((b) => b.id !== id);
8343
+ await this.persistBrokerEntries(nextBrokers);
8300
8344
  },
8301
8345
  testConnection: async ({ id }) => {
8302
8346
  if (!owns(id)) return {
@@ -8318,18 +8362,18 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8318
8362
  };
8319
8363
  },
8320
8364
  getSettings: async ({ id }) => {
8321
- const entry = this.config.brokers.find((b) => b.id === id);
8365
+ const entry = this.activeBrokers.find((b) => b.id === id);
8322
8366
  if (!entry) return null;
8323
8367
  return authToSettings(entry.auth);
8324
8368
  },
8325
8369
  setSettings: async ({ id, settings }) => {
8326
8370
  if (!owns(id)) return;
8327
8371
  const entry = await this.requireRegistry().updateEntry(id, settings);
8328
- const next = this.config.brokers.map((b) => b.id === id ? entry : b);
8329
- await this.updateGlobalSettings({ brokers: next });
8372
+ const next = this.activeBrokers.map((b) => b.id === id ? entry : b);
8373
+ await this.persistBrokerEntries(next);
8330
8374
  },
8331
8375
  getBrokerConfig: async ({ id }) => {
8332
- const entry = this.config.brokers.find((b) => b.id === id);
8376
+ const entry = this.activeBrokers.find((b) => b.id === id);
8333
8377
  if (!entry) return null;
8334
8378
  return authToSettings(entry.auth);
8335
8379
  },