@camstack/addon-provider-homeassistant 1.2.653 → 1.2.654
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 +7 -0
- package/dist/addon.js +669 -495
- package/dist/addon.mjs +206 -32
- package/dist/ha-export/ha-export.addon.js +382 -130
- package/dist/ha-export/ha-export.addon.mjs +336 -84
- package/dist/{ha-broker-source-DnSL-mbs.js → ha-instance-settings-Bn_Trz6p.js} +464 -218
- package/dist/{ha-broker-source-CKdH9XHn.mjs → ha-instance-settings-g5pwb3Fi.mjs} +446 -218
- package/package.json +2 -2
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as fanControlCapability, A as bestLocationMatch, At as scriptRunnerCapability, B as climateControlCapability, Bt as valveCapability, C as accessoriesCapability, Ct as quantityFromUnit, D as ambientLightSensorCapability, Dt as runningFromMediaPlayer, E as alarmPanelCapability, Et as runningFromLawnMower, F as buildAddonRouteProvider, Ft as temperatureSensorCapability, G as contactCapability, Gt as RecordSecretUnavailableError, H as connectivityCapability, Ht as waterHeaterCapability, I as buildDeviceAdminUrl, It as tryConvertDelta, J as deviceAdminLinkCapability, Jt as defineAddonMigrations, K as controlCapability, Kt as baseUnitOf, L as buttonCapability, Lt as tryConvertUnit, M as boardKeySourceCapability, Mt as smokeCapability, N as brightnessCapability, Nt as switchCapability, O as automationControlCapability, Ot as runningFromVacuumState, P as brokerCapability, Pt as tamperCapability, Q as eventEmitterCapability, R as calendarSourceCapability, Rt as updateCapability, S as TargetSchema, St as publicEndpointReaderFrom, T as airQualitySensorCapability, Ut as weatherCapability, V as colorCapability, Vt as vibrationCapability, Xt as defineRecordKinds, Y as deviceAdoptionCapability, Yt as defineEntryKind, Z as enumSensorCapability, Zt as entryBody, _t as preconditionOf, an as number, at as ingestReading, bt as pressureSensorCapability, c as BaseDevice, cn as string, ct as mediaPlayerCapability, dn as url, dt as notificationOutputCapability, en as _enum, et as floodCapability, fn as DeviceFeature, ft as notifierCapability, g as EnumSensorDateTimeFormatSchema, gn as EventCategory, gt as powerMeterCapability, hn as errMsg, ht as parseAdminBase, i as HA_BROKER_KIND, in as literal, it as imageCapability, j as binaryCapability, k as batteryCapability, kt as runningStateCapability, l as BaseDeviceProvider, ln as union, lt as motionCapability, mn as DeviceType, nn as boolean, nt as humidifierCapability, on as object, ot as lawnMowerControlCapability, pn as DeviceRole, pt as numericSensorCapability, q as coverCapability, qt as createEvent, rn as discriminatedUnion, rt as humiditySensorCapability, sn as record, st as lockControlCapability, t as haInstanceSettingsPortsOver, tn as array, tt as gasCapability, un as unknown, ut as normalizeUnit, vt as prepareNotification, w as addonRoutesCapability, wt as resolveAttachmentUrls, x as RUNNING_STATE_COLD, yt as presenceCapability, z as carbonMonoxideCapability, zt as vacuumControlCapability } from "./ha-instance-settings-g5pwb3Fi.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
|
|
@@ -2496,11 +2496,33 @@ var HaMediaPlayerDevice = class extends HaBaseChildDevice {
|
|
|
2496
2496
|
registerRunningCap() {
|
|
2497
2497
|
this.ctx.registerNativeCap(runningStateCapability, { getStatus: async () => this.runtimeState.getCapState("running-state") ?? RUNNING_STATE_COLD });
|
|
2498
2498
|
}
|
|
2499
|
+
/** One service call per step, in order: HA has no "N steps" form. */
|
|
2500
|
+
async repeatHaService(service, steps) {
|
|
2501
|
+
for (let step = 0; step < steps; step += 1) await this.callHaService("media_player", service);
|
|
2502
|
+
}
|
|
2499
2503
|
registerMediaPlayerCap() {
|
|
2500
2504
|
this.ctx.registerNativeCap(mediaPlayerCapability, {
|
|
2501
2505
|
getStatus: async () => {
|
|
2502
2506
|
return this.runtimeState.getCapState("media-player") ?? COLD_START$10;
|
|
2503
2507
|
},
|
|
2508
|
+
turnOn: async () => {
|
|
2509
|
+
await this.callHaService("media_player", "turn_on");
|
|
2510
|
+
},
|
|
2511
|
+
turnOff: async () => {
|
|
2512
|
+
await this.callHaService("media_player", "turn_off");
|
|
2513
|
+
},
|
|
2514
|
+
volumeUp: async ({ steps }) => {
|
|
2515
|
+
await this.repeatHaService("volume_up", steps);
|
|
2516
|
+
},
|
|
2517
|
+
volumeDown: async ({ steps }) => {
|
|
2518
|
+
await this.repeatHaService("volume_down", steps);
|
|
2519
|
+
},
|
|
2520
|
+
channelUp: async ({ steps }) => {
|
|
2521
|
+
await this.repeatHaService("media_next_track", steps);
|
|
2522
|
+
},
|
|
2523
|
+
channelDown: async ({ steps }) => {
|
|
2524
|
+
await this.repeatHaService("media_previous_track", steps);
|
|
2525
|
+
},
|
|
2504
2526
|
play: async () => {
|
|
2505
2527
|
await this.callHaService("media_player", "media_play");
|
|
2506
2528
|
},
|
|
@@ -6439,6 +6461,8 @@ function mapMediaPlayer(attributes) {
|
|
|
6439
6461
|
if ((sf & 8) !== 0) features.push(DeviceFeature.MediaPlayerMute);
|
|
6440
6462
|
if ((sf & 16) !== 0) features.push(DeviceFeature.MediaPlayerPrevious);
|
|
6441
6463
|
if ((sf & 32) !== 0) features.push(DeviceFeature.MediaPlayerNext);
|
|
6464
|
+
if ((sf & 384) === 384) features.push(DeviceFeature.MediaPlayerPower);
|
|
6465
|
+
if ((sf & 1024) !== 0) features.push(DeviceFeature.MediaPlayerVolumeStep);
|
|
6442
6466
|
if ((sf & 512) !== 0) features.push(DeviceFeature.MediaPlayerPlayMedia);
|
|
6443
6467
|
if ((sf & 2048) !== 0) features.push(DeviceFeature.MediaPlayerSelectSource);
|
|
6444
6468
|
if ((sf & 4096) !== 0) features.push(DeviceFeature.MediaPlayerStop);
|
|
@@ -7932,6 +7956,127 @@ function createHaBoardKeyProvider(source) {
|
|
|
7932
7956
|
};
|
|
7933
7957
|
}
|
|
7934
7958
|
//#endregion
|
|
7959
|
+
//#region src/migrations/calendar-ids-to-integration.ts
|
|
7960
|
+
/**
|
|
7961
|
+
* The HA calendars an instance imports are a fact of THAT instance (D985): each Home Assistant
|
|
7962
|
+
* integration carries its own `calendarIds` in its settings, and the addon-level `calendarIds`
|
|
7963
|
+
* setting (one set of `calendar.*` entity ids for every broker) goes away.
|
|
7964
|
+
*
|
|
7965
|
+
* Two migrations, in this order:
|
|
7966
|
+
*
|
|
7967
|
+
* 1. `calendar-ids-to-integration` (procedure) copies the shared set to EVERY integration that
|
|
7968
|
+
* has no `calendarIds` yet. An integration that already has one is never overwritten; one
|
|
7969
|
+
* deleted meanwhile is skipped, never recreated; a stored value that is not a list of strings
|
|
7970
|
+
* is an ambiguity and throws (nothing is guessed).
|
|
7971
|
+
* 2. `drop-shared-calendar-ids` (plan) deletes the shared setting after a read-back: every
|
|
7972
|
+
* integration must show its own set. When one holds a set that differs from the shared one,
|
|
7973
|
+
* the shared value is the loser and stays PARKED in place, never deleted.
|
|
7974
|
+
*
|
|
7975
|
+
* Both are idempotent.
|
|
7976
|
+
*/
|
|
7977
|
+
var HA_PROVIDER_ID = "provider-homeassistant";
|
|
7978
|
+
/** The canonical settings table: one row per key, id `<addonId>.<key>`. */
|
|
7979
|
+
var ADDON_SETTINGS_TABLE = "addon-settings";
|
|
7980
|
+
var SHARED_CALENDAR_IDS_KEY = "calendarIds";
|
|
7981
|
+
async function readSharedSet(ports) {
|
|
7982
|
+
const answer = await ports.readStored(SHARED_CALENDAR_IDS_KEY);
|
|
7983
|
+
if (answer.kind === "unreadable") throw new Error(`provider-homeassistant: the shared calendarIds cannot be read (${answer.reason})`);
|
|
7984
|
+
if (answer.kind === "absent") return null;
|
|
7985
|
+
const value = answer.value;
|
|
7986
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) throw new Error("provider-homeassistant: the shared calendarIds is not a list of entity ids — nothing copied");
|
|
7987
|
+
return value;
|
|
7988
|
+
}
|
|
7989
|
+
async function copyCalendarIdsToIntegrations(ports) {
|
|
7990
|
+
const shared = await readSharedSet(ports);
|
|
7991
|
+
if (shared === null) return {
|
|
7992
|
+
outcome: "already-done",
|
|
7993
|
+
summary: { reason: "no-shared-set" }
|
|
7994
|
+
};
|
|
7995
|
+
let copied = 0;
|
|
7996
|
+
let kept = 0;
|
|
7997
|
+
let gone = 0;
|
|
7998
|
+
for (const instance of await ports.listInstances()) {
|
|
7999
|
+
if ("calendarIds" in instance.settings) {
|
|
8000
|
+
kept += 1;
|
|
8001
|
+
continue;
|
|
8002
|
+
}
|
|
8003
|
+
if (await ports.patchInstanceSettings(instance.integrationId, { calendarIds: [...shared] })) copied += 1;
|
|
8004
|
+
else gone += 1;
|
|
8005
|
+
}
|
|
8006
|
+
return {
|
|
8007
|
+
outcome: copied === 0 ? "already-done" : "done",
|
|
8008
|
+
summary: {
|
|
8009
|
+
copied,
|
|
8010
|
+
kept,
|
|
8011
|
+
gone
|
|
8012
|
+
}
|
|
8013
|
+
};
|
|
8014
|
+
}
|
|
8015
|
+
var asIds = (value) => Array.isArray(value) && value.every((item) => typeof item === "string") ? value : null;
|
|
8016
|
+
var sameSet = (a, b) => {
|
|
8017
|
+
const left = new Set(a);
|
|
8018
|
+
const right = new Set(b);
|
|
8019
|
+
return left.size === right.size && [...left].every((id) => right.has(id));
|
|
8020
|
+
};
|
|
8021
|
+
async function planDropSharedCalendarIds(ports) {
|
|
8022
|
+
const shared = await readSharedSet(ports);
|
|
8023
|
+
if (shared === null) return { edits: [] };
|
|
8024
|
+
const instances = await ports.listInstances();
|
|
8025
|
+
if (instances.length === 0) return { edits: [] };
|
|
8026
|
+
const own = instances.map((instance) => ({
|
|
8027
|
+
integrationId: instance.integrationId,
|
|
8028
|
+
ids: asIds(instance.settings[SHARED_CALENDAR_IDS_KEY])
|
|
8029
|
+
}));
|
|
8030
|
+
const missing = own.filter((entry) => entry.ids === null);
|
|
8031
|
+
if (missing.length > 0) throw new Error(`provider-homeassistant: the shared calendarIds is not dropped, ${missing.map((m) => m.integrationId).join(", ")} has no set of its own`);
|
|
8032
|
+
if (!own.every((entry) => sameSet(entry.ids ?? [], shared))) return { edits: [] };
|
|
8033
|
+
const rowId = `${HA_PROVIDER_ID}.${SHARED_CALENDAR_IDS_KEY}`;
|
|
8034
|
+
return { edits: [{
|
|
8035
|
+
op: "deleteKeys",
|
|
8036
|
+
scope: ADDON_SETTINGS_TABLE,
|
|
8037
|
+
keys: [rowId],
|
|
8038
|
+
precondition: preconditionOf({ [rowId]: {
|
|
8039
|
+
addonId: HA_PROVIDER_ID,
|
|
8040
|
+
key: SHARED_CALENDAR_IDS_KEY,
|
|
8041
|
+
value: shared
|
|
8042
|
+
} })
|
|
8043
|
+
}] };
|
|
8044
|
+
}
|
|
8045
|
+
//#endregion
|
|
8046
|
+
//#region src/migrations/index.ts
|
|
8047
|
+
/** The `provider-homeassistant` catalogue (D987): offered to the engine when the addon initialises. */
|
|
8048
|
+
var ADDON_MIGRATIONS = [{
|
|
8049
|
+
meta: {
|
|
8050
|
+
id: "provider-homeassistant/2026-11-03-calendar-ids-to-integration",
|
|
8051
|
+
owner: "provider-homeassistant",
|
|
8052
|
+
level: "addon-state",
|
|
8053
|
+
seq: 1,
|
|
8054
|
+
dependsOn: [],
|
|
8055
|
+
store: "settings",
|
|
8056
|
+
scope: "cluster",
|
|
8057
|
+
onFailure: "tolerate",
|
|
8058
|
+
bodyKind: "procedure",
|
|
8059
|
+
description: "Copy the shared calendar set to the settings of every Home Assistant integration that has none",
|
|
8060
|
+
fixtureMark: "the broker link stored beside the calendars of an integration"
|
|
8061
|
+
},
|
|
8062
|
+
procedure: copyCalendarIdsToIntegrations
|
|
8063
|
+
}, {
|
|
8064
|
+
meta: {
|
|
8065
|
+
id: "provider-homeassistant/2026-11-03-drop-shared-calendar-ids",
|
|
8066
|
+
owner: "provider-homeassistant",
|
|
8067
|
+
level: "addon-state",
|
|
8068
|
+
seq: 2,
|
|
8069
|
+
dependsOn: ["provider-homeassistant/2026-11-03-calendar-ids-to-integration"],
|
|
8070
|
+
store: "settings",
|
|
8071
|
+
scope: "cluster",
|
|
8072
|
+
onFailure: "tolerate",
|
|
8073
|
+
bodyKind: "plan",
|
|
8074
|
+
description: "Delete the shared calendar set once every integration shows its own",
|
|
8075
|
+
fixtureMark: "the shared set parked when an integration holds a different one"
|
|
8076
|
+
},
|
|
8077
|
+
plan: planDropSharedCalendarIds
|
|
8078
|
+
}];
|
|
8079
|
+
//#endregion
|
|
7935
8080
|
//#region src/ha-calendar-source.ts
|
|
7936
8081
|
function isCalendarEntity(entityId) {
|
|
7937
8082
|
return entityId.startsWith("calendar.");
|
|
@@ -8042,9 +8187,9 @@ function isSelected(entityId, selected) {
|
|
|
8042
8187
|
function createHaCalendarSource(deps) {
|
|
8043
8188
|
return {
|
|
8044
8189
|
listCalendars: async () => {
|
|
8045
|
-
const selected = deps.selectedCalendarIds();
|
|
8046
8190
|
const out = [];
|
|
8047
|
-
for (const { manager } of deps.brokers.listManagers()) {
|
|
8191
|
+
for (const { brokerId, manager } of deps.brokers.listManagers()) {
|
|
8192
|
+
const selected = deps.selectedCalendarIds(brokerId);
|
|
8048
8193
|
const states = manager.snapshotAllStates();
|
|
8049
8194
|
for (const [entityId, raw] of Object.entries(states)) {
|
|
8050
8195
|
if (!isCalendarEntity(entityId)) continue;
|
|
@@ -8064,10 +8209,10 @@ function createHaCalendarSource(deps) {
|
|
|
8064
8209
|
listEvents: async (input) => {
|
|
8065
8210
|
const startIso = new Date(input.startMs).toISOString();
|
|
8066
8211
|
const endIso = new Date(input.endMs).toISOString();
|
|
8067
|
-
const selected = deps.selectedCalendarIds();
|
|
8068
8212
|
const wanted = input.calendarIds !== void 0 && input.calendarIds.length > 0 ? new Set(input.calendarIds) : null;
|
|
8069
8213
|
const out = [];
|
|
8070
8214
|
for (const { brokerId, manager } of deps.brokers.listManagers()) {
|
|
8215
|
+
const selected = deps.selectedCalendarIds(brokerId);
|
|
8071
8216
|
const states = manager.snapshotAllStates();
|
|
8072
8217
|
const entityIds = Object.keys(states).filter((id) => {
|
|
8073
8218
|
if (!isCalendarEntity(id)) return false;
|
|
@@ -8091,10 +8236,11 @@ function createHaCalendarSource(deps) {
|
|
|
8091
8236
|
};
|
|
8092
8237
|
}
|
|
8093
8238
|
/** Discover every HA calendar entity (ignores the import filter) — for settings catalog. */
|
|
8094
|
-
function listHaCalendarCatalog(brokers) {
|
|
8239
|
+
function listHaCalendarCatalog(brokers, onlyBrokerId) {
|
|
8095
8240
|
const out = [];
|
|
8096
8241
|
const seen = /* @__PURE__ */ new Set();
|
|
8097
|
-
for (const { manager } of brokers.listManagers()) {
|
|
8242
|
+
for (const { brokerId, manager } of brokers.listManagers()) {
|
|
8243
|
+
if (onlyBrokerId !== void 0 && brokerId !== onlyBrokerId) continue;
|
|
8098
8244
|
const states = manager.snapshotAllStates();
|
|
8099
8245
|
for (const [entityId, raw] of Object.entries(states)) {
|
|
8100
8246
|
if (!isCalendarEntity(entityId) || seen.has(entityId)) continue;
|
|
@@ -8628,6 +8774,10 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8628
8774
|
this.reconcileAllConnectedBrokers();
|
|
8629
8775
|
}, HaProviderAddon.RESYNC_INTERVAL_MS);
|
|
8630
8776
|
this.ctx.logger.info("ha provider initialised", { meta: { brokerCount: this.activeBrokers.length } });
|
|
8777
|
+
await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, {
|
|
8778
|
+
readStored: (key) => this.readStoredGlobalKey(key),
|
|
8779
|
+
...haInstanceSettingsPortsOver(this.ctx.api.integrations)
|
|
8780
|
+
}));
|
|
8631
8781
|
await this.reconcileIntegrationsToBrokers();
|
|
8632
8782
|
this.subscribeIntegrationLifecycle();
|
|
8633
8783
|
return [
|
|
@@ -8656,7 +8806,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8656
8806
|
provider: createHaCalendarSource({
|
|
8657
8807
|
brokers: this.requireRegistry(),
|
|
8658
8808
|
logger: this.ctx.logger.child("calendar-source"),
|
|
8659
|
-
selectedCalendarIds: () =>
|
|
8809
|
+
selectedCalendarIds: (brokerId) => this.calendarIdsOf(brokerId)
|
|
8660
8810
|
})
|
|
8661
8811
|
}
|
|
8662
8812
|
];
|
|
@@ -8754,6 +8904,20 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8754
8904
|
*
|
|
8755
8905
|
* Idempotent — runs on boot + on every integration.* event.
|
|
8756
8906
|
*/
|
|
8907
|
+
/**
|
|
8908
|
+
* Calendars to import, per broker (D985): the integration's own selection, filled by the
|
|
8909
|
+
* reconcile that already reads each integration's settings. A broker whose integration has no
|
|
8910
|
+
* selection yet (the move has not run) uses the legacy shared set, so a boot mid-move never
|
|
8911
|
+
* changes what is imported.
|
|
8912
|
+
*/
|
|
8913
|
+
calendarIdsByBroker = /* @__PURE__ */ new Map();
|
|
8914
|
+
noteInstanceCalendars(brokerId, raw) {
|
|
8915
|
+
if (Array.isArray(raw) && raw.every((id) => typeof id === "string")) this.calendarIdsByBroker.set(brokerId, new Set(raw));
|
|
8916
|
+
else this.calendarIdsByBroker.delete(brokerId);
|
|
8917
|
+
}
|
|
8918
|
+
calendarIdsOf(brokerId) {
|
|
8919
|
+
return this.calendarIdsByBroker.get(brokerId) ?? new Set(this.config.calendarIds ?? []);
|
|
8920
|
+
}
|
|
8757
8921
|
async reconcileIntegrationsToBrokers() {
|
|
8758
8922
|
const api = this.ctx.api;
|
|
8759
8923
|
if (!api.integrations?.list?.query || !api.integrations.getSettings?.query) return;
|
|
@@ -8765,6 +8929,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8765
8929
|
const settings = await api.integrations.getSettings.query({ id: integration.id });
|
|
8766
8930
|
const brokerId = typeof settings["brokerId"] === "string" ? settings["brokerId"] : null;
|
|
8767
8931
|
if (!brokerId) continue;
|
|
8932
|
+
this.noteInstanceCalendars(brokerId, settings["calendarIds"]);
|
|
8768
8933
|
if (!this.activeBrokers.find((b) => b.id === brokerId)) {
|
|
8769
8934
|
this.ctx.logger.warn("integration→broker: linked broker not found", { meta: {
|
|
8770
8935
|
integrationId: integration.id,
|
|
@@ -8804,23 +8969,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8804
8969
|
eb.subscribe({ category: "integration.disabled" }, handler);
|
|
8805
8970
|
eb.subscribe({ category: "integration.deleted" }, handler);
|
|
8806
8971
|
}
|
|
8807
|
-
globalSettingsSchema(
|
|
8808
|
-
if (cap === "calendar-source") return this.schema({ sections: [{
|
|
8809
|
-
id: "ha-calendars-import",
|
|
8810
|
-
title: "Calendars to import",
|
|
8811
|
-
titleKey: "provider-homeassistant:globalSettings.s_ha-calendars-import.title",
|
|
8812
|
-
columns: 1,
|
|
8813
|
-
fields: [{
|
|
8814
|
-
type: "multiselect",
|
|
8815
|
-
key: "calendarIds",
|
|
8816
|
-
label: "Calendars",
|
|
8817
|
-
labelKey: "provider-homeassistant:globalSettings.s_ha-calendars-import.calendarIds.label",
|
|
8818
|
-
options: [],
|
|
8819
|
-
optionsFrom: "_calendarCatalog",
|
|
8820
|
-
description: "Discovered from connected HA brokers. Select at least one to import; leave empty to import none.",
|
|
8821
|
-
descriptionKey: "provider-homeassistant:globalSettings.s_ha-calendars-import.calendarIds.description"
|
|
8822
|
-
}]
|
|
8823
|
-
}] });
|
|
8972
|
+
globalSettingsSchema(_cap) {
|
|
8824
8973
|
return this.schema({ sections: [{
|
|
8825
8974
|
id: "ha-broker",
|
|
8826
8975
|
title: "Home Assistant broker",
|
|
@@ -8835,14 +8984,39 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8835
8984
|
}]
|
|
8836
8985
|
}] });
|
|
8837
8986
|
}
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8987
|
+
/**
|
|
8988
|
+
* The calendars of ONE Home Assistant instance (D985): picked on its integration, offered from
|
|
8989
|
+
* that broker's own `calendar.*` entities.
|
|
8990
|
+
*/
|
|
8991
|
+
async integrationSettingsSchema(instance) {
|
|
8992
|
+
if (instance.integrationId === null || !this.registry) return null;
|
|
8993
|
+
let brokerId;
|
|
8994
|
+
try {
|
|
8995
|
+
brokerId = (await this.ctx.api.integrations.getSettings.query({ id: instance.integrationId }))["brokerId"];
|
|
8996
|
+
} catch (err) {
|
|
8997
|
+
this.ctx.logger.warn("HA integration form: instance lookup failed — broker form shown", { meta: {
|
|
8998
|
+
integrationId: instance.integrationId,
|
|
8999
|
+
error: errMsg(err)
|
|
9000
|
+
} });
|
|
9001
|
+
return null;
|
|
9002
|
+
}
|
|
9003
|
+
if (typeof brokerId !== "string") return null;
|
|
9004
|
+
const options = listHaCalendarCatalog(this.registry, brokerId);
|
|
9005
|
+
return this.schema({ sections: [{
|
|
9006
|
+
id: "ha-calendars-import",
|
|
9007
|
+
title: "Calendars to import",
|
|
9008
|
+
titleKey: "provider-homeassistant:globalSettings.s_ha-calendars-import.title",
|
|
9009
|
+
columns: 1,
|
|
9010
|
+
fields: [{
|
|
9011
|
+
type: "multiselect",
|
|
9012
|
+
key: "calendarIds",
|
|
9013
|
+
label: "Calendars",
|
|
9014
|
+
labelKey: "provider-homeassistant:globalSettings.s_ha-calendars-import.calendarIds.label",
|
|
9015
|
+
options,
|
|
9016
|
+
description: "Discovered from this Home Assistant instance. Select at least one to import; leave empty to import none.",
|
|
9017
|
+
descriptionKey: "provider-homeassistant:globalSettings.s_ha-calendars-import.calendarIds.description"
|
|
9018
|
+
}]
|
|
9019
|
+
}] });
|
|
8846
9020
|
}
|
|
8847
9021
|
async onShutdown() {
|
|
8848
9022
|
if (this.resyncTimer) {
|