@camstack/addon-provider-homeassistant 1.2.639 → 1.2.641

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 { $ as lawnMowerControlCapability, $t as object, A as buttonCapability, At as valveCapability, B as coverCapability, Bt as baseUnitOf, C as batteryCapability, Ct as switchCapability, D as brokerCapability, Dt as tryConvertUnit, E as brightnessCapability, Et as tryConvertDelta, F as connectivityCapability, Ft as DeviceRole, G as eventEmitterCapability, H as deviceAdoptionCapability, Ht as defineEntryKind, It as DeviceType, J as gasCapability, Jt as array, K as fanControlCapability, Lt as errMsg, M as carbonMonoxideCapability, Mt as waterHeaterCapability, N as climateControlCapability, Nt as weatherCapability, O as buildAddonRouteProvider, Ot as updateCapability, P as colorCapability, Pt as DeviceFeature, Q as ingestReading, Qt as number, R as contactCapability, S as automationControlCapability, St as smokeCapability, T as binaryCapability, Tt as temperatureSensorCapability, Ut as defineRecordKinds, V as deviceAdminLinkCapability, Vt as createEvent, W as enumSensorCapability, Wt as entryBody, X as humiditySensorCapability, Xt as discriminatedUnion, Y as humidifierCapability, Yt as boolean, Z as imageCapability, Zt as literal, _ as accessoriesCapability, _t as runningFromMediaPlayer, an as EventCategory, at as notifierCapability, b as alarmPanelCapability, bt as scriptRunnerCapability, ct as parseAdminBase, dt as presenceCapability, en as record, et as lockControlCapability, ft as pressureSensorCapability, g as TargetSchema, gt as runningFromLawnMower, h as RUNNING_STATE_COLD, in as url, it as notificationOutputCapability, j as calendarSourceCapability, jt as vibrationCapability, k as buildDeviceAdminUrl, kt as vacuumControlCapability, lt as powerMeterCapability, mt as quantityFromUnit, n as BaseDevice, nn as union, nt as motionCapability, ot as numericSensorCapability, q as floodCapability, qt as _enum, r as BaseDeviceProvider, rn as unknown, rt as normalizeUnit, tn as string, tt as mediaPlayerCapability, u as EnumSensorDateTimeFormatSchema, ut as prepareNotification, v as addonRoutesCapability, vt as runningFromVacuumState, w as bestLocationMatch, wt as tamperCapability, x as ambientLightSensorCapability, y as airQualitySensorCapability, yt as runningStateCapability, z as controlCapability, zt as RecordSecretUnavailableError } from "./dist-Cvz76AD1.mjs";
1
+ import { $ as humiditySensorCapability, $t as array, A as brokerCapability, At as temperatureSensorCapability, Bt as DeviceRole, C as alarmPanelCapability, Ct as runningFromVacuumState, D as bestLocationMatch, Dt as smokeCapability, E as batteryCapability, F as carbonMonoxideCapability, Ft as valveCapability, G as deviceAdoptionCapability, Gt as baseUnitOf, H as controlCapability, Ht as errMsg, I as climateControlCapability, It as vibrationCapability, J as eventEmitterCapability, Jt as defineRecordKinds, Kt as createEvent, L as colorCapability, Lt as waterHeaterCapability, M as buildDeviceAdminUrl, Mt as tryConvertUnit, N as buttonCapability, Nt as updateCapability, O as binaryCapability, Ot as switchCapability, P as calendarSourceCapability, Pt as vacuumControlCapability, Q as humidifierCapability, Qt as _enum, R as connectivityCapability, Rt as weatherCapability, S as airQualitySensorCapability, St as runningFromMediaPlayer, T as automationControlCapability, Tt as scriptRunnerCapability, U as coverCapability, V as contactCapability, Vt as DeviceType, W as deviceAdminLinkCapability, Wt as RecordSecretUnavailableError, X as floodCapability, Y as fanControlCapability, Yt as entryBody, Z as gasCapability, _t as publicEndpointReaderFrom, a as BaseDevice, an as record, at as motionCapability, b as accessoriesCapability, cn as unknown, ct as notifierCapability, dt as parseAdminBase, en as boolean, et as imageCapability, ft as powerMeterCapability, ht as pressureSensorCapability, in as object, it as mediaPlayerCapability, j as buildAddonRouteProvider, jt as tryConvertDelta, k as brightnessCapability, kt as tamperCapability, ln as url, lt as numericSensorCapability, mt as presenceCapability, nn as literal, nt as lawnMowerControlCapability, o as BaseDeviceProvider, on as string, ot as normalizeUnit, p as EnumSensorDateTimeFormatSchema, pt as prepareNotification, q as enumSensorCapability, qt as defineEntryKind, rn as number, rt as lockControlCapability, sn as union, st as notificationOutputCapability, t as HA_BROKER_KIND, tn as discriminatedUnion, tt as ingestReading, un as EventCategory, v as RUNNING_STATE_COLD, vt as quantityFromUnit, w as ambientLightSensorCapability, wt as runningStateCapability, x as addonRoutesCapability, xt as runningFromLawnMower, y as TargetSchema, yt as resolveAttachmentUrls, zt as DeviceFeature } from "./ha-broker-source-vytM8SXk.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
@@ -7230,21 +7230,27 @@ function haConfigSchema() {
7230
7230
  title: "Home Assistant notify",
7231
7231
  titleKey: "provider-homeassistant:haConfig.s_connection.title",
7232
7232
  fields: [{
7233
- type: "text",
7233
+ type: "broker-select",
7234
+ key: "brokerId",
7235
+ label: "Broker",
7236
+ labelKey: "provider-homeassistant:haConfig.s_connection.brokerId.label",
7237
+ description: "The Home Assistant connection the notification goes through.",
7238
+ descriptionKey: "provider-homeassistant:haConfig.s_connection.brokerId.description",
7239
+ brokerKind: HA_BROKER_KIND
7240
+ }, {
7241
+ type: "select",
7234
7242
  key: "service",
7235
7243
  label: "Notify service",
7236
7244
  labelKey: "provider-homeassistant:haConfig.s_connection.service.label",
7237
7245
  required: true,
7246
+ options: [],
7238
7247
  placeholder: "mobile_app_pixel_7",
7239
- description: "The part after `notify.` (e.g. `mobile_app_pixel_7`); use Discover to list them.",
7240
- descriptionKey: "provider-homeassistant:haConfig.s_connection.service.description"
7241
- }, {
7242
- type: "text",
7243
- key: "brokerId",
7244
- label: "Broker",
7245
- labelKey: "provider-homeassistant:haConfig.s_connection.brokerId.label",
7246
- description: "Optional; blank uses the only configured broker.",
7247
- descriptionKey: "provider-homeassistant:haConfig.s_connection.brokerId.description"
7248
+ description: "Loaded from the broker once it is picked. If the list does not load, type the part after `notify.`.",
7249
+ descriptionKey: "provider-homeassistant:haConfig.s_connection.service.description",
7250
+ discoverOptions: {
7251
+ kind: HA_NOTIFICATION_KIND,
7252
+ dependsOn: ["brokerId"]
7253
+ }
7248
7254
  }]
7249
7255
  }] };
7250
7256
  }
@@ -7385,24 +7391,70 @@ function buildNotifyServiceData(prepared) {
7385
7391
  * `get_services` returns `{ [domain]: { [service]: {...} } }`; we read the
7386
7392
  * `notify` domain. mobile_app_* services (rich push features) sort first.
7387
7393
  */
7388
- function parseNotifyServices(result, brokerId) {
7394
+ function parseNotifyServices(result, brokerId, deviceNames) {
7389
7395
  if (typeof result !== "object" || result === null) return [];
7390
7396
  const notify = result["notify"];
7391
7397
  if (typeof notify !== "object" || notify === null) return [];
7392
- return [...Object.keys(notify)].sort((a, b) => {
7398
+ return [...Object.entries(notify)].sort(([a], [b]) => {
7393
7399
  const am = a.startsWith("mobile_app_") ? 0 : 1;
7394
7400
  const bm = b.startsWith("mobile_app_") ? 0 : 1;
7395
7401
  if (am !== bm) return am - bm;
7396
7402
  return a.localeCompare(b);
7397
- }).map((service) => ({
7403
+ }).map(([service, definition]) => ({
7398
7404
  kind: HA_NOTIFICATION_KIND,
7399
- suggestedName: `notify.${service}`,
7405
+ suggestedName: friendlyServiceName(service, definition, deviceNames),
7400
7406
  config: {
7401
7407
  service,
7402
7408
  ...brokerId !== void 0 ? { brokerId } : {}
7403
7409
  }
7404
7410
  }));
7405
7411
  }
7412
+ function nonEmptyText(value) {
7413
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
7414
+ }
7415
+ /** `alexa_media_echo` -> `Alexa Media Echo`. */
7416
+ function prettifyServiceId(service) {
7417
+ return service.split("_").filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
7418
+ }
7419
+ /**
7420
+ * The name an operator recognises: the device name for a mobile app, else the
7421
+ * service's own `name`, else its `description`, else the prettified id.
7422
+ */
7423
+ function friendlyServiceName(service, definition, deviceNames) {
7424
+ const device = deviceNames?.get(service);
7425
+ if (device !== void 0) return device;
7426
+ if (typeof definition === "object" && definition !== null) {
7427
+ const fields = { ...definition };
7428
+ const name = nonEmptyText(fields["name"]);
7429
+ if (name !== void 0) return name;
7430
+ const description = nonEmptyText(fields["description"]);
7431
+ if (description !== void 0) return description;
7432
+ }
7433
+ return prettifyServiceId(service);
7434
+ }
7435
+ /** HA's slug of a device name, as it appears in `mobile_app_<slug>`. */
7436
+ function slugifyDeviceName(name) {
7437
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
7438
+ }
7439
+ /**
7440
+ * `mobile_app_<slug>` -> device name, read from a `config/device_registry/list`
7441
+ * reply. Only devices of the mobile_app integration count; the name the user
7442
+ * gave wins over the one the app reported.
7443
+ */
7444
+ function mobileAppDeviceNames(registry) {
7445
+ const names = /* @__PURE__ */ new Map();
7446
+ if (!Array.isArray(registry)) return names;
7447
+ for (const entry of registry) {
7448
+ if (typeof entry !== "object" || entry === null) continue;
7449
+ const device = { ...entry };
7450
+ const identifiers = device["identifiers"];
7451
+ if (!(Array.isArray(identifiers) && identifiers.some((pair) => Array.isArray(pair) && pair[0] === "mobile_app"))) continue;
7452
+ const name = nonEmptyText(device["name_by_user"]) ?? nonEmptyText(device["name"]);
7453
+ if (name === void 0) continue;
7454
+ names.set(`mobile_app_${slugifyDeviceName(name)}`, name);
7455
+ }
7456
+ return names;
7457
+ }
7406
7458
  /**
7407
7459
  * CRUD over the `ha-notification-targets` record kind.
7408
7460
  *
@@ -7495,7 +7547,12 @@ function createHaNotificationOutputProvider(deps) {
7495
7547
  return deps.listBrokerIds()[0] ?? null;
7496
7548
  }
7497
7549
  /** Degrade → build → publish. NEVER throws through the cap. */
7498
- async function dispatch(target, notification) {
7550
+ async function dispatch(target, raw) {
7551
+ const notification = deps.readPublicEndpoint === void 0 ? raw : await resolveAttachmentUrls({
7552
+ notification: raw,
7553
+ readPublicEndpoint: deps.readPublicEndpoint,
7554
+ ...logger !== void 0 ? { logger } : {}
7555
+ });
7499
7556
  const prepared = prepareNotification(descriptor.caps, notification);
7500
7557
  const service = readService(target.config);
7501
7558
  if (!service) return {
@@ -7556,12 +7613,24 @@ function createHaNotificationOutputProvider(deps) {
7556
7613
  },
7557
7614
  discoverTargets: async ({ kind, config }) => {
7558
7615
  if (kind !== "homeassistant") return [];
7616
+ const deviceNamesOf = async (brokerId) => {
7617
+ if (deps.listDevices === void 0) return /* @__PURE__ */ new Map();
7618
+ try {
7619
+ return mobileAppDeviceNames(await deps.listDevices(brokerId));
7620
+ } catch (err) {
7621
+ logger?.warn("ha device registry read failed; naming services from their ids", { meta: {
7622
+ brokerId,
7623
+ error: errMsg(err)
7624
+ } });
7625
+ return /* @__PURE__ */ new Map();
7626
+ }
7627
+ };
7559
7628
  const explicit = config ? readBrokerId(config) : void 0;
7560
7629
  const brokerIds = explicit ? [explicit] : [...deps.listBrokerIds()];
7561
7630
  const out = [];
7562
7631
  for (const brokerId of brokerIds) try {
7563
7632
  const result = await deps.getServices(brokerId);
7564
- out.push(...parseNotifyServices(result, brokerId));
7633
+ out.push(...parseNotifyServices(result, brokerId, await deviceNamesOf(brokerId)));
7565
7634
  } catch (err) {
7566
7635
  logger?.warn("ha get_services discovery failed", { meta: {
7567
7636
  brokerId,
@@ -8430,7 +8499,13 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
8430
8499
  if (!manager) throw new Error(`ha-broker '${brokerId}' not found`);
8431
8500
  return manager.sendMessage({ type: "get_services" });
8432
8501
  },
8502
+ listDevices: async (brokerId) => {
8503
+ const manager = this.requireRegistry().getManager(brokerId);
8504
+ if (!manager) throw new Error(`ha-broker '${brokerId}' not found`);
8505
+ return manager.sendMessage({ type: "config/device_registry/list" });
8506
+ },
8433
8507
  listBrokerIds: () => this.activeBrokers.map((b) => b.id),
8508
+ readPublicEndpoint: publicEndpointReaderFrom(this.ctx.api.localNetwork),
8434
8509
  logger: this.ctx.logger.child("notification-output")
8435
8510
  });
8436
8511
  }