@camstack/addon-provider-homeassistant 1.2.153 → 1.2.155

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("./dist-DMn4t1u_.js");
2
+ const require_dist = require("./dist-CERTsNmo.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_zlib = require("node:zlib");
5
5
  //#region src/ha-admin-link.ts
@@ -2290,6 +2290,7 @@ function readBooleanAttr(attrs, key) {
2290
2290
  //#region src/devices/ha-alarm-panel-device.ts
2291
2291
  var COLD_START$9 = {
2292
2292
  state: "disarmed",
2293
+ targetMode: null,
2293
2294
  availableModes: [],
2294
2295
  requiresCode: false,
2295
2296
  lastChangedAt: 0
@@ -2316,6 +2317,7 @@ var HaAlarmPanelDevice = class extends HaBaseChildDevice {
2316
2317
  const attrs = state.attributes;
2317
2318
  this.runtimeState.setCapState("alarm-panel", {
2318
2319
  state: lifecycle,
2320
+ targetMode: targetModeFromState(lifecycle),
2319
2321
  availableModes: deriveAvailableModes(attrs),
2320
2322
  requiresCode: attrs["code_format"] !== void 0 && attrs["code_format"] !== null,
2321
2323
  lastChangedAt
@@ -2354,6 +2356,17 @@ function mapHaState(s) {
2354
2356
  default: return null;
2355
2357
  }
2356
2358
  }
2359
+ /** Settled `armed_*` → mode; transitional HA words carry no mode. */
2360
+ function targetModeFromState(state) {
2361
+ switch (state) {
2362
+ case "armed_home": return "home";
2363
+ case "armed_away": return "away";
2364
+ case "armed_night": return "night";
2365
+ case "armed_vacation": return "vacation";
2366
+ case "armed_custom_bypass": return "custom_bypass";
2367
+ default: return null;
2368
+ }
2369
+ }
2357
2370
  function deriveAvailableModes(attrs) {
2358
2371
  const sf = attrs["supported_features"];
2359
2372
  if (typeof sf !== "number") return [];
@@ -5130,6 +5143,7 @@ var HaIntegrationManager = class HaIntegrationManager {
5130
5143
  if (target.deviceId) targetEnvelope["device_id"] = target.deviceId;
5131
5144
  if (Object.keys(targetEnvelope).length > 0) message["target"] = targetEnvelope;
5132
5145
  if (payload !== void 0 && payload !== null) message["service_data"] = payload;
5146
+ if (target.returnResponse === true) message["return_response"] = true;
5133
5147
  const result = await conn.sendMessagePromise(message);
5134
5148
  if (result && typeof result === "object") return result;
5135
5149
  return null;
@@ -7071,6 +7085,122 @@ function createHaNotificationOutputProvider(deps) {
7071
7085
  };
7072
7086
  }
7073
7087
  //#endregion
7088
+ //#region src/ha-calendar-source.ts
7089
+ function isCalendarEntity(entityId) {
7090
+ return entityId.startsWith("calendar.");
7091
+ }
7092
+ function calendarName(state) {
7093
+ const friendly = state.attributes["friendly_name"];
7094
+ if (typeof friendly === "string" && friendly.trim().length > 0) return friendly.trim();
7095
+ return state.entity_id.replace(/^calendar\./, "").replace(/_/g, " ");
7096
+ }
7097
+ function parseHaDate(value) {
7098
+ if (typeof value !== "string" || value.length === 0) return null;
7099
+ const ms = Date.parse(value.length === 10 ? `${value}T00:00:00` : value);
7100
+ return Number.isFinite(ms) ? ms : null;
7101
+ }
7102
+ function isRecord(x) {
7103
+ return typeof x === "object" && x !== null && !Array.isArray(x);
7104
+ }
7105
+ function asEntityState(raw) {
7106
+ const parsed = HaEntityStateSchema.safeParse(raw);
7107
+ return parsed.success ? parsed.data : null;
7108
+ }
7109
+ /**
7110
+ * HA's `calendar.list_events` response shape (return_response):
7111
+ * `{ response: { [entity_id]: { events: [...] } } }` or a flat
7112
+ * `{ events: [...] }` depending on target width.
7113
+ */
7114
+ function extractEvents(result, entityId) {
7115
+ if (!isRecord(result)) return [];
7116
+ const response = result["response"];
7117
+ if (isRecord(response)) {
7118
+ const forEntity = response[entityId];
7119
+ if (isRecord(forEntity) && Array.isArray(forEntity["events"])) return forEntity["events"].filter(isRecord);
7120
+ for (const value of Object.values(response)) if (isRecord(value) && Array.isArray(value["events"])) return value["events"].filter(isRecord);
7121
+ }
7122
+ if (Array.isArray(result["events"])) return result["events"].filter(isRecord);
7123
+ return [];
7124
+ }
7125
+ function mapEvent(entityId, raw, index) {
7126
+ const startRaw = raw["start"];
7127
+ const endRaw = raw["end"];
7128
+ const startMs = parseHaDate(isRecord(startRaw) ? startRaw["dateTime"] ?? startRaw["date"] : raw["start"] ?? raw["start_date_time"] ?? raw["dtstart"]);
7129
+ const endMs = parseHaDate(isRecord(endRaw) ? endRaw["dateTime"] ?? endRaw["date"] : raw["end"] ?? raw["end_date_time"] ?? raw["dtend"]) ?? startMs;
7130
+ if (startMs === null || endMs === null) return null;
7131
+ const summary = raw["summary"] ?? raw["title"];
7132
+ const title = typeof summary === "string" ? summary : "(no title)";
7133
+ const uid = raw["uid"] ?? raw["id"];
7134
+ const id = typeof uid === "string" && uid.length > 0 ? `${entityId}:${uid}` : `${entityId}:${String(startMs)}:${String(index)}`;
7135
+ const description = raw["description"];
7136
+ const location = raw["location"];
7137
+ return {
7138
+ id,
7139
+ calendarId: entityId,
7140
+ title,
7141
+ startMs,
7142
+ endMs,
7143
+ ...isRecord(startRaw) && typeof startRaw["date"] === "string" || typeof raw["start"] === "string" && raw["start"].length === 10 ? { allDay: true } : {},
7144
+ ...typeof description === "string" && description.length > 0 ? { description } : {},
7145
+ ...typeof location === "string" && location.length > 0 ? { location } : {}
7146
+ };
7147
+ }
7148
+ function createHaCalendarSource(deps) {
7149
+ return {
7150
+ listCalendars: async () => {
7151
+ const out = [];
7152
+ for (const { manager } of deps.brokers.listManagers()) {
7153
+ const states = manager.snapshotAllStates();
7154
+ for (const [entityId, raw] of Object.entries(states)) {
7155
+ if (!isCalendarEntity(entityId)) continue;
7156
+ const state = asEntityState(raw);
7157
+ if (state === null) continue;
7158
+ out.push({
7159
+ id: entityId,
7160
+ name: calendarName(state),
7161
+ source: "ha",
7162
+ enabled: state.state !== "unavailable"
7163
+ });
7164
+ }
7165
+ }
7166
+ return out;
7167
+ },
7168
+ listEvents: async (input) => {
7169
+ const startIso = new Date(input.startMs).toISOString();
7170
+ const endIso = new Date(input.endMs).toISOString();
7171
+ const wanted = input.calendarIds !== void 0 && input.calendarIds.length > 0 ? new Set(input.calendarIds) : null;
7172
+ const out = [];
7173
+ for (const { brokerId, manager } of deps.brokers.listManagers()) {
7174
+ const states = manager.snapshotAllStates();
7175
+ const entityIds = Object.keys(states).filter((id) => isCalendarEntity(id) && (wanted === null || wanted.has(id)));
7176
+ for (const entityId of entityIds) try {
7177
+ const target = {
7178
+ domain: "calendar",
7179
+ service: "list_events",
7180
+ entityId,
7181
+ returnResponse: true
7182
+ };
7183
+ const rawEvents = extractEvents(await manager.publish(target, {
7184
+ start_date_time: startIso,
7185
+ end_date_time: endIso
7186
+ }), entityId);
7187
+ for (let i = 0; i < rawEvents.length; i += 1) {
7188
+ const mapped = mapEvent(entityId, rawEvents[i], i);
7189
+ if (mapped !== null) out.push(mapped);
7190
+ }
7191
+ } catch (err) {
7192
+ deps.logger.warn("HA calendar.list_events failed", { meta: {
7193
+ entityId,
7194
+ brokerId,
7195
+ error: err instanceof Error ? err.message : String(err)
7196
+ } });
7197
+ }
7198
+ }
7199
+ return out.toSorted((a, b) => a.startMs - b.startMs);
7200
+ }
7201
+ };
7202
+ }
7203
+ //#endregion
7074
7204
  //#region src/ha-reconcile-broker.ts
7075
7205
  /**
7076
7206
  * Run BOTH reconcilers for one broker off a SINGLE shared inventory fetch.
@@ -7596,6 +7726,13 @@ var HaProviderAddon = class HaProviderAddon extends require_dist.BaseDeviceProvi
7596
7726
  {
7597
7727
  capability: require_dist.addonRoutesCapability,
7598
7728
  provider: createHaIconRouteProvider()
7729
+ },
7730
+ {
7731
+ capability: require_dist.calendarSourceCapability,
7732
+ provider: createHaCalendarSource({
7733
+ brokers: this.requireRegistry(),
7734
+ logger: this.ctx.logger.child("calendar-source")
7735
+ })
7599
7736
  }
7600
7737
  ];
7601
7738
  }
package/dist/addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as parseAdminBase, A as contactCapability, At as union, B as gasCapability, C as buildAddonRouteProvider, Ct as array, D as climateControlCapability, Dt as object, E as carbonMonoxideCapability, Et as number, G as lockControlCapability, H as humiditySensorCapability, I as enumSensorCapability, J as normalizeUnit, K as mediaPlayerCapability, L as eventEmitterCapability, M as coverCapability, Mt as url, N as deviceAdminLinkCapability, Nt as EventCategory, O as colorCapability, Ot as record, P as deviceAdoptionCapability, R as fanControlCapability, S as brokerCapability, St as _enum, T as buttonCapability, Tt as literal, U as imageCapability, V as humidifierCapability, W as lawnMowerControlCapability, X as notifierCapability, Y as notificationOutputCapability, Z as numericSensorCapability, _ as automationControlCapability, _t as DeviceType, at as smokeCapability, b as binaryCapability, bt as createEvent, ct as temperatureSensorCapability, d as TargetSchema, dt as valveCapability, et as powerMeterCapability, f as accessoriesCapability, ft as vibrationCapability, g as ambientLightSensorCapability, gt as DeviceRole, h as alarmPanelCapability, ht as DeviceFeature, it as scriptRunnerCapability, j as controlCapability, jt as unknown, k as connectivityCapability, kt as string, lt as updateCapability, m as airQualitySensorCapability, mt as weatherCapability, n as BaseDevice, nt as presenceCapability, ot as switchCapability, p as addonRoutesCapability, pt as waterHeaterCapability, q as motionCapability, r as BaseDeviceProvider, rt as pressureSensorCapability, s as EnumSensorDateTimeFormatSchema, st as tamperCapability, tt as prepareNotification, ut as vacuumControlCapability, v as batteryCapability, vt as errMsg, w as buildDeviceAdminUrl, wt as discriminatedUnion, x as brightnessCapability, y as bestLocationMatch, z as floodCapability } from "./dist-evNLjXgk.mjs";
1
+ import { A as connectivityCapability, At as string, B as floodCapability, C as buildAddonRouteProvider, Ct as _enum, D as carbonMonoxideCapability, Dt as number, E as calendarSourceCapability, Et as literal, F as deviceAdoptionCapability, G as lawnMowerControlCapability, H as humidifierCapability, J as motionCapability, K as lockControlCapability, L as enumSensorCapability, M as controlCapability, Mt as unknown, N as coverCapability, Nt as url, O as climateControlCapability, Ot as object, P as deviceAdminLinkCapability, Pt as EventCategory, Q as numericSensorCapability, R as eventEmitterCapability, S as brokerCapability, T as buttonCapability, Tt as discriminatedUnion, 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 union, k as colorCapability, kt as record, 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-DqKiElqb.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
@@ -2289,6 +2289,7 @@ function readBooleanAttr(attrs, key) {
2289
2289
  //#region src/devices/ha-alarm-panel-device.ts
2290
2290
  var COLD_START$9 = {
2291
2291
  state: "disarmed",
2292
+ targetMode: null,
2292
2293
  availableModes: [],
2293
2294
  requiresCode: false,
2294
2295
  lastChangedAt: 0
@@ -2315,6 +2316,7 @@ var HaAlarmPanelDevice = class extends HaBaseChildDevice {
2315
2316
  const attrs = state.attributes;
2316
2317
  this.runtimeState.setCapState("alarm-panel", {
2317
2318
  state: lifecycle,
2319
+ targetMode: targetModeFromState(lifecycle),
2318
2320
  availableModes: deriveAvailableModes(attrs),
2319
2321
  requiresCode: attrs["code_format"] !== void 0 && attrs["code_format"] !== null,
2320
2322
  lastChangedAt
@@ -2353,6 +2355,17 @@ function mapHaState(s) {
2353
2355
  default: return null;
2354
2356
  }
2355
2357
  }
2358
+ /** Settled `armed_*` → mode; transitional HA words carry no mode. */
2359
+ function targetModeFromState(state) {
2360
+ switch (state) {
2361
+ case "armed_home": return "home";
2362
+ case "armed_away": return "away";
2363
+ case "armed_night": return "night";
2364
+ case "armed_vacation": return "vacation";
2365
+ case "armed_custom_bypass": return "custom_bypass";
2366
+ default: return null;
2367
+ }
2368
+ }
2356
2369
  function deriveAvailableModes(attrs) {
2357
2370
  const sf = attrs["supported_features"];
2358
2371
  if (typeof sf !== "number") return [];
@@ -5129,6 +5142,7 @@ var HaIntegrationManager = class HaIntegrationManager {
5129
5142
  if (target.deviceId) targetEnvelope["device_id"] = target.deviceId;
5130
5143
  if (Object.keys(targetEnvelope).length > 0) message["target"] = targetEnvelope;
5131
5144
  if (payload !== void 0 && payload !== null) message["service_data"] = payload;
5145
+ if (target.returnResponse === true) message["return_response"] = true;
5132
5146
  const result = await conn.sendMessagePromise(message);
5133
5147
  if (result && typeof result === "object") return result;
5134
5148
  return null;
@@ -7070,6 +7084,122 @@ function createHaNotificationOutputProvider(deps) {
7070
7084
  };
7071
7085
  }
7072
7086
  //#endregion
7087
+ //#region src/ha-calendar-source.ts
7088
+ function isCalendarEntity(entityId) {
7089
+ return entityId.startsWith("calendar.");
7090
+ }
7091
+ function calendarName(state) {
7092
+ const friendly = state.attributes["friendly_name"];
7093
+ if (typeof friendly === "string" && friendly.trim().length > 0) return friendly.trim();
7094
+ return state.entity_id.replace(/^calendar\./, "").replace(/_/g, " ");
7095
+ }
7096
+ function parseHaDate(value) {
7097
+ if (typeof value !== "string" || value.length === 0) return null;
7098
+ const ms = Date.parse(value.length === 10 ? `${value}T00:00:00` : value);
7099
+ return Number.isFinite(ms) ? ms : null;
7100
+ }
7101
+ function isRecord(x) {
7102
+ return typeof x === "object" && x !== null && !Array.isArray(x);
7103
+ }
7104
+ function asEntityState(raw) {
7105
+ const parsed = HaEntityStateSchema.safeParse(raw);
7106
+ return parsed.success ? parsed.data : null;
7107
+ }
7108
+ /**
7109
+ * HA's `calendar.list_events` response shape (return_response):
7110
+ * `{ response: { [entity_id]: { events: [...] } } }` or a flat
7111
+ * `{ events: [...] }` depending on target width.
7112
+ */
7113
+ function extractEvents(result, entityId) {
7114
+ if (!isRecord(result)) return [];
7115
+ const response = result["response"];
7116
+ if (isRecord(response)) {
7117
+ const forEntity = response[entityId];
7118
+ if (isRecord(forEntity) && Array.isArray(forEntity["events"])) return forEntity["events"].filter(isRecord);
7119
+ for (const value of Object.values(response)) if (isRecord(value) && Array.isArray(value["events"])) return value["events"].filter(isRecord);
7120
+ }
7121
+ if (Array.isArray(result["events"])) return result["events"].filter(isRecord);
7122
+ return [];
7123
+ }
7124
+ function mapEvent(entityId, raw, index) {
7125
+ const startRaw = raw["start"];
7126
+ const endRaw = raw["end"];
7127
+ const startMs = parseHaDate(isRecord(startRaw) ? startRaw["dateTime"] ?? startRaw["date"] : raw["start"] ?? raw["start_date_time"] ?? raw["dtstart"]);
7128
+ const endMs = parseHaDate(isRecord(endRaw) ? endRaw["dateTime"] ?? endRaw["date"] : raw["end"] ?? raw["end_date_time"] ?? raw["dtend"]) ?? startMs;
7129
+ if (startMs === null || endMs === null) return null;
7130
+ const summary = raw["summary"] ?? raw["title"];
7131
+ const title = typeof summary === "string" ? summary : "(no title)";
7132
+ const uid = raw["uid"] ?? raw["id"];
7133
+ const id = typeof uid === "string" && uid.length > 0 ? `${entityId}:${uid}` : `${entityId}:${String(startMs)}:${String(index)}`;
7134
+ const description = raw["description"];
7135
+ const location = raw["location"];
7136
+ return {
7137
+ id,
7138
+ calendarId: entityId,
7139
+ title,
7140
+ startMs,
7141
+ endMs,
7142
+ ...isRecord(startRaw) && typeof startRaw["date"] === "string" || typeof raw["start"] === "string" && raw["start"].length === 10 ? { allDay: true } : {},
7143
+ ...typeof description === "string" && description.length > 0 ? { description } : {},
7144
+ ...typeof location === "string" && location.length > 0 ? { location } : {}
7145
+ };
7146
+ }
7147
+ function createHaCalendarSource(deps) {
7148
+ return {
7149
+ listCalendars: async () => {
7150
+ const out = [];
7151
+ for (const { manager } of deps.brokers.listManagers()) {
7152
+ const states = manager.snapshotAllStates();
7153
+ for (const [entityId, raw] of Object.entries(states)) {
7154
+ if (!isCalendarEntity(entityId)) continue;
7155
+ const state = asEntityState(raw);
7156
+ if (state === null) continue;
7157
+ out.push({
7158
+ id: entityId,
7159
+ name: calendarName(state),
7160
+ source: "ha",
7161
+ enabled: state.state !== "unavailable"
7162
+ });
7163
+ }
7164
+ }
7165
+ return out;
7166
+ },
7167
+ listEvents: async (input) => {
7168
+ const startIso = new Date(input.startMs).toISOString();
7169
+ const endIso = new Date(input.endMs).toISOString();
7170
+ const wanted = input.calendarIds !== void 0 && input.calendarIds.length > 0 ? new Set(input.calendarIds) : null;
7171
+ const out = [];
7172
+ for (const { brokerId, manager } of deps.brokers.listManagers()) {
7173
+ const states = manager.snapshotAllStates();
7174
+ const entityIds = Object.keys(states).filter((id) => isCalendarEntity(id) && (wanted === null || wanted.has(id)));
7175
+ for (const entityId of entityIds) try {
7176
+ const target = {
7177
+ domain: "calendar",
7178
+ service: "list_events",
7179
+ entityId,
7180
+ returnResponse: true
7181
+ };
7182
+ const rawEvents = extractEvents(await manager.publish(target, {
7183
+ start_date_time: startIso,
7184
+ end_date_time: endIso
7185
+ }), entityId);
7186
+ for (let i = 0; i < rawEvents.length; i += 1) {
7187
+ const mapped = mapEvent(entityId, rawEvents[i], i);
7188
+ if (mapped !== null) out.push(mapped);
7189
+ }
7190
+ } catch (err) {
7191
+ deps.logger.warn("HA calendar.list_events failed", { meta: {
7192
+ entityId,
7193
+ brokerId,
7194
+ error: err instanceof Error ? err.message : String(err)
7195
+ } });
7196
+ }
7197
+ }
7198
+ return out.toSorted((a, b) => a.startMs - b.startMs);
7199
+ }
7200
+ };
7201
+ }
7202
+ //#endregion
7073
7203
  //#region src/ha-reconcile-broker.ts
7074
7204
  /**
7075
7205
  * Run BOTH reconcilers for one broker off a SINGLE shared inventory fetch.
@@ -7595,6 +7725,13 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
7595
7725
  {
7596
7726
  capability: addonRoutesCapability,
7597
7727
  provider: createHaIconRouteProvider()
7728
+ },
7729
+ {
7730
+ capability: calendarSourceCapability,
7731
+ provider: createHaCalendarSource({
7732
+ brokers: this.requireRegistry(),
7733
+ logger: this.ctx.logger.child("calendar-source")
7734
+ })
7598
7735
  }
7599
7736
  ];
7600
7737
  }