@camstack/addon-provider-homeassistant 1.2.173 → 1.2.602
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.js +205 -147
- package/dist/addon.mjs +187 -147
- package/dist/{dist-CSIrSsu7.mjs → dist-Dcum8auE.mjs} +1452 -101
- package/dist/{dist-DTt9hwTQ.js → dist-xuu2eG_W.js} +1481 -100
- package/dist/ha-export/ha-export.addon.js +5 -1
- package/dist/ha-export/ha-export.addon.mjs +5 -1
- package/package.json +1 -1
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as connectivityCapability, At as
|
|
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-Dcum8auE.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
|
|
@@ -6781,6 +6781,36 @@ function createHaIconRouteProvider() {
|
|
|
6781
6781
|
}
|
|
6782
6782
|
}]);
|
|
6783
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
|
+
};
|
|
6784
6814
|
//#endregion
|
|
6785
6815
|
//#region src/ha-notification-output.ts
|
|
6786
6816
|
/**
|
|
@@ -6815,8 +6845,6 @@ function createHaIconRouteProvider() {
|
|
|
6815
6845
|
*/
|
|
6816
6846
|
/** The single kind this provider contributes to the shared catalog. */
|
|
6817
6847
|
var HA_NOTIFICATION_KIND = "homeassistant";
|
|
6818
|
-
/** addonId-namespaced settings-store collection holding HA notify targets. */
|
|
6819
|
-
var HA_TARGETS_COLLECTION = "provider-homeassistant:ha-notification-targets";
|
|
6820
6848
|
function haConfigSchema() {
|
|
6821
6849
|
return { sections: [{
|
|
6822
6850
|
id: "connection",
|
|
@@ -6996,71 +7024,59 @@ function parseNotifyServices(result, brokerId) {
|
|
|
6996
7024
|
}
|
|
6997
7025
|
}));
|
|
6998
7026
|
}
|
|
6999
|
-
/**
|
|
7000
|
-
*
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
return parsed.success ? parsed.data : null;
|
|
7012
|
-
}
|
|
7013
|
-
function targetToData(target) {
|
|
7014
|
-
return {
|
|
7015
|
-
name: target.name,
|
|
7016
|
-
kind: target.kind,
|
|
7017
|
-
addonId: target.addonId,
|
|
7018
|
-
enabled: target.enabled,
|
|
7019
|
-
config: target.config
|
|
7020
|
-
};
|
|
7021
|
-
}
|
|
7022
|
-
/** CRUD over one settings-store collection of `homeassistant` notify targets. */
|
|
7023
|
-
var HaTargetStore = class {
|
|
7024
|
-
store;
|
|
7025
|
-
collection;
|
|
7026
|
-
constructor(store, collection = HA_TARGETS_COLLECTION) {
|
|
7027
|
-
this.store = store;
|
|
7028
|
-
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;
|
|
7029
7039
|
}
|
|
7030
|
-
/**
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
async init() {
|
|
7034
|
-
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"]));
|
|
7035
7043
|
}
|
|
7036
7044
|
async list() {
|
|
7037
|
-
const
|
|
7045
|
+
const page = await this.kind.query({ limit: 1e3 });
|
|
7038
7046
|
const out = [];
|
|
7039
|
-
for (const
|
|
7040
|
-
const
|
|
7041
|
-
|
|
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);
|
|
7042
7053
|
}
|
|
7043
7054
|
return out;
|
|
7044
7055
|
}
|
|
7045
7056
|
async getById(id) {
|
|
7046
|
-
const row =
|
|
7047
|
-
|
|
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;
|
|
7048
7064
|
}
|
|
7049
7065
|
async upsert(target) {
|
|
7050
|
-
const
|
|
7051
|
-
const
|
|
7052
|
-
if (
|
|
7053
|
-
|
|
7054
|
-
id
|
|
7055
|
-
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
|
|
7056
7072
|
});
|
|
7057
7073
|
}
|
|
7058
7074
|
async delete(id) {
|
|
7059
|
-
await this.
|
|
7075
|
+
await this.kind.delete(id);
|
|
7060
7076
|
}
|
|
7061
7077
|
async setEnabled(id, enabled) {
|
|
7062
7078
|
const target = await this.getById(id);
|
|
7063
|
-
if (
|
|
7079
|
+
if (target === null) return null;
|
|
7064
7080
|
const next = {
|
|
7065
7081
|
...target,
|
|
7066
7082
|
enabled
|
|
@@ -7069,78 +7085,6 @@ var HaTargetStore = class {
|
|
|
7069
7085
|
return next;
|
|
7070
7086
|
}
|
|
7071
7087
|
};
|
|
7072
|
-
/** Adapt the tRPC-shaped `settings-store` cap client to the narrow port. */
|
|
7073
|
-
function createApiHaSettingsStorePort(api) {
|
|
7074
|
-
return {
|
|
7075
|
-
query: async (collection) => {
|
|
7076
|
-
return (await api.settingsStore.query.query({ collection })).map((r) => ({
|
|
7077
|
-
id: r.id,
|
|
7078
|
-
data: r.data
|
|
7079
|
-
}));
|
|
7080
|
-
},
|
|
7081
|
-
insert: async (collection, record) => {
|
|
7082
|
-
await api.settingsStore.insert.mutate({
|
|
7083
|
-
collection,
|
|
7084
|
-
record: {
|
|
7085
|
-
id: record.id,
|
|
7086
|
-
data: { ...record.data }
|
|
7087
|
-
}
|
|
7088
|
-
});
|
|
7089
|
-
},
|
|
7090
|
-
update: async (collection, id, data) => {
|
|
7091
|
-
await api.settingsStore.update.mutate({
|
|
7092
|
-
collection,
|
|
7093
|
-
id,
|
|
7094
|
-
data
|
|
7095
|
-
});
|
|
7096
|
-
},
|
|
7097
|
-
remove: async (collection, id) => {
|
|
7098
|
-
await api.settingsStore.delete.mutate({
|
|
7099
|
-
collection,
|
|
7100
|
-
key: id
|
|
7101
|
-
});
|
|
7102
|
-
},
|
|
7103
|
-
declareCollection: async (collection, columns) => {
|
|
7104
|
-
await api.settingsStore.declareCollection.mutate({
|
|
7105
|
-
collection,
|
|
7106
|
-
columns: columns.map((c) => ({ ...c }))
|
|
7107
|
-
});
|
|
7108
|
-
}
|
|
7109
|
-
};
|
|
7110
|
-
}
|
|
7111
|
-
/** In-memory port — a resilient fallback when no `api` is wired. */
|
|
7112
|
-
function createMemoryHaSettingsStorePort() {
|
|
7113
|
-
const collections = /* @__PURE__ */ new Map();
|
|
7114
|
-
const bucket = (collection) => {
|
|
7115
|
-
let map = collections.get(collection);
|
|
7116
|
-
if (!map) {
|
|
7117
|
-
map = /* @__PURE__ */ new Map();
|
|
7118
|
-
collections.set(collection, map);
|
|
7119
|
-
}
|
|
7120
|
-
return map;
|
|
7121
|
-
};
|
|
7122
|
-
return {
|
|
7123
|
-
query: async (collection) => [...bucket(collection).values()],
|
|
7124
|
-
insert: async (collection, record) => {
|
|
7125
|
-
bucket(collection).set(record.id, {
|
|
7126
|
-
id: record.id,
|
|
7127
|
-
data: { ...record.data }
|
|
7128
|
-
});
|
|
7129
|
-
},
|
|
7130
|
-
update: async (collection, id, data) => {
|
|
7131
|
-
bucket(collection).set(id, {
|
|
7132
|
-
id,
|
|
7133
|
-
data: { ...data }
|
|
7134
|
-
});
|
|
7135
|
-
},
|
|
7136
|
-
remove: async (collection, id) => {
|
|
7137
|
-
bucket(collection).delete(id);
|
|
7138
|
-
},
|
|
7139
|
-
declareCollection: async (collection) => {
|
|
7140
|
-
bucket(collection);
|
|
7141
|
-
}
|
|
7142
|
-
};
|
|
7143
|
-
}
|
|
7144
7088
|
/** The canned notification `testTarget` sends when the caller omits a sample. */
|
|
7145
7089
|
function cannedNotification() {
|
|
7146
7090
|
return {
|
|
@@ -7290,6 +7234,78 @@ function createHaNotificationOutputProvider(deps) {
|
|
|
7290
7234
|
};
|
|
7291
7235
|
}
|
|
7292
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
|
|
7293
7309
|
//#region src/ha-calendar-source.ts
|
|
7294
7310
|
function isCalendarEntity(entityId) {
|
|
7295
7311
|
return entityId.startsWith("calendar.");
|
|
@@ -7950,6 +7966,9 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
7950
7966
|
return `ha:${String(config?.["brokerId"] ?? "unknown")}:dev:${String(config?.["haDeviceId"] ?? Date.now())}`;
|
|
7951
7967
|
}
|
|
7952
7968
|
registry = null;
|
|
7969
|
+
brokerStore = null;
|
|
7970
|
+
/** Runtime broker rows (records when B2 is live, else settings). */
|
|
7971
|
+
activeBrokers = [];
|
|
7953
7972
|
/** Periodic re-sync sweep handle (cleared on shutdown). */
|
|
7954
7973
|
resyncTimer = null;
|
|
7955
7974
|
/** `brokerId → aggregator deviceId` cache for the per-broker generic
|
|
@@ -7963,6 +7982,11 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
7963
7982
|
super({ ...DEFAULT_CONFIG });
|
|
7964
7983
|
}
|
|
7965
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();
|
|
7966
7990
|
this.registry = new HaBrokerRegistry(this.ctx.logger, this.ctx.eventBus);
|
|
7967
7991
|
this.registry.setOnBrokerConnected((brokerId) => {
|
|
7968
7992
|
this.reconcileBroker(brokerId);
|
|
@@ -7973,11 +7997,11 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
7973
7997
|
this.registry.setOnBusEvent((brokerId, ev) => {
|
|
7974
7998
|
this.routeBusEvent(brokerId, ev);
|
|
7975
7999
|
});
|
|
7976
|
-
await this.registry.restore(this.
|
|
8000
|
+
await this.registry.restore(this.activeBrokers);
|
|
7977
8001
|
this.resyncTimer = setInterval(() => {
|
|
7978
8002
|
this.reconcileAllConnectedBrokers();
|
|
7979
8003
|
}, HaProviderAddon.RESYNC_INTERVAL_MS);
|
|
7980
|
-
this.ctx.logger.info("ha provider initialised", { meta: { brokerCount: this.
|
|
8004
|
+
this.ctx.logger.info("ha provider initialised", { meta: { brokerCount: this.activeBrokers.length } });
|
|
7981
8005
|
await this.reconcileIntegrationsToBrokers();
|
|
7982
8006
|
this.subscribeIntegrationLifecycle();
|
|
7983
8007
|
return [
|
|
@@ -8008,11 +8032,9 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8008
8032
|
];
|
|
8009
8033
|
}
|
|
8010
8034
|
async buildNotificationOutputProvider() {
|
|
8011
|
-
const
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
const store = new HaTargetStore(port);
|
|
8015
|
-
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);
|
|
8016
8038
|
return createHaNotificationOutputProvider({
|
|
8017
8039
|
addonId: this.ctx.id,
|
|
8018
8040
|
iconUrl: HA_NOTIFICATION_ICON_URL,
|
|
@@ -8029,7 +8051,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8029
8051
|
if (!manager) throw new Error(`ha-broker '${brokerId}' not found`);
|
|
8030
8052
|
return manager.sendMessage({ type: "get_services" });
|
|
8031
8053
|
},
|
|
8032
|
-
listBrokerIds: () => this.
|
|
8054
|
+
listBrokerIds: () => this.activeBrokers.map((b) => b.id),
|
|
8033
8055
|
logger: this.ctx.logger.child("notification-output")
|
|
8034
8056
|
});
|
|
8035
8057
|
}
|
|
@@ -8090,7 +8112,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8090
8112
|
const settings = await api.integrations.getSettings.query({ id: integration.id });
|
|
8091
8113
|
const brokerId = typeof settings["brokerId"] === "string" ? settings["brokerId"] : null;
|
|
8092
8114
|
if (!brokerId) continue;
|
|
8093
|
-
if (!this.
|
|
8115
|
+
if (!this.activeBrokers.find((b) => b.id === brokerId)) {
|
|
8094
8116
|
this.ctx.logger.warn("integration→broker: linked broker not found", { meta: {
|
|
8095
8117
|
integrationId: integration.id,
|
|
8096
8118
|
brokerId
|
|
@@ -8103,11 +8125,11 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8103
8125
|
brokerId
|
|
8104
8126
|
});
|
|
8105
8127
|
}
|
|
8106
|
-
const linkedBrokers = linkBrokersToIntegrations(this.
|
|
8107
|
-
if (linkedBrokers.some((b, i) => b !== this.
|
|
8108
|
-
const orphans = computeBrokerCleanup(this.
|
|
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);
|
|
8109
8131
|
this.ctx.logger.info("integration→broker reconcile", { meta: {
|
|
8110
|
-
brokerCount: this.
|
|
8132
|
+
brokerCount: this.activeBrokers.length,
|
|
8111
8133
|
integrationCount: haIntegrations.length,
|
|
8112
8134
|
orphans: orphans.length
|
|
8113
8135
|
} });
|
|
@@ -8204,7 +8226,8 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8204
8226
|
async onConfigChanged() {
|
|
8205
8227
|
const reg = this.registry;
|
|
8206
8228
|
if (!reg) return;
|
|
8207
|
-
|
|
8229
|
+
this.activeBrokers = await this.loadPersistedBrokers();
|
|
8230
|
+
const persisted = this.activeBrokers;
|
|
8208
8231
|
const liveIds = new Set(reg.list().map((b) => b.id));
|
|
8209
8232
|
const persistedIds = new Set(persisted.map((e) => e.id));
|
|
8210
8233
|
for (const liveId of liveIds) if (!persistedIds.has(liveId)) try {
|
|
@@ -8242,7 +8265,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8242
8265
|
...b,
|
|
8243
8266
|
id: created.id
|
|
8244
8267
|
} : b);
|
|
8245
|
-
await this.
|
|
8268
|
+
await this.persistBrokerEntries(next);
|
|
8246
8269
|
}
|
|
8247
8270
|
} catch (err) {
|
|
8248
8271
|
this.ctx.logger.warn("ha onConfigChanged: failed to start new broker", { meta: {
|
|
@@ -8252,9 +8275,26 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8252
8275
|
}
|
|
8253
8276
|
}
|
|
8254
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
|
+
}
|
|
8255
8295
|
buildBrokerProvider() {
|
|
8256
8296
|
const ownerAddonId = this.ctx.id;
|
|
8257
|
-
const owns = (id) => this.
|
|
8297
|
+
const owns = (id) => this.activeBrokers.some((b) => b.id === id);
|
|
8258
8298
|
return {
|
|
8259
8299
|
list: async ({ kind }) => {
|
|
8260
8300
|
if (kind && kind !== HA_KIND) return [];
|
|
@@ -8284,7 +8324,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8284
8324
|
brokerId: entry.id,
|
|
8285
8325
|
name
|
|
8286
8326
|
} });
|
|
8287
|
-
await this.
|
|
8327
|
+
await this.persistBrokerEntries([...this.activeBrokers, entry]);
|
|
8288
8328
|
return { id: entry.id };
|
|
8289
8329
|
},
|
|
8290
8330
|
remove: async ({ id }) => {
|
|
@@ -8299,8 +8339,8 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8299
8339
|
brokerId: id,
|
|
8300
8340
|
logger: this.ctx.logger
|
|
8301
8341
|
});
|
|
8302
|
-
const nextBrokers = this.
|
|
8303
|
-
await this.
|
|
8342
|
+
const nextBrokers = this.activeBrokers.filter((b) => b.id !== id);
|
|
8343
|
+
await this.persistBrokerEntries(nextBrokers);
|
|
8304
8344
|
},
|
|
8305
8345
|
testConnection: async ({ id }) => {
|
|
8306
8346
|
if (!owns(id)) return {
|
|
@@ -8322,18 +8362,18 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8322
8362
|
};
|
|
8323
8363
|
},
|
|
8324
8364
|
getSettings: async ({ id }) => {
|
|
8325
|
-
const entry = this.
|
|
8365
|
+
const entry = this.activeBrokers.find((b) => b.id === id);
|
|
8326
8366
|
if (!entry) return null;
|
|
8327
8367
|
return authToSettings(entry.auth);
|
|
8328
8368
|
},
|
|
8329
8369
|
setSettings: async ({ id, settings }) => {
|
|
8330
8370
|
if (!owns(id)) return;
|
|
8331
8371
|
const entry = await this.requireRegistry().updateEntry(id, settings);
|
|
8332
|
-
const next = this.
|
|
8333
|
-
await this.
|
|
8372
|
+
const next = this.activeBrokers.map((b) => b.id === id ? entry : b);
|
|
8373
|
+
await this.persistBrokerEntries(next);
|
|
8334
8374
|
},
|
|
8335
8375
|
getBrokerConfig: async ({ id }) => {
|
|
8336
|
-
const entry = this.
|
|
8376
|
+
const entry = this.activeBrokers.find((b) => b.id === id);
|
|
8337
8377
|
if (!entry) return null;
|
|
8338
8378
|
return authToSettings(entry.auth);
|
|
8339
8379
|
},
|