@camstack/addon-provider-homeassistant 1.2.630 → 1.2.632
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 +208 -32
- package/dist/addon.mjs +208 -32
- package/dist/{dist-CT3sP-p0.mjs → dist-CPkvghX8.mjs} +383 -35
- package/dist/{dist-Dcmr-Isu.js → dist-D0ZiJUPy.js} +383 -35
- 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.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-D0ZiJUPy.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
|
|
@@ -126,7 +126,14 @@ var haSourceSchemaFields = {
|
|
|
126
126
|
deviceClass: require_dist.string().optional(),
|
|
127
127
|
integrationId: require_dist.string().optional(),
|
|
128
128
|
options: require_dist.array(require_dist.string()).optional(),
|
|
129
|
-
features: require_dist.array(require_dist.string()).optional()
|
|
129
|
+
features: require_dist.array(require_dist.string()).optional(),
|
|
130
|
+
/** Sibling meter sensors whose readings this entity carries as `power-meter` (D951). */
|
|
131
|
+
meterEntities: require_dist.object({
|
|
132
|
+
watts: require_dist.string().optional(),
|
|
133
|
+
kwhTotal: require_dist.string().optional(),
|
|
134
|
+
volts: require_dist.string().optional(),
|
|
135
|
+
amps: require_dist.string().optional()
|
|
136
|
+
}).optional()
|
|
130
137
|
};
|
|
131
138
|
/** Project a persisted config blob into a typed source, or null when the
|
|
132
139
|
* blob is not an HA-adopted device (no brokerId). */
|
|
@@ -292,6 +299,168 @@ var HaDomain = /* @__PURE__ */ function(HaDomain) {
|
|
|
292
299
|
return HaDomain;
|
|
293
300
|
}({});
|
|
294
301
|
//#endregion
|
|
302
|
+
//#region src/ha-reading.ts
|
|
303
|
+
/**
|
|
304
|
+
* Convert one sample into the quantity's base unit. A refusal publishes
|
|
305
|
+
* nothing (the caller stores null) and is logged with the device id —
|
|
306
|
+
* an unknown unit is never guessed (D393, D944).
|
|
307
|
+
*/
|
|
308
|
+
function takeReading(logger, deviceId, value, rawUnit, quantity) {
|
|
309
|
+
const result = require_dist.ingestReading({
|
|
310
|
+
value,
|
|
311
|
+
rawUnit,
|
|
312
|
+
quantity
|
|
313
|
+
});
|
|
314
|
+
if (result.ok) return result.value;
|
|
315
|
+
logger.warn("numeric reading refused", {
|
|
316
|
+
tags: { deviceId },
|
|
317
|
+
meta: {
|
|
318
|
+
quantity,
|
|
319
|
+
unit: rawUnit ?? "",
|
|
320
|
+
reason: result.reason
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region src/ha-meter-fold.ts
|
|
327
|
+
/**
|
|
328
|
+
* Power and energy are a feature of the electrical device itself (D951). A
|
|
329
|
+
* metered plug is exposed by Home Assistant as a switch plus separate
|
|
330
|
+
* power / energy / voltage / current sensors; the switch carries the readings
|
|
331
|
+
* and the sensors stay as children (D865), flagged `foldedInto`.
|
|
332
|
+
*/
|
|
333
|
+
/** The `power-meter` status field a reading lands in. */
|
|
334
|
+
var METER_FIELDS = [
|
|
335
|
+
"watts",
|
|
336
|
+
"kwhTotal",
|
|
337
|
+
"volts",
|
|
338
|
+
"amps"
|
|
339
|
+
];
|
|
340
|
+
var METER_BY_DEVICE_CLASS = {
|
|
341
|
+
power: {
|
|
342
|
+
field: "watts",
|
|
343
|
+
quantity: "power"
|
|
344
|
+
},
|
|
345
|
+
energy: {
|
|
346
|
+
field: "kwhTotal",
|
|
347
|
+
quantity: "energy"
|
|
348
|
+
},
|
|
349
|
+
voltage: {
|
|
350
|
+
field: "volts",
|
|
351
|
+
quantity: "voltage"
|
|
352
|
+
},
|
|
353
|
+
current: {
|
|
354
|
+
field: "amps",
|
|
355
|
+
quantity: "current"
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
/** The meter field and quantity an HA `device_class` feeds; undefined for any other class. */
|
|
359
|
+
function meterKindForDeviceClass(deviceClass) {
|
|
360
|
+
return deviceClass === null || deviceClass === void 0 ? void 0 : METER_BY_DEVICE_CLASS[deviceClass];
|
|
361
|
+
}
|
|
362
|
+
/** Status with one reading applied; the clock moves with it. */
|
|
363
|
+
function applyMeterReading(prev, field, value, lastFetchedAt) {
|
|
364
|
+
return {
|
|
365
|
+
...prev,
|
|
366
|
+
[field]: value,
|
|
367
|
+
lastFetchedAt
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
var LOAD_TYPES = new Set(require_dist.powerMeterCapability.deviceTypes.filter((type) => type !== require_dist.DeviceType.Sensor));
|
|
371
|
+
function isLoad(entity) {
|
|
372
|
+
return LOAD_TYPES.has(entity.deviceType) && entity.entityCategory === void 0 && !entity.capabilities.includes("power-meter");
|
|
373
|
+
}
|
|
374
|
+
function meterFieldOf(entity) {
|
|
375
|
+
if (!entity.capabilities.includes("power-meter")) return void 0;
|
|
376
|
+
return meterKindForDeviceClass(entity.deviceClass)?.field;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Fold the device's meter sensors into its electrical load: the first normal
|
|
380
|
+
* control of a load type gains `power-meter` and the entity ids it reads; each
|
|
381
|
+
* sensor it reads is flagged `foldedInto`. A device with no load, or no meter,
|
|
382
|
+
* comes back unchanged.
|
|
383
|
+
*/
|
|
384
|
+
function foldMeterEntities(entities) {
|
|
385
|
+
const primary = entities.find(isLoad);
|
|
386
|
+
if (primary === void 0) return [...entities];
|
|
387
|
+
const meters = {};
|
|
388
|
+
for (const entity of entities) {
|
|
389
|
+
const field = meterFieldOf(entity);
|
|
390
|
+
if (field !== void 0 && meters[field] === void 0) meters[field] = entity.entityId;
|
|
391
|
+
}
|
|
392
|
+
const folded = new Set(Object.values(meters));
|
|
393
|
+
if (folded.size === 0) return [...entities];
|
|
394
|
+
return entities.map((entity) => {
|
|
395
|
+
if (entity.entityId === primary.entityId) return {
|
|
396
|
+
...entity,
|
|
397
|
+
capabilities: [...entity.capabilities, "power-meter"],
|
|
398
|
+
meterEntities: meters
|
|
399
|
+
};
|
|
400
|
+
return folded.has(entity.entityId) ? {
|
|
401
|
+
...entity,
|
|
402
|
+
foldedInto: primary.entityId
|
|
403
|
+
} : entity;
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region src/ha-meter-carrier.ts
|
|
408
|
+
var COLD_START$20 = { lastFetchedAt: 0 };
|
|
409
|
+
/**
|
|
410
|
+
* The `power-meter` of a device whose power / energy readings come from sibling
|
|
411
|
+
* HA sensors (D951). It registers the cap, names the entities the owner must
|
|
412
|
+
* subscribe to, and turns each of their pushes into one field of the slice.
|
|
413
|
+
* The owner is any HA child that is an electrical load (switch, light, fan, ...);
|
|
414
|
+
* none of them knows how a reading is converted.
|
|
415
|
+
*/
|
|
416
|
+
var HaMeterCarrier = class {
|
|
417
|
+
ctx;
|
|
418
|
+
runtimeState;
|
|
419
|
+
logger;
|
|
420
|
+
deviceId;
|
|
421
|
+
fieldByEntity;
|
|
422
|
+
constructor(ctx, runtimeState, logger, deviceId, meters) {
|
|
423
|
+
this.ctx = ctx;
|
|
424
|
+
this.runtimeState = runtimeState;
|
|
425
|
+
this.logger = logger;
|
|
426
|
+
this.deviceId = deviceId;
|
|
427
|
+
this.fieldByEntity = new Map(METER_FIELDS.flatMap((field) => {
|
|
428
|
+
const entityId = meters[field];
|
|
429
|
+
return entityId === void 0 ? [] : [[entityId, field]];
|
|
430
|
+
}));
|
|
431
|
+
this.ctx.registerNativeCap(require_dist.powerMeterCapability, { getStatus: async () => this.runtimeState.getCapState("power-meter") ?? COLD_START$20 });
|
|
432
|
+
this.runtimeState.setCapState("power-meter", {
|
|
433
|
+
...COLD_START$20,
|
|
434
|
+
...this.runtimeState.getCapState("power-meter")
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
/** The meter entities the owner must subscribe to, beside its own. */
|
|
438
|
+
entityIds() {
|
|
439
|
+
return [...this.fieldByEntity.keys()];
|
|
440
|
+
}
|
|
441
|
+
/** Whether a push belongs to one of the meter entities. */
|
|
442
|
+
carries(entityId) {
|
|
443
|
+
return this.fieldByEntity.has(entityId);
|
|
444
|
+
}
|
|
445
|
+
/** Apply one meter push; an unavailable or unreadable sample changes nothing. */
|
|
446
|
+
push(entityId, state) {
|
|
447
|
+
const field = this.fieldByEntity.get(entityId);
|
|
448
|
+
if (field === void 0) return;
|
|
449
|
+
if (state.state === "unavailable" || state.state === "unknown") return;
|
|
450
|
+
const value = Number(state.state);
|
|
451
|
+
if (!Number.isFinite(value)) return;
|
|
452
|
+
const deviceClass = state.attributes["device_class"];
|
|
453
|
+
const kind = meterKindForDeviceClass(typeof deviceClass === "string" ? deviceClass : null);
|
|
454
|
+
if (kind === void 0 || kind.field !== field) return;
|
|
455
|
+
const unit = state.attributes["unit_of_measurement"];
|
|
456
|
+
const stored = takeReading(this.logger, this.deviceId, value, typeof unit === "string" ? unit : void 0, kind.quantity);
|
|
457
|
+
if (stored === null) return;
|
|
458
|
+
const prev = this.runtimeState.getCapState("power-meter") ?? COLD_START$20;
|
|
459
|
+
const lastFetchedAt = Date.parse(state.last_updated) || Date.now();
|
|
460
|
+
this.runtimeState.setCapState("power-meter", applyMeterReading(prev, field, stored, lastFetchedAt));
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
//#endregion
|
|
295
464
|
//#region src/ha-base-child-device.ts
|
|
296
465
|
/**
|
|
297
466
|
* Persisted config shared by every per-domain HA child device class.
|
|
@@ -357,6 +526,9 @@ var HaBaseChildDevice = class HaBaseChildDevice extends require_dist.BaseDevice
|
|
|
357
526
|
/** Handle to the accessories cap when this device acts as a parent
|
|
358
527
|
* that hosts entity-children. Null for plain child devices. */
|
|
359
528
|
accessories = null;
|
|
529
|
+
/** The power-meter this device carries for its sibling meter sensors (D951);
|
|
530
|
+
* null for a device that is not metered through other entities. */
|
|
531
|
+
meterCarrier;
|
|
360
532
|
constructor(ctx, options = {}) {
|
|
361
533
|
const persisted = ctx.persistedConfig ?? {};
|
|
362
534
|
const brokerId = persisted.brokerId;
|
|
@@ -372,6 +544,7 @@ var HaBaseChildDevice = class HaBaseChildDevice extends require_dist.BaseDevice
|
|
|
372
544
|
this.features = parseDeviceFeatures(persisted.features);
|
|
373
545
|
this.deviceLogger = ctx.deviceMeta.integrationId !== void 0 ? ctx.logger.withTags({ integrationId: ctx.deviceMeta.integrationId }) ?? ctx.logger : ctx.logger;
|
|
374
546
|
this.online = true;
|
|
547
|
+
this.meterCarrier = persisted.meterEntities !== void 0 ? new HaMeterCarrier(this.ctx, this.runtimeState, this.deviceLogger, this.id, persisted.meterEntities) : null;
|
|
375
548
|
this.attachEventBusListener();
|
|
376
549
|
const childIds = Array.isArray(persisted.childDeviceIds) ? persisted.childDeviceIds : [];
|
|
377
550
|
if (childIds.length > 0) {
|
|
@@ -535,7 +708,7 @@ var HaBaseChildDevice = class HaBaseChildDevice extends require_dist.BaseDevice
|
|
|
535
708
|
try {
|
|
536
709
|
const result = await this.ctx.api.broker.subscribe.mutate({
|
|
537
710
|
brokerId: this.brokerId,
|
|
538
|
-
filter: { entityIds: [this.entityId] }
|
|
711
|
+
filter: { entityIds: [this.entityId, ...this.meterCarrier?.entityIds() ?? []] }
|
|
539
712
|
});
|
|
540
713
|
this.brokerSubscriptionId = result.subscriptionId;
|
|
541
714
|
return true;
|
|
@@ -554,8 +727,12 @@ var HaBaseChildDevice = class HaBaseChildDevice extends require_dist.BaseDevice
|
|
|
554
727
|
const data = event.data;
|
|
555
728
|
if (!data) return;
|
|
556
729
|
if (data.brokerId !== this.brokerId) return;
|
|
557
|
-
if (data.key !== this.entityId) return;
|
|
558
730
|
const state = data.payload;
|
|
731
|
+
if (data.key !== void 0 && this.meterCarrier?.carries(data.key) === true) {
|
|
732
|
+
if (state) this.meterCarrier.push(data.key, state);
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
if (data.key !== this.entityId) return;
|
|
559
736
|
if (!state) return;
|
|
560
737
|
if (state.state !== this.lastLoggedState) {
|
|
561
738
|
this.deviceLogger.debug("HA→device value change", { meta: {
|
|
@@ -766,30 +943,6 @@ var HaSwitchDevice = class extends HaBaseChildDevice {
|
|
|
766
943
|
}
|
|
767
944
|
};
|
|
768
945
|
//#endregion
|
|
769
|
-
//#region src/ha-reading.ts
|
|
770
|
-
/**
|
|
771
|
-
* Convert one sample into the quantity's base unit. A refusal publishes
|
|
772
|
-
* nothing (the caller stores null) and is logged with the device id —
|
|
773
|
-
* an unknown unit is never guessed (D393, D944).
|
|
774
|
-
*/
|
|
775
|
-
function takeReading(logger, deviceId, value, rawUnit, quantity) {
|
|
776
|
-
const result = require_dist.ingestReading({
|
|
777
|
-
value,
|
|
778
|
-
rawUnit,
|
|
779
|
-
quantity
|
|
780
|
-
});
|
|
781
|
-
if (result.ok) return result.value;
|
|
782
|
-
logger.warn("numeric reading refused", {
|
|
783
|
-
tags: { deviceId },
|
|
784
|
-
meta: {
|
|
785
|
-
quantity,
|
|
786
|
-
unit: rawUnit ?? "",
|
|
787
|
-
reason: result.reason
|
|
788
|
-
}
|
|
789
|
-
});
|
|
790
|
-
return null;
|
|
791
|
-
}
|
|
792
|
-
//#endregion
|
|
793
946
|
//#region src/devices/ha-numeric-sensor-device.ts
|
|
794
947
|
var SUPPORTED_CAP_NAMES$1 = [
|
|
795
948
|
"temperature-sensor",
|
|
@@ -3699,7 +3852,14 @@ var containerEntitySchema = require_dist.object({
|
|
|
3699
3852
|
features: require_dist.array(require_dist.string()).optional(),
|
|
3700
3853
|
name: require_dist.string().optional(),
|
|
3701
3854
|
dateTimeFormat: require_dist.EnumSensorDateTimeFormatSchema.optional(),
|
|
3702
|
-
entityCategory: require_dist._enum(["config", "diagnostic"]).optional()
|
|
3855
|
+
entityCategory: require_dist._enum(["config", "diagnostic"]).optional(),
|
|
3856
|
+
meterEntities: require_dist.object({
|
|
3857
|
+
watts: require_dist.string().optional(),
|
|
3858
|
+
kwhTotal: require_dist.string().optional(),
|
|
3859
|
+
volts: require_dist.string().optional(),
|
|
3860
|
+
amps: require_dist.string().optional()
|
|
3861
|
+
}).optional(),
|
|
3862
|
+
foldedInto: require_dist.string().optional()
|
|
3703
3863
|
});
|
|
3704
3864
|
function parseContainerEntities(raw) {
|
|
3705
3865
|
const result = require_dist.array(containerEntitySchema).safeParse(raw);
|
|
@@ -3712,6 +3872,9 @@ function parseContainerEntities(raw) {
|
|
|
3712
3872
|
* skipped (the adoption path skips them too).
|
|
3713
3873
|
*/
|
|
3714
3874
|
function buildContainerEntities(haDevice) {
|
|
3875
|
+
return foldMeterEntities(mapContainerEntities(haDevice));
|
|
3876
|
+
}
|
|
3877
|
+
function mapContainerEntities(haDevice) {
|
|
3715
3878
|
const out = [];
|
|
3716
3879
|
for (const ent of haDevice.entities) {
|
|
3717
3880
|
if (ent.mappedTo === null) continue;
|
|
@@ -3742,7 +3905,8 @@ function derivedKey(e) {
|
|
|
3742
3905
|
deviceClass: e.deviceClass ?? null,
|
|
3743
3906
|
options: e.options ?? null,
|
|
3744
3907
|
features: e.features ?? null,
|
|
3745
|
-
dateTimeFormat: e.dateTimeFormat ?? null
|
|
3908
|
+
dateTimeFormat: e.dateTimeFormat ?? null,
|
|
3909
|
+
meterEntities: e.meterEntities ?? null
|
|
3746
3910
|
});
|
|
3747
3911
|
}
|
|
3748
3912
|
/** Stable key over the FULL entry incl. name + entityId + the layout-affecting
|
|
@@ -3755,6 +3919,7 @@ function fullKey(e) {
|
|
|
3755
3919
|
entityId: e.entityId,
|
|
3756
3920
|
name: e.name ?? null,
|
|
3757
3921
|
entityCategory: e.entityCategory ?? null,
|
|
3922
|
+
foldedInto: e.foldedInto ?? null,
|
|
3758
3923
|
derived: derivedKey(e)
|
|
3759
3924
|
});
|
|
3760
3925
|
}
|
|
@@ -3766,6 +3931,7 @@ function childConfigPatch(e) {
|
|
|
3766
3931
|
...e.options !== void 0 ? { options: e.options } : {},
|
|
3767
3932
|
...e.features !== void 0 ? { features: e.features } : {},
|
|
3768
3933
|
...e.dateTimeFormat !== void 0 ? { dateTimeFormat: e.dateTimeFormat } : {},
|
|
3934
|
+
...e.meterEntities !== void 0 ? { meterEntities: e.meterEntities } : {},
|
|
3769
3935
|
...e.role !== void 0 ? { role: e.role } : {}
|
|
3770
3936
|
};
|
|
3771
3937
|
}
|
|
@@ -3819,7 +3985,7 @@ var SECTION_RANK = {
|
|
|
3819
3985
|
* normal control). */
|
|
3820
3986
|
function sectionForEntity(e) {
|
|
3821
3987
|
if (e.entityCategory === "config") return SECTION_CONFIGURATION;
|
|
3822
|
-
if (e.entityCategory === "diagnostic") return SECTION_DIAGNOSTIC;
|
|
3988
|
+
if (e.entityCategory === "diagnostic" || e.foldedInto !== void 0) return SECTION_DIAGNOSTIC;
|
|
3823
3989
|
return SECTION_CONTROLS;
|
|
3824
3990
|
}
|
|
3825
3991
|
/**
|
|
@@ -3872,7 +4038,16 @@ var haEntitySchema = require_dist.object({
|
|
|
3872
4038
|
dateTimeFormat: require_dist.EnumSensorDateTimeFormatSchema.optional(),
|
|
3873
4039
|
/** HA `entity_category` — `config` / `diagnostic` / absent (a normal
|
|
3874
4040
|
* control). Drives the accordion section the child renders in. */
|
|
3875
|
-
entityCategory: require_dist._enum(["config", "diagnostic"]).optional()
|
|
4041
|
+
entityCategory: require_dist._enum(["config", "diagnostic"]).optional(),
|
|
4042
|
+
/** Sibling meter sensors this entity reads as its own power-meter (D951). */
|
|
4043
|
+
meterEntities: require_dist.object({
|
|
4044
|
+
watts: require_dist.string().optional(),
|
|
4045
|
+
kwhTotal: require_dist.string().optional(),
|
|
4046
|
+
volts: require_dist.string().optional(),
|
|
4047
|
+
amps: require_dist.string().optional()
|
|
4048
|
+
}).optional(),
|
|
4049
|
+
/** Set on a meter sensor folded into another entity: sectioned as Diagnostic. */
|
|
4050
|
+
foldedInto: require_dist.string().optional()
|
|
3876
4051
|
});
|
|
3877
4052
|
var haContainerSchema = require_dist.object({
|
|
3878
4053
|
system: require_dist.literal("homeassistant"),
|
|
@@ -4187,6 +4362,7 @@ var HaContainerDevice = class HaContainerDevice extends require_dist.BaseDevice
|
|
|
4187
4362
|
...e.options !== void 0 ? { options: e.options } : {},
|
|
4188
4363
|
...e.features !== void 0 ? { features: e.features } : {},
|
|
4189
4364
|
...e.dateTimeFormat !== void 0 ? { dateTimeFormat: e.dateTimeFormat } : {},
|
|
4365
|
+
...e.meterEntities !== void 0 ? { meterEntities: e.meterEntities } : {},
|
|
4190
4366
|
...e.role !== void 0 ? { role: e.role } : {}
|
|
4191
4367
|
};
|
|
4192
4368
|
const factory = (childCtx) => {
|
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as lawnMowerControlCapability, $t as union, A as buttonCapability, At as weatherCapability, B as coverCapability, Bt as defineRecordKinds, C as batteryCapability, Ct as tryConvertDelta, D as brokerCapability, Dt as valveCapability, E as brightnessCapability, Et as vacuumControlCapability, F as connectivityCapability, G as eventEmitterCapability, Gt as array, H as deviceAdoptionCapability, It as RecordSecretUnavailableError, J as gasCapability, Jt as literal, K as fanControlCapability, Kt as boolean, Lt as baseUnitOf, M as carbonMonoxideCapability, Mt as DeviceRole, N as climateControlCapability, Nt as DeviceType, O as buildAddonRouteProvider, Ot as vibrationCapability, P as colorCapability, Pt as errMsg, Q as ingestReading, Qt as string, R as contactCapability, Rt as createEvent, S as automationControlCapability, St as temperatureSensorCapability, T as binaryCapability, Tt as updateCapability, V as deviceAdminLinkCapability, Vt as entryBody, W as enumSensorCapability, Wt as _enum, X as humiditySensorCapability, Xt as object, Y as humidifierCapability, Yt as number, Z as imageCapability, Zt as record, _ as accessoriesCapability, _t as runningStateCapability, at as notifierCapability, b as alarmPanelCapability, bt as switchCapability, ct as parseAdminBase, dt as presenceCapability, en as unknown, et as lockControlCapability, ft as pressureSensorCapability, g as TargetSchema, gt as runningFromVacuumState, h as RUNNING_STATE_COLD, ht as runningFromMediaPlayer, it as notificationOutputCapability, j as calendarSourceCapability, jt as DeviceFeature, k as buildDeviceAdminUrl, kt as waterHeaterCapability, lt as powerMeterCapability, mt as runningFromLawnMower, n as BaseDevice, nn as EventCategory, nt as motionCapability, ot as numericSensorCapability, pt as quantityFromUnit, q as floodCapability, qt as discriminatedUnion, r as BaseDeviceProvider, rt as normalizeUnit, tn as url, tt as mediaPlayerCapability, u as EnumSensorDateTimeFormatSchema, ut as prepareNotification, v as addonRoutesCapability, vt as scriptRunnerCapability, w as bestLocationMatch, wt as tryConvertUnit, x as ambientLightSensorCapability, xt as tamperCapability, y as airQualitySensorCapability, yt as smokeCapability, z as controlCapability, zt as defineEntryKind } from "./dist-
|
|
1
|
+
import { $ as lawnMowerControlCapability, $t as union, A as buttonCapability, At as weatherCapability, B as coverCapability, Bt as defineRecordKinds, C as batteryCapability, Ct as tryConvertDelta, D as brokerCapability, Dt as valveCapability, E as brightnessCapability, Et as vacuumControlCapability, F as connectivityCapability, G as eventEmitterCapability, Gt as array, H as deviceAdoptionCapability, It as RecordSecretUnavailableError, J as gasCapability, Jt as literal, K as fanControlCapability, Kt as boolean, Lt as baseUnitOf, M as carbonMonoxideCapability, Mt as DeviceRole, N as climateControlCapability, Nt as DeviceType, O as buildAddonRouteProvider, Ot as vibrationCapability, P as colorCapability, Pt as errMsg, Q as ingestReading, Qt as string, R as contactCapability, Rt as createEvent, S as automationControlCapability, St as temperatureSensorCapability, T as binaryCapability, Tt as updateCapability, V as deviceAdminLinkCapability, Vt as entryBody, W as enumSensorCapability, Wt as _enum, X as humiditySensorCapability, Xt as object, Y as humidifierCapability, Yt as number, Z as imageCapability, Zt as record, _ as accessoriesCapability, _t as runningStateCapability, at as notifierCapability, b as alarmPanelCapability, bt as switchCapability, ct as parseAdminBase, dt as presenceCapability, en as unknown, et as lockControlCapability, ft as pressureSensorCapability, g as TargetSchema, gt as runningFromVacuumState, h as RUNNING_STATE_COLD, ht as runningFromMediaPlayer, it as notificationOutputCapability, j as calendarSourceCapability, jt as DeviceFeature, k as buildDeviceAdminUrl, kt as waterHeaterCapability, lt as powerMeterCapability, mt as runningFromLawnMower, n as BaseDevice, nn as EventCategory, nt as motionCapability, ot as numericSensorCapability, pt as quantityFromUnit, q as floodCapability, qt as discriminatedUnion, r as BaseDeviceProvider, rt as normalizeUnit, tn as url, tt as mediaPlayerCapability, u as EnumSensorDateTimeFormatSchema, ut as prepareNotification, v as addonRoutesCapability, vt as scriptRunnerCapability, w as bestLocationMatch, wt as tryConvertUnit, x as ambientLightSensorCapability, xt as tamperCapability, y as airQualitySensorCapability, yt as smokeCapability, z as controlCapability, zt as defineEntryKind } from "./dist-CPkvghX8.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
|
|
@@ -125,7 +125,14 @@ var haSourceSchemaFields = {
|
|
|
125
125
|
deviceClass: string().optional(),
|
|
126
126
|
integrationId: string().optional(),
|
|
127
127
|
options: array(string()).optional(),
|
|
128
|
-
features: array(string()).optional()
|
|
128
|
+
features: array(string()).optional(),
|
|
129
|
+
/** Sibling meter sensors whose readings this entity carries as `power-meter` (D951). */
|
|
130
|
+
meterEntities: object({
|
|
131
|
+
watts: string().optional(),
|
|
132
|
+
kwhTotal: string().optional(),
|
|
133
|
+
volts: string().optional(),
|
|
134
|
+
amps: string().optional()
|
|
135
|
+
}).optional()
|
|
129
136
|
};
|
|
130
137
|
/** Project a persisted config blob into a typed source, or null when the
|
|
131
138
|
* blob is not an HA-adopted device (no brokerId). */
|
|
@@ -291,6 +298,168 @@ var HaDomain = /* @__PURE__ */ function(HaDomain) {
|
|
|
291
298
|
return HaDomain;
|
|
292
299
|
}({});
|
|
293
300
|
//#endregion
|
|
301
|
+
//#region src/ha-reading.ts
|
|
302
|
+
/**
|
|
303
|
+
* Convert one sample into the quantity's base unit. A refusal publishes
|
|
304
|
+
* nothing (the caller stores null) and is logged with the device id —
|
|
305
|
+
* an unknown unit is never guessed (D393, D944).
|
|
306
|
+
*/
|
|
307
|
+
function takeReading(logger, deviceId, value, rawUnit, quantity) {
|
|
308
|
+
const result = ingestReading({
|
|
309
|
+
value,
|
|
310
|
+
rawUnit,
|
|
311
|
+
quantity
|
|
312
|
+
});
|
|
313
|
+
if (result.ok) return result.value;
|
|
314
|
+
logger.warn("numeric reading refused", {
|
|
315
|
+
tags: { deviceId },
|
|
316
|
+
meta: {
|
|
317
|
+
quantity,
|
|
318
|
+
unit: rawUnit ?? "",
|
|
319
|
+
reason: result.reason
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region src/ha-meter-fold.ts
|
|
326
|
+
/**
|
|
327
|
+
* Power and energy are a feature of the electrical device itself (D951). A
|
|
328
|
+
* metered plug is exposed by Home Assistant as a switch plus separate
|
|
329
|
+
* power / energy / voltage / current sensors; the switch carries the readings
|
|
330
|
+
* and the sensors stay as children (D865), flagged `foldedInto`.
|
|
331
|
+
*/
|
|
332
|
+
/** The `power-meter` status field a reading lands in. */
|
|
333
|
+
var METER_FIELDS = [
|
|
334
|
+
"watts",
|
|
335
|
+
"kwhTotal",
|
|
336
|
+
"volts",
|
|
337
|
+
"amps"
|
|
338
|
+
];
|
|
339
|
+
var METER_BY_DEVICE_CLASS = {
|
|
340
|
+
power: {
|
|
341
|
+
field: "watts",
|
|
342
|
+
quantity: "power"
|
|
343
|
+
},
|
|
344
|
+
energy: {
|
|
345
|
+
field: "kwhTotal",
|
|
346
|
+
quantity: "energy"
|
|
347
|
+
},
|
|
348
|
+
voltage: {
|
|
349
|
+
field: "volts",
|
|
350
|
+
quantity: "voltage"
|
|
351
|
+
},
|
|
352
|
+
current: {
|
|
353
|
+
field: "amps",
|
|
354
|
+
quantity: "current"
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
/** The meter field and quantity an HA `device_class` feeds; undefined for any other class. */
|
|
358
|
+
function meterKindForDeviceClass(deviceClass) {
|
|
359
|
+
return deviceClass === null || deviceClass === void 0 ? void 0 : METER_BY_DEVICE_CLASS[deviceClass];
|
|
360
|
+
}
|
|
361
|
+
/** Status with one reading applied; the clock moves with it. */
|
|
362
|
+
function applyMeterReading(prev, field, value, lastFetchedAt) {
|
|
363
|
+
return {
|
|
364
|
+
...prev,
|
|
365
|
+
[field]: value,
|
|
366
|
+
lastFetchedAt
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
var LOAD_TYPES = new Set(powerMeterCapability.deviceTypes.filter((type) => type !== DeviceType.Sensor));
|
|
370
|
+
function isLoad(entity) {
|
|
371
|
+
return LOAD_TYPES.has(entity.deviceType) && entity.entityCategory === void 0 && !entity.capabilities.includes("power-meter");
|
|
372
|
+
}
|
|
373
|
+
function meterFieldOf(entity) {
|
|
374
|
+
if (!entity.capabilities.includes("power-meter")) return void 0;
|
|
375
|
+
return meterKindForDeviceClass(entity.deviceClass)?.field;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Fold the device's meter sensors into its electrical load: the first normal
|
|
379
|
+
* control of a load type gains `power-meter` and the entity ids it reads; each
|
|
380
|
+
* sensor it reads is flagged `foldedInto`. A device with no load, or no meter,
|
|
381
|
+
* comes back unchanged.
|
|
382
|
+
*/
|
|
383
|
+
function foldMeterEntities(entities) {
|
|
384
|
+
const primary = entities.find(isLoad);
|
|
385
|
+
if (primary === void 0) return [...entities];
|
|
386
|
+
const meters = {};
|
|
387
|
+
for (const entity of entities) {
|
|
388
|
+
const field = meterFieldOf(entity);
|
|
389
|
+
if (field !== void 0 && meters[field] === void 0) meters[field] = entity.entityId;
|
|
390
|
+
}
|
|
391
|
+
const folded = new Set(Object.values(meters));
|
|
392
|
+
if (folded.size === 0) return [...entities];
|
|
393
|
+
return entities.map((entity) => {
|
|
394
|
+
if (entity.entityId === primary.entityId) return {
|
|
395
|
+
...entity,
|
|
396
|
+
capabilities: [...entity.capabilities, "power-meter"],
|
|
397
|
+
meterEntities: meters
|
|
398
|
+
};
|
|
399
|
+
return folded.has(entity.entityId) ? {
|
|
400
|
+
...entity,
|
|
401
|
+
foldedInto: primary.entityId
|
|
402
|
+
} : entity;
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
//#endregion
|
|
406
|
+
//#region src/ha-meter-carrier.ts
|
|
407
|
+
var COLD_START$20 = { lastFetchedAt: 0 };
|
|
408
|
+
/**
|
|
409
|
+
* The `power-meter` of a device whose power / energy readings come from sibling
|
|
410
|
+
* HA sensors (D951). It registers the cap, names the entities the owner must
|
|
411
|
+
* subscribe to, and turns each of their pushes into one field of the slice.
|
|
412
|
+
* The owner is any HA child that is an electrical load (switch, light, fan, ...);
|
|
413
|
+
* none of them knows how a reading is converted.
|
|
414
|
+
*/
|
|
415
|
+
var HaMeterCarrier = class {
|
|
416
|
+
ctx;
|
|
417
|
+
runtimeState;
|
|
418
|
+
logger;
|
|
419
|
+
deviceId;
|
|
420
|
+
fieldByEntity;
|
|
421
|
+
constructor(ctx, runtimeState, logger, deviceId, meters) {
|
|
422
|
+
this.ctx = ctx;
|
|
423
|
+
this.runtimeState = runtimeState;
|
|
424
|
+
this.logger = logger;
|
|
425
|
+
this.deviceId = deviceId;
|
|
426
|
+
this.fieldByEntity = new Map(METER_FIELDS.flatMap((field) => {
|
|
427
|
+
const entityId = meters[field];
|
|
428
|
+
return entityId === void 0 ? [] : [[entityId, field]];
|
|
429
|
+
}));
|
|
430
|
+
this.ctx.registerNativeCap(powerMeterCapability, { getStatus: async () => this.runtimeState.getCapState("power-meter") ?? COLD_START$20 });
|
|
431
|
+
this.runtimeState.setCapState("power-meter", {
|
|
432
|
+
...COLD_START$20,
|
|
433
|
+
...this.runtimeState.getCapState("power-meter")
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
/** The meter entities the owner must subscribe to, beside its own. */
|
|
437
|
+
entityIds() {
|
|
438
|
+
return [...this.fieldByEntity.keys()];
|
|
439
|
+
}
|
|
440
|
+
/** Whether a push belongs to one of the meter entities. */
|
|
441
|
+
carries(entityId) {
|
|
442
|
+
return this.fieldByEntity.has(entityId);
|
|
443
|
+
}
|
|
444
|
+
/** Apply one meter push; an unavailable or unreadable sample changes nothing. */
|
|
445
|
+
push(entityId, state) {
|
|
446
|
+
const field = this.fieldByEntity.get(entityId);
|
|
447
|
+
if (field === void 0) return;
|
|
448
|
+
if (state.state === "unavailable" || state.state === "unknown") return;
|
|
449
|
+
const value = Number(state.state);
|
|
450
|
+
if (!Number.isFinite(value)) return;
|
|
451
|
+
const deviceClass = state.attributes["device_class"];
|
|
452
|
+
const kind = meterKindForDeviceClass(typeof deviceClass === "string" ? deviceClass : null);
|
|
453
|
+
if (kind === void 0 || kind.field !== field) return;
|
|
454
|
+
const unit = state.attributes["unit_of_measurement"];
|
|
455
|
+
const stored = takeReading(this.logger, this.deviceId, value, typeof unit === "string" ? unit : void 0, kind.quantity);
|
|
456
|
+
if (stored === null) return;
|
|
457
|
+
const prev = this.runtimeState.getCapState("power-meter") ?? COLD_START$20;
|
|
458
|
+
const lastFetchedAt = Date.parse(state.last_updated) || Date.now();
|
|
459
|
+
this.runtimeState.setCapState("power-meter", applyMeterReading(prev, field, stored, lastFetchedAt));
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
//#endregion
|
|
294
463
|
//#region src/ha-base-child-device.ts
|
|
295
464
|
/**
|
|
296
465
|
* Persisted config shared by every per-domain HA child device class.
|
|
@@ -356,6 +525,9 @@ var HaBaseChildDevice = class HaBaseChildDevice extends BaseDevice {
|
|
|
356
525
|
/** Handle to the accessories cap when this device acts as a parent
|
|
357
526
|
* that hosts entity-children. Null for plain child devices. */
|
|
358
527
|
accessories = null;
|
|
528
|
+
/** The power-meter this device carries for its sibling meter sensors (D951);
|
|
529
|
+
* null for a device that is not metered through other entities. */
|
|
530
|
+
meterCarrier;
|
|
359
531
|
constructor(ctx, options = {}) {
|
|
360
532
|
const persisted = ctx.persistedConfig ?? {};
|
|
361
533
|
const brokerId = persisted.brokerId;
|
|
@@ -371,6 +543,7 @@ var HaBaseChildDevice = class HaBaseChildDevice extends BaseDevice {
|
|
|
371
543
|
this.features = parseDeviceFeatures(persisted.features);
|
|
372
544
|
this.deviceLogger = ctx.deviceMeta.integrationId !== void 0 ? ctx.logger.withTags({ integrationId: ctx.deviceMeta.integrationId }) ?? ctx.logger : ctx.logger;
|
|
373
545
|
this.online = true;
|
|
546
|
+
this.meterCarrier = persisted.meterEntities !== void 0 ? new HaMeterCarrier(this.ctx, this.runtimeState, this.deviceLogger, this.id, persisted.meterEntities) : null;
|
|
374
547
|
this.attachEventBusListener();
|
|
375
548
|
const childIds = Array.isArray(persisted.childDeviceIds) ? persisted.childDeviceIds : [];
|
|
376
549
|
if (childIds.length > 0) {
|
|
@@ -534,7 +707,7 @@ var HaBaseChildDevice = class HaBaseChildDevice extends BaseDevice {
|
|
|
534
707
|
try {
|
|
535
708
|
const result = await this.ctx.api.broker.subscribe.mutate({
|
|
536
709
|
brokerId: this.brokerId,
|
|
537
|
-
filter: { entityIds: [this.entityId] }
|
|
710
|
+
filter: { entityIds: [this.entityId, ...this.meterCarrier?.entityIds() ?? []] }
|
|
538
711
|
});
|
|
539
712
|
this.brokerSubscriptionId = result.subscriptionId;
|
|
540
713
|
return true;
|
|
@@ -553,8 +726,12 @@ var HaBaseChildDevice = class HaBaseChildDevice extends BaseDevice {
|
|
|
553
726
|
const data = event.data;
|
|
554
727
|
if (!data) return;
|
|
555
728
|
if (data.brokerId !== this.brokerId) return;
|
|
556
|
-
if (data.key !== this.entityId) return;
|
|
557
729
|
const state = data.payload;
|
|
730
|
+
if (data.key !== void 0 && this.meterCarrier?.carries(data.key) === true) {
|
|
731
|
+
if (state) this.meterCarrier.push(data.key, state);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (data.key !== this.entityId) return;
|
|
558
735
|
if (!state) return;
|
|
559
736
|
if (state.state !== this.lastLoggedState) {
|
|
560
737
|
this.deviceLogger.debug("HA→device value change", { meta: {
|
|
@@ -765,30 +942,6 @@ var HaSwitchDevice = class extends HaBaseChildDevice {
|
|
|
765
942
|
}
|
|
766
943
|
};
|
|
767
944
|
//#endregion
|
|
768
|
-
//#region src/ha-reading.ts
|
|
769
|
-
/**
|
|
770
|
-
* Convert one sample into the quantity's base unit. A refusal publishes
|
|
771
|
-
* nothing (the caller stores null) and is logged with the device id —
|
|
772
|
-
* an unknown unit is never guessed (D393, D944).
|
|
773
|
-
*/
|
|
774
|
-
function takeReading(logger, deviceId, value, rawUnit, quantity) {
|
|
775
|
-
const result = ingestReading({
|
|
776
|
-
value,
|
|
777
|
-
rawUnit,
|
|
778
|
-
quantity
|
|
779
|
-
});
|
|
780
|
-
if (result.ok) return result.value;
|
|
781
|
-
logger.warn("numeric reading refused", {
|
|
782
|
-
tags: { deviceId },
|
|
783
|
-
meta: {
|
|
784
|
-
quantity,
|
|
785
|
-
unit: rawUnit ?? "",
|
|
786
|
-
reason: result.reason
|
|
787
|
-
}
|
|
788
|
-
});
|
|
789
|
-
return null;
|
|
790
|
-
}
|
|
791
|
-
//#endregion
|
|
792
945
|
//#region src/devices/ha-numeric-sensor-device.ts
|
|
793
946
|
var SUPPORTED_CAP_NAMES$1 = [
|
|
794
947
|
"temperature-sensor",
|
|
@@ -3698,7 +3851,14 @@ var containerEntitySchema = object({
|
|
|
3698
3851
|
features: array(string()).optional(),
|
|
3699
3852
|
name: string().optional(),
|
|
3700
3853
|
dateTimeFormat: EnumSensorDateTimeFormatSchema.optional(),
|
|
3701
|
-
entityCategory: _enum(["config", "diagnostic"]).optional()
|
|
3854
|
+
entityCategory: _enum(["config", "diagnostic"]).optional(),
|
|
3855
|
+
meterEntities: object({
|
|
3856
|
+
watts: string().optional(),
|
|
3857
|
+
kwhTotal: string().optional(),
|
|
3858
|
+
volts: string().optional(),
|
|
3859
|
+
amps: string().optional()
|
|
3860
|
+
}).optional(),
|
|
3861
|
+
foldedInto: string().optional()
|
|
3702
3862
|
});
|
|
3703
3863
|
function parseContainerEntities(raw) {
|
|
3704
3864
|
const result = array(containerEntitySchema).safeParse(raw);
|
|
@@ -3711,6 +3871,9 @@ function parseContainerEntities(raw) {
|
|
|
3711
3871
|
* skipped (the adoption path skips them too).
|
|
3712
3872
|
*/
|
|
3713
3873
|
function buildContainerEntities(haDevice) {
|
|
3874
|
+
return foldMeterEntities(mapContainerEntities(haDevice));
|
|
3875
|
+
}
|
|
3876
|
+
function mapContainerEntities(haDevice) {
|
|
3714
3877
|
const out = [];
|
|
3715
3878
|
for (const ent of haDevice.entities) {
|
|
3716
3879
|
if (ent.mappedTo === null) continue;
|
|
@@ -3741,7 +3904,8 @@ function derivedKey(e) {
|
|
|
3741
3904
|
deviceClass: e.deviceClass ?? null,
|
|
3742
3905
|
options: e.options ?? null,
|
|
3743
3906
|
features: e.features ?? null,
|
|
3744
|
-
dateTimeFormat: e.dateTimeFormat ?? null
|
|
3907
|
+
dateTimeFormat: e.dateTimeFormat ?? null,
|
|
3908
|
+
meterEntities: e.meterEntities ?? null
|
|
3745
3909
|
});
|
|
3746
3910
|
}
|
|
3747
3911
|
/** Stable key over the FULL entry incl. name + entityId + the layout-affecting
|
|
@@ -3754,6 +3918,7 @@ function fullKey(e) {
|
|
|
3754
3918
|
entityId: e.entityId,
|
|
3755
3919
|
name: e.name ?? null,
|
|
3756
3920
|
entityCategory: e.entityCategory ?? null,
|
|
3921
|
+
foldedInto: e.foldedInto ?? null,
|
|
3757
3922
|
derived: derivedKey(e)
|
|
3758
3923
|
});
|
|
3759
3924
|
}
|
|
@@ -3765,6 +3930,7 @@ function childConfigPatch(e) {
|
|
|
3765
3930
|
...e.options !== void 0 ? { options: e.options } : {},
|
|
3766
3931
|
...e.features !== void 0 ? { features: e.features } : {},
|
|
3767
3932
|
...e.dateTimeFormat !== void 0 ? { dateTimeFormat: e.dateTimeFormat } : {},
|
|
3933
|
+
...e.meterEntities !== void 0 ? { meterEntities: e.meterEntities } : {},
|
|
3768
3934
|
...e.role !== void 0 ? { role: e.role } : {}
|
|
3769
3935
|
};
|
|
3770
3936
|
}
|
|
@@ -3818,7 +3984,7 @@ var SECTION_RANK = {
|
|
|
3818
3984
|
* normal control). */
|
|
3819
3985
|
function sectionForEntity(e) {
|
|
3820
3986
|
if (e.entityCategory === "config") return SECTION_CONFIGURATION;
|
|
3821
|
-
if (e.entityCategory === "diagnostic") return SECTION_DIAGNOSTIC;
|
|
3987
|
+
if (e.entityCategory === "diagnostic" || e.foldedInto !== void 0) return SECTION_DIAGNOSTIC;
|
|
3822
3988
|
return SECTION_CONTROLS;
|
|
3823
3989
|
}
|
|
3824
3990
|
/**
|
|
@@ -3871,7 +4037,16 @@ var haEntitySchema = object({
|
|
|
3871
4037
|
dateTimeFormat: EnumSensorDateTimeFormatSchema.optional(),
|
|
3872
4038
|
/** HA `entity_category` — `config` / `diagnostic` / absent (a normal
|
|
3873
4039
|
* control). Drives the accordion section the child renders in. */
|
|
3874
|
-
entityCategory: _enum(["config", "diagnostic"]).optional()
|
|
4040
|
+
entityCategory: _enum(["config", "diagnostic"]).optional(),
|
|
4041
|
+
/** Sibling meter sensors this entity reads as its own power-meter (D951). */
|
|
4042
|
+
meterEntities: object({
|
|
4043
|
+
watts: string().optional(),
|
|
4044
|
+
kwhTotal: string().optional(),
|
|
4045
|
+
volts: string().optional(),
|
|
4046
|
+
amps: string().optional()
|
|
4047
|
+
}).optional(),
|
|
4048
|
+
/** Set on a meter sensor folded into another entity: sectioned as Diagnostic. */
|
|
4049
|
+
foldedInto: string().optional()
|
|
3875
4050
|
});
|
|
3876
4051
|
var haContainerSchema = object({
|
|
3877
4052
|
system: literal("homeassistant"),
|
|
@@ -4186,6 +4361,7 @@ var HaContainerDevice = class HaContainerDevice extends BaseDevice {
|
|
|
4186
4361
|
...e.options !== void 0 ? { options: e.options } : {},
|
|
4187
4362
|
...e.features !== void 0 ? { features: e.features } : {},
|
|
4188
4363
|
...e.dateTimeFormat !== void 0 ? { dateTimeFormat: e.dateTimeFormat } : {},
|
|
4364
|
+
...e.meterEntities !== void 0 ? { meterEntities: e.meterEntities } : {},
|
|
4189
4365
|
...e.role !== void 0 ? { role: e.role } : {}
|
|
4190
4366
|
};
|
|
4191
4367
|
const factory = (childCtx) => {
|