@camstack/addon-provider-homeassistant 1.2.638 → 1.2.640
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 +13 -0
- package/dist/addon.js +541 -463
- package/dist/addon.mjs +97 -19
- package/dist/{dist-C_XoeULr.js → ha-broker-source-0HGmZj7D.js} +2225 -853
- package/dist/{dist-Cvz76AD1.mjs → ha-broker-source-FHFpUlgq.mjs} +2208 -854
- package/dist/ha-export/ha-export.addon.js +209 -86
- package/dist/ha-export/ha-export.addon.mjs +164 -41
- package/i18n/en.json +2 -2
- package/i18n/it.json +2 -2
- package/package.json +1 -1
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as humiditySensorCapability, $t as discriminatedUnion, A as brokerCapability, At as tryConvertUnit, Bt as errMsg, C as alarmPanelCapability, Ct as scriptRunnerCapability, D as bestLocationMatch, Dt as tamperCapability, E as batteryCapability, Et as switchCapability, F as carbonMonoxideCapability, Ft as waterHeaterCapability, G as deviceAdoptionCapability, Gt as defineEntryKind, H as controlCapability, Ht as RecordSecretUnavailableError, I as climateControlCapability, It as weatherCapability, J as eventEmitterCapability, Kt as defineRecordKinds, L as colorCapability, Lt as DeviceFeature, M as buildDeviceAdminUrl, Mt as vacuumControlCapability, N as buttonCapability, Nt as valveCapability, O as binaryCapability, Ot as temperatureSensorCapability, P as calendarSourceCapability, Pt as vibrationCapability, Q as humidifierCapability, Qt as boolean, R as connectivityCapability, Rt as DeviceRole, S as airQualitySensorCapability, St as runningStateCapability, T as automationControlCapability, Tt as smokeCapability, U as coverCapability, Ut as baseUnitOf, V as contactCapability, W as deviceAdminLinkCapability, Wt as createEvent, X as floodCapability, Xt as _enum, Y as fanControlCapability, Z as gasCapability, Zt as array, _t as quantityFromUnit, a as BaseDevice, an as union, at as motionCapability, b as accessoriesCapability, bt as runningFromMediaPlayer, cn as EventCategory, ct as notifierCapability, dt as parseAdminBase, en as literal, et as imageCapability, ft as powerMeterCapability, ht as pressureSensorCapability, in as string, it as mediaPlayerCapability, j as buildAddonRouteProvider, jt as updateCapability, k as brightnessCapability, kt as tryConvertDelta, lt as numericSensorCapability, mt as presenceCapability, nn as object, nt as lawnMowerControlCapability, o as BaseDeviceProvider, on as unknown, ot as normalizeUnit, p as EnumSensorDateTimeFormatSchema, pt as prepareNotification, q as enumSensorCapability, qt as entryBody, rn as record, rt as lockControlCapability, sn as url, st as notificationOutputCapability, t as HA_BROKER_KIND, tn as number, tt as ingestReading, v as RUNNING_STATE_COLD, w as ambientLightSensorCapability, x as addonRoutesCapability, xt as runningFromVacuumState, y as TargetSchema, yt as runningFromLawnMower, zt as DeviceType } from "./ha-broker-source-FHFpUlgq.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
|
|
@@ -5353,8 +5353,10 @@ function subscribeEntities(opts) {
|
|
|
5353
5353
|
* the same HA broker for overlapping entity sets. We keep ONE
|
|
5354
5354
|
* upstream `subscribe_entities` to HA with the UNION of every
|
|
5355
5355
|
* active subscription's entity ids, and fan inbound state changes
|
|
5356
|
-
* out via `EventCategory.BrokerMessage`
|
|
5357
|
-
* subscription
|
|
5356
|
+
* out via `EventCategory.BrokerMessage` ONCE per change when at
|
|
5357
|
+
* least one subscription matches (the bus is shared and consumers
|
|
5358
|
+
* filter by `key`). A new subscriber's cache replay is the one
|
|
5359
|
+
* targeted emission: it carries that subscription's id.
|
|
5358
5360
|
* - When the active union changes (subscribe / unsubscribe call),
|
|
5359
5361
|
* we re-issue the upstream subscribe with the new set. HA accepts
|
|
5360
5362
|
* this efficiently (server-side filter).
|
|
@@ -5366,6 +5368,10 @@ function subscribeEntities(opts) {
|
|
|
5366
5368
|
* subscribe's initial `a` (add) frames.
|
|
5367
5369
|
*/
|
|
5368
5370
|
var HA_KIND$1 = "home-assistant";
|
|
5371
|
+
/** `subscriptionId` of a `broker.message` that is not addressed to one
|
|
5372
|
+
* subscription (a live state change fanned out once). The payload type
|
|
5373
|
+
* requires the field; the empty string is the repo convention for "none". */
|
|
5374
|
+
var FANOUT_SUBSCRIPTION_ID = "";
|
|
5369
5375
|
/**
|
|
5370
5376
|
* Curated device-event allowlist for the per-broker bus-event aggregator
|
|
5371
5377
|
* (Case 2). Home Assistant fires arbitrary events on its bus; subscribing
|
|
@@ -5644,7 +5650,10 @@ var HaIntegrationManager = class HaIntegrationManager {
|
|
|
5644
5650
|
for (const entityId of changed) {
|
|
5645
5651
|
const state = next[entityId] ?? null;
|
|
5646
5652
|
if (state === null) continue;
|
|
5647
|
-
for (const sub of this.subscriptions.values()) if (this.subscriptionMatches(sub, entityId))
|
|
5653
|
+
for (const sub of this.subscriptions.values()) if (this.subscriptionMatches(sub, entityId)) {
|
|
5654
|
+
this.emitBrokerMessage(FANOUT_SUBSCRIPTION_ID, entityId, state);
|
|
5655
|
+
break;
|
|
5656
|
+
}
|
|
5648
5657
|
}
|
|
5649
5658
|
}
|
|
5650
5659
|
subscriptionMatches(sub, entityId) {
|
|
@@ -7221,21 +7230,27 @@ function haConfigSchema() {
|
|
|
7221
7230
|
title: "Home Assistant notify",
|
|
7222
7231
|
titleKey: "provider-homeassistant:haConfig.s_connection.title",
|
|
7223
7232
|
fields: [{
|
|
7224
|
-
type: "
|
|
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",
|
|
7225
7242
|
key: "service",
|
|
7226
7243
|
label: "Notify service",
|
|
7227
7244
|
labelKey: "provider-homeassistant:haConfig.s_connection.service.label",
|
|
7228
7245
|
required: true,
|
|
7246
|
+
options: [],
|
|
7229
7247
|
placeholder: "mobile_app_pixel_7",
|
|
7230
|
-
description: "
|
|
7231
|
-
descriptionKey: "provider-homeassistant:haConfig.s_connection.service.description"
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
labelKey: "provider-homeassistant:haConfig.s_connection.brokerId.label",
|
|
7237
|
-
description: "Optional; blank uses the only configured broker.",
|
|
7238
|
-
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
|
+
}
|
|
7239
7254
|
}]
|
|
7240
7255
|
}] };
|
|
7241
7256
|
}
|
|
@@ -7376,24 +7391,70 @@ function buildNotifyServiceData(prepared) {
|
|
|
7376
7391
|
* `get_services` returns `{ [domain]: { [service]: {...} } }`; we read the
|
|
7377
7392
|
* `notify` domain. mobile_app_* services (rich push features) sort first.
|
|
7378
7393
|
*/
|
|
7379
|
-
function parseNotifyServices(result, brokerId) {
|
|
7394
|
+
function parseNotifyServices(result, brokerId, deviceNames) {
|
|
7380
7395
|
if (typeof result !== "object" || result === null) return [];
|
|
7381
7396
|
const notify = result["notify"];
|
|
7382
7397
|
if (typeof notify !== "object" || notify === null) return [];
|
|
7383
|
-
return [...Object.
|
|
7398
|
+
return [...Object.entries(notify)].sort(([a], [b]) => {
|
|
7384
7399
|
const am = a.startsWith("mobile_app_") ? 0 : 1;
|
|
7385
7400
|
const bm = b.startsWith("mobile_app_") ? 0 : 1;
|
|
7386
7401
|
if (am !== bm) return am - bm;
|
|
7387
7402
|
return a.localeCompare(b);
|
|
7388
|
-
}).map((service) => ({
|
|
7403
|
+
}).map(([service, definition]) => ({
|
|
7389
7404
|
kind: HA_NOTIFICATION_KIND,
|
|
7390
|
-
suggestedName:
|
|
7405
|
+
suggestedName: friendlyServiceName(service, definition, deviceNames),
|
|
7391
7406
|
config: {
|
|
7392
7407
|
service,
|
|
7393
7408
|
...brokerId !== void 0 ? { brokerId } : {}
|
|
7394
7409
|
}
|
|
7395
7410
|
}));
|
|
7396
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
|
+
}
|
|
7397
7458
|
/**
|
|
7398
7459
|
* CRUD over the `ha-notification-targets` record kind.
|
|
7399
7460
|
*
|
|
@@ -7547,12 +7608,24 @@ function createHaNotificationOutputProvider(deps) {
|
|
|
7547
7608
|
},
|
|
7548
7609
|
discoverTargets: async ({ kind, config }) => {
|
|
7549
7610
|
if (kind !== "homeassistant") return [];
|
|
7611
|
+
const deviceNamesOf = async (brokerId) => {
|
|
7612
|
+
if (deps.listDevices === void 0) return /* @__PURE__ */ new Map();
|
|
7613
|
+
try {
|
|
7614
|
+
return mobileAppDeviceNames(await deps.listDevices(brokerId));
|
|
7615
|
+
} catch (err) {
|
|
7616
|
+
logger?.warn("ha device registry read failed; naming services from their ids", { meta: {
|
|
7617
|
+
brokerId,
|
|
7618
|
+
error: errMsg(err)
|
|
7619
|
+
} });
|
|
7620
|
+
return /* @__PURE__ */ new Map();
|
|
7621
|
+
}
|
|
7622
|
+
};
|
|
7550
7623
|
const explicit = config ? readBrokerId(config) : void 0;
|
|
7551
7624
|
const brokerIds = explicit ? [explicit] : [...deps.listBrokerIds()];
|
|
7552
7625
|
const out = [];
|
|
7553
7626
|
for (const brokerId of brokerIds) try {
|
|
7554
7627
|
const result = await deps.getServices(brokerId);
|
|
7555
|
-
out.push(...parseNotifyServices(result, brokerId));
|
|
7628
|
+
out.push(...parseNotifyServices(result, brokerId, await deviceNamesOf(brokerId)));
|
|
7556
7629
|
} catch (err) {
|
|
7557
7630
|
logger?.warn("ha get_services discovery failed", { meta: {
|
|
7558
7631
|
brokerId,
|
|
@@ -8421,6 +8494,11 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
8421
8494
|
if (!manager) throw new Error(`ha-broker '${brokerId}' not found`);
|
|
8422
8495
|
return manager.sendMessage({ type: "get_services" });
|
|
8423
8496
|
},
|
|
8497
|
+
listDevices: async (brokerId) => {
|
|
8498
|
+
const manager = this.requireRegistry().getManager(brokerId);
|
|
8499
|
+
if (!manager) throw new Error(`ha-broker '${brokerId}' not found`);
|
|
8500
|
+
return manager.sendMessage({ type: "config/device_registry/list" });
|
|
8501
|
+
},
|
|
8424
8502
|
listBrokerIds: () => this.activeBrokers.map((b) => b.id),
|
|
8425
8503
|
logger: this.ctx.logger.child("notification-output")
|
|
8426
8504
|
});
|