@camstack/addon-provider-ecowitt 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +208 -41
- package/dist/addon.mjs +208 -41
- package/package.json +2 -2
package/dist/addon.js
CHANGED
|
@@ -10082,15 +10082,18 @@ var humiditySensorCapability = {
|
|
|
10082
10082
|
runtimeState: HumiditySensorStatusSchema
|
|
10083
10083
|
};
|
|
10084
10084
|
/**
|
|
10085
|
-
* Image display cap. Models
|
|
10086
|
-
*
|
|
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.
|
|
10087
10087
|
*
|
|
10088
|
-
* Read-only: there are no setters. The provider resolves
|
|
10089
|
-
*
|
|
10090
|
-
*
|
|
10091
|
-
*
|
|
10092
|
-
*
|
|
10093
|
-
*
|
|
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.
|
|
10094
10097
|
*/
|
|
10095
10098
|
var ImageStatusSchema = object({
|
|
10096
10099
|
/** Absolute signed URL the browser loads directly. Null when the
|
|
@@ -10116,18 +10119,47 @@ var imageCapability = {
|
|
|
10116
10119
|
*/
|
|
10117
10120
|
runtimeState: ImageStatusSchema
|
|
10118
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
|
+
]);
|
|
10119
10146
|
var LawnMowerControlStatusSchema = object({
|
|
10120
10147
|
/** Lifecycle activity of the mower. */
|
|
10121
|
-
activity:
|
|
10122
|
-
"idle",
|
|
10123
|
-
"mowing",
|
|
10124
|
-
"paused",
|
|
10125
|
-
"docked",
|
|
10126
|
-
"error"
|
|
10127
|
-
]),
|
|
10148
|
+
activity: LawnMowerActivitySchema,
|
|
10128
10149
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
10129
10150
|
* reading. */
|
|
10130
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,
|
|
10131
10163
|
/** Ms epoch when the slice was last updated. */
|
|
10132
10164
|
lastChangedAt: number()
|
|
10133
10165
|
});
|
|
@@ -12353,6 +12385,7 @@ var VacuumStateSchema = _enum([
|
|
|
12353
12385
|
"paused",
|
|
12354
12386
|
"returning",
|
|
12355
12387
|
"docked",
|
|
12388
|
+
"drying",
|
|
12356
12389
|
"error"
|
|
12357
12390
|
]);
|
|
12358
12391
|
/**
|
|
@@ -12391,6 +12424,12 @@ var VacuumControlStatusSchema = object({
|
|
|
12391
12424
|
detergent: TankStatusSchema.nullable(),
|
|
12392
12425
|
/** Dust bin. Null when the hardware has no dust bin. */
|
|
12393
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(),
|
|
12394
12433
|
/** Ms epoch when the slice was last updated. */
|
|
12395
12434
|
lastChangedAt: number()
|
|
12396
12435
|
});
|
|
@@ -13357,9 +13396,29 @@ var BaseDevice = class {
|
|
|
13357
13396
|
* is open (Reolink writes `hasPtz/hasIntercom`, Hikvision writes
|
|
13358
13397
|
* `hasSupplementalLight/hasAlarmIo`, etc).
|
|
13359
13398
|
*
|
|
13360
|
-
* Default:
|
|
13361
|
-
|
|
13362
|
-
|
|
13399
|
+
* Default: nothing to probe → mark the device PROBED (set `lastProbedAt`) so
|
|
13400
|
+
* the kernel treats it as ready immediately. A device that derives its shape
|
|
13401
|
+
* from a spec (a container, or an accessory sensor) rather than from a
|
|
13402
|
+
* hardware probe has no probe to "complete"; without stamping `lastProbedAt`
|
|
13403
|
+
* it would look perpetually un-probed — logging "Initial probe did not
|
|
13404
|
+
* complete" on every boot and spinning a pointless retry chain. Drivers that
|
|
13405
|
+
* DO probe override this and write their own `feature-probe` slice (including
|
|
13406
|
+
* `lastProbedAt`) once their probe actually succeeds.
|
|
13407
|
+
*/
|
|
13408
|
+
async onProbe() {
|
|
13409
|
+
const base = this.runtimeState.getCapState("feature-probe") ?? {
|
|
13410
|
+
flags: {},
|
|
13411
|
+
deviceType: null,
|
|
13412
|
+
model: null,
|
|
13413
|
+
channelCount: null,
|
|
13414
|
+
lastProbedAt: 0,
|
|
13415
|
+
lastFetchedAt: 0
|
|
13416
|
+
};
|
|
13417
|
+
this.runtimeState.setCapState("feature-probe", {
|
|
13418
|
+
...base,
|
|
13419
|
+
lastProbedAt: Date.now()
|
|
13420
|
+
});
|
|
13421
|
+
}
|
|
13363
13422
|
/**
|
|
13364
13423
|
* Phase 5 — fired after the device + its accessories are registered.
|
|
13365
13424
|
* Drivers publish streams to the broker, kick off background tasks,
|
|
@@ -14821,17 +14880,32 @@ var ReleaseInputSchema = object({
|
|
|
14821
14880
|
* the parent cascades into every accessory. */
|
|
14822
14881
|
camDeviceId: number().int().nonnegative()
|
|
14823
14882
|
});
|
|
14824
|
-
var ResyncInputSchema = object({
|
|
14825
|
-
/** Parent CamStack device id of an adopted device. The provider resolves its
|
|
14826
|
-
* source (integration/broker + native id) and re-aligns the device's
|
|
14827
|
-
* structural spec (type/role/capabilities/units) with the live mapping,
|
|
14828
|
-
* rebuilding any child whose class changed while preserving operator edits. */
|
|
14829
|
-
camDeviceId: number().int().nonnegative()
|
|
14883
|
+
var ResyncInputSchema = object({
|
|
14884
|
+
/** Parent CamStack device id of an adopted device. The provider resolves its
|
|
14885
|
+
* source (integration/broker + native id) and re-aligns the device's
|
|
14886
|
+
* structural spec (type/role/capabilities/units) with the live mapping,
|
|
14887
|
+
* rebuilding any child whose class changed while preserving operator edits. */
|
|
14888
|
+
camDeviceId: number().int().nonnegative(),
|
|
14889
|
+
/** "Resync from zero" (#19). When true, the kernel PURGES every accessory
|
|
14890
|
+
* child of `camDeviceId` BEFORE the provider re-derives the device, so the
|
|
14891
|
+
* children are rebuilt fresh from source — correct names, coords, and units —
|
|
14892
|
+
* instead of being preserved by the incremental reconcile. Use to recover from
|
|
14893
|
+
* legacy generic/placeholder names that the normal name-precedence keeps frozen
|
|
14894
|
+
* (the operator's explicit reset). Push-driven integrations (no-op resync)
|
|
14895
|
+
* rebuild on their next snapshot; pull/command integrations rebuild in `resync`.
|
|
14896
|
+
* Operator edits on the PARENT (its name, layout, primary-child pick) survive —
|
|
14897
|
+
* only the children are torn down. Omitted/false ⇒ the normal incremental
|
|
14898
|
+
* re-sync that preserves children. */
|
|
14899
|
+
resetToSource: boolean().optional()
|
|
14900
|
+
});
|
|
14830
14901
|
var ResyncResultSchema = object({
|
|
14831
14902
|
/** True when the persisted spec actually changed (children may have been rebuilt). */
|
|
14832
14903
|
changed: boolean(),
|
|
14833
14904
|
/** Number of child devices rebuilt into a new class by this re-sync. */
|
|
14834
|
-
rebuiltChildren: number().int().nonnegative()
|
|
14905
|
+
rebuiltChildren: number().int().nonnegative(),
|
|
14906
|
+
/** Number of accessory children torn down by a `resetToSource` purge before the
|
|
14907
|
+
* provider re-derived the device. 0/absent for a normal incremental re-sync. */
|
|
14908
|
+
removedChildren: number().int().nonnegative().optional()
|
|
14835
14909
|
});
|
|
14836
14910
|
var deviceAdoptionCapability = {
|
|
14837
14911
|
name: "device-adoption",
|
|
@@ -16596,6 +16670,11 @@ var DeviceMetaSchema = object({
|
|
|
16596
16670
|
addonId: string(),
|
|
16597
16671
|
type: string(),
|
|
16598
16672
|
name: string(),
|
|
16673
|
+
/** True once an operator explicitly renamed the device via `setName`. Drives
|
|
16674
|
+
* reconcile name-precedence (preserve operator name vs adopt fresh provider
|
|
16675
|
+
* name). Absent ⇒ treated as user-named (PRESERVE) for legacy rows. See
|
|
16676
|
+
* `DeviceMeta.userNamed`. */
|
|
16677
|
+
userNamed: boolean().optional(),
|
|
16599
16678
|
location: string().nullable(),
|
|
16600
16679
|
disabled: boolean(),
|
|
16601
16680
|
parentDeviceId: number().nullable(),
|
|
@@ -16906,6 +16985,9 @@ method(object({
|
|
|
16906
16985
|
}), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
|
|
16907
16986
|
kind: "mutation",
|
|
16908
16987
|
auth: "admin"
|
|
16988
|
+
}), method(ResyncInputSchema, ResyncResultSchema, {
|
|
16989
|
+
kind: "mutation",
|
|
16990
|
+
auth: "admin"
|
|
16909
16991
|
}), method(object({
|
|
16910
16992
|
deviceId: number(),
|
|
16911
16993
|
key: string(),
|
|
@@ -20668,6 +20750,12 @@ Object.freeze({
|
|
|
20668
20750
|
addonId: null,
|
|
20669
20751
|
access: "create"
|
|
20670
20752
|
},
|
|
20753
|
+
"deviceManager.adoptionResync": {
|
|
20754
|
+
capName: "device-manager",
|
|
20755
|
+
capScope: "system",
|
|
20756
|
+
addonId: null,
|
|
20757
|
+
access: "create"
|
|
20758
|
+
},
|
|
20671
20759
|
"deviceManager.allocateDeviceId": {
|
|
20672
20760
|
capName: "device-manager",
|
|
20673
20761
|
capScope: "system",
|
|
@@ -44576,7 +44664,8 @@ function classifyKey(key) {
|
|
|
44576
44664
|
const hex = lookupHexId(key);
|
|
44577
44665
|
if (hex !== void 0) return {
|
|
44578
44666
|
kind: "measurement",
|
|
44579
|
-
quantity: hex.quantity
|
|
44667
|
+
quantity: hex.quantity,
|
|
44668
|
+
name: hex.name
|
|
44580
44669
|
};
|
|
44581
44670
|
const named = Object.prototype.hasOwnProperty.call(NAMED_QUANTITY, key) ? NAMED_QUANTITY[key] : void 0;
|
|
44582
44671
|
if (named !== void 0) return {
|
|
@@ -44612,6 +44701,7 @@ var Station = class {
|
|
|
44612
44701
|
...owner !== void 0 ? { hardwareId: owner } : {},
|
|
44613
44702
|
...info?.model ? { model: info.model } : {},
|
|
44614
44703
|
...info?.channel !== void 0 ? { channel: info.channel } : {},
|
|
44704
|
+
...cls.name !== void 0 ? { name: cls.name } : {},
|
|
44615
44705
|
quantity: cls.quantity,
|
|
44616
44706
|
value: r.value,
|
|
44617
44707
|
unit: r.unit,
|
|
@@ -44645,6 +44735,7 @@ var Station = class {
|
|
|
44645
44735
|
this.upsert(id, {
|
|
44646
44736
|
id,
|
|
44647
44737
|
...r.channel !== void 0 ? { channel: r.channel } : {},
|
|
44738
|
+
...cls.name !== void 0 ? { name: cls.name } : {},
|
|
44648
44739
|
quantity: cls.quantity,
|
|
44649
44740
|
value: r.value,
|
|
44650
44741
|
unit: r.unit,
|
|
@@ -45670,17 +45761,84 @@ function valueFieldForCap(cap) {
|
|
|
45670
45761
|
}
|
|
45671
45762
|
}
|
|
45672
45763
|
/**
|
|
45673
|
-
* Build a human label for a sensor
|
|
45674
|
-
*
|
|
45764
|
+
* Build a human label for a sensor. The label is resolved in priority order so
|
|
45765
|
+
* SAME-QUANTITY sensors are always distinguishable:
|
|
45675
45766
|
*
|
|
45676
|
-
*
|
|
45677
|
-
*
|
|
45678
|
-
*
|
|
45767
|
+
* 1. The SPECIFIC measurement `name` the library resolved from its hex-id
|
|
45768
|
+
* table (e.g. "Outdoor Temperature", "Wind Gust") — present for the poll /
|
|
45769
|
+
* hex-id path.
|
|
45770
|
+
* 2. A specific name derived from the raw gateway FIELD KEY (e.g. `dailyrainin`
|
|
45771
|
+
* → "Daily Rain", `tempinf` → "Indoor Temperature") — covers the push /
|
|
45772
|
+
* named-and-pattern-key path where the library leaves `name` absent and
|
|
45773
|
+
* every same-quantity reading would otherwise collapse to one generic
|
|
45774
|
+
* label (five rain readings all "Precipitation", indoor+outdoor temp both
|
|
45775
|
+
* "Temperature", etc.). See {@link FIELD_KEY_LABEL}.
|
|
45776
|
+
* 3. The generic quantity label as a last resort.
|
|
45777
|
+
*
|
|
45778
|
+
* A channel suffix is appended when present (`temperature` + channel 2 →
|
|
45779
|
+
* `Temperature CH2`).
|
|
45780
|
+
*
|
|
45781
|
+
* `quantity`/`name` accept a plain `string` (not just {@link Quantity}) so a
|
|
45782
|
+
* persisted config value flows through without a cast; an unknown quantity falls
|
|
45783
|
+
* back to its raw string label. `sensorId` is the nodewitt `Sensor.id`
|
|
45784
|
+
* (`<owner>:<key>` or `<owner>:ch<n>:<key>`); the field key is its final
|
|
45785
|
+
* colon-segment.
|
|
45679
45786
|
*/
|
|
45680
|
-
function sensorLabel(quantity, channel) {
|
|
45681
|
-
const base = isKnownQuantity(quantity) ? QUANTITY_LABEL[quantity] : quantity;
|
|
45787
|
+
function sensorLabel(quantity, channel, name, sensorId) {
|
|
45788
|
+
const base = labelFromFieldKey(sensorId) ?? (name !== void 0 && name.length > 0 ? name : isKnownQuantity(quantity) ? QUANTITY_LABEL[quantity] : quantity);
|
|
45682
45789
|
return channel !== void 0 ? `${base} CH${channel}` : base;
|
|
45683
45790
|
}
|
|
45791
|
+
/**
|
|
45792
|
+
* Resolve a specific human label from a nodewitt sensor id by extracting its raw
|
|
45793
|
+
* gateway field key (the final colon-segment of `<owner>[:ch<n>]:<key>`) and
|
|
45794
|
+
* looking it up in {@link FIELD_KEY_LABEL}. Returns `undefined` when no id is
|
|
45795
|
+
* given or the key is unrecognised, so the caller falls through to the generic
|
|
45796
|
+
* quantity label.
|
|
45797
|
+
*/
|
|
45798
|
+
function labelFromFieldKey(sensorId) {
|
|
45799
|
+
if (sensorId === void 0) return void 0;
|
|
45800
|
+
const key = sensorId.slice(sensorId.lastIndexOf(":") + 1);
|
|
45801
|
+
return Object.prototype.hasOwnProperty.call(FIELD_KEY_LABEL, key) ? FIELD_KEY_LABEL[key] : void 0;
|
|
45802
|
+
}
|
|
45803
|
+
/**
|
|
45804
|
+
* Specific human labels for the raw Ecowitt gateway field keys that the library
|
|
45805
|
+
* classifies via its named / pattern tables (the push path) and therefore leaves
|
|
45806
|
+
* WITHOUT a `name`. Mirrors the vocabulary the library's hex-id table already
|
|
45807
|
+
* uses for the poll path, so a station reached over either transport reads the
|
|
45808
|
+
* same. Channel-suffixed keys (`temp3f`, `humidity2`, …) are handled by the
|
|
45809
|
+
* `channel` argument and intentionally omitted here.
|
|
45810
|
+
*/
|
|
45811
|
+
var FIELD_KEY_LABEL = {
|
|
45812
|
+
tempinf: "Indoor Temperature",
|
|
45813
|
+
tempf: "Outdoor Temperature",
|
|
45814
|
+
humidityin: "Indoor Humidity",
|
|
45815
|
+
humidity: "Outdoor Humidity",
|
|
45816
|
+
baromrelin: "Relative Pressure",
|
|
45817
|
+
baromabsin: "Absolute Pressure",
|
|
45818
|
+
windspeedmph: "Wind Speed",
|
|
45819
|
+
windgustmph: "Wind Gust",
|
|
45820
|
+
maxdailygust: "Max Daily Gust",
|
|
45821
|
+
winddir: "Wind Direction",
|
|
45822
|
+
solarradiation: "Solar Radiation",
|
|
45823
|
+
uv: "UV Index",
|
|
45824
|
+
lightning: "Lightning Distance",
|
|
45825
|
+
lightning_num: "Lightning Count",
|
|
45826
|
+
rainratein: "Rain Rate",
|
|
45827
|
+
eventrainin: "Rain Event",
|
|
45828
|
+
hourlyrainin: "Hourly Rain",
|
|
45829
|
+
dailyrainin: "Daily Rain",
|
|
45830
|
+
weeklyrainin: "Weekly Rain",
|
|
45831
|
+
monthlyrainin: "Monthly Rain",
|
|
45832
|
+
yearlyrainin: "Yearly Rain",
|
|
45833
|
+
totalrainin: "Total Rain",
|
|
45834
|
+
rrain_piezo: "Rain Rate",
|
|
45835
|
+
erain_piezo: "Rain Event",
|
|
45836
|
+
hrain_piezo: "Hourly Rain",
|
|
45837
|
+
drain_piezo: "Daily Rain",
|
|
45838
|
+
wrain_piezo: "Weekly Rain",
|
|
45839
|
+
mrain_piezo: "Monthly Rain",
|
|
45840
|
+
yrain_piezo: "Yearly Rain"
|
|
45841
|
+
};
|
|
45684
45842
|
/** Type guard: is `q` one of the known {@link Quantity} union members? */
|
|
45685
45843
|
function isKnownQuantity(q) {
|
|
45686
45844
|
return Object.prototype.hasOwnProperty.call(QUANTITY_LABEL, q);
|
|
@@ -45712,7 +45870,7 @@ var QUANTITY_LABEL = {
|
|
|
45712
45870
|
function buildEcowittGatewayCandidate(input) {
|
|
45713
45871
|
const children = input.sensors.map((sensor) => ({
|
|
45714
45872
|
childNativeId: sensor.id,
|
|
45715
|
-
name: sensorLabel(sensor.quantity, sensor.channel),
|
|
45873
|
+
name: sensorLabel(sensor.quantity, sensor.channel, sensor.name, sensor.id),
|
|
45716
45874
|
type: DeviceType.Sensor,
|
|
45717
45875
|
status: "online",
|
|
45718
45876
|
metadata: sensor.model !== void 0 ? { model: sensor.model } : {},
|
|
@@ -46187,7 +46345,8 @@ function clampForCap(cap, value) {
|
|
|
46187
46345
|
var ecowittSensorEntrySchema = object({
|
|
46188
46346
|
sensorId: string(),
|
|
46189
46347
|
quantity: string(),
|
|
46190
|
-
channel: number().optional()
|
|
46348
|
+
channel: number().optional(),
|
|
46349
|
+
name: string().optional()
|
|
46191
46350
|
});
|
|
46192
46351
|
/**
|
|
46193
46352
|
* Persisted config for an Ecowitt gateway Container. `brokerId` selects the
|
|
@@ -46230,7 +46389,7 @@ var EcowittContainerDevice = class extends BaseDevice {
|
|
|
46230
46389
|
const meta = {
|
|
46231
46390
|
type: DeviceType.Sensor,
|
|
46232
46391
|
role: roleForQuantity(sensor.quantity),
|
|
46233
|
-
name: sensorLabel(sensor.quantity, sensor.channel),
|
|
46392
|
+
name: sensorLabel(sensor.quantity, sensor.channel, sensor.name, sensor.sensorId),
|
|
46234
46393
|
linkDeviceId: this.id,
|
|
46235
46394
|
...this.integrationId !== void 0 ? { integrationId: this.integrationId } : {}
|
|
46236
46395
|
};
|
|
@@ -46385,7 +46544,8 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46385
46544
|
const nextSensors = sensors.map((s) => ({
|
|
46386
46545
|
sensorId: s.id,
|
|
46387
46546
|
quantity: s.quantity,
|
|
46388
|
-
...s.channel !== void 0 ? { channel: s.channel } : {}
|
|
46547
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
46548
|
+
...s.name !== void 0 ? { name: s.name } : {}
|
|
46389
46549
|
}));
|
|
46390
46550
|
const cfg = await devices.loadConfig(containerId).catch(() => ({}));
|
|
46391
46551
|
if (sensorsEqual(Array.isArray(cfg["sensors"]) ? cfg["sensors"] : [], nextSensors)) return;
|
|
@@ -46506,7 +46666,8 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46506
46666
|
sensors: ecowittFacades.getSensors(brokerId).map((s) => ({
|
|
46507
46667
|
sensorId: s.id,
|
|
46508
46668
|
quantity: s.quantity,
|
|
46509
|
-
...s.channel !== void 0 ? { channel: s.channel } : {}
|
|
46669
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
46670
|
+
...s.name !== void 0 ? { name: s.name } : {}
|
|
46510
46671
|
})),
|
|
46511
46672
|
system: "ecowitt",
|
|
46512
46673
|
integrationId,
|
|
@@ -46574,12 +46735,18 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46574
46735
|
/** Structural equality of two persisted sensor lists (order-independent by id). */
|
|
46575
46736
|
function sensorsEqual(a, b) {
|
|
46576
46737
|
if (a.length !== b.length) return false;
|
|
46577
|
-
const
|
|
46738
|
+
const aById = /* @__PURE__ */ new Map();
|
|
46578
46739
|
for (const entry of a) if (typeof entry === "object" && entry !== null && "sensorId" in entry) {
|
|
46579
46740
|
const id = entry.sensorId;
|
|
46580
|
-
if (typeof id === "string")
|
|
46741
|
+
if (typeof id === "string") {
|
|
46742
|
+
const name = "name" in entry && typeof entry.name === "string" ? entry.name : void 0;
|
|
46743
|
+
aById.set(id, name);
|
|
46744
|
+
}
|
|
46745
|
+
}
|
|
46746
|
+
for (const entry of b) {
|
|
46747
|
+
if (!aById.has(entry.sensorId)) return false;
|
|
46748
|
+
if (aById.get(entry.sensorId) !== entry.name) return false;
|
|
46581
46749
|
}
|
|
46582
|
-
for (const entry of b) if (!aIds.has(entry.sensorId)) return false;
|
|
46583
46750
|
return true;
|
|
46584
46751
|
}
|
|
46585
46752
|
//#endregion
|
package/dist/addon.mjs
CHANGED
|
@@ -10083,15 +10083,18 @@ var humiditySensorCapability = {
|
|
|
10083
10083
|
runtimeState: HumiditySensorStatusSchema
|
|
10084
10084
|
};
|
|
10085
10085
|
/**
|
|
10086
|
-
* Image display cap. Models
|
|
10087
|
-
*
|
|
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.
|
|
10088
10088
|
*
|
|
10089
|
-
* Read-only: there are no setters. The provider resolves
|
|
10090
|
-
*
|
|
10091
|
-
*
|
|
10092
|
-
*
|
|
10093
|
-
*
|
|
10094
|
-
*
|
|
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.
|
|
10095
10098
|
*/
|
|
10096
10099
|
var ImageStatusSchema = object({
|
|
10097
10100
|
/** Absolute signed URL the browser loads directly. Null when the
|
|
@@ -10117,18 +10120,47 @@ var imageCapability = {
|
|
|
10117
10120
|
*/
|
|
10118
10121
|
runtimeState: ImageStatusSchema
|
|
10119
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
|
+
]);
|
|
10120
10147
|
var LawnMowerControlStatusSchema = object({
|
|
10121
10148
|
/** Lifecycle activity of the mower. */
|
|
10122
|
-
activity:
|
|
10123
|
-
"idle",
|
|
10124
|
-
"mowing",
|
|
10125
|
-
"paused",
|
|
10126
|
-
"docked",
|
|
10127
|
-
"error"
|
|
10128
|
-
]),
|
|
10149
|
+
activity: LawnMowerActivitySchema,
|
|
10129
10150
|
/** 0..100 battery percentage. Null when the device has no battery
|
|
10130
10151
|
* reading. */
|
|
10131
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,
|
|
10132
10164
|
/** Ms epoch when the slice was last updated. */
|
|
10133
10165
|
lastChangedAt: number()
|
|
10134
10166
|
});
|
|
@@ -12354,6 +12386,7 @@ var VacuumStateSchema = _enum([
|
|
|
12354
12386
|
"paused",
|
|
12355
12387
|
"returning",
|
|
12356
12388
|
"docked",
|
|
12389
|
+
"drying",
|
|
12357
12390
|
"error"
|
|
12358
12391
|
]);
|
|
12359
12392
|
/**
|
|
@@ -12392,6 +12425,12 @@ var VacuumControlStatusSchema = object({
|
|
|
12392
12425
|
detergent: TankStatusSchema.nullable(),
|
|
12393
12426
|
/** Dust bin. Null when the hardware has no dust bin. */
|
|
12394
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(),
|
|
12395
12434
|
/** Ms epoch when the slice was last updated. */
|
|
12396
12435
|
lastChangedAt: number()
|
|
12397
12436
|
});
|
|
@@ -13358,9 +13397,29 @@ var BaseDevice = class {
|
|
|
13358
13397
|
* is open (Reolink writes `hasPtz/hasIntercom`, Hikvision writes
|
|
13359
13398
|
* `hasSupplementalLight/hasAlarmIo`, etc).
|
|
13360
13399
|
*
|
|
13361
|
-
* Default:
|
|
13362
|
-
|
|
13363
|
-
|
|
13400
|
+
* Default: nothing to probe → mark the device PROBED (set `lastProbedAt`) so
|
|
13401
|
+
* the kernel treats it as ready immediately. A device that derives its shape
|
|
13402
|
+
* from a spec (a container, or an accessory sensor) rather than from a
|
|
13403
|
+
* hardware probe has no probe to "complete"; without stamping `lastProbedAt`
|
|
13404
|
+
* it would look perpetually un-probed — logging "Initial probe did not
|
|
13405
|
+
* complete" on every boot and spinning a pointless retry chain. Drivers that
|
|
13406
|
+
* DO probe override this and write their own `feature-probe` slice (including
|
|
13407
|
+
* `lastProbedAt`) once their probe actually succeeds.
|
|
13408
|
+
*/
|
|
13409
|
+
async onProbe() {
|
|
13410
|
+
const base = this.runtimeState.getCapState("feature-probe") ?? {
|
|
13411
|
+
flags: {},
|
|
13412
|
+
deviceType: null,
|
|
13413
|
+
model: null,
|
|
13414
|
+
channelCount: null,
|
|
13415
|
+
lastProbedAt: 0,
|
|
13416
|
+
lastFetchedAt: 0
|
|
13417
|
+
};
|
|
13418
|
+
this.runtimeState.setCapState("feature-probe", {
|
|
13419
|
+
...base,
|
|
13420
|
+
lastProbedAt: Date.now()
|
|
13421
|
+
});
|
|
13422
|
+
}
|
|
13364
13423
|
/**
|
|
13365
13424
|
* Phase 5 — fired after the device + its accessories are registered.
|
|
13366
13425
|
* Drivers publish streams to the broker, kick off background tasks,
|
|
@@ -14822,17 +14881,32 @@ var ReleaseInputSchema = object({
|
|
|
14822
14881
|
* the parent cascades into every accessory. */
|
|
14823
14882
|
camDeviceId: number().int().nonnegative()
|
|
14824
14883
|
});
|
|
14825
|
-
var ResyncInputSchema = object({
|
|
14826
|
-
/** Parent CamStack device id of an adopted device. The provider resolves its
|
|
14827
|
-
* source (integration/broker + native id) and re-aligns the device's
|
|
14828
|
-
* structural spec (type/role/capabilities/units) with the live mapping,
|
|
14829
|
-
* rebuilding any child whose class changed while preserving operator edits. */
|
|
14830
|
-
camDeviceId: number().int().nonnegative()
|
|
14884
|
+
var ResyncInputSchema = object({
|
|
14885
|
+
/** Parent CamStack device id of an adopted device. The provider resolves its
|
|
14886
|
+
* source (integration/broker + native id) and re-aligns the device's
|
|
14887
|
+
* structural spec (type/role/capabilities/units) with the live mapping,
|
|
14888
|
+
* rebuilding any child whose class changed while preserving operator edits. */
|
|
14889
|
+
camDeviceId: number().int().nonnegative(),
|
|
14890
|
+
/** "Resync from zero" (#19). When true, the kernel PURGES every accessory
|
|
14891
|
+
* child of `camDeviceId` BEFORE the provider re-derives the device, so the
|
|
14892
|
+
* children are rebuilt fresh from source — correct names, coords, and units —
|
|
14893
|
+
* instead of being preserved by the incremental reconcile. Use to recover from
|
|
14894
|
+
* legacy generic/placeholder names that the normal name-precedence keeps frozen
|
|
14895
|
+
* (the operator's explicit reset). Push-driven integrations (no-op resync)
|
|
14896
|
+
* rebuild on their next snapshot; pull/command integrations rebuild in `resync`.
|
|
14897
|
+
* Operator edits on the PARENT (its name, layout, primary-child pick) survive —
|
|
14898
|
+
* only the children are torn down. Omitted/false ⇒ the normal incremental
|
|
14899
|
+
* re-sync that preserves children. */
|
|
14900
|
+
resetToSource: boolean().optional()
|
|
14901
|
+
});
|
|
14831
14902
|
var ResyncResultSchema = object({
|
|
14832
14903
|
/** True when the persisted spec actually changed (children may have been rebuilt). */
|
|
14833
14904
|
changed: boolean(),
|
|
14834
14905
|
/** Number of child devices rebuilt into a new class by this re-sync. */
|
|
14835
|
-
rebuiltChildren: number().int().nonnegative()
|
|
14906
|
+
rebuiltChildren: number().int().nonnegative(),
|
|
14907
|
+
/** Number of accessory children torn down by a `resetToSource` purge before the
|
|
14908
|
+
* provider re-derived the device. 0/absent for a normal incremental re-sync. */
|
|
14909
|
+
removedChildren: number().int().nonnegative().optional()
|
|
14836
14910
|
});
|
|
14837
14911
|
var deviceAdoptionCapability = {
|
|
14838
14912
|
name: "device-adoption",
|
|
@@ -16597,6 +16671,11 @@ var DeviceMetaSchema = object({
|
|
|
16597
16671
|
addonId: string(),
|
|
16598
16672
|
type: string(),
|
|
16599
16673
|
name: string(),
|
|
16674
|
+
/** True once an operator explicitly renamed the device via `setName`. Drives
|
|
16675
|
+
* reconcile name-precedence (preserve operator name vs adopt fresh provider
|
|
16676
|
+
* name). Absent ⇒ treated as user-named (PRESERVE) for legacy rows. See
|
|
16677
|
+
* `DeviceMeta.userNamed`. */
|
|
16678
|
+
userNamed: boolean().optional(),
|
|
16600
16679
|
location: string().nullable(),
|
|
16601
16680
|
disabled: boolean(),
|
|
16602
16681
|
parentDeviceId: number().nullable(),
|
|
@@ -16907,6 +16986,9 @@ method(object({
|
|
|
16907
16986
|
}), method(ReleaseInputSchema.extend({ addonId: string() }), _void(), {
|
|
16908
16987
|
kind: "mutation",
|
|
16909
16988
|
auth: "admin"
|
|
16989
|
+
}), method(ResyncInputSchema, ResyncResultSchema, {
|
|
16990
|
+
kind: "mutation",
|
|
16991
|
+
auth: "admin"
|
|
16910
16992
|
}), method(object({
|
|
16911
16993
|
deviceId: number(),
|
|
16912
16994
|
key: string(),
|
|
@@ -20669,6 +20751,12 @@ Object.freeze({
|
|
|
20669
20751
|
addonId: null,
|
|
20670
20752
|
access: "create"
|
|
20671
20753
|
},
|
|
20754
|
+
"deviceManager.adoptionResync": {
|
|
20755
|
+
capName: "device-manager",
|
|
20756
|
+
capScope: "system",
|
|
20757
|
+
addonId: null,
|
|
20758
|
+
access: "create"
|
|
20759
|
+
},
|
|
20672
20760
|
"deviceManager.allocateDeviceId": {
|
|
20673
20761
|
capName: "device-manager",
|
|
20674
20762
|
capScope: "system",
|
|
@@ -44577,7 +44665,8 @@ function classifyKey(key) {
|
|
|
44577
44665
|
const hex = lookupHexId(key);
|
|
44578
44666
|
if (hex !== void 0) return {
|
|
44579
44667
|
kind: "measurement",
|
|
44580
|
-
quantity: hex.quantity
|
|
44668
|
+
quantity: hex.quantity,
|
|
44669
|
+
name: hex.name
|
|
44581
44670
|
};
|
|
44582
44671
|
const named = Object.prototype.hasOwnProperty.call(NAMED_QUANTITY, key) ? NAMED_QUANTITY[key] : void 0;
|
|
44583
44672
|
if (named !== void 0) return {
|
|
@@ -44613,6 +44702,7 @@ var Station = class {
|
|
|
44613
44702
|
...owner !== void 0 ? { hardwareId: owner } : {},
|
|
44614
44703
|
...info?.model ? { model: info.model } : {},
|
|
44615
44704
|
...info?.channel !== void 0 ? { channel: info.channel } : {},
|
|
44705
|
+
...cls.name !== void 0 ? { name: cls.name } : {},
|
|
44616
44706
|
quantity: cls.quantity,
|
|
44617
44707
|
value: r.value,
|
|
44618
44708
|
unit: r.unit,
|
|
@@ -44646,6 +44736,7 @@ var Station = class {
|
|
|
44646
44736
|
this.upsert(id, {
|
|
44647
44737
|
id,
|
|
44648
44738
|
...r.channel !== void 0 ? { channel: r.channel } : {},
|
|
44739
|
+
...cls.name !== void 0 ? { name: cls.name } : {},
|
|
44649
44740
|
quantity: cls.quantity,
|
|
44650
44741
|
value: r.value,
|
|
44651
44742
|
unit: r.unit,
|
|
@@ -45671,17 +45762,84 @@ function valueFieldForCap(cap) {
|
|
|
45671
45762
|
}
|
|
45672
45763
|
}
|
|
45673
45764
|
/**
|
|
45674
|
-
* Build a human label for a sensor
|
|
45675
|
-
*
|
|
45765
|
+
* Build a human label for a sensor. The label is resolved in priority order so
|
|
45766
|
+
* SAME-QUANTITY sensors are always distinguishable:
|
|
45676
45767
|
*
|
|
45677
|
-
*
|
|
45678
|
-
*
|
|
45679
|
-
*
|
|
45768
|
+
* 1. The SPECIFIC measurement `name` the library resolved from its hex-id
|
|
45769
|
+
* table (e.g. "Outdoor Temperature", "Wind Gust") — present for the poll /
|
|
45770
|
+
* hex-id path.
|
|
45771
|
+
* 2. A specific name derived from the raw gateway FIELD KEY (e.g. `dailyrainin`
|
|
45772
|
+
* → "Daily Rain", `tempinf` → "Indoor Temperature") — covers the push /
|
|
45773
|
+
* named-and-pattern-key path where the library leaves `name` absent and
|
|
45774
|
+
* every same-quantity reading would otherwise collapse to one generic
|
|
45775
|
+
* label (five rain readings all "Precipitation", indoor+outdoor temp both
|
|
45776
|
+
* "Temperature", etc.). See {@link FIELD_KEY_LABEL}.
|
|
45777
|
+
* 3. The generic quantity label as a last resort.
|
|
45778
|
+
*
|
|
45779
|
+
* A channel suffix is appended when present (`temperature` + channel 2 →
|
|
45780
|
+
* `Temperature CH2`).
|
|
45781
|
+
*
|
|
45782
|
+
* `quantity`/`name` accept a plain `string` (not just {@link Quantity}) so a
|
|
45783
|
+
* persisted config value flows through without a cast; an unknown quantity falls
|
|
45784
|
+
* back to its raw string label. `sensorId` is the nodewitt `Sensor.id`
|
|
45785
|
+
* (`<owner>:<key>` or `<owner>:ch<n>:<key>`); the field key is its final
|
|
45786
|
+
* colon-segment.
|
|
45680
45787
|
*/
|
|
45681
|
-
function sensorLabel(quantity, channel) {
|
|
45682
|
-
const base = isKnownQuantity(quantity) ? QUANTITY_LABEL[quantity] : quantity;
|
|
45788
|
+
function sensorLabel(quantity, channel, name, sensorId) {
|
|
45789
|
+
const base = labelFromFieldKey(sensorId) ?? (name !== void 0 && name.length > 0 ? name : isKnownQuantity(quantity) ? QUANTITY_LABEL[quantity] : quantity);
|
|
45683
45790
|
return channel !== void 0 ? `${base} CH${channel}` : base;
|
|
45684
45791
|
}
|
|
45792
|
+
/**
|
|
45793
|
+
* Resolve a specific human label from a nodewitt sensor id by extracting its raw
|
|
45794
|
+
* gateway field key (the final colon-segment of `<owner>[:ch<n>]:<key>`) and
|
|
45795
|
+
* looking it up in {@link FIELD_KEY_LABEL}. Returns `undefined` when no id is
|
|
45796
|
+
* given or the key is unrecognised, so the caller falls through to the generic
|
|
45797
|
+
* quantity label.
|
|
45798
|
+
*/
|
|
45799
|
+
function labelFromFieldKey(sensorId) {
|
|
45800
|
+
if (sensorId === void 0) return void 0;
|
|
45801
|
+
const key = sensorId.slice(sensorId.lastIndexOf(":") + 1);
|
|
45802
|
+
return Object.prototype.hasOwnProperty.call(FIELD_KEY_LABEL, key) ? FIELD_KEY_LABEL[key] : void 0;
|
|
45803
|
+
}
|
|
45804
|
+
/**
|
|
45805
|
+
* Specific human labels for the raw Ecowitt gateway field keys that the library
|
|
45806
|
+
* classifies via its named / pattern tables (the push path) and therefore leaves
|
|
45807
|
+
* WITHOUT a `name`. Mirrors the vocabulary the library's hex-id table already
|
|
45808
|
+
* uses for the poll path, so a station reached over either transport reads the
|
|
45809
|
+
* same. Channel-suffixed keys (`temp3f`, `humidity2`, …) are handled by the
|
|
45810
|
+
* `channel` argument and intentionally omitted here.
|
|
45811
|
+
*/
|
|
45812
|
+
var FIELD_KEY_LABEL = {
|
|
45813
|
+
tempinf: "Indoor Temperature",
|
|
45814
|
+
tempf: "Outdoor Temperature",
|
|
45815
|
+
humidityin: "Indoor Humidity",
|
|
45816
|
+
humidity: "Outdoor Humidity",
|
|
45817
|
+
baromrelin: "Relative Pressure",
|
|
45818
|
+
baromabsin: "Absolute Pressure",
|
|
45819
|
+
windspeedmph: "Wind Speed",
|
|
45820
|
+
windgustmph: "Wind Gust",
|
|
45821
|
+
maxdailygust: "Max Daily Gust",
|
|
45822
|
+
winddir: "Wind Direction",
|
|
45823
|
+
solarradiation: "Solar Radiation",
|
|
45824
|
+
uv: "UV Index",
|
|
45825
|
+
lightning: "Lightning Distance",
|
|
45826
|
+
lightning_num: "Lightning Count",
|
|
45827
|
+
rainratein: "Rain Rate",
|
|
45828
|
+
eventrainin: "Rain Event",
|
|
45829
|
+
hourlyrainin: "Hourly Rain",
|
|
45830
|
+
dailyrainin: "Daily Rain",
|
|
45831
|
+
weeklyrainin: "Weekly Rain",
|
|
45832
|
+
monthlyrainin: "Monthly Rain",
|
|
45833
|
+
yearlyrainin: "Yearly Rain",
|
|
45834
|
+
totalrainin: "Total Rain",
|
|
45835
|
+
rrain_piezo: "Rain Rate",
|
|
45836
|
+
erain_piezo: "Rain Event",
|
|
45837
|
+
hrain_piezo: "Hourly Rain",
|
|
45838
|
+
drain_piezo: "Daily Rain",
|
|
45839
|
+
wrain_piezo: "Weekly Rain",
|
|
45840
|
+
mrain_piezo: "Monthly Rain",
|
|
45841
|
+
yrain_piezo: "Yearly Rain"
|
|
45842
|
+
};
|
|
45685
45843
|
/** Type guard: is `q` one of the known {@link Quantity} union members? */
|
|
45686
45844
|
function isKnownQuantity(q) {
|
|
45687
45845
|
return Object.prototype.hasOwnProperty.call(QUANTITY_LABEL, q);
|
|
@@ -45713,7 +45871,7 @@ var QUANTITY_LABEL = {
|
|
|
45713
45871
|
function buildEcowittGatewayCandidate(input) {
|
|
45714
45872
|
const children = input.sensors.map((sensor) => ({
|
|
45715
45873
|
childNativeId: sensor.id,
|
|
45716
|
-
name: sensorLabel(sensor.quantity, sensor.channel),
|
|
45874
|
+
name: sensorLabel(sensor.quantity, sensor.channel, sensor.name, sensor.id),
|
|
45717
45875
|
type: DeviceType.Sensor,
|
|
45718
45876
|
status: "online",
|
|
45719
45877
|
metadata: sensor.model !== void 0 ? { model: sensor.model } : {},
|
|
@@ -46188,7 +46346,8 @@ function clampForCap(cap, value) {
|
|
|
46188
46346
|
var ecowittSensorEntrySchema = object({
|
|
46189
46347
|
sensorId: string(),
|
|
46190
46348
|
quantity: string(),
|
|
46191
|
-
channel: number().optional()
|
|
46349
|
+
channel: number().optional(),
|
|
46350
|
+
name: string().optional()
|
|
46192
46351
|
});
|
|
46193
46352
|
/**
|
|
46194
46353
|
* Persisted config for an Ecowitt gateway Container. `brokerId` selects the
|
|
@@ -46231,7 +46390,7 @@ var EcowittContainerDevice = class extends BaseDevice {
|
|
|
46231
46390
|
const meta = {
|
|
46232
46391
|
type: DeviceType.Sensor,
|
|
46233
46392
|
role: roleForQuantity(sensor.quantity),
|
|
46234
|
-
name: sensorLabel(sensor.quantity, sensor.channel),
|
|
46393
|
+
name: sensorLabel(sensor.quantity, sensor.channel, sensor.name, sensor.sensorId),
|
|
46235
46394
|
linkDeviceId: this.id,
|
|
46236
46395
|
...this.integrationId !== void 0 ? { integrationId: this.integrationId } : {}
|
|
46237
46396
|
};
|
|
@@ -46386,7 +46545,8 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46386
46545
|
const nextSensors = sensors.map((s) => ({
|
|
46387
46546
|
sensorId: s.id,
|
|
46388
46547
|
quantity: s.quantity,
|
|
46389
|
-
...s.channel !== void 0 ? { channel: s.channel } : {}
|
|
46548
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
46549
|
+
...s.name !== void 0 ? { name: s.name } : {}
|
|
46390
46550
|
}));
|
|
46391
46551
|
const cfg = await devices.loadConfig(containerId).catch(() => ({}));
|
|
46392
46552
|
if (sensorsEqual(Array.isArray(cfg["sensors"]) ? cfg["sensors"] : [], nextSensors)) return;
|
|
@@ -46507,7 +46667,8 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46507
46667
|
sensors: ecowittFacades.getSensors(brokerId).map((s) => ({
|
|
46508
46668
|
sensorId: s.id,
|
|
46509
46669
|
quantity: s.quantity,
|
|
46510
|
-
...s.channel !== void 0 ? { channel: s.channel } : {}
|
|
46670
|
+
...s.channel !== void 0 ? { channel: s.channel } : {},
|
|
46671
|
+
...s.name !== void 0 ? { name: s.name } : {}
|
|
46511
46672
|
})),
|
|
46512
46673
|
system: "ecowitt",
|
|
46513
46674
|
integrationId,
|
|
@@ -46575,12 +46736,18 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
|
|
|
46575
46736
|
/** Structural equality of two persisted sensor lists (order-independent by id). */
|
|
46576
46737
|
function sensorsEqual(a, b) {
|
|
46577
46738
|
if (a.length !== b.length) return false;
|
|
46578
|
-
const
|
|
46739
|
+
const aById = /* @__PURE__ */ new Map();
|
|
46579
46740
|
for (const entry of a) if (typeof entry === "object" && entry !== null && "sensorId" in entry) {
|
|
46580
46741
|
const id = entry.sensorId;
|
|
46581
|
-
if (typeof id === "string")
|
|
46742
|
+
if (typeof id === "string") {
|
|
46743
|
+
const name = "name" in entry && typeof entry.name === "string" ? entry.name : void 0;
|
|
46744
|
+
aById.set(id, name);
|
|
46745
|
+
}
|
|
46746
|
+
}
|
|
46747
|
+
for (const entry of b) {
|
|
46748
|
+
if (!aById.has(entry.sensorId)) return false;
|
|
46749
|
+
if (aById.get(entry.sensorId) !== entry.name) return false;
|
|
46582
46750
|
}
|
|
46583
|
-
for (const entry of b) if (!aIds.has(entry.sensorId)) return false;
|
|
46584
46751
|
return true;
|
|
46585
46752
|
}
|
|
46586
46753
|
//#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.7",
|
|
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",
|