@camstack/addon-provider-ecowitt 0.2.153 → 0.2.154
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 +6 -0
- package/dist/addon.js +228 -104
- package/dist/addon.mjs +228 -104
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.154] - 2026-11-01
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Ecowitt: weather-station and CH1 sensors are no longer duplicated under the gateway container; existing duplicates are removed on the next reading
|
|
15
|
+
|
|
10
16
|
## [0.2.153] - 2026-11-01
|
|
11
17
|
|
|
12
18
|
### Changed
|
package/dist/addon.js
CHANGED
|
@@ -95229,6 +95229,73 @@ function deviceGroupForSensor(category, channel) {
|
|
|
95229
95229
|
return "gateway";
|
|
95230
95230
|
}
|
|
95231
95231
|
//#endregion
|
|
95232
|
+
//#region src/ecowitt-sensor-groups.ts
|
|
95233
|
+
/** Resolve `deviceGroup` from a persisted container config (defaults to 'gateway'). */
|
|
95234
|
+
function configDeviceGroup(config) {
|
|
95235
|
+
const g = config["deviceGroup"];
|
|
95236
|
+
return ECOWITT_DEVICE_GROUPS.find((group) => group === g) ?? "gateway";
|
|
95237
|
+
}
|
|
95238
|
+
/** Build the persisted sensor entry (incl. self-describing category) from a live Sensor. */
|
|
95239
|
+
function sensorToEntry(s) {
|
|
95240
|
+
return {
|
|
95241
|
+
sensorId: s.id,
|
|
95242
|
+
quantity: s.quantity,
|
|
95243
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
95244
|
+
...s.name !== void 0 ? { name: s.name } : {},
|
|
95245
|
+
...s.category !== void 0 ? { category: s.category } : {}
|
|
95246
|
+
};
|
|
95247
|
+
}
|
|
95248
|
+
/** Bucket live sensors into their device group. */
|
|
95249
|
+
function bucketSensorsByGroup(sensors) {
|
|
95250
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
95251
|
+
for (const group of ECOWITT_DEVICE_GROUPS) byGroup.set(group, []);
|
|
95252
|
+
for (const s of sensors) {
|
|
95253
|
+
const group = deviceGroupForSensor(s.category, s.channel);
|
|
95254
|
+
byGroup.get(group)?.push(sensorToEntry(s));
|
|
95255
|
+
}
|
|
95256
|
+
return byGroup;
|
|
95257
|
+
}
|
|
95258
|
+
/** Structural equality of two persisted sensor lists (order-independent by id + name). */
|
|
95259
|
+
function sensorsEqual(a, b) {
|
|
95260
|
+
if (a.length !== b.length) return false;
|
|
95261
|
+
const aById = /* @__PURE__ */ new Map();
|
|
95262
|
+
for (const entry of a) if (typeof entry === "object" && entry !== null && "sensorId" in entry) {
|
|
95263
|
+
const id = entry.sensorId;
|
|
95264
|
+
if (typeof id === "string") {
|
|
95265
|
+
const name = "name" in entry && typeof entry.name === "string" ? entry.name : void 0;
|
|
95266
|
+
aById.set(id, name);
|
|
95267
|
+
}
|
|
95268
|
+
}
|
|
95269
|
+
for (const entry of b) {
|
|
95270
|
+
if (!aById.has(entry.sensorId)) return false;
|
|
95271
|
+
if (aById.get(entry.sensorId) !== entry.name) return false;
|
|
95272
|
+
}
|
|
95273
|
+
return true;
|
|
95274
|
+
}
|
|
95275
|
+
/** The measurement key of a sensor id: the part after the first ':' (`8E:0x02` gives `0x02`). */
|
|
95276
|
+
function sensorKeyOf(sensorId) {
|
|
95277
|
+
const i = sensorId.indexOf(":");
|
|
95278
|
+
return i < 0 ? sensorId : sensorId.slice(i + 1);
|
|
95279
|
+
}
|
|
95280
|
+
/** The owner prefix of a sensor id (`8E` of `8E:0x02`, `gateway` for a gateway-owned reading). */
|
|
95281
|
+
function sensorOwnerOf(sensorId) {
|
|
95282
|
+
const i = sensorId.indexOf(":");
|
|
95283
|
+
return i < 0 ? "" : sensorId.slice(0, i);
|
|
95284
|
+
}
|
|
95285
|
+
/**
|
|
95286
|
+
* The persisted entries a Container of `group` may spawn as children. An early snapshot had no
|
|
95287
|
+
* model, so nodewitt called a weather-station reading `gateway:<key>`; once the sensor map is
|
|
95288
|
+
* known the same reading is `<hardwareId>:<key>`. An entry is dropped when its live sensor (the
|
|
95289
|
+
* exact id, or for a `gateway:` id the hardware-owned sensor of the same key) belongs to another
|
|
95290
|
+
* group. An entry the live set does not know is kept: unknown is not gone (D49).
|
|
95291
|
+
*/
|
|
95292
|
+
function entriesForGroup(entries, group, live) {
|
|
95293
|
+
return entries.filter((entry) => {
|
|
95294
|
+
const sensor = live.find((s) => s.id === entry.sensorId) ?? (sensorOwnerOf(entry.sensorId) === "gateway" ? live.find((s) => sensorOwnerOf(s.id) !== "gateway" && sensorKeyOf(s.id) === sensorKeyOf(entry.sensorId)) : void 0);
|
|
95295
|
+
return sensor === void 0 || deviceGroupForSensor(sensor.category, sensor.channel) === group;
|
|
95296
|
+
});
|
|
95297
|
+
}
|
|
95298
|
+
//#endregion
|
|
95232
95299
|
//#region src/devices/ecowitt-sensor-device.ts
|
|
95233
95300
|
/**
|
|
95234
95301
|
* Persisted config every Ecowitt sensor child carries. `sensorId` resolves the
|
|
@@ -95566,7 +95633,7 @@ var EcowittContainerDevice = class extends BaseDevice {
|
|
|
95566
95633
|
});
|
|
95567
95634
|
}
|
|
95568
95635
|
getAccessoryChildren() {
|
|
95569
|
-
return this.config.values.sensors.map((sensor) => {
|
|
95636
|
+
return entriesForGroup(this.config.values.sensors, this.config.values.deviceGroup, ecowittFacades.getSensors(this.gatewayId)).map((sensor) => {
|
|
95570
95637
|
const meta = {
|
|
95571
95638
|
type: DeviceType.Sensor,
|
|
95572
95639
|
role: roleForQuantity(sensor.quantity),
|
|
@@ -99413,7 +99480,7 @@ function parseValue(input) {
|
|
|
99413
99480
|
raw
|
|
99414
99481
|
};
|
|
99415
99482
|
}
|
|
99416
|
-
function str(item, field) {
|
|
99483
|
+
function str$1(item, field) {
|
|
99417
99484
|
const v = item[field];
|
|
99418
99485
|
if (typeof v === "string") return v;
|
|
99419
99486
|
if (typeof v === "number") return String(v);
|
|
@@ -99453,7 +99520,7 @@ function makeReading(key, val, unit, forceId) {
|
|
|
99453
99520
|
}
|
|
99454
99521
|
}
|
|
99455
99522
|
function channelOf(item) {
|
|
99456
|
-
const ch = str(item, "channel");
|
|
99523
|
+
const ch = str$1(item, "channel");
|
|
99457
99524
|
return ch && ch.trim() !== "" ? ch.trim() : void 0;
|
|
99458
99525
|
}
|
|
99459
99526
|
function push(out, reading) {
|
|
@@ -99463,8 +99530,8 @@ function decodeChSoil(items, out) {
|
|
|
99463
99530
|
for (const item of items) {
|
|
99464
99531
|
const ch = channelOf(item);
|
|
99465
99532
|
if (ch === void 0) continue;
|
|
99466
|
-
const humidity = stripPercent(str(item, "humidity"));
|
|
99467
|
-
const battery = str(item, "battery");
|
|
99533
|
+
const humidity = stripPercent(str$1(item, "humidity"));
|
|
99534
|
+
const battery = str$1(item, "battery");
|
|
99468
99535
|
if (humidity) push(out, makeReading(`soilmoisture${ch}`, humidity));
|
|
99469
99536
|
if (battery) push(out, makeReading(`soilbatt${ch}`, batteryPercent(battery, true)));
|
|
99470
99537
|
}
|
|
@@ -99473,11 +99540,11 @@ function decodeChEc(items, out) {
|
|
|
99473
99540
|
for (const item of items) {
|
|
99474
99541
|
const ch = channelOf(item);
|
|
99475
99542
|
if (ch === void 0) continue;
|
|
99476
|
-
const humidity = stripPercent(str(item, "humidity"));
|
|
99477
|
-
const temp = str(item, "temp");
|
|
99478
|
-
const unit = str(item, "unit") ?? "C";
|
|
99479
|
-
const ec = str(item, "ec");
|
|
99480
|
-
const battery = str(item, "battery");
|
|
99543
|
+
const humidity = stripPercent(str$1(item, "humidity"));
|
|
99544
|
+
const temp = str$1(item, "temp");
|
|
99545
|
+
const unit = str$1(item, "unit") ?? "C";
|
|
99546
|
+
const ec = str$1(item, "ec");
|
|
99547
|
+
const battery = str$1(item, "battery");
|
|
99481
99548
|
if (humidity) push(out, makeReading(`soilmoisture${ch}`, humidity));
|
|
99482
99549
|
if (temp) push(out, makeReading(`soiltemp${ch}`, temp, unit));
|
|
99483
99550
|
if (ec) push(out, makeReading(`soilec${ch}`, ec));
|
|
@@ -99488,9 +99555,9 @@ function decodeChAisle(items, gatewayTempUnit, out) {
|
|
|
99488
99555
|
for (const item of items) {
|
|
99489
99556
|
const ch = channelOf(item);
|
|
99490
99557
|
if (ch === void 0) continue;
|
|
99491
|
-
const temp = str(item, "temp");
|
|
99492
|
-
const humidity = str(item, "humidity");
|
|
99493
|
-
const battery = str(item, "battery");
|
|
99558
|
+
const temp = str$1(item, "temp");
|
|
99559
|
+
const humidity = str$1(item, "humidity");
|
|
99560
|
+
const battery = str$1(item, "battery");
|
|
99494
99561
|
if (temp && temp !== "None") push(out, makeReading(`temp${ch}f`, temp, gatewayTempUnit));
|
|
99495
99562
|
if (humidity && humidity !== "None") push(out, makeReading(`humidity${ch}`, stripPercent(humidity)));
|
|
99496
99563
|
if (battery && battery !== "None") push(out, makeReading(`batt${ch}`, batteryPercent(battery, true)));
|
|
@@ -99500,8 +99567,8 @@ function decodeChTemp(items, gatewayTempUnit, out) {
|
|
|
99500
99567
|
for (const item of items) {
|
|
99501
99568
|
const ch = channelOf(item);
|
|
99502
99569
|
if (ch === void 0) continue;
|
|
99503
|
-
const temp = str(item, "temp");
|
|
99504
|
-
const battery = str(item, "battery");
|
|
99570
|
+
const temp = str$1(item, "temp");
|
|
99571
|
+
const battery = str$1(item, "battery");
|
|
99505
99572
|
if (temp && temp !== "None") push(out, makeReading(`tf_ch${ch}`, temp, gatewayTempUnit));
|
|
99506
99573
|
if (battery && battery !== "None") push(out, makeReading(`tf_batt${ch}`, batteryPercent(battery, false)));
|
|
99507
99574
|
}
|
|
@@ -99510,11 +99577,11 @@ function decodeChPm25(items, out) {
|
|
|
99510
99577
|
for (const item of items) {
|
|
99511
99578
|
const ch = channelOf(item);
|
|
99512
99579
|
if (ch === void 0) continue;
|
|
99513
|
-
const pm25 = str(item, "pm25") ?? str(item, "PM25");
|
|
99514
|
-
const pm25_24h = str(item, "pm25_avg_24h") ?? str(item, "pm25_24h");
|
|
99515
|
-
const realaqi = str(item, "PM25_RealAQI");
|
|
99516
|
-
const aqi24h = str(item, "PM25_24HAQI");
|
|
99517
|
-
const battery = str(item, "battery");
|
|
99580
|
+
const pm25 = str$1(item, "pm25") ?? str$1(item, "PM25");
|
|
99581
|
+
const pm25_24h = str$1(item, "pm25_avg_24h") ?? str$1(item, "pm25_24h");
|
|
99582
|
+
const realaqi = str$1(item, "PM25_RealAQI");
|
|
99583
|
+
const aqi24h = str$1(item, "PM25_24HAQI");
|
|
99584
|
+
const battery = str$1(item, "battery");
|
|
99518
99585
|
if (pm25 && pm25 !== "None") push(out, makeReading(`pm25_ch${ch}`, pm25));
|
|
99519
99586
|
if (pm25_24h && pm25_24h !== "None") push(out, makeReading(`pm25_avg_24h_ch${ch}`, pm25_24h));
|
|
99520
99587
|
if (realaqi && realaqi !== "None") push(out, makeReading(`pm25_aqi_realtime_ch${ch}`, realaqi));
|
|
@@ -99526,8 +99593,8 @@ function decodeChLeaf(items, out) {
|
|
|
99526
99593
|
for (const item of items) {
|
|
99527
99594
|
const ch = channelOf(item);
|
|
99528
99595
|
if (ch === void 0) continue;
|
|
99529
|
-
const humidity = stripPercent(str(item, "humidity"));
|
|
99530
|
-
const battery = str(item, "battery");
|
|
99596
|
+
const humidity = stripPercent(str$1(item, "humidity"));
|
|
99597
|
+
const battery = str$1(item, "battery");
|
|
99531
99598
|
if (humidity) push(out, makeReading(`leafwetness_ch${ch}`, humidity));
|
|
99532
99599
|
if (battery && battery !== "None") push(out, makeReading(`leaf_batt${ch}`, batteryPercent(battery, false)));
|
|
99533
99600
|
}
|
|
@@ -99536,8 +99603,8 @@ function decodeChLeak(items, out) {
|
|
|
99536
99603
|
for (const item of items) {
|
|
99537
99604
|
const ch = channelOf(item);
|
|
99538
99605
|
if (ch === void 0) continue;
|
|
99539
|
-
const status = str(item, "status");
|
|
99540
|
-
const battery = str(item, "battery");
|
|
99606
|
+
const status = str$1(item, "status");
|
|
99607
|
+
const battery = str$1(item, "battery");
|
|
99541
99608
|
if (status !== void 0 && status !== "") {
|
|
99542
99609
|
const leak = status.trim().toLowerCase() === "normal" ? "0" : "1";
|
|
99543
99610
|
push(out, makeReading(`leak_ch${ch}`, leak));
|
|
@@ -99549,10 +99616,10 @@ function decodeChLds(items, out) {
|
|
|
99549
99616
|
for (const item of items) {
|
|
99550
99617
|
const ch = channelOf(item);
|
|
99551
99618
|
if (ch === void 0) continue;
|
|
99552
|
-
const air = str(item, "air");
|
|
99553
|
-
const depth = str(item, "depth");
|
|
99554
|
-
const voltage = str(item, "voltage");
|
|
99555
|
-
const battery = str(item, "battery");
|
|
99619
|
+
const air = str$1(item, "air");
|
|
99620
|
+
const depth = str$1(item, "depth");
|
|
99621
|
+
const voltage = str$1(item, "voltage");
|
|
99622
|
+
const battery = str$1(item, "battery");
|
|
99556
99623
|
if (air && air !== "None") push(out, makeReading(`lds_air_ch${ch}`, air));
|
|
99557
99624
|
if (depth && depth !== "None") push(out, makeReading(`lds_depth_ch${ch}`, depth));
|
|
99558
99625
|
if (voltage && voltage !== "None") push(out, makeReading(`lds_voltage_ch${ch}`, voltage));
|
|
@@ -99581,23 +99648,23 @@ var CO2_FIELDS = [
|
|
|
99581
99648
|
];
|
|
99582
99649
|
function firstField(item, fields) {
|
|
99583
99650
|
for (const f of fields) {
|
|
99584
|
-
const v = str(item, f);
|
|
99651
|
+
const v = str$1(item, f);
|
|
99585
99652
|
if (v && v !== "None") return v;
|
|
99586
99653
|
}
|
|
99587
99654
|
}
|
|
99588
99655
|
function decodeCo2(items, out) {
|
|
99589
99656
|
const item = items[0];
|
|
99590
99657
|
if (item === void 0) return;
|
|
99591
|
-
const temp = str(item, "temp");
|
|
99592
|
-
const unit = str(item, "unit") ?? "C";
|
|
99658
|
+
const temp = str$1(item, "temp");
|
|
99659
|
+
const unit = str$1(item, "unit") ?? "C";
|
|
99593
99660
|
if (temp) push(out, makeReading(unit === "C" ? "tf_co2c" : "tf_co2", temp, unit));
|
|
99594
|
-
const humidity = stripPercent(str(item, "humidity"));
|
|
99661
|
+
const humidity = stripPercent(str$1(item, "humidity"));
|
|
99595
99662
|
if (humidity) push(out, makeReading("humi_co2", humidity));
|
|
99596
99663
|
for (const [fields, key] of CO2_FIELDS) {
|
|
99597
99664
|
const v = firstField(item, fields);
|
|
99598
99665
|
if (v !== void 0) push(out, makeReading(key, v));
|
|
99599
99666
|
}
|
|
99600
|
-
const battery = str(item, "battery");
|
|
99667
|
+
const battery = str$1(item, "battery");
|
|
99601
99668
|
if (battery && battery !== "None") {
|
|
99602
99669
|
const n = num(battery);
|
|
99603
99670
|
push(out, makeReading("co2_batt", n !== void 0 ? String(Math.min(n * 20, 100)) : battery));
|
|
@@ -99609,12 +99676,12 @@ function push2(out, reading) {
|
|
|
99609
99676
|
}
|
|
99610
99677
|
function decodeCommonList(items, mapper, out) {
|
|
99611
99678
|
for (const item of items) {
|
|
99612
|
-
const id = str(item, "id");
|
|
99613
|
-
const val = str(item, "val");
|
|
99614
|
-
const unit = str(item, "unit");
|
|
99679
|
+
const id = str$1(item, "id");
|
|
99680
|
+
const val = str$1(item, "val");
|
|
99681
|
+
const unit = str$1(item, "unit");
|
|
99615
99682
|
if (id && val !== void 0) push2(out, makeReading(id, val, unit));
|
|
99616
99683
|
if (id === "0x03" && mapper.getHardwareId("wh26batt") !== void 0) {
|
|
99617
|
-
const battery = str(item, "battery");
|
|
99684
|
+
const battery = str$1(item, "battery");
|
|
99618
99685
|
if (battery !== void 0) push2(out, makeReading("wh26batt", battery === "0" ? "100" : "10"));
|
|
99619
99686
|
}
|
|
99620
99687
|
}
|
|
@@ -99622,12 +99689,12 @@ function decodeCommonList(items, mapper, out) {
|
|
|
99622
99689
|
function decodeRain(items, mapper, out) {
|
|
99623
99690
|
const rainHwId = mapper.getHardwareId("wh69batt") ?? mapper.getHardwareId("wh40batt");
|
|
99624
99691
|
for (const item of items) {
|
|
99625
|
-
const id = str(item, "id");
|
|
99626
|
-
const val = str(item, "val");
|
|
99692
|
+
const id = str$1(item, "id");
|
|
99693
|
+
const val = str$1(item, "val");
|
|
99627
99694
|
if (!id || val === void 0) continue;
|
|
99628
|
-
push2(out, makeReading(id, val, str(item, "unit"), rainHwId));
|
|
99695
|
+
push2(out, makeReading(id, val, str$1(item, "unit"), rainHwId));
|
|
99629
99696
|
if (id === "0x13") {
|
|
99630
|
-
const battery = str(item, "battery");
|
|
99697
|
+
const battery = str$1(item, "battery");
|
|
99631
99698
|
if (battery) {
|
|
99632
99699
|
const n = num(battery);
|
|
99633
99700
|
const pct = n !== void 0 && n > 1 ? String(n * 20) : battery === "0" ? "100" : "10";
|
|
@@ -99659,30 +99726,30 @@ function piezoKeysFor(mapper) {
|
|
|
99659
99726
|
function decodePiezo(items, mapper, out) {
|
|
99660
99727
|
const piezoHwId = mapper.getHardwareId("ws85batt") ?? mapper.getHardwareId("ws90batt") ?? mapper.getHardwareId("wh90batt");
|
|
99661
99728
|
for (const item of items) {
|
|
99662
|
-
const id = str(item, "id");
|
|
99663
|
-
const val = str(item, "val");
|
|
99729
|
+
const id = str$1(item, "id");
|
|
99730
|
+
const val = str$1(item, "val");
|
|
99664
99731
|
if (!id || val === void 0) continue;
|
|
99665
|
-
push2(out, makeReading(id, val, str(item, "unit"), piezoHwId));
|
|
99732
|
+
push2(out, makeReading(id, val, str$1(item, "unit"), piezoHwId));
|
|
99666
99733
|
if (id !== "0x13") continue;
|
|
99667
|
-
const battery = str(item, "battery");
|
|
99734
|
+
const battery = str$1(item, "battery");
|
|
99668
99735
|
if (!battery) continue;
|
|
99669
99736
|
const keys = piezoKeysFor(mapper);
|
|
99670
99737
|
const n = num(battery);
|
|
99671
99738
|
push2(out, makeReading(keys.batt, n !== void 0 ? String(n * 20) : battery));
|
|
99672
|
-
const voltage = str(item, "voltage");
|
|
99739
|
+
const voltage = str$1(item, "voltage");
|
|
99673
99740
|
if (voltage) push2(out, makeReading(keys.volt, voltage));
|
|
99674
|
-
const cap = str(item, keys.capField);
|
|
99741
|
+
const cap = str$1(item, keys.capField);
|
|
99675
99742
|
if (cap) push2(out, makeReading(keys.capKey, cap));
|
|
99676
99743
|
}
|
|
99677
99744
|
}
|
|
99678
99745
|
function decodeLightning(items, out) {
|
|
99679
99746
|
const item = items[0];
|
|
99680
99747
|
if (item === void 0) return;
|
|
99681
|
-
const count = str(item, "count");
|
|
99748
|
+
const count = str$1(item, "count");
|
|
99682
99749
|
if (count !== void 0) push2(out, makeReading("lightning_num", count));
|
|
99683
|
-
const distance = str(item, "distance");
|
|
99750
|
+
const distance = str$1(item, "distance");
|
|
99684
99751
|
if (distance !== void 0) push2(out, makeReading("lightning", distance.replace(" km", "").trim()));
|
|
99685
|
-
const battery = str(item, "battery");
|
|
99752
|
+
const battery = str$1(item, "battery");
|
|
99686
99753
|
if (battery !== void 0) {
|
|
99687
99754
|
const n = num(battery);
|
|
99688
99755
|
push2(out, makeReading("wh57batt", n !== void 0 ? String(n * 20) : battery));
|
|
@@ -99691,30 +99758,30 @@ function decodeLightning(items, out) {
|
|
|
99691
99758
|
function decodeWh25(items, out) {
|
|
99692
99759
|
const item = items[0];
|
|
99693
99760
|
if (item === void 0) return;
|
|
99694
|
-
const intemp = str(item, "intemp");
|
|
99695
|
-
const unit = str(item, "unit") ?? "F";
|
|
99761
|
+
const intemp = str$1(item, "intemp");
|
|
99762
|
+
const unit = str$1(item, "unit") ?? "F";
|
|
99696
99763
|
if (intemp !== void 0) push2(out, makeReading("tempinf", intemp, unit));
|
|
99697
|
-
const inhumi = str(item, "inhumi");
|
|
99764
|
+
const inhumi = str$1(item, "inhumi");
|
|
99698
99765
|
if (inhumi !== void 0) push2(out, makeReading("humidityin", stripPercent(inhumi)));
|
|
99699
|
-
const abs = str(item, "abs");
|
|
99766
|
+
const abs = str$1(item, "abs");
|
|
99700
99767
|
if (abs !== void 0) push2(out, makeReading("baromabsin", abs.replace(" hPa", "").trim()));
|
|
99701
|
-
const rel = str(item, "rel");
|
|
99768
|
+
const rel = str$1(item, "rel");
|
|
99702
99769
|
if (rel !== void 0) push2(out, makeReading("baromrelin", rel.replace(" hPa", "").trim()));
|
|
99703
99770
|
}
|
|
99704
99771
|
function decodeSoilCalibration(items, out) {
|
|
99705
99772
|
for (const item of items) {
|
|
99706
|
-
const ch = str(item, "ch");
|
|
99707
|
-
const nowAd = str(item, "nowAd");
|
|
99773
|
+
const ch = str$1(item, "ch");
|
|
99774
|
+
const nowAd = str$1(item, "nowAd");
|
|
99708
99775
|
if (ch && nowAd !== void 0) push2(out, makeReading(`soilad${ch}`, nowAd));
|
|
99709
99776
|
}
|
|
99710
99777
|
}
|
|
99711
99778
|
function decodeLdsConfig(items, out) {
|
|
99712
99779
|
for (const item of items) {
|
|
99713
|
-
const ch = str(item, "ch");
|
|
99780
|
+
const ch = str$1(item, "ch");
|
|
99714
99781
|
if (!ch) continue;
|
|
99715
|
-
const level = str(item, "level");
|
|
99782
|
+
const level = str$1(item, "level");
|
|
99716
99783
|
if (level !== void 0 && level !== "None") push2(out, makeReading(`lds_level_ch${ch}`, level));
|
|
99717
|
-
const totalHeat = str(item, "total_heat");
|
|
99784
|
+
const totalHeat = str$1(item, "total_heat");
|
|
99718
99785
|
if (totalHeat !== void 0 && totalHeat !== "None") push2(out, makeReading(`lds_total_heat_ch${ch}`, totalHeat));
|
|
99719
99786
|
}
|
|
99720
99787
|
}
|
|
@@ -101781,50 +101848,6 @@ function buildEcowittGatewayCandidates(input) {
|
|
|
101781
101848
|
})];
|
|
101782
101849
|
}
|
|
101783
101850
|
//#endregion
|
|
101784
|
-
//#region src/ecowitt-sensor-groups.ts
|
|
101785
|
-
/** Resolve `deviceGroup` from a persisted container config (defaults to 'gateway'). */
|
|
101786
|
-
function configDeviceGroup(config) {
|
|
101787
|
-
const g = config["deviceGroup"];
|
|
101788
|
-
return ECOWITT_DEVICE_GROUPS.find((group) => group === g) ?? "gateway";
|
|
101789
|
-
}
|
|
101790
|
-
/** Build the persisted sensor entry (incl. self-describing category) from a live Sensor. */
|
|
101791
|
-
function sensorToEntry(s) {
|
|
101792
|
-
return {
|
|
101793
|
-
sensorId: s.id,
|
|
101794
|
-
quantity: s.quantity,
|
|
101795
|
-
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
101796
|
-
...s.name !== void 0 ? { name: s.name } : {},
|
|
101797
|
-
...s.category !== void 0 ? { category: s.category } : {}
|
|
101798
|
-
};
|
|
101799
|
-
}
|
|
101800
|
-
/** Bucket live sensors into their device group. */
|
|
101801
|
-
function bucketSensorsByGroup(sensors) {
|
|
101802
|
-
const byGroup = /* @__PURE__ */ new Map();
|
|
101803
|
-
for (const group of ECOWITT_DEVICE_GROUPS) byGroup.set(group, []);
|
|
101804
|
-
for (const s of sensors) {
|
|
101805
|
-
const group = deviceGroupForSensor(s.category, s.channel);
|
|
101806
|
-
byGroup.get(group)?.push(sensorToEntry(s));
|
|
101807
|
-
}
|
|
101808
|
-
return byGroup;
|
|
101809
|
-
}
|
|
101810
|
-
/** Structural equality of two persisted sensor lists (order-independent by id + name). */
|
|
101811
|
-
function sensorsEqual(a, b) {
|
|
101812
|
-
if (a.length !== b.length) return false;
|
|
101813
|
-
const aById = /* @__PURE__ */ new Map();
|
|
101814
|
-
for (const entry of a) if (typeof entry === "object" && entry !== null && "sensorId" in entry) {
|
|
101815
|
-
const id = entry.sensorId;
|
|
101816
|
-
if (typeof id === "string") {
|
|
101817
|
-
const name = "name" in entry && typeof entry.name === "string" ? entry.name : void 0;
|
|
101818
|
-
aById.set(id, name);
|
|
101819
|
-
}
|
|
101820
|
-
}
|
|
101821
|
-
for (const entry of b) {
|
|
101822
|
-
if (!aById.has(entry.sensorId)) return false;
|
|
101823
|
-
if (aById.get(entry.sensorId) !== entry.name) return false;
|
|
101824
|
-
}
|
|
101825
|
-
return true;
|
|
101826
|
-
}
|
|
101827
|
-
//#endregion
|
|
101828
101851
|
//#region src/ecowitt-adoption-provider.ts
|
|
101829
101852
|
var DEVICES_FILTER = {
|
|
101830
101853
|
id: "devices",
|
|
@@ -102128,6 +102151,56 @@ function ecowittCandidates(gateways, known = /* @__PURE__ */ new Set()) {
|
|
|
102128
102151
|
});
|
|
102129
102152
|
}
|
|
102130
102153
|
//#endregion
|
|
102154
|
+
//#region src/ecowitt-duplicate-sensors.ts
|
|
102155
|
+
function str(config, key) {
|
|
102156
|
+
const v = config[key];
|
|
102157
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
102158
|
+
}
|
|
102159
|
+
/**
|
|
102160
|
+
* Sensors minted under the `gateway` group Container (id `gateway:<key>`) that are the SAME reading
|
|
102161
|
+
* as a hardware-owned child (`<hw>:<key>`) of the weather-station / CH1 Container of the same
|
|
102162
|
+
* gateway. Early snapshots carry no
|
|
102163
|
+
* model for a sensor, which nodewitt classifies as `gateway`, so a weather-station reading was
|
|
102164
|
+
* once minted under the gateway Container as well; the live readings reach only the sensor under
|
|
102165
|
+
* its own group. Only a duplicate is returned: a sensor that exists nowhere else is the
|
|
102166
|
+
* gateway's own and is never touched. `scanned` is the number of sensor children seen, so a
|
|
102167
|
+
* caller can tell "nothing to remove" from "children not spawned yet".
|
|
102168
|
+
*/
|
|
102169
|
+
function findDuplicateGatewaySensors(rows) {
|
|
102170
|
+
const containers = /* @__PURE__ */ new Map();
|
|
102171
|
+
for (const row of rows) {
|
|
102172
|
+
if (row.type !== DeviceType.Container || row.config["system"] !== "ecowitt") continue;
|
|
102173
|
+
const gatewayId = str(row.config, "gatewayId");
|
|
102174
|
+
if (gatewayId === null) continue;
|
|
102175
|
+
containers.set(row.id, {
|
|
102176
|
+
gatewayId,
|
|
102177
|
+
gateway: configDeviceGroup(row.config) === "gateway"
|
|
102178
|
+
});
|
|
102179
|
+
}
|
|
102180
|
+
const ownedElsewhere = /* @__PURE__ */ new Set();
|
|
102181
|
+
const underGateway = [];
|
|
102182
|
+
let scanned = 0;
|
|
102183
|
+
for (const row of rows) {
|
|
102184
|
+
if (row.type !== DeviceType.Sensor || row.parentDeviceId === null) continue;
|
|
102185
|
+
const container = containers.get(row.parentDeviceId);
|
|
102186
|
+
const sensorId = str(row.config, "sensorId");
|
|
102187
|
+
if (container === void 0 || sensorId === null) continue;
|
|
102188
|
+
scanned += 1;
|
|
102189
|
+
const key = `${container.gatewayId}\u0000${sensorKeyOf(sensorId)}`;
|
|
102190
|
+
const owner = sensorOwnerOf(sensorId);
|
|
102191
|
+
if (container.gateway) {
|
|
102192
|
+
if (owner === "gateway") underGateway.push({
|
|
102193
|
+
id: row.id,
|
|
102194
|
+
key
|
|
102195
|
+
});
|
|
102196
|
+
} else if (owner !== "gateway" && owner !== "") ownedElsewhere.add(key);
|
|
102197
|
+
}
|
|
102198
|
+
return {
|
|
102199
|
+
duplicates: underGateway.filter((s) => ownedElsewhere.has(s.key)).map((s) => s.id),
|
|
102200
|
+
scanned
|
|
102201
|
+
};
|
|
102202
|
+
}
|
|
102203
|
+
//#endregion
|
|
102131
102204
|
//#region src/ecowitt-reconciler.ts
|
|
102132
102205
|
/**
|
|
102133
102206
|
* Keeps one live client per enabled integration. Devices are removed ONLY on an explicit
|
|
@@ -102581,6 +102654,55 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
102581
102654
|
const devices = this.ctx.kernel.devices;
|
|
102582
102655
|
if (!reg || !devices) return;
|
|
102583
102656
|
const byGroup = bucketSensorsByGroup(sensors);
|
|
102657
|
+
if (await this.reconcileEachContainer(gatewayKey, byGroup) || !this.duplicatesChecked.has(gatewayKey)) await this.removeDuplicateGatewaySensors(gatewayKey);
|
|
102658
|
+
}
|
|
102659
|
+
/** Gateway keys whose duplicate scan already saw spawned sensor children (idempotent anyway). */
|
|
102660
|
+
duplicatesChecked = /* @__PURE__ */ new Set();
|
|
102661
|
+
/**
|
|
102662
|
+
* Idempotent cleanup of the sensors minted under the `gateway` Container that duplicate a
|
|
102663
|
+
* sensor of the weather-station / CH1 Container (see {@link findDuplicateGatewaySensors}).
|
|
102664
|
+
* Goes through the normal device removal (tombstone); a read that failed removes nothing.
|
|
102665
|
+
*/
|
|
102666
|
+
async removeDuplicateGatewaySensors(gatewayKey) {
|
|
102667
|
+
const reg = this.ctx.kernel.deviceRegistry;
|
|
102668
|
+
const devices = this.ctx.kernel.devices;
|
|
102669
|
+
if (!reg || !devices) return;
|
|
102670
|
+
const rows = [];
|
|
102671
|
+
for (const d of reg.getAllForAddon(this.addonId)) {
|
|
102672
|
+
if (d.type !== DeviceType.Container && d.type !== DeviceType.Sensor) continue;
|
|
102673
|
+
const config = await devices.loadConfig(d.id).catch(() => null);
|
|
102674
|
+
if (config === null) return;
|
|
102675
|
+
if (config["system"] !== "ecowitt" || config["gatewayId"] !== gatewayKey) continue;
|
|
102676
|
+
rows.push({
|
|
102677
|
+
id: d.id,
|
|
102678
|
+
type: d.type,
|
|
102679
|
+
parentDeviceId: d.parentDeviceId,
|
|
102680
|
+
config
|
|
102681
|
+
});
|
|
102682
|
+
}
|
|
102683
|
+
const { duplicates, scanned } = findDuplicateGatewaySensors(rows);
|
|
102684
|
+
if (scanned > 0) this.duplicatesChecked.add(gatewayKey);
|
|
102685
|
+
for (const id of duplicates) try {
|
|
102686
|
+
await devices.remove(id);
|
|
102687
|
+
this.ctx.logger.info("Ecowitt: duplicate sensor under the gateway container removed", {
|
|
102688
|
+
tags: { deviceId: id },
|
|
102689
|
+
meta: { gatewayKey }
|
|
102690
|
+
});
|
|
102691
|
+
} catch (err) {
|
|
102692
|
+
this.ctx.logger.warn("Ecowitt: duplicate sensor removal failed", {
|
|
102693
|
+
tags: { deviceId: id },
|
|
102694
|
+
meta: {
|
|
102695
|
+
gatewayKey,
|
|
102696
|
+
error: errMsg(err)
|
|
102697
|
+
}
|
|
102698
|
+
});
|
|
102699
|
+
}
|
|
102700
|
+
}
|
|
102701
|
+
async reconcileEachContainer(gatewayKey, byGroup) {
|
|
102702
|
+
const reg = this.ctx.kernel.deviceRegistry;
|
|
102703
|
+
const devices = this.ctx.kernel.devices;
|
|
102704
|
+
if (!reg || !devices) return false;
|
|
102705
|
+
let changed = false;
|
|
102584
102706
|
for (const d of reg.getAllForAddon(this.addonId)) {
|
|
102585
102707
|
if (d.parentDeviceId !== null || d.type !== DeviceType.Container) continue;
|
|
102586
102708
|
const cfg = await devices.loadConfig(d.id).catch(() => ({}));
|
|
@@ -102588,6 +102710,7 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
102588
102710
|
const nextSensors = byGroup.get(configDeviceGroup(cfg)) ?? [];
|
|
102589
102711
|
if (sensorsEqual(Array.isArray(cfg["sensors"]) ? cfg["sensors"] : [], nextSensors)) continue;
|
|
102590
102712
|
await devices.applyDerivedSpec(d.id, { config: { sensors: nextSensors } });
|
|
102713
|
+
changed = true;
|
|
102591
102714
|
this.ctx.logger.info("Ecowitt: group sensors reconciled", {
|
|
102592
102715
|
tags: { deviceId: d.id },
|
|
102593
102716
|
meta: {
|
|
@@ -102596,6 +102719,7 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
102596
102719
|
}
|
|
102597
102720
|
});
|
|
102598
102721
|
}
|
|
102722
|
+
return changed;
|
|
102599
102723
|
}
|
|
102600
102724
|
async adoptGatewayDevice(integrationId, gatewayKey, name) {
|
|
102601
102725
|
const devices = this.ctx.kernel.devices;
|
package/dist/addon.mjs
CHANGED
|
@@ -95228,6 +95228,73 @@ function deviceGroupForSensor(category, channel) {
|
|
|
95228
95228
|
return "gateway";
|
|
95229
95229
|
}
|
|
95230
95230
|
//#endregion
|
|
95231
|
+
//#region src/ecowitt-sensor-groups.ts
|
|
95232
|
+
/** Resolve `deviceGroup` from a persisted container config (defaults to 'gateway'). */
|
|
95233
|
+
function configDeviceGroup(config) {
|
|
95234
|
+
const g = config["deviceGroup"];
|
|
95235
|
+
return ECOWITT_DEVICE_GROUPS.find((group) => group === g) ?? "gateway";
|
|
95236
|
+
}
|
|
95237
|
+
/** Build the persisted sensor entry (incl. self-describing category) from a live Sensor. */
|
|
95238
|
+
function sensorToEntry(s) {
|
|
95239
|
+
return {
|
|
95240
|
+
sensorId: s.id,
|
|
95241
|
+
quantity: s.quantity,
|
|
95242
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
95243
|
+
...s.name !== void 0 ? { name: s.name } : {},
|
|
95244
|
+
...s.category !== void 0 ? { category: s.category } : {}
|
|
95245
|
+
};
|
|
95246
|
+
}
|
|
95247
|
+
/** Bucket live sensors into their device group. */
|
|
95248
|
+
function bucketSensorsByGroup(sensors) {
|
|
95249
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
95250
|
+
for (const group of ECOWITT_DEVICE_GROUPS) byGroup.set(group, []);
|
|
95251
|
+
for (const s of sensors) {
|
|
95252
|
+
const group = deviceGroupForSensor(s.category, s.channel);
|
|
95253
|
+
byGroup.get(group)?.push(sensorToEntry(s));
|
|
95254
|
+
}
|
|
95255
|
+
return byGroup;
|
|
95256
|
+
}
|
|
95257
|
+
/** Structural equality of two persisted sensor lists (order-independent by id + name). */
|
|
95258
|
+
function sensorsEqual(a, b) {
|
|
95259
|
+
if (a.length !== b.length) return false;
|
|
95260
|
+
const aById = /* @__PURE__ */ new Map();
|
|
95261
|
+
for (const entry of a) if (typeof entry === "object" && entry !== null && "sensorId" in entry) {
|
|
95262
|
+
const id = entry.sensorId;
|
|
95263
|
+
if (typeof id === "string") {
|
|
95264
|
+
const name = "name" in entry && typeof entry.name === "string" ? entry.name : void 0;
|
|
95265
|
+
aById.set(id, name);
|
|
95266
|
+
}
|
|
95267
|
+
}
|
|
95268
|
+
for (const entry of b) {
|
|
95269
|
+
if (!aById.has(entry.sensorId)) return false;
|
|
95270
|
+
if (aById.get(entry.sensorId) !== entry.name) return false;
|
|
95271
|
+
}
|
|
95272
|
+
return true;
|
|
95273
|
+
}
|
|
95274
|
+
/** The measurement key of a sensor id: the part after the first ':' (`8E:0x02` gives `0x02`). */
|
|
95275
|
+
function sensorKeyOf(sensorId) {
|
|
95276
|
+
const i = sensorId.indexOf(":");
|
|
95277
|
+
return i < 0 ? sensorId : sensorId.slice(i + 1);
|
|
95278
|
+
}
|
|
95279
|
+
/** The owner prefix of a sensor id (`8E` of `8E:0x02`, `gateway` for a gateway-owned reading). */
|
|
95280
|
+
function sensorOwnerOf(sensorId) {
|
|
95281
|
+
const i = sensorId.indexOf(":");
|
|
95282
|
+
return i < 0 ? "" : sensorId.slice(0, i);
|
|
95283
|
+
}
|
|
95284
|
+
/**
|
|
95285
|
+
* The persisted entries a Container of `group` may spawn as children. An early snapshot had no
|
|
95286
|
+
* model, so nodewitt called a weather-station reading `gateway:<key>`; once the sensor map is
|
|
95287
|
+
* known the same reading is `<hardwareId>:<key>`. An entry is dropped when its live sensor (the
|
|
95288
|
+
* exact id, or for a `gateway:` id the hardware-owned sensor of the same key) belongs to another
|
|
95289
|
+
* group. An entry the live set does not know is kept: unknown is not gone (D49).
|
|
95290
|
+
*/
|
|
95291
|
+
function entriesForGroup(entries, group, live) {
|
|
95292
|
+
return entries.filter((entry) => {
|
|
95293
|
+
const sensor = live.find((s) => s.id === entry.sensorId) ?? (sensorOwnerOf(entry.sensorId) === "gateway" ? live.find((s) => sensorOwnerOf(s.id) !== "gateway" && sensorKeyOf(s.id) === sensorKeyOf(entry.sensorId)) : void 0);
|
|
95294
|
+
return sensor === void 0 || deviceGroupForSensor(sensor.category, sensor.channel) === group;
|
|
95295
|
+
});
|
|
95296
|
+
}
|
|
95297
|
+
//#endregion
|
|
95231
95298
|
//#region src/devices/ecowitt-sensor-device.ts
|
|
95232
95299
|
/**
|
|
95233
95300
|
* Persisted config every Ecowitt sensor child carries. `sensorId` resolves the
|
|
@@ -95565,7 +95632,7 @@ var EcowittContainerDevice = class extends BaseDevice {
|
|
|
95565
95632
|
});
|
|
95566
95633
|
}
|
|
95567
95634
|
getAccessoryChildren() {
|
|
95568
|
-
return this.config.values.sensors.map((sensor) => {
|
|
95635
|
+
return entriesForGroup(this.config.values.sensors, this.config.values.deviceGroup, ecowittFacades.getSensors(this.gatewayId)).map((sensor) => {
|
|
95569
95636
|
const meta = {
|
|
95570
95637
|
type: DeviceType.Sensor,
|
|
95571
95638
|
role: roleForQuantity(sensor.quantity),
|
|
@@ -99412,7 +99479,7 @@ function parseValue(input) {
|
|
|
99412
99479
|
raw
|
|
99413
99480
|
};
|
|
99414
99481
|
}
|
|
99415
|
-
function str(item, field) {
|
|
99482
|
+
function str$1(item, field) {
|
|
99416
99483
|
const v = item[field];
|
|
99417
99484
|
if (typeof v === "string") return v;
|
|
99418
99485
|
if (typeof v === "number") return String(v);
|
|
@@ -99452,7 +99519,7 @@ function makeReading(key, val, unit, forceId) {
|
|
|
99452
99519
|
}
|
|
99453
99520
|
}
|
|
99454
99521
|
function channelOf(item) {
|
|
99455
|
-
const ch = str(item, "channel");
|
|
99522
|
+
const ch = str$1(item, "channel");
|
|
99456
99523
|
return ch && ch.trim() !== "" ? ch.trim() : void 0;
|
|
99457
99524
|
}
|
|
99458
99525
|
function push(out, reading) {
|
|
@@ -99462,8 +99529,8 @@ function decodeChSoil(items, out) {
|
|
|
99462
99529
|
for (const item of items) {
|
|
99463
99530
|
const ch = channelOf(item);
|
|
99464
99531
|
if (ch === void 0) continue;
|
|
99465
|
-
const humidity = stripPercent(str(item, "humidity"));
|
|
99466
|
-
const battery = str(item, "battery");
|
|
99532
|
+
const humidity = stripPercent(str$1(item, "humidity"));
|
|
99533
|
+
const battery = str$1(item, "battery");
|
|
99467
99534
|
if (humidity) push(out, makeReading(`soilmoisture${ch}`, humidity));
|
|
99468
99535
|
if (battery) push(out, makeReading(`soilbatt${ch}`, batteryPercent(battery, true)));
|
|
99469
99536
|
}
|
|
@@ -99472,11 +99539,11 @@ function decodeChEc(items, out) {
|
|
|
99472
99539
|
for (const item of items) {
|
|
99473
99540
|
const ch = channelOf(item);
|
|
99474
99541
|
if (ch === void 0) continue;
|
|
99475
|
-
const humidity = stripPercent(str(item, "humidity"));
|
|
99476
|
-
const temp = str(item, "temp");
|
|
99477
|
-
const unit = str(item, "unit") ?? "C";
|
|
99478
|
-
const ec = str(item, "ec");
|
|
99479
|
-
const battery = str(item, "battery");
|
|
99542
|
+
const humidity = stripPercent(str$1(item, "humidity"));
|
|
99543
|
+
const temp = str$1(item, "temp");
|
|
99544
|
+
const unit = str$1(item, "unit") ?? "C";
|
|
99545
|
+
const ec = str$1(item, "ec");
|
|
99546
|
+
const battery = str$1(item, "battery");
|
|
99480
99547
|
if (humidity) push(out, makeReading(`soilmoisture${ch}`, humidity));
|
|
99481
99548
|
if (temp) push(out, makeReading(`soiltemp${ch}`, temp, unit));
|
|
99482
99549
|
if (ec) push(out, makeReading(`soilec${ch}`, ec));
|
|
@@ -99487,9 +99554,9 @@ function decodeChAisle(items, gatewayTempUnit, out) {
|
|
|
99487
99554
|
for (const item of items) {
|
|
99488
99555
|
const ch = channelOf(item);
|
|
99489
99556
|
if (ch === void 0) continue;
|
|
99490
|
-
const temp = str(item, "temp");
|
|
99491
|
-
const humidity = str(item, "humidity");
|
|
99492
|
-
const battery = str(item, "battery");
|
|
99557
|
+
const temp = str$1(item, "temp");
|
|
99558
|
+
const humidity = str$1(item, "humidity");
|
|
99559
|
+
const battery = str$1(item, "battery");
|
|
99493
99560
|
if (temp && temp !== "None") push(out, makeReading(`temp${ch}f`, temp, gatewayTempUnit));
|
|
99494
99561
|
if (humidity && humidity !== "None") push(out, makeReading(`humidity${ch}`, stripPercent(humidity)));
|
|
99495
99562
|
if (battery && battery !== "None") push(out, makeReading(`batt${ch}`, batteryPercent(battery, true)));
|
|
@@ -99499,8 +99566,8 @@ function decodeChTemp(items, gatewayTempUnit, out) {
|
|
|
99499
99566
|
for (const item of items) {
|
|
99500
99567
|
const ch = channelOf(item);
|
|
99501
99568
|
if (ch === void 0) continue;
|
|
99502
|
-
const temp = str(item, "temp");
|
|
99503
|
-
const battery = str(item, "battery");
|
|
99569
|
+
const temp = str$1(item, "temp");
|
|
99570
|
+
const battery = str$1(item, "battery");
|
|
99504
99571
|
if (temp && temp !== "None") push(out, makeReading(`tf_ch${ch}`, temp, gatewayTempUnit));
|
|
99505
99572
|
if (battery && battery !== "None") push(out, makeReading(`tf_batt${ch}`, batteryPercent(battery, false)));
|
|
99506
99573
|
}
|
|
@@ -99509,11 +99576,11 @@ function decodeChPm25(items, out) {
|
|
|
99509
99576
|
for (const item of items) {
|
|
99510
99577
|
const ch = channelOf(item);
|
|
99511
99578
|
if (ch === void 0) continue;
|
|
99512
|
-
const pm25 = str(item, "pm25") ?? str(item, "PM25");
|
|
99513
|
-
const pm25_24h = str(item, "pm25_avg_24h") ?? str(item, "pm25_24h");
|
|
99514
|
-
const realaqi = str(item, "PM25_RealAQI");
|
|
99515
|
-
const aqi24h = str(item, "PM25_24HAQI");
|
|
99516
|
-
const battery = str(item, "battery");
|
|
99579
|
+
const pm25 = str$1(item, "pm25") ?? str$1(item, "PM25");
|
|
99580
|
+
const pm25_24h = str$1(item, "pm25_avg_24h") ?? str$1(item, "pm25_24h");
|
|
99581
|
+
const realaqi = str$1(item, "PM25_RealAQI");
|
|
99582
|
+
const aqi24h = str$1(item, "PM25_24HAQI");
|
|
99583
|
+
const battery = str$1(item, "battery");
|
|
99517
99584
|
if (pm25 && pm25 !== "None") push(out, makeReading(`pm25_ch${ch}`, pm25));
|
|
99518
99585
|
if (pm25_24h && pm25_24h !== "None") push(out, makeReading(`pm25_avg_24h_ch${ch}`, pm25_24h));
|
|
99519
99586
|
if (realaqi && realaqi !== "None") push(out, makeReading(`pm25_aqi_realtime_ch${ch}`, realaqi));
|
|
@@ -99525,8 +99592,8 @@ function decodeChLeaf(items, out) {
|
|
|
99525
99592
|
for (const item of items) {
|
|
99526
99593
|
const ch = channelOf(item);
|
|
99527
99594
|
if (ch === void 0) continue;
|
|
99528
|
-
const humidity = stripPercent(str(item, "humidity"));
|
|
99529
|
-
const battery = str(item, "battery");
|
|
99595
|
+
const humidity = stripPercent(str$1(item, "humidity"));
|
|
99596
|
+
const battery = str$1(item, "battery");
|
|
99530
99597
|
if (humidity) push(out, makeReading(`leafwetness_ch${ch}`, humidity));
|
|
99531
99598
|
if (battery && battery !== "None") push(out, makeReading(`leaf_batt${ch}`, batteryPercent(battery, false)));
|
|
99532
99599
|
}
|
|
@@ -99535,8 +99602,8 @@ function decodeChLeak(items, out) {
|
|
|
99535
99602
|
for (const item of items) {
|
|
99536
99603
|
const ch = channelOf(item);
|
|
99537
99604
|
if (ch === void 0) continue;
|
|
99538
|
-
const status = str(item, "status");
|
|
99539
|
-
const battery = str(item, "battery");
|
|
99605
|
+
const status = str$1(item, "status");
|
|
99606
|
+
const battery = str$1(item, "battery");
|
|
99540
99607
|
if (status !== void 0 && status !== "") {
|
|
99541
99608
|
const leak = status.trim().toLowerCase() === "normal" ? "0" : "1";
|
|
99542
99609
|
push(out, makeReading(`leak_ch${ch}`, leak));
|
|
@@ -99548,10 +99615,10 @@ function decodeChLds(items, out) {
|
|
|
99548
99615
|
for (const item of items) {
|
|
99549
99616
|
const ch = channelOf(item);
|
|
99550
99617
|
if (ch === void 0) continue;
|
|
99551
|
-
const air = str(item, "air");
|
|
99552
|
-
const depth = str(item, "depth");
|
|
99553
|
-
const voltage = str(item, "voltage");
|
|
99554
|
-
const battery = str(item, "battery");
|
|
99618
|
+
const air = str$1(item, "air");
|
|
99619
|
+
const depth = str$1(item, "depth");
|
|
99620
|
+
const voltage = str$1(item, "voltage");
|
|
99621
|
+
const battery = str$1(item, "battery");
|
|
99555
99622
|
if (air && air !== "None") push(out, makeReading(`lds_air_ch${ch}`, air));
|
|
99556
99623
|
if (depth && depth !== "None") push(out, makeReading(`lds_depth_ch${ch}`, depth));
|
|
99557
99624
|
if (voltage && voltage !== "None") push(out, makeReading(`lds_voltage_ch${ch}`, voltage));
|
|
@@ -99580,23 +99647,23 @@ var CO2_FIELDS = [
|
|
|
99580
99647
|
];
|
|
99581
99648
|
function firstField(item, fields) {
|
|
99582
99649
|
for (const f of fields) {
|
|
99583
|
-
const v = str(item, f);
|
|
99650
|
+
const v = str$1(item, f);
|
|
99584
99651
|
if (v && v !== "None") return v;
|
|
99585
99652
|
}
|
|
99586
99653
|
}
|
|
99587
99654
|
function decodeCo2(items, out) {
|
|
99588
99655
|
const item = items[0];
|
|
99589
99656
|
if (item === void 0) return;
|
|
99590
|
-
const temp = str(item, "temp");
|
|
99591
|
-
const unit = str(item, "unit") ?? "C";
|
|
99657
|
+
const temp = str$1(item, "temp");
|
|
99658
|
+
const unit = str$1(item, "unit") ?? "C";
|
|
99592
99659
|
if (temp) push(out, makeReading(unit === "C" ? "tf_co2c" : "tf_co2", temp, unit));
|
|
99593
|
-
const humidity = stripPercent(str(item, "humidity"));
|
|
99660
|
+
const humidity = stripPercent(str$1(item, "humidity"));
|
|
99594
99661
|
if (humidity) push(out, makeReading("humi_co2", humidity));
|
|
99595
99662
|
for (const [fields, key] of CO2_FIELDS) {
|
|
99596
99663
|
const v = firstField(item, fields);
|
|
99597
99664
|
if (v !== void 0) push(out, makeReading(key, v));
|
|
99598
99665
|
}
|
|
99599
|
-
const battery = str(item, "battery");
|
|
99666
|
+
const battery = str$1(item, "battery");
|
|
99600
99667
|
if (battery && battery !== "None") {
|
|
99601
99668
|
const n = num(battery);
|
|
99602
99669
|
push(out, makeReading("co2_batt", n !== void 0 ? String(Math.min(n * 20, 100)) : battery));
|
|
@@ -99608,12 +99675,12 @@ function push2(out, reading) {
|
|
|
99608
99675
|
}
|
|
99609
99676
|
function decodeCommonList(items, mapper, out) {
|
|
99610
99677
|
for (const item of items) {
|
|
99611
|
-
const id = str(item, "id");
|
|
99612
|
-
const val = str(item, "val");
|
|
99613
|
-
const unit = str(item, "unit");
|
|
99678
|
+
const id = str$1(item, "id");
|
|
99679
|
+
const val = str$1(item, "val");
|
|
99680
|
+
const unit = str$1(item, "unit");
|
|
99614
99681
|
if (id && val !== void 0) push2(out, makeReading(id, val, unit));
|
|
99615
99682
|
if (id === "0x03" && mapper.getHardwareId("wh26batt") !== void 0) {
|
|
99616
|
-
const battery = str(item, "battery");
|
|
99683
|
+
const battery = str$1(item, "battery");
|
|
99617
99684
|
if (battery !== void 0) push2(out, makeReading("wh26batt", battery === "0" ? "100" : "10"));
|
|
99618
99685
|
}
|
|
99619
99686
|
}
|
|
@@ -99621,12 +99688,12 @@ function decodeCommonList(items, mapper, out) {
|
|
|
99621
99688
|
function decodeRain(items, mapper, out) {
|
|
99622
99689
|
const rainHwId = mapper.getHardwareId("wh69batt") ?? mapper.getHardwareId("wh40batt");
|
|
99623
99690
|
for (const item of items) {
|
|
99624
|
-
const id = str(item, "id");
|
|
99625
|
-
const val = str(item, "val");
|
|
99691
|
+
const id = str$1(item, "id");
|
|
99692
|
+
const val = str$1(item, "val");
|
|
99626
99693
|
if (!id || val === void 0) continue;
|
|
99627
|
-
push2(out, makeReading(id, val, str(item, "unit"), rainHwId));
|
|
99694
|
+
push2(out, makeReading(id, val, str$1(item, "unit"), rainHwId));
|
|
99628
99695
|
if (id === "0x13") {
|
|
99629
|
-
const battery = str(item, "battery");
|
|
99696
|
+
const battery = str$1(item, "battery");
|
|
99630
99697
|
if (battery) {
|
|
99631
99698
|
const n = num(battery);
|
|
99632
99699
|
const pct = n !== void 0 && n > 1 ? String(n * 20) : battery === "0" ? "100" : "10";
|
|
@@ -99658,30 +99725,30 @@ function piezoKeysFor(mapper) {
|
|
|
99658
99725
|
function decodePiezo(items, mapper, out) {
|
|
99659
99726
|
const piezoHwId = mapper.getHardwareId("ws85batt") ?? mapper.getHardwareId("ws90batt") ?? mapper.getHardwareId("wh90batt");
|
|
99660
99727
|
for (const item of items) {
|
|
99661
|
-
const id = str(item, "id");
|
|
99662
|
-
const val = str(item, "val");
|
|
99728
|
+
const id = str$1(item, "id");
|
|
99729
|
+
const val = str$1(item, "val");
|
|
99663
99730
|
if (!id || val === void 0) continue;
|
|
99664
|
-
push2(out, makeReading(id, val, str(item, "unit"), piezoHwId));
|
|
99731
|
+
push2(out, makeReading(id, val, str$1(item, "unit"), piezoHwId));
|
|
99665
99732
|
if (id !== "0x13") continue;
|
|
99666
|
-
const battery = str(item, "battery");
|
|
99733
|
+
const battery = str$1(item, "battery");
|
|
99667
99734
|
if (!battery) continue;
|
|
99668
99735
|
const keys = piezoKeysFor(mapper);
|
|
99669
99736
|
const n = num(battery);
|
|
99670
99737
|
push2(out, makeReading(keys.batt, n !== void 0 ? String(n * 20) : battery));
|
|
99671
|
-
const voltage = str(item, "voltage");
|
|
99738
|
+
const voltage = str$1(item, "voltage");
|
|
99672
99739
|
if (voltage) push2(out, makeReading(keys.volt, voltage));
|
|
99673
|
-
const cap = str(item, keys.capField);
|
|
99740
|
+
const cap = str$1(item, keys.capField);
|
|
99674
99741
|
if (cap) push2(out, makeReading(keys.capKey, cap));
|
|
99675
99742
|
}
|
|
99676
99743
|
}
|
|
99677
99744
|
function decodeLightning(items, out) {
|
|
99678
99745
|
const item = items[0];
|
|
99679
99746
|
if (item === void 0) return;
|
|
99680
|
-
const count = str(item, "count");
|
|
99747
|
+
const count = str$1(item, "count");
|
|
99681
99748
|
if (count !== void 0) push2(out, makeReading("lightning_num", count));
|
|
99682
|
-
const distance = str(item, "distance");
|
|
99749
|
+
const distance = str$1(item, "distance");
|
|
99683
99750
|
if (distance !== void 0) push2(out, makeReading("lightning", distance.replace(" km", "").trim()));
|
|
99684
|
-
const battery = str(item, "battery");
|
|
99751
|
+
const battery = str$1(item, "battery");
|
|
99685
99752
|
if (battery !== void 0) {
|
|
99686
99753
|
const n = num(battery);
|
|
99687
99754
|
push2(out, makeReading("wh57batt", n !== void 0 ? String(n * 20) : battery));
|
|
@@ -99690,30 +99757,30 @@ function decodeLightning(items, out) {
|
|
|
99690
99757
|
function decodeWh25(items, out) {
|
|
99691
99758
|
const item = items[0];
|
|
99692
99759
|
if (item === void 0) return;
|
|
99693
|
-
const intemp = str(item, "intemp");
|
|
99694
|
-
const unit = str(item, "unit") ?? "F";
|
|
99760
|
+
const intemp = str$1(item, "intemp");
|
|
99761
|
+
const unit = str$1(item, "unit") ?? "F";
|
|
99695
99762
|
if (intemp !== void 0) push2(out, makeReading("tempinf", intemp, unit));
|
|
99696
|
-
const inhumi = str(item, "inhumi");
|
|
99763
|
+
const inhumi = str$1(item, "inhumi");
|
|
99697
99764
|
if (inhumi !== void 0) push2(out, makeReading("humidityin", stripPercent(inhumi)));
|
|
99698
|
-
const abs = str(item, "abs");
|
|
99765
|
+
const abs = str$1(item, "abs");
|
|
99699
99766
|
if (abs !== void 0) push2(out, makeReading("baromabsin", abs.replace(" hPa", "").trim()));
|
|
99700
|
-
const rel = str(item, "rel");
|
|
99767
|
+
const rel = str$1(item, "rel");
|
|
99701
99768
|
if (rel !== void 0) push2(out, makeReading("baromrelin", rel.replace(" hPa", "").trim()));
|
|
99702
99769
|
}
|
|
99703
99770
|
function decodeSoilCalibration(items, out) {
|
|
99704
99771
|
for (const item of items) {
|
|
99705
|
-
const ch = str(item, "ch");
|
|
99706
|
-
const nowAd = str(item, "nowAd");
|
|
99772
|
+
const ch = str$1(item, "ch");
|
|
99773
|
+
const nowAd = str$1(item, "nowAd");
|
|
99707
99774
|
if (ch && nowAd !== void 0) push2(out, makeReading(`soilad${ch}`, nowAd));
|
|
99708
99775
|
}
|
|
99709
99776
|
}
|
|
99710
99777
|
function decodeLdsConfig(items, out) {
|
|
99711
99778
|
for (const item of items) {
|
|
99712
|
-
const ch = str(item, "ch");
|
|
99779
|
+
const ch = str$1(item, "ch");
|
|
99713
99780
|
if (!ch) continue;
|
|
99714
|
-
const level = str(item, "level");
|
|
99781
|
+
const level = str$1(item, "level");
|
|
99715
99782
|
if (level !== void 0 && level !== "None") push2(out, makeReading(`lds_level_ch${ch}`, level));
|
|
99716
|
-
const totalHeat = str(item, "total_heat");
|
|
99783
|
+
const totalHeat = str$1(item, "total_heat");
|
|
99717
99784
|
if (totalHeat !== void 0 && totalHeat !== "None") push2(out, makeReading(`lds_total_heat_ch${ch}`, totalHeat));
|
|
99718
99785
|
}
|
|
99719
99786
|
}
|
|
@@ -101780,50 +101847,6 @@ function buildEcowittGatewayCandidates(input) {
|
|
|
101780
101847
|
})];
|
|
101781
101848
|
}
|
|
101782
101849
|
//#endregion
|
|
101783
|
-
//#region src/ecowitt-sensor-groups.ts
|
|
101784
|
-
/** Resolve `deviceGroup` from a persisted container config (defaults to 'gateway'). */
|
|
101785
|
-
function configDeviceGroup(config) {
|
|
101786
|
-
const g = config["deviceGroup"];
|
|
101787
|
-
return ECOWITT_DEVICE_GROUPS.find((group) => group === g) ?? "gateway";
|
|
101788
|
-
}
|
|
101789
|
-
/** Build the persisted sensor entry (incl. self-describing category) from a live Sensor. */
|
|
101790
|
-
function sensorToEntry(s) {
|
|
101791
|
-
return {
|
|
101792
|
-
sensorId: s.id,
|
|
101793
|
-
quantity: s.quantity,
|
|
101794
|
-
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
101795
|
-
...s.name !== void 0 ? { name: s.name } : {},
|
|
101796
|
-
...s.category !== void 0 ? { category: s.category } : {}
|
|
101797
|
-
};
|
|
101798
|
-
}
|
|
101799
|
-
/** Bucket live sensors into their device group. */
|
|
101800
|
-
function bucketSensorsByGroup(sensors) {
|
|
101801
|
-
const byGroup = /* @__PURE__ */ new Map();
|
|
101802
|
-
for (const group of ECOWITT_DEVICE_GROUPS) byGroup.set(group, []);
|
|
101803
|
-
for (const s of sensors) {
|
|
101804
|
-
const group = deviceGroupForSensor(s.category, s.channel);
|
|
101805
|
-
byGroup.get(group)?.push(sensorToEntry(s));
|
|
101806
|
-
}
|
|
101807
|
-
return byGroup;
|
|
101808
|
-
}
|
|
101809
|
-
/** Structural equality of two persisted sensor lists (order-independent by id + name). */
|
|
101810
|
-
function sensorsEqual(a, b) {
|
|
101811
|
-
if (a.length !== b.length) return false;
|
|
101812
|
-
const aById = /* @__PURE__ */ new Map();
|
|
101813
|
-
for (const entry of a) if (typeof entry === "object" && entry !== null && "sensorId" in entry) {
|
|
101814
|
-
const id = entry.sensorId;
|
|
101815
|
-
if (typeof id === "string") {
|
|
101816
|
-
const name = "name" in entry && typeof entry.name === "string" ? entry.name : void 0;
|
|
101817
|
-
aById.set(id, name);
|
|
101818
|
-
}
|
|
101819
|
-
}
|
|
101820
|
-
for (const entry of b) {
|
|
101821
|
-
if (!aById.has(entry.sensorId)) return false;
|
|
101822
|
-
if (aById.get(entry.sensorId) !== entry.name) return false;
|
|
101823
|
-
}
|
|
101824
|
-
return true;
|
|
101825
|
-
}
|
|
101826
|
-
//#endregion
|
|
101827
101850
|
//#region src/ecowitt-adoption-provider.ts
|
|
101828
101851
|
var DEVICES_FILTER = {
|
|
101829
101852
|
id: "devices",
|
|
@@ -102127,6 +102150,56 @@ function ecowittCandidates(gateways, known = /* @__PURE__ */ new Set()) {
|
|
|
102127
102150
|
});
|
|
102128
102151
|
}
|
|
102129
102152
|
//#endregion
|
|
102153
|
+
//#region src/ecowitt-duplicate-sensors.ts
|
|
102154
|
+
function str(config, key) {
|
|
102155
|
+
const v = config[key];
|
|
102156
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
102157
|
+
}
|
|
102158
|
+
/**
|
|
102159
|
+
* Sensors minted under the `gateway` group Container (id `gateway:<key>`) that are the SAME reading
|
|
102160
|
+
* as a hardware-owned child (`<hw>:<key>`) of the weather-station / CH1 Container of the same
|
|
102161
|
+
* gateway. Early snapshots carry no
|
|
102162
|
+
* model for a sensor, which nodewitt classifies as `gateway`, so a weather-station reading was
|
|
102163
|
+
* once minted under the gateway Container as well; the live readings reach only the sensor under
|
|
102164
|
+
* its own group. Only a duplicate is returned: a sensor that exists nowhere else is the
|
|
102165
|
+
* gateway's own and is never touched. `scanned` is the number of sensor children seen, so a
|
|
102166
|
+
* caller can tell "nothing to remove" from "children not spawned yet".
|
|
102167
|
+
*/
|
|
102168
|
+
function findDuplicateGatewaySensors(rows) {
|
|
102169
|
+
const containers = /* @__PURE__ */ new Map();
|
|
102170
|
+
for (const row of rows) {
|
|
102171
|
+
if (row.type !== DeviceType.Container || row.config["system"] !== "ecowitt") continue;
|
|
102172
|
+
const gatewayId = str(row.config, "gatewayId");
|
|
102173
|
+
if (gatewayId === null) continue;
|
|
102174
|
+
containers.set(row.id, {
|
|
102175
|
+
gatewayId,
|
|
102176
|
+
gateway: configDeviceGroup(row.config) === "gateway"
|
|
102177
|
+
});
|
|
102178
|
+
}
|
|
102179
|
+
const ownedElsewhere = /* @__PURE__ */ new Set();
|
|
102180
|
+
const underGateway = [];
|
|
102181
|
+
let scanned = 0;
|
|
102182
|
+
for (const row of rows) {
|
|
102183
|
+
if (row.type !== DeviceType.Sensor || row.parentDeviceId === null) continue;
|
|
102184
|
+
const container = containers.get(row.parentDeviceId);
|
|
102185
|
+
const sensorId = str(row.config, "sensorId");
|
|
102186
|
+
if (container === void 0 || sensorId === null) continue;
|
|
102187
|
+
scanned += 1;
|
|
102188
|
+
const key = `${container.gatewayId}\u0000${sensorKeyOf(sensorId)}`;
|
|
102189
|
+
const owner = sensorOwnerOf(sensorId);
|
|
102190
|
+
if (container.gateway) {
|
|
102191
|
+
if (owner === "gateway") underGateway.push({
|
|
102192
|
+
id: row.id,
|
|
102193
|
+
key
|
|
102194
|
+
});
|
|
102195
|
+
} else if (owner !== "gateway" && owner !== "") ownedElsewhere.add(key);
|
|
102196
|
+
}
|
|
102197
|
+
return {
|
|
102198
|
+
duplicates: underGateway.filter((s) => ownedElsewhere.has(s.key)).map((s) => s.id),
|
|
102199
|
+
scanned
|
|
102200
|
+
};
|
|
102201
|
+
}
|
|
102202
|
+
//#endregion
|
|
102130
102203
|
//#region src/ecowitt-reconciler.ts
|
|
102131
102204
|
/**
|
|
102132
102205
|
* Keeps one live client per enabled integration. Devices are removed ONLY on an explicit
|
|
@@ -102580,6 +102653,55 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
102580
102653
|
const devices = this.ctx.kernel.devices;
|
|
102581
102654
|
if (!reg || !devices) return;
|
|
102582
102655
|
const byGroup = bucketSensorsByGroup(sensors);
|
|
102656
|
+
if (await this.reconcileEachContainer(gatewayKey, byGroup) || !this.duplicatesChecked.has(gatewayKey)) await this.removeDuplicateGatewaySensors(gatewayKey);
|
|
102657
|
+
}
|
|
102658
|
+
/** Gateway keys whose duplicate scan already saw spawned sensor children (idempotent anyway). */
|
|
102659
|
+
duplicatesChecked = /* @__PURE__ */ new Set();
|
|
102660
|
+
/**
|
|
102661
|
+
* Idempotent cleanup of the sensors minted under the `gateway` Container that duplicate a
|
|
102662
|
+
* sensor of the weather-station / CH1 Container (see {@link findDuplicateGatewaySensors}).
|
|
102663
|
+
* Goes through the normal device removal (tombstone); a read that failed removes nothing.
|
|
102664
|
+
*/
|
|
102665
|
+
async removeDuplicateGatewaySensors(gatewayKey) {
|
|
102666
|
+
const reg = this.ctx.kernel.deviceRegistry;
|
|
102667
|
+
const devices = this.ctx.kernel.devices;
|
|
102668
|
+
if (!reg || !devices) return;
|
|
102669
|
+
const rows = [];
|
|
102670
|
+
for (const d of reg.getAllForAddon(this.addonId)) {
|
|
102671
|
+
if (d.type !== DeviceType.Container && d.type !== DeviceType.Sensor) continue;
|
|
102672
|
+
const config = await devices.loadConfig(d.id).catch(() => null);
|
|
102673
|
+
if (config === null) return;
|
|
102674
|
+
if (config["system"] !== "ecowitt" || config["gatewayId"] !== gatewayKey) continue;
|
|
102675
|
+
rows.push({
|
|
102676
|
+
id: d.id,
|
|
102677
|
+
type: d.type,
|
|
102678
|
+
parentDeviceId: d.parentDeviceId,
|
|
102679
|
+
config
|
|
102680
|
+
});
|
|
102681
|
+
}
|
|
102682
|
+
const { duplicates, scanned } = findDuplicateGatewaySensors(rows);
|
|
102683
|
+
if (scanned > 0) this.duplicatesChecked.add(gatewayKey);
|
|
102684
|
+
for (const id of duplicates) try {
|
|
102685
|
+
await devices.remove(id);
|
|
102686
|
+
this.ctx.logger.info("Ecowitt: duplicate sensor under the gateway container removed", {
|
|
102687
|
+
tags: { deviceId: id },
|
|
102688
|
+
meta: { gatewayKey }
|
|
102689
|
+
});
|
|
102690
|
+
} catch (err) {
|
|
102691
|
+
this.ctx.logger.warn("Ecowitt: duplicate sensor removal failed", {
|
|
102692
|
+
tags: { deviceId: id },
|
|
102693
|
+
meta: {
|
|
102694
|
+
gatewayKey,
|
|
102695
|
+
error: errMsg(err)
|
|
102696
|
+
}
|
|
102697
|
+
});
|
|
102698
|
+
}
|
|
102699
|
+
}
|
|
102700
|
+
async reconcileEachContainer(gatewayKey, byGroup) {
|
|
102701
|
+
const reg = this.ctx.kernel.deviceRegistry;
|
|
102702
|
+
const devices = this.ctx.kernel.devices;
|
|
102703
|
+
if (!reg || !devices) return false;
|
|
102704
|
+
let changed = false;
|
|
102583
102705
|
for (const d of reg.getAllForAddon(this.addonId)) {
|
|
102584
102706
|
if (d.parentDeviceId !== null || d.type !== DeviceType.Container) continue;
|
|
102585
102707
|
const cfg = await devices.loadConfig(d.id).catch(() => ({}));
|
|
@@ -102587,6 +102709,7 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
102587
102709
|
const nextSensors = byGroup.get(configDeviceGroup(cfg)) ?? [];
|
|
102588
102710
|
if (sensorsEqual(Array.isArray(cfg["sensors"]) ? cfg["sensors"] : [], nextSensors)) continue;
|
|
102589
102711
|
await devices.applyDerivedSpec(d.id, { config: { sensors: nextSensors } });
|
|
102712
|
+
changed = true;
|
|
102590
102713
|
this.ctx.logger.info("Ecowitt: group sensors reconciled", {
|
|
102591
102714
|
tags: { deviceId: d.id },
|
|
102592
102715
|
meta: {
|
|
@@ -102595,6 +102718,7 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
102595
102718
|
}
|
|
102596
102719
|
});
|
|
102597
102720
|
}
|
|
102721
|
+
return changed;
|
|
102598
102722
|
}
|
|
102599
102723
|
async adoptGatewayDevice(integrationId, gatewayKey, name) {
|
|
102600
102724
|
const devices = this.ctx.kernel.devices;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-ecowitt",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.154",
|
|
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",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
}
|
|
60
60
|
],
|
|
61
61
|
"requires": {
|
|
62
|
-
"server": ">=1.2.
|
|
62
|
+
"server": ">=1.2.722"
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"files": [
|