@camstack/addon-provider-ecowitt 0.1.4 → 0.1.6
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 +133 -39
- package/dist/addon.mjs +133 -39
- package/package.json +2 -2
package/dist/addon.js
CHANGED
|
@@ -4647,7 +4647,7 @@ function preprocess(fn, schema) {
|
|
|
4647
4647
|
});
|
|
4648
4648
|
}
|
|
4649
4649
|
//#endregion
|
|
4650
|
-
//#region ../types/dist/sleep-
|
|
4650
|
+
//#region ../types/dist/sleep-DVmKHFGi.mjs
|
|
4651
4651
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4652
4652
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4653
4653
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -6447,6 +6447,17 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
6447
6447
|
DeviceRole["HumiditySensor"] = "humidity-sensor";
|
|
6448
6448
|
DeviceRole["AmbientLightSensor"] = "ambient-light-sensor";
|
|
6449
6449
|
DeviceRole["PressureSensor"] = "pressure-sensor";
|
|
6450
|
+
/** Wind speed or direction (weather-station `wind-sensor` cap). */
|
|
6451
|
+
DeviceRole["WindSensor"] = "wind-sensor";
|
|
6452
|
+
/** Rain accumulation or rate (weather-station `rain-sensor` cap). */
|
|
6453
|
+
DeviceRole["RainSensor"] = "rain-sensor";
|
|
6454
|
+
/** UV index (weather-station `uv-sensor` cap). */
|
|
6455
|
+
DeviceRole["UvSensor"] = "uv-sensor";
|
|
6456
|
+
/** Solar irradiance W/m² (weather-station `solar-radiation-sensor` cap).
|
|
6457
|
+
* Distinct from AmbientLightSensor (lux). */
|
|
6458
|
+
DeviceRole["SolarRadiationSensor"] = "solar-radiation-sensor";
|
|
6459
|
+
/** Soil moisture % (garden/weather `soil-moisture-sensor` cap). */
|
|
6460
|
+
DeviceRole["SoilMoistureSensor"] = "soil-moisture-sensor";
|
|
6450
6461
|
DeviceRole["PowerSensor"] = "power-sensor";
|
|
6451
6462
|
DeviceRole["EnergySensor"] = "energy-sensor";
|
|
6452
6463
|
DeviceRole["VoltageSensor"] = "voltage-sensor";
|
|
@@ -10071,15 +10082,18 @@ var humiditySensorCapability = {
|
|
|
10071
10082
|
runtimeState: HumiditySensorStatusSchema
|
|
10072
10083
|
};
|
|
10073
10084
|
/**
|
|
10074
|
-
* Image display cap. Models
|
|
10075
|
-
*
|
|
10085
|
+
* Image display cap. Models a single still image exposed by an integration —
|
|
10086
|
+
* a snapshot, a chart, a generated picture, or a robot's cleaning-map render.
|
|
10076
10087
|
*
|
|
10077
|
-
* Read-only: there are no setters. The provider resolves
|
|
10078
|
-
*
|
|
10079
|
-
*
|
|
10080
|
-
*
|
|
10081
|
-
*
|
|
10082
|
-
*
|
|
10088
|
+
* Read-only: there are no setters. The provider resolves whatever upstream
|
|
10089
|
+
* source it has into an ABSOLUTE URL the browser loads directly:
|
|
10090
|
+
* - HA `image.*` entities → the `entity_picture` signed-token path
|
|
10091
|
+
* (token stays in the query string, so no auth header is needed);
|
|
10092
|
+
* - a Dreame/robot map → the cloud/OSS map-image URL (or an addon
|
|
10093
|
+
* data-plane URL serving the rendered map bytes), exposed as its own
|
|
10094
|
+
* Image child device grouped under the robot's container.
|
|
10095
|
+
* The slice carries that URL plus the upstream last-updated timestamp; the
|
|
10096
|
+
* image changes when the source's last-updated marker changes.
|
|
10083
10097
|
*/
|
|
10084
10098
|
var ImageStatusSchema = object({
|
|
10085
10099
|
/** Absolute signed URL the browser loads directly. Null when the
|
|
@@ -10105,18 +10119,47 @@ var imageCapability = {
|
|
|
10105
10119
|
*/
|
|
10106
10120
|
runtimeState: ImageStatusSchema
|
|
10107
10121
|
};
|
|
10122
|
+
/**
|
|
10123
|
+
* Robotic lawn-mower cap. Models HA `lawn_mower.*` entities — anything
|
|
10124
|
+
* with a mowing lifecycle plus a dock action.
|
|
10125
|
+
*
|
|
10126
|
+
* Activity follows HA's canonical lawn-mower lifecycle: `idle` /
|
|
10127
|
+
* `mowing` / `paused` / `docked` / `error`. `batteryLevel` (0..100) is
|
|
10128
|
+
* nullable — some mowers don't report a battery percentage.
|
|
10129
|
+
*
|
|
10130
|
+
* `startMowing` begins a mowing run, `pause` halts it in place, and
|
|
10131
|
+
* `dock` sends the mower back to its charging station.
|
|
10132
|
+
*/
|
|
10133
|
+
var LawnMowerActivitySchema = _enum([
|
|
10134
|
+
"idle",
|
|
10135
|
+
"mowing",
|
|
10136
|
+
"paused",
|
|
10137
|
+
"docked",
|
|
10138
|
+
"error"
|
|
10139
|
+
]);
|
|
10140
|
+
/** Severity of the current device/error code — info (status), warning, error. */
|
|
10141
|
+
var DeviceCodeSeveritySchema = _enum([
|
|
10142
|
+
"info",
|
|
10143
|
+
"warning",
|
|
10144
|
+
"error"
|
|
10145
|
+
]);
|
|
10108
10146
|
var LawnMowerControlStatusSchema = object({
|
|
10109
10147
|
/** Lifecycle activity of the mower. */
|
|
10110
|
-
activity:
|
|
10111
|
-
"idle",
|
|
10112
|
-
"mowing",
|
|
10113
|
-
"paused",
|
|
10114
|
-
"docked",
|
|
10115
|
-
"error"
|
|
10116
|
-
]),
|
|
10148
|
+
activity: LawnMowerActivitySchema,
|
|
10117
10149
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
10118
10150
|
* reading. */
|
|
10119
10151
|
batteryLevel: number().min(0).max(100).nullable(),
|
|
10152
|
+
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
10153
|
+
* task is active / progress is unavailable. */
|
|
10154
|
+
progressPercent: number().min(0).max(100).nullable(),
|
|
10155
|
+
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
10156
|
+
* or null when unknown. */
|
|
10157
|
+
currentCode: number().nullable(),
|
|
10158
|
+
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
10159
|
+
currentCodeLabel: string().nullable(),
|
|
10160
|
+
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
10161
|
+
* attention; `info` is normal status. */
|
|
10162
|
+
severity: DeviceCodeSeveritySchema,
|
|
10120
10163
|
/** Ms epoch when the slice was last updated. */
|
|
10121
10164
|
lastChangedAt: number()
|
|
10122
10165
|
});
|
|
@@ -12342,6 +12385,7 @@ var VacuumStateSchema = _enum([
|
|
|
12342
12385
|
"paused",
|
|
12343
12386
|
"returning",
|
|
12344
12387
|
"docked",
|
|
12388
|
+
"drying",
|
|
12345
12389
|
"error"
|
|
12346
12390
|
]);
|
|
12347
12391
|
/**
|
|
@@ -12380,6 +12424,12 @@ var VacuumControlStatusSchema = object({
|
|
|
12380
12424
|
detergent: TankStatusSchema.nullable(),
|
|
12381
12425
|
/** Dust bin. Null when the hardware has no dust bin. */
|
|
12382
12426
|
dustBin: TankStatusSchema.nullable(),
|
|
12427
|
+
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
12428
|
+
progressPercent: number().min(0).max(100).nullable(),
|
|
12429
|
+
/** Current error code (0 / null = no error). */
|
|
12430
|
+
errorCode: number().nullable(),
|
|
12431
|
+
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
12432
|
+
errorLabel: string().nullable(),
|
|
12383
12433
|
/** Ms epoch when the slice was last updated. */
|
|
12384
12434
|
lastChangedAt: number()
|
|
12385
12435
|
});
|
|
@@ -16895,6 +16945,9 @@ method(object({
|
|
|
16895
16945
|
}), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
|
|
16896
16946
|
kind: "mutation",
|
|
16897
16947
|
auth: "admin"
|
|
16948
|
+
}), method(ResyncInputSchema, ResyncResultSchema, {
|
|
16949
|
+
kind: "mutation",
|
|
16950
|
+
auth: "admin"
|
|
16898
16951
|
}), method(object({
|
|
16899
16952
|
deviceId: number(),
|
|
16900
16953
|
key: string(),
|
|
@@ -20657,6 +20710,12 @@ Object.freeze({
|
|
|
20657
20710
|
addonId: null,
|
|
20658
20711
|
access: "create"
|
|
20659
20712
|
},
|
|
20713
|
+
"deviceManager.adoptionResync": {
|
|
20714
|
+
capName: "device-manager",
|
|
20715
|
+
capScope: "system",
|
|
20716
|
+
addonId: null,
|
|
20717
|
+
access: "create"
|
|
20718
|
+
},
|
|
20660
20719
|
"deviceManager.allocateDeviceId": {
|
|
20661
20720
|
capName: "device-manager",
|
|
20662
20721
|
capScope: "system",
|
|
@@ -44565,7 +44624,8 @@ function classifyKey(key) {
|
|
|
44565
44624
|
const hex = lookupHexId(key);
|
|
44566
44625
|
if (hex !== void 0) return {
|
|
44567
44626
|
kind: "measurement",
|
|
44568
|
-
quantity: hex.quantity
|
|
44627
|
+
quantity: hex.quantity,
|
|
44628
|
+
name: hex.name
|
|
44569
44629
|
};
|
|
44570
44630
|
const named = Object.prototype.hasOwnProperty.call(NAMED_QUANTITY, key) ? NAMED_QUANTITY[key] : void 0;
|
|
44571
44631
|
if (named !== void 0) return {
|
|
@@ -44601,6 +44661,7 @@ var Station = class {
|
|
|
44601
44661
|
...owner !== void 0 ? { hardwareId: owner } : {},
|
|
44602
44662
|
...info?.model ? { model: info.model } : {},
|
|
44603
44663
|
...info?.channel !== void 0 ? { channel: info.channel } : {},
|
|
44664
|
+
...cls.name !== void 0 ? { name: cls.name } : {},
|
|
44604
44665
|
quantity: cls.quantity,
|
|
44605
44666
|
value: r.value,
|
|
44606
44667
|
unit: r.unit,
|
|
@@ -44634,6 +44695,7 @@ var Station = class {
|
|
|
44634
44695
|
this.upsert(id, {
|
|
44635
44696
|
id,
|
|
44636
44697
|
...r.channel !== void 0 ? { channel: r.channel } : {},
|
|
44698
|
+
...cls.name !== void 0 ? { name: cls.name } : {},
|
|
44637
44699
|
quantity: cls.quantity,
|
|
44638
44700
|
value: r.value,
|
|
44639
44701
|
unit: r.unit,
|
|
@@ -45597,27 +45659,46 @@ function buildEcowittBrokerProvider(deps) {
|
|
|
45597
45659
|
//#endregion
|
|
45598
45660
|
//#region src/ecowitt-mapping.ts
|
|
45599
45661
|
/**
|
|
45600
|
-
* Map an Ecowitt quantity to its camstack
|
|
45662
|
+
* Map an Ecowitt quantity to its camstack sensor cap. Pure, total.
|
|
45601
45663
|
*
|
|
45602
|
-
*
|
|
45603
|
-
*
|
|
45604
|
-
*
|
|
45664
|
+
* Only temperature/humidity/pressure get a dedicated typed cap (the thermostat
|
|
45665
|
+
* surface consumes them); every other quantity rides `numeric-sensor` and is
|
|
45666
|
+
* typed by its {@link roleForQuantity} role instead. Accepts a plain `string`
|
|
45667
|
+
* so a persisted config value flows through without a cast — any unrecognised
|
|
45668
|
+
* string falls to `numeric-sensor`, the same safe default the tail uses.
|
|
45605
45669
|
*/
|
|
45606
45670
|
function capForQuantity(quantity) {
|
|
45607
45671
|
switch (quantity) {
|
|
45608
45672
|
case "temperature": return "temperature-sensor";
|
|
45609
45673
|
case "humidity": return "humidity-sensor";
|
|
45610
45674
|
case "pressure": return "pressure-sensor";
|
|
45611
|
-
|
|
45612
|
-
|
|
45675
|
+
default: return "numeric-sensor";
|
|
45676
|
+
}
|
|
45677
|
+
}
|
|
45678
|
+
/**
|
|
45679
|
+
* Map an Ecowitt quantity to the semantic {@link DeviceRole} that drives the
|
|
45680
|
+
* child's icon/label (and, in future, its canonical unit). Pure, total — an
|
|
45681
|
+
* unrecognised quantity falls to {@link DeviceRole.NumericSensor}.
|
|
45682
|
+
*
|
|
45683
|
+
* This is where weather metrics get their typing: wind/rain/uv/irradiance ride
|
|
45684
|
+
* the generic `numeric-sensor` cap but carry a typed role, so the UI renders a
|
|
45685
|
+
* wind / rain / UV / solar glyph instead of a bare hash.
|
|
45686
|
+
*/
|
|
45687
|
+
function roleForQuantity(quantity) {
|
|
45688
|
+
switch (quantity) {
|
|
45689
|
+
case "temperature": return DeviceRole.TemperatureSensor;
|
|
45690
|
+
case "humidity": return DeviceRole.HumiditySensor;
|
|
45691
|
+
case "pressure": return DeviceRole.PressureSensor;
|
|
45613
45692
|
case "wind_speed":
|
|
45614
|
-
case "wind_direction":
|
|
45693
|
+
case "wind_direction": return DeviceRole.WindSensor;
|
|
45615
45694
|
case "precipitation":
|
|
45616
|
-
case "precipitation_rate":
|
|
45695
|
+
case "precipitation_rate": return DeviceRole.RainSensor;
|
|
45696
|
+
case "uv": return DeviceRole.UvSensor;
|
|
45697
|
+
case "irradiance": return DeviceRole.SolarRadiationSensor;
|
|
45617
45698
|
case "lightning_distance":
|
|
45618
45699
|
case "lightning_count":
|
|
45619
|
-
case "vpd": return
|
|
45620
|
-
default: return
|
|
45700
|
+
case "vpd": return DeviceRole.NumericSensor;
|
|
45701
|
+
default: return DeviceRole.NumericSensor;
|
|
45621
45702
|
}
|
|
45622
45703
|
}
|
|
45623
45704
|
/**
|
|
@@ -45640,15 +45721,18 @@ function valueFieldForCap(cap) {
|
|
|
45640
45721
|
}
|
|
45641
45722
|
}
|
|
45642
45723
|
/**
|
|
45643
|
-
* Build a human label for a sensor
|
|
45644
|
-
*
|
|
45724
|
+
* Build a human label for a sensor. Prefers the SPECIFIC measurement `name` when
|
|
45725
|
+
* the library resolved one (e.g. "Outdoor Temperature", "Dewpoint Temperature",
|
|
45726
|
+
* "Wind Gust") so same-quantity sensors are distinguishable; otherwise falls back
|
|
45727
|
+
* to the quantity label. A channel suffix is appended when present
|
|
45728
|
+
* (`temperature` + channel 2 → `Temperature CH2`).
|
|
45645
45729
|
*
|
|
45646
45730
|
* Accepts a plain `string` (not just {@link Quantity}) so a persisted config
|
|
45647
45731
|
* value flows through without a cast; an unknown quantity falls back to its raw
|
|
45648
45732
|
* string label.
|
|
45649
45733
|
*/
|
|
45650
|
-
function sensorLabel(quantity, channel) {
|
|
45651
|
-
const base = isKnownQuantity(quantity) ? QUANTITY_LABEL[quantity] : quantity;
|
|
45734
|
+
function sensorLabel(quantity, channel, name) {
|
|
45735
|
+
const base = name !== void 0 && name.length > 0 ? name : isKnownQuantity(quantity) ? QUANTITY_LABEL[quantity] : quantity;
|
|
45652
45736
|
return channel !== void 0 ? `${base} CH${channel}` : base;
|
|
45653
45737
|
}
|
|
45654
45738
|
/** Type guard: is `q` one of the known {@link Quantity} union members? */
|
|
@@ -45682,7 +45766,7 @@ var QUANTITY_LABEL = {
|
|
|
45682
45766
|
function buildEcowittGatewayCandidate(input) {
|
|
45683
45767
|
const children = input.sensors.map((sensor) => ({
|
|
45684
45768
|
childNativeId: sensor.id,
|
|
45685
|
-
name: sensorLabel(sensor.quantity, sensor.channel),
|
|
45769
|
+
name: sensorLabel(sensor.quantity, sensor.channel, sensor.name),
|
|
45686
45770
|
type: DeviceType.Sensor,
|
|
45687
45771
|
status: "online",
|
|
45688
45772
|
metadata: sensor.model !== void 0 ? { model: sensor.model } : {},
|
|
@@ -46157,7 +46241,8 @@ function clampForCap(cap, value) {
|
|
|
46157
46241
|
var ecowittSensorEntrySchema = object({
|
|
46158
46242
|
sensorId: string(),
|
|
46159
46243
|
quantity: string(),
|
|
46160
|
-
channel: number().optional()
|
|
46244
|
+
channel: number().optional(),
|
|
46245
|
+
name: string().optional()
|
|
46161
46246
|
});
|
|
46162
46247
|
/**
|
|
46163
46248
|
* Persisted config for an Ecowitt gateway Container. `brokerId` selects the
|
|
@@ -46199,7 +46284,8 @@ var EcowittContainerDevice = class extends BaseDevice {
|
|
|
46199
46284
|
return this.config.values.sensors.map((sensor) => {
|
|
46200
46285
|
const meta = {
|
|
46201
46286
|
type: DeviceType.Sensor,
|
|
46202
|
-
|
|
46287
|
+
role: roleForQuantity(sensor.quantity),
|
|
46288
|
+
name: sensorLabel(sensor.quantity, sensor.channel, sensor.name),
|
|
46203
46289
|
linkDeviceId: this.id,
|
|
46204
46290
|
...this.integrationId !== void 0 ? { integrationId: this.integrationId } : {}
|
|
46205
46291
|
};
|
|
@@ -46354,7 +46440,8 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46354
46440
|
const nextSensors = sensors.map((s) => ({
|
|
46355
46441
|
sensorId: s.id,
|
|
46356
46442
|
quantity: s.quantity,
|
|
46357
|
-
...s.channel !== void 0 ? { channel: s.channel } : {}
|
|
46443
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
46444
|
+
...s.name !== void 0 ? { name: s.name } : {}
|
|
46358
46445
|
}));
|
|
46359
46446
|
const cfg = await devices.loadConfig(containerId).catch(() => ({}));
|
|
46360
46447
|
if (sensorsEqual(Array.isArray(cfg["sensors"]) ? cfg["sensors"] : [], nextSensors)) return;
|
|
@@ -46475,7 +46562,8 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46475
46562
|
sensors: ecowittFacades.getSensors(brokerId).map((s) => ({
|
|
46476
46563
|
sensorId: s.id,
|
|
46477
46564
|
quantity: s.quantity,
|
|
46478
|
-
...s.channel !== void 0 ? { channel: s.channel } : {}
|
|
46565
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
46566
|
+
...s.name !== void 0 ? { name: s.name } : {}
|
|
46479
46567
|
})),
|
|
46480
46568
|
system: "ecowitt",
|
|
46481
46569
|
integrationId,
|
|
@@ -46543,12 +46631,18 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46543
46631
|
/** Structural equality of two persisted sensor lists (order-independent by id). */
|
|
46544
46632
|
function sensorsEqual(a, b) {
|
|
46545
46633
|
if (a.length !== b.length) return false;
|
|
46546
|
-
const
|
|
46634
|
+
const aById = /* @__PURE__ */ new Map();
|
|
46547
46635
|
for (const entry of a) if (typeof entry === "object" && entry !== null && "sensorId" in entry) {
|
|
46548
46636
|
const id = entry.sensorId;
|
|
46549
|
-
if (typeof id === "string")
|
|
46637
|
+
if (typeof id === "string") {
|
|
46638
|
+
const name = "name" in entry && typeof entry.name === "string" ? entry.name : void 0;
|
|
46639
|
+
aById.set(id, name);
|
|
46640
|
+
}
|
|
46641
|
+
}
|
|
46642
|
+
for (const entry of b) {
|
|
46643
|
+
if (!aById.has(entry.sensorId)) return false;
|
|
46644
|
+
if (aById.get(entry.sensorId) !== entry.name) return false;
|
|
46550
46645
|
}
|
|
46551
|
-
for (const entry of b) if (!aIds.has(entry.sensorId)) return false;
|
|
46552
46646
|
return true;
|
|
46553
46647
|
}
|
|
46554
46648
|
//#endregion
|
package/dist/addon.mjs
CHANGED
|
@@ -4648,7 +4648,7 @@ function preprocess(fn, schema) {
|
|
|
4648
4648
|
});
|
|
4649
4649
|
}
|
|
4650
4650
|
//#endregion
|
|
4651
|
-
//#region ../types/dist/sleep-
|
|
4651
|
+
//#region ../types/dist/sleep-DVmKHFGi.mjs
|
|
4652
4652
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4653
4653
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4654
4654
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -6448,6 +6448,17 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
6448
6448
|
DeviceRole["HumiditySensor"] = "humidity-sensor";
|
|
6449
6449
|
DeviceRole["AmbientLightSensor"] = "ambient-light-sensor";
|
|
6450
6450
|
DeviceRole["PressureSensor"] = "pressure-sensor";
|
|
6451
|
+
/** Wind speed or direction (weather-station `wind-sensor` cap). */
|
|
6452
|
+
DeviceRole["WindSensor"] = "wind-sensor";
|
|
6453
|
+
/** Rain accumulation or rate (weather-station `rain-sensor` cap). */
|
|
6454
|
+
DeviceRole["RainSensor"] = "rain-sensor";
|
|
6455
|
+
/** UV index (weather-station `uv-sensor` cap). */
|
|
6456
|
+
DeviceRole["UvSensor"] = "uv-sensor";
|
|
6457
|
+
/** Solar irradiance W/m² (weather-station `solar-radiation-sensor` cap).
|
|
6458
|
+
* Distinct from AmbientLightSensor (lux). */
|
|
6459
|
+
DeviceRole["SolarRadiationSensor"] = "solar-radiation-sensor";
|
|
6460
|
+
/** Soil moisture % (garden/weather `soil-moisture-sensor` cap). */
|
|
6461
|
+
DeviceRole["SoilMoistureSensor"] = "soil-moisture-sensor";
|
|
6451
6462
|
DeviceRole["PowerSensor"] = "power-sensor";
|
|
6452
6463
|
DeviceRole["EnergySensor"] = "energy-sensor";
|
|
6453
6464
|
DeviceRole["VoltageSensor"] = "voltage-sensor";
|
|
@@ -10072,15 +10083,18 @@ var humiditySensorCapability = {
|
|
|
10072
10083
|
runtimeState: HumiditySensorStatusSchema
|
|
10073
10084
|
};
|
|
10074
10085
|
/**
|
|
10075
|
-
* Image display cap. Models
|
|
10076
|
-
*
|
|
10086
|
+
* Image display cap. Models a single still image exposed by an integration —
|
|
10087
|
+
* a snapshot, a chart, a generated picture, or a robot's cleaning-map render.
|
|
10077
10088
|
*
|
|
10078
|
-
* Read-only: there are no setters. The provider resolves
|
|
10079
|
-
*
|
|
10080
|
-
*
|
|
10081
|
-
*
|
|
10082
|
-
*
|
|
10083
|
-
*
|
|
10089
|
+
* Read-only: there are no setters. The provider resolves whatever upstream
|
|
10090
|
+
* source it has into an ABSOLUTE URL the browser loads directly:
|
|
10091
|
+
* - HA `image.*` entities → the `entity_picture` signed-token path
|
|
10092
|
+
* (token stays in the query string, so no auth header is needed);
|
|
10093
|
+
* - a Dreame/robot map → the cloud/OSS map-image URL (or an addon
|
|
10094
|
+
* data-plane URL serving the rendered map bytes), exposed as its own
|
|
10095
|
+
* Image child device grouped under the robot's container.
|
|
10096
|
+
* The slice carries that URL plus the upstream last-updated timestamp; the
|
|
10097
|
+
* image changes when the source's last-updated marker changes.
|
|
10084
10098
|
*/
|
|
10085
10099
|
var ImageStatusSchema = object({
|
|
10086
10100
|
/** Absolute signed URL the browser loads directly. Null when the
|
|
@@ -10106,18 +10120,47 @@ var imageCapability = {
|
|
|
10106
10120
|
*/
|
|
10107
10121
|
runtimeState: ImageStatusSchema
|
|
10108
10122
|
};
|
|
10123
|
+
/**
|
|
10124
|
+
* Robotic lawn-mower cap. Models HA `lawn_mower.*` entities — anything
|
|
10125
|
+
* with a mowing lifecycle plus a dock action.
|
|
10126
|
+
*
|
|
10127
|
+
* Activity follows HA's canonical lawn-mower lifecycle: `idle` /
|
|
10128
|
+
* `mowing` / `paused` / `docked` / `error`. `batteryLevel` (0..100) is
|
|
10129
|
+
* nullable — some mowers don't report a battery percentage.
|
|
10130
|
+
*
|
|
10131
|
+
* `startMowing` begins a mowing run, `pause` halts it in place, and
|
|
10132
|
+
* `dock` sends the mower back to its charging station.
|
|
10133
|
+
*/
|
|
10134
|
+
var LawnMowerActivitySchema = _enum([
|
|
10135
|
+
"idle",
|
|
10136
|
+
"mowing",
|
|
10137
|
+
"paused",
|
|
10138
|
+
"docked",
|
|
10139
|
+
"error"
|
|
10140
|
+
]);
|
|
10141
|
+
/** Severity of the current device/error code — info (status), warning, error. */
|
|
10142
|
+
var DeviceCodeSeveritySchema = _enum([
|
|
10143
|
+
"info",
|
|
10144
|
+
"warning",
|
|
10145
|
+
"error"
|
|
10146
|
+
]);
|
|
10109
10147
|
var LawnMowerControlStatusSchema = object({
|
|
10110
10148
|
/** Lifecycle activity of the mower. */
|
|
10111
|
-
activity:
|
|
10112
|
-
"idle",
|
|
10113
|
-
"mowing",
|
|
10114
|
-
"paused",
|
|
10115
|
-
"docked",
|
|
10116
|
-
"error"
|
|
10117
|
-
]),
|
|
10149
|
+
activity: LawnMowerActivitySchema,
|
|
10118
10150
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
10119
10151
|
* reading. */
|
|
10120
10152
|
batteryLevel: number().min(0).max(100).nullable(),
|
|
10153
|
+
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
10154
|
+
* task is active / progress is unavailable. */
|
|
10155
|
+
progressPercent: number().min(0).max(100).nullable(),
|
|
10156
|
+
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
10157
|
+
* or null when unknown. */
|
|
10158
|
+
currentCode: number().nullable(),
|
|
10159
|
+
/** Human label for {@link currentCode}, or null when undecodable. */
|
|
10160
|
+
currentCodeLabel: string().nullable(),
|
|
10161
|
+
/** Severity of {@link currentCode}. `error` (and often `warning`) warrants UI
|
|
10162
|
+
* attention; `info` is normal status. */
|
|
10163
|
+
severity: DeviceCodeSeveritySchema,
|
|
10121
10164
|
/** Ms epoch when the slice was last updated. */
|
|
10122
10165
|
lastChangedAt: number()
|
|
10123
10166
|
});
|
|
@@ -12343,6 +12386,7 @@ var VacuumStateSchema = _enum([
|
|
|
12343
12386
|
"paused",
|
|
12344
12387
|
"returning",
|
|
12345
12388
|
"docked",
|
|
12389
|
+
"drying",
|
|
12346
12390
|
"error"
|
|
12347
12391
|
]);
|
|
12348
12392
|
/**
|
|
@@ -12381,6 +12425,12 @@ var VacuumControlStatusSchema = object({
|
|
|
12381
12425
|
detergent: TankStatusSchema.nullable(),
|
|
12382
12426
|
/** Dust bin. Null when the hardware has no dust bin. */
|
|
12383
12427
|
dustBin: TankStatusSchema.nullable(),
|
|
12428
|
+
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
12429
|
+
progressPercent: number().min(0).max(100).nullable(),
|
|
12430
|
+
/** Current error code (0 / null = no error). */
|
|
12431
|
+
errorCode: number().nullable(),
|
|
12432
|
+
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
12433
|
+
errorLabel: string().nullable(),
|
|
12384
12434
|
/** Ms epoch when the slice was last updated. */
|
|
12385
12435
|
lastChangedAt: number()
|
|
12386
12436
|
});
|
|
@@ -16896,6 +16946,9 @@ method(object({
|
|
|
16896
16946
|
}), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
|
|
16897
16947
|
kind: "mutation",
|
|
16898
16948
|
auth: "admin"
|
|
16949
|
+
}), method(ResyncInputSchema, ResyncResultSchema, {
|
|
16950
|
+
kind: "mutation",
|
|
16951
|
+
auth: "admin"
|
|
16899
16952
|
}), method(object({
|
|
16900
16953
|
deviceId: number(),
|
|
16901
16954
|
key: string(),
|
|
@@ -20658,6 +20711,12 @@ Object.freeze({
|
|
|
20658
20711
|
addonId: null,
|
|
20659
20712
|
access: "create"
|
|
20660
20713
|
},
|
|
20714
|
+
"deviceManager.adoptionResync": {
|
|
20715
|
+
capName: "device-manager",
|
|
20716
|
+
capScope: "system",
|
|
20717
|
+
addonId: null,
|
|
20718
|
+
access: "create"
|
|
20719
|
+
},
|
|
20661
20720
|
"deviceManager.allocateDeviceId": {
|
|
20662
20721
|
capName: "device-manager",
|
|
20663
20722
|
capScope: "system",
|
|
@@ -44566,7 +44625,8 @@ function classifyKey(key) {
|
|
|
44566
44625
|
const hex = lookupHexId(key);
|
|
44567
44626
|
if (hex !== void 0) return {
|
|
44568
44627
|
kind: "measurement",
|
|
44569
|
-
quantity: hex.quantity
|
|
44628
|
+
quantity: hex.quantity,
|
|
44629
|
+
name: hex.name
|
|
44570
44630
|
};
|
|
44571
44631
|
const named = Object.prototype.hasOwnProperty.call(NAMED_QUANTITY, key) ? NAMED_QUANTITY[key] : void 0;
|
|
44572
44632
|
if (named !== void 0) return {
|
|
@@ -44602,6 +44662,7 @@ var Station = class {
|
|
|
44602
44662
|
...owner !== void 0 ? { hardwareId: owner } : {},
|
|
44603
44663
|
...info?.model ? { model: info.model } : {},
|
|
44604
44664
|
...info?.channel !== void 0 ? { channel: info.channel } : {},
|
|
44665
|
+
...cls.name !== void 0 ? { name: cls.name } : {},
|
|
44605
44666
|
quantity: cls.quantity,
|
|
44606
44667
|
value: r.value,
|
|
44607
44668
|
unit: r.unit,
|
|
@@ -44635,6 +44696,7 @@ var Station = class {
|
|
|
44635
44696
|
this.upsert(id, {
|
|
44636
44697
|
id,
|
|
44637
44698
|
...r.channel !== void 0 ? { channel: r.channel } : {},
|
|
44699
|
+
...cls.name !== void 0 ? { name: cls.name } : {},
|
|
44638
44700
|
quantity: cls.quantity,
|
|
44639
44701
|
value: r.value,
|
|
44640
44702
|
unit: r.unit,
|
|
@@ -45598,27 +45660,46 @@ function buildEcowittBrokerProvider(deps) {
|
|
|
45598
45660
|
//#endregion
|
|
45599
45661
|
//#region src/ecowitt-mapping.ts
|
|
45600
45662
|
/**
|
|
45601
|
-
* Map an Ecowitt quantity to its camstack
|
|
45663
|
+
* Map an Ecowitt quantity to its camstack sensor cap. Pure, total.
|
|
45602
45664
|
*
|
|
45603
|
-
*
|
|
45604
|
-
*
|
|
45605
|
-
*
|
|
45665
|
+
* Only temperature/humidity/pressure get a dedicated typed cap (the thermostat
|
|
45666
|
+
* surface consumes them); every other quantity rides `numeric-sensor` and is
|
|
45667
|
+
* typed by its {@link roleForQuantity} role instead. Accepts a plain `string`
|
|
45668
|
+
* so a persisted config value flows through without a cast — any unrecognised
|
|
45669
|
+
* string falls to `numeric-sensor`, the same safe default the tail uses.
|
|
45606
45670
|
*/
|
|
45607
45671
|
function capForQuantity(quantity) {
|
|
45608
45672
|
switch (quantity) {
|
|
45609
45673
|
case "temperature": return "temperature-sensor";
|
|
45610
45674
|
case "humidity": return "humidity-sensor";
|
|
45611
45675
|
case "pressure": return "pressure-sensor";
|
|
45612
|
-
|
|
45613
|
-
|
|
45676
|
+
default: return "numeric-sensor";
|
|
45677
|
+
}
|
|
45678
|
+
}
|
|
45679
|
+
/**
|
|
45680
|
+
* Map an Ecowitt quantity to the semantic {@link DeviceRole} that drives the
|
|
45681
|
+
* child's icon/label (and, in future, its canonical unit). Pure, total — an
|
|
45682
|
+
* unrecognised quantity falls to {@link DeviceRole.NumericSensor}.
|
|
45683
|
+
*
|
|
45684
|
+
* This is where weather metrics get their typing: wind/rain/uv/irradiance ride
|
|
45685
|
+
* the generic `numeric-sensor` cap but carry a typed role, so the UI renders a
|
|
45686
|
+
* wind / rain / UV / solar glyph instead of a bare hash.
|
|
45687
|
+
*/
|
|
45688
|
+
function roleForQuantity(quantity) {
|
|
45689
|
+
switch (quantity) {
|
|
45690
|
+
case "temperature": return DeviceRole.TemperatureSensor;
|
|
45691
|
+
case "humidity": return DeviceRole.HumiditySensor;
|
|
45692
|
+
case "pressure": return DeviceRole.PressureSensor;
|
|
45614
45693
|
case "wind_speed":
|
|
45615
|
-
case "wind_direction":
|
|
45694
|
+
case "wind_direction": return DeviceRole.WindSensor;
|
|
45616
45695
|
case "precipitation":
|
|
45617
|
-
case "precipitation_rate":
|
|
45696
|
+
case "precipitation_rate": return DeviceRole.RainSensor;
|
|
45697
|
+
case "uv": return DeviceRole.UvSensor;
|
|
45698
|
+
case "irradiance": return DeviceRole.SolarRadiationSensor;
|
|
45618
45699
|
case "lightning_distance":
|
|
45619
45700
|
case "lightning_count":
|
|
45620
|
-
case "vpd": return
|
|
45621
|
-
default: return
|
|
45701
|
+
case "vpd": return DeviceRole.NumericSensor;
|
|
45702
|
+
default: return DeviceRole.NumericSensor;
|
|
45622
45703
|
}
|
|
45623
45704
|
}
|
|
45624
45705
|
/**
|
|
@@ -45641,15 +45722,18 @@ function valueFieldForCap(cap) {
|
|
|
45641
45722
|
}
|
|
45642
45723
|
}
|
|
45643
45724
|
/**
|
|
45644
|
-
* Build a human label for a sensor
|
|
45645
|
-
*
|
|
45725
|
+
* Build a human label for a sensor. Prefers the SPECIFIC measurement `name` when
|
|
45726
|
+
* the library resolved one (e.g. "Outdoor Temperature", "Dewpoint Temperature",
|
|
45727
|
+
* "Wind Gust") so same-quantity sensors are distinguishable; otherwise falls back
|
|
45728
|
+
* to the quantity label. A channel suffix is appended when present
|
|
45729
|
+
* (`temperature` + channel 2 → `Temperature CH2`).
|
|
45646
45730
|
*
|
|
45647
45731
|
* Accepts a plain `string` (not just {@link Quantity}) so a persisted config
|
|
45648
45732
|
* value flows through without a cast; an unknown quantity falls back to its raw
|
|
45649
45733
|
* string label.
|
|
45650
45734
|
*/
|
|
45651
|
-
function sensorLabel(quantity, channel) {
|
|
45652
|
-
const base = isKnownQuantity(quantity) ? QUANTITY_LABEL[quantity] : quantity;
|
|
45735
|
+
function sensorLabel(quantity, channel, name) {
|
|
45736
|
+
const base = name !== void 0 && name.length > 0 ? name : isKnownQuantity(quantity) ? QUANTITY_LABEL[quantity] : quantity;
|
|
45653
45737
|
return channel !== void 0 ? `${base} CH${channel}` : base;
|
|
45654
45738
|
}
|
|
45655
45739
|
/** Type guard: is `q` one of the known {@link Quantity} union members? */
|
|
@@ -45683,7 +45767,7 @@ var QUANTITY_LABEL = {
|
|
|
45683
45767
|
function buildEcowittGatewayCandidate(input) {
|
|
45684
45768
|
const children = input.sensors.map((sensor) => ({
|
|
45685
45769
|
childNativeId: sensor.id,
|
|
45686
|
-
name: sensorLabel(sensor.quantity, sensor.channel),
|
|
45770
|
+
name: sensorLabel(sensor.quantity, sensor.channel, sensor.name),
|
|
45687
45771
|
type: DeviceType.Sensor,
|
|
45688
45772
|
status: "online",
|
|
45689
45773
|
metadata: sensor.model !== void 0 ? { model: sensor.model } : {},
|
|
@@ -46158,7 +46242,8 @@ function clampForCap(cap, value) {
|
|
|
46158
46242
|
var ecowittSensorEntrySchema = object({
|
|
46159
46243
|
sensorId: string(),
|
|
46160
46244
|
quantity: string(),
|
|
46161
|
-
channel: number().optional()
|
|
46245
|
+
channel: number().optional(),
|
|
46246
|
+
name: string().optional()
|
|
46162
46247
|
});
|
|
46163
46248
|
/**
|
|
46164
46249
|
* Persisted config for an Ecowitt gateway Container. `brokerId` selects the
|
|
@@ -46200,7 +46285,8 @@ var EcowittContainerDevice = class extends BaseDevice {
|
|
|
46200
46285
|
return this.config.values.sensors.map((sensor) => {
|
|
46201
46286
|
const meta = {
|
|
46202
46287
|
type: DeviceType.Sensor,
|
|
46203
|
-
|
|
46288
|
+
role: roleForQuantity(sensor.quantity),
|
|
46289
|
+
name: sensorLabel(sensor.quantity, sensor.channel, sensor.name),
|
|
46204
46290
|
linkDeviceId: this.id,
|
|
46205
46291
|
...this.integrationId !== void 0 ? { integrationId: this.integrationId } : {}
|
|
46206
46292
|
};
|
|
@@ -46355,7 +46441,8 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46355
46441
|
const nextSensors = sensors.map((s) => ({
|
|
46356
46442
|
sensorId: s.id,
|
|
46357
46443
|
quantity: s.quantity,
|
|
46358
|
-
...s.channel !== void 0 ? { channel: s.channel } : {}
|
|
46444
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
46445
|
+
...s.name !== void 0 ? { name: s.name } : {}
|
|
46359
46446
|
}));
|
|
46360
46447
|
const cfg = await devices.loadConfig(containerId).catch(() => ({}));
|
|
46361
46448
|
if (sensorsEqual(Array.isArray(cfg["sensors"]) ? cfg["sensors"] : [], nextSensors)) return;
|
|
@@ -46476,7 +46563,8 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46476
46563
|
sensors: ecowittFacades.getSensors(brokerId).map((s) => ({
|
|
46477
46564
|
sensorId: s.id,
|
|
46478
46565
|
quantity: s.quantity,
|
|
46479
|
-
...s.channel !== void 0 ? { channel: s.channel } : {}
|
|
46566
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
46567
|
+
...s.name !== void 0 ? { name: s.name } : {}
|
|
46480
46568
|
})),
|
|
46481
46569
|
system: "ecowitt",
|
|
46482
46570
|
integrationId,
|
|
@@ -46544,12 +46632,18 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46544
46632
|
/** Structural equality of two persisted sensor lists (order-independent by id). */
|
|
46545
46633
|
function sensorsEqual(a, b) {
|
|
46546
46634
|
if (a.length !== b.length) return false;
|
|
46547
|
-
const
|
|
46635
|
+
const aById = /* @__PURE__ */ new Map();
|
|
46548
46636
|
for (const entry of a) if (typeof entry === "object" && entry !== null && "sensorId" in entry) {
|
|
46549
46637
|
const id = entry.sensorId;
|
|
46550
|
-
if (typeof id === "string")
|
|
46638
|
+
if (typeof id === "string") {
|
|
46639
|
+
const name = "name" in entry && typeof entry.name === "string" ? entry.name : void 0;
|
|
46640
|
+
aById.set(id, name);
|
|
46641
|
+
}
|
|
46642
|
+
}
|
|
46643
|
+
for (const entry of b) {
|
|
46644
|
+
if (!aById.has(entry.sensorId)) return false;
|
|
46645
|
+
if (aById.get(entry.sensorId) !== entry.name) return false;
|
|
46551
46646
|
}
|
|
46552
|
-
for (const entry of b) if (!aIds.has(entry.sensorId)) return false;
|
|
46553
46647
|
return true;
|
|
46554
46648
|
}
|
|
46555
46649
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-ecowitt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Ecowitt weather-station device-provider addon for CamStack — wraps the @apocaliss92/nodewitt local-poll / push client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"publish": "npm publish --access public"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@apocaliss92/nodewitt": "^1.
|
|
74
|
+
"@apocaliss92/nodewitt": "^1.2.0",
|
|
75
75
|
"@camstack/types": "*",
|
|
76
76
|
"typescript": "~6.0.3",
|
|
77
77
|
"vite": "^8.0.11",
|