@camstack/addon-provider-reolink 1.2.88 → 1.2.90
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 +117 -25
- package/dist/addon.mjs +117 -25
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -7592,7 +7592,8 @@ var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
|
|
|
7592
7592
|
var CameraSwitchUnavailableReasonSchema = _enum([
|
|
7593
7593
|
"no-provider",
|
|
7594
7594
|
"source-unreachable",
|
|
7595
|
-
"not-configured"
|
|
7595
|
+
"not-configured",
|
|
7596
|
+
"device-disabled"
|
|
7596
7597
|
]);
|
|
7597
7598
|
/**
|
|
7598
7599
|
* One switch, resolved for one camera.
|
|
@@ -24325,12 +24326,20 @@ var BatteryStatusSchema = object({
|
|
|
24325
24326
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
24326
24327
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
24327
24328
|
* common on other battery cams). `'none'` means running on battery
|
|
24328
|
-
* alone.
|
|
24329
|
+
* alone — a NEGATIVE the firmware actually reported.
|
|
24330
|
+
*
|
|
24331
|
+
* `'unknown'` is the absence of an answer: the provider has not read the
|
|
24332
|
+
* power fields yet, or the firmware reported neither. Before it existed,
|
|
24333
|
+
* two unreadable fields produced `'none'`, and a camera nobody had reached
|
|
24334
|
+
* was drawn "on battery alone" — the same D315 defect `percentage` closed
|
|
24335
|
+
* by going nullable. Consumers SKIP it (no charger state published, no
|
|
24336
|
+
* glyph, no HomeKit ChargingState) rather than coerce it either way.
|
|
24329
24337
|
*/
|
|
24330
24338
|
charging: _enum([
|
|
24331
24339
|
"dc",
|
|
24332
24340
|
"solar",
|
|
24333
|
-
"none"
|
|
24341
|
+
"none",
|
|
24342
|
+
"unknown"
|
|
24334
24343
|
]),
|
|
24335
24344
|
/**
|
|
24336
24345
|
* True when the camera firmware has gone into low-power mode. Battery
|
|
@@ -24420,7 +24429,7 @@ onStatusChanged: { data: object({
|
|
|
24420
24429
|
kind: "push",
|
|
24421
24430
|
empty: {
|
|
24422
24431
|
percentage: 0,
|
|
24423
|
-
charging: "
|
|
24432
|
+
charging: "unknown",
|
|
24424
24433
|
sleeping: false,
|
|
24425
24434
|
lastUpdated: 0
|
|
24426
24435
|
}
|
|
@@ -34128,13 +34137,30 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
34128
34137
|
async onRestoreDevices(savedDevices) {
|
|
34129
34138
|
const restored = /* @__PURE__ */ new Set();
|
|
34130
34139
|
const failures = [];
|
|
34140
|
+
const liveStableIds = new Set((await this.ctx.kernel.devices.getAll()).map((device) => device.stableId));
|
|
34131
34141
|
const attemptRestore = async (saved) => {
|
|
34132
34142
|
if (restored.has(saved.id)) return;
|
|
34143
|
+
if (liveStableIds.has(saved.stableId)) {
|
|
34144
|
+
this.ctx.logger.debug("Restore row already live — not dialled again", { tags: {
|
|
34145
|
+
deviceId: saved.id,
|
|
34146
|
+
stableId: saved.stableId
|
|
34147
|
+
} });
|
|
34148
|
+
restored.add(saved.id);
|
|
34149
|
+
return;
|
|
34150
|
+
}
|
|
34133
34151
|
const Class = this.deviceClasses[saved.type];
|
|
34134
34152
|
if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
|
|
34135
34153
|
if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
|
|
34136
34154
|
await this.healSavedConfig(saved, savedDevices);
|
|
34137
|
-
|
|
34155
|
+
try {
|
|
34156
|
+
await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
|
|
34157
|
+
} catch (err) {
|
|
34158
|
+
if (!(await this.ctx.kernel.devices.getAll()).some((device) => device.stableId === saved.stableId)) throw err;
|
|
34159
|
+
this.ctx.logger.debug("Restore row came to life during its own dial — kept", { tags: {
|
|
34160
|
+
deviceId: saved.id,
|
|
34161
|
+
stableId: saved.stableId
|
|
34162
|
+
} });
|
|
34163
|
+
}
|
|
34138
34164
|
restored.add(saved.id);
|
|
34139
34165
|
};
|
|
34140
34166
|
const topLevel = savedDevices.filter((saved) => saved.parentDeviceId === null);
|
|
@@ -231801,13 +231827,15 @@ var reolinkDebugActions = defineCustomActions({ debugRawRead: customAction(RawRe
|
|
|
231801
231827
|
* passato da `adapterStatus: 1` a `0`, con `chargeStatus: 0` da entrambe le
|
|
231802
231828
|
* parti — cioè `chargeStatus` da solo NON distingue i due stati.
|
|
231803
231829
|
*
|
|
231804
|
-
*
|
|
231805
|
-
*
|
|
231806
|
-
*
|
|
231807
|
-
* `
|
|
231808
|
-
*
|
|
231830
|
+
* Due campi assenti NON sono `'none'`: `'none'` è un negativo che il firmware
|
|
231831
|
+
* ha detto ("nessun adattatore, cella ferma"), l'assenza di entrambi è
|
|
231832
|
+
* `'unknown'` — lo stesso confine che `percentage` ha chiuso diventando
|
|
231833
|
+
* `nullable` (D315). Il chiamante (`mapBatteryInfo`) non arriva qui con un
|
|
231834
|
+
* push che non parla di alimentazione: `reportsPowerSource` lo filtra prima e
|
|
231835
|
+
* conserva la convinzione precedente.
|
|
231809
231836
|
*/
|
|
231810
231837
|
function deriveChargingSource(adapterStatus, chargeStatus) {
|
|
231838
|
+
if (adapterStatus === void 0 && chargeStatus === void 0) return "unknown";
|
|
231811
231839
|
const adapter = (adapterStatus ?? "").toLowerCase();
|
|
231812
231840
|
const charge = (chargeStatus ?? "").toLowerCase();
|
|
231813
231841
|
if (adapter.includes("solar")) return "solar";
|
|
@@ -236946,7 +236974,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
236946
236974
|
mapBatteryInfo(info) {
|
|
236947
236975
|
return {
|
|
236948
236976
|
percentage: typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.percentage ?? null,
|
|
236949
|
-
charging: reportsPowerSource(info) ? deriveChargingSource(info.adapterStatus, info.chargeStatus) : this.state.battery.charging ?? "
|
|
236977
|
+
charging: reportsPowerSource(info) ? deriveChargingSource(info.adapterStatus, info.chargeStatus) : this.state.battery.charging ?? "unknown",
|
|
236950
236978
|
sleeping: info.sleeping === true || this.sleeping,
|
|
236951
236979
|
lastUpdated: Date.now()
|
|
236952
236980
|
};
|
|
@@ -243003,6 +243031,69 @@ function asDebugSocketFlags(value) {
|
|
|
243003
243031
|
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
243004
243032
|
}
|
|
243005
243033
|
//#endregion
|
|
243034
|
+
//#region src/hub-battery-fields.ts
|
|
243035
|
+
/**
|
|
243036
|
+
* The hub boundary for the per-channel battery power fields.
|
|
243037
|
+
*
|
|
243038
|
+
* The Reolink hub/NVR reports `chargeStatus` and `adapterStatus` as NUMBERS
|
|
243039
|
+
* (0/1/2); the standalone camera path reports the same facts as strings, and
|
|
243040
|
+
* `deriveChargingSource` (`battery-charging.ts`) speaks only the string
|
|
243041
|
+
* vocabulary. This module is the translation, and — the part that was
|
|
243042
|
+
* missing — it NAMES a code it cannot translate instead of dropping the field
|
|
243043
|
+
* silently. A dropped field reads as "the push said nothing about power", so
|
|
243044
|
+
* a new firmware value would have kept the previous belief forever, with not
|
|
243045
|
+
* one log line. The ledger lets the hub warn once per (device, field, code).
|
|
243046
|
+
*/
|
|
243047
|
+
var CHARGE_STATUS_BY_CODE = {
|
|
243048
|
+
0: "none",
|
|
243049
|
+
1: "charging",
|
|
243050
|
+
2: "chargeComplete"
|
|
243051
|
+
};
|
|
243052
|
+
var ADAPTER_STATUS_BY_CODE = {
|
|
243053
|
+
0: "none",
|
|
243054
|
+
1: "dc",
|
|
243055
|
+
2: "solarPanel"
|
|
243056
|
+
};
|
|
243057
|
+
function mapOne(field, value, byCode) {
|
|
243058
|
+
if (value === void 0 || value === null) return {};
|
|
243059
|
+
if (typeof value === "string") return { mapped: value };
|
|
243060
|
+
if (typeof value === "number") {
|
|
243061
|
+
const mapped = byCode[value];
|
|
243062
|
+
if (mapped !== void 0) return { mapped };
|
|
243063
|
+
}
|
|
243064
|
+
return { unmapped: {
|
|
243065
|
+
field,
|
|
243066
|
+
value
|
|
243067
|
+
} };
|
|
243068
|
+
}
|
|
243069
|
+
/** Translate one channel's raw CGI power fields; never throws. */
|
|
243070
|
+
function mapHubBatteryPower(cgi) {
|
|
243071
|
+
const charge = mapOne("chargeStatus", cgi?.["chargeStatus"], CHARGE_STATUS_BY_CODE);
|
|
243072
|
+
const adapter = mapOne("adapterStatus", cgi?.["adapterStatus"], ADAPTER_STATUS_BY_CODE);
|
|
243073
|
+
return {
|
|
243074
|
+
fields: {
|
|
243075
|
+
...charge.mapped !== void 0 ? { chargeStatus: charge.mapped } : {},
|
|
243076
|
+
...adapter.mapped !== void 0 ? { adapterStatus: adapter.mapped } : {}
|
|
243077
|
+
},
|
|
243078
|
+
unmapped: [...charge.unmapped !== void 0 ? [charge.unmapped] : [], ...adapter.unmapped !== void 0 ? [adapter.unmapped] : []]
|
|
243079
|
+
};
|
|
243080
|
+
}
|
|
243081
|
+
/**
|
|
243082
|
+
* Remembers which (device, field, code) triples were already reported, so a
|
|
243083
|
+
* polled read warns once per finding and not once per tick. Unbounded only in
|
|
243084
|
+
* theory: the key space is a few cameras times two fields times the handful of
|
|
243085
|
+
* codes a firmware can invent.
|
|
243086
|
+
*/
|
|
243087
|
+
var UnmappedCodeLedger = class {
|
|
243088
|
+
seen = /* @__PURE__ */ new Set();
|
|
243089
|
+
firstSeen(deviceId, field, value) {
|
|
243090
|
+
const key = `${deviceId}:${field}:${String(value)}`;
|
|
243091
|
+
if (this.seen.has(key)) return false;
|
|
243092
|
+
this.seen.add(key);
|
|
243093
|
+
return true;
|
|
243094
|
+
}
|
|
243095
|
+
};
|
|
243096
|
+
//#endregion
|
|
243006
243097
|
//#region src/reolink-hub.ts
|
|
243007
243098
|
var HUB_DISCOVERY_REFRESH_TIMEOUT_MS = 8e3;
|
|
243008
243099
|
/**
|
|
@@ -243064,6 +243155,8 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
243064
243155
|
/** One-shot: the raw per-channel battery payload is logged once per
|
|
243065
243156
|
* process so the firmware's charge-field naming is on record. */
|
|
243066
243157
|
batteryShapeLogged = false;
|
|
243158
|
+
/** Firmware power codes this hub could not translate — warned once each. */
|
|
243159
|
+
unmappedBatteryCodes = new UnmappedCodeLedger();
|
|
243067
243160
|
/** Channels the last successful discovery refresh listed as present but
|
|
243068
243161
|
* NOT adopted — their pushes are expected drops (debug), not faults. */
|
|
243069
243162
|
knownUnadoptedChannels = /* @__PURE__ */ new Set();
|
|
@@ -243544,10 +243637,21 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
243544
243637
|
const child = all.find((d) => d.id === deviceId);
|
|
243545
243638
|
if (!(child instanceof ReolinkCamera)) continue;
|
|
243546
243639
|
const cgi = info.entries[0];
|
|
243640
|
+
const power = mapHubBatteryPower(cgi);
|
|
243641
|
+
for (const { field, value } of power.unmapped) {
|
|
243642
|
+
if (!this.unmappedBatteryCodes.firstSeen(deviceId, field, value)) continue;
|
|
243643
|
+
this.ctx.logger.warn("Reolink Hub: unmapped battery power code — field dropped", {
|
|
243644
|
+
tags: { deviceId },
|
|
243645
|
+
meta: {
|
|
243646
|
+
channel: Number(chKey),
|
|
243647
|
+
field,
|
|
243648
|
+
value
|
|
243649
|
+
}
|
|
243650
|
+
});
|
|
243651
|
+
}
|
|
243547
243652
|
child.applyHubBatteryInfo({
|
|
243548
243653
|
batteryPercent: info.batteryLevel,
|
|
243549
|
-
...
|
|
243550
|
-
...mapHubAdapterStatus(cgi?.["adapterStatus"]) !== void 0 ? { adapterStatus: mapHubAdapterStatus(cgi?.["adapterStatus"]) } : {}
|
|
243654
|
+
...power.fields
|
|
243551
243655
|
});
|
|
243552
243656
|
}
|
|
243553
243657
|
} catch (err) {
|
|
@@ -243988,18 +244092,6 @@ function discoveredStatusFor(d) {
|
|
|
243988
244092
|
* chargeStatus: 0 none · 1 charging · 2 charge complete
|
|
243989
244093
|
* adapterStatus: 0 none · 1 DC adapter · 2 solar panel
|
|
243990
244094
|
*/
|
|
243991
|
-
function mapHubChargeStatus(value) {
|
|
243992
|
-
if (typeof value === "string") return value;
|
|
243993
|
-
if (value === 0) return "none";
|
|
243994
|
-
if (value === 1) return "charging";
|
|
243995
|
-
if (value === 2) return "chargeComplete";
|
|
243996
|
-
}
|
|
243997
|
-
function mapHubAdapterStatus(value) {
|
|
243998
|
-
if (typeof value === "string") return value;
|
|
243999
|
-
if (value === 0) return "none";
|
|
244000
|
-
if (value === 1) return "dc";
|
|
244001
|
-
if (value === 2) return "solarPanel";
|
|
244002
|
-
}
|
|
244003
244095
|
//#endregion
|
|
244004
244096
|
//#region src/creation-schema.ts
|
|
244005
244097
|
/**
|
package/dist/addon.mjs
CHANGED
|
@@ -7587,7 +7587,8 @@ var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
|
|
|
7587
7587
|
var CameraSwitchUnavailableReasonSchema = _enum([
|
|
7588
7588
|
"no-provider",
|
|
7589
7589
|
"source-unreachable",
|
|
7590
|
-
"not-configured"
|
|
7590
|
+
"not-configured",
|
|
7591
|
+
"device-disabled"
|
|
7591
7592
|
]);
|
|
7592
7593
|
/**
|
|
7593
7594
|
* One switch, resolved for one camera.
|
|
@@ -24320,12 +24321,20 @@ var BatteryStatusSchema = object({
|
|
|
24320
24321
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
24321
24322
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
24322
24323
|
* common on other battery cams). `'none'` means running on battery
|
|
24323
|
-
* alone.
|
|
24324
|
+
* alone — a NEGATIVE the firmware actually reported.
|
|
24325
|
+
*
|
|
24326
|
+
* `'unknown'` is the absence of an answer: the provider has not read the
|
|
24327
|
+
* power fields yet, or the firmware reported neither. Before it existed,
|
|
24328
|
+
* two unreadable fields produced `'none'`, and a camera nobody had reached
|
|
24329
|
+
* was drawn "on battery alone" — the same D315 defect `percentage` closed
|
|
24330
|
+
* by going nullable. Consumers SKIP it (no charger state published, no
|
|
24331
|
+
* glyph, no HomeKit ChargingState) rather than coerce it either way.
|
|
24324
24332
|
*/
|
|
24325
24333
|
charging: _enum([
|
|
24326
24334
|
"dc",
|
|
24327
24335
|
"solar",
|
|
24328
|
-
"none"
|
|
24336
|
+
"none",
|
|
24337
|
+
"unknown"
|
|
24329
24338
|
]),
|
|
24330
24339
|
/**
|
|
24331
24340
|
* True when the camera firmware has gone into low-power mode. Battery
|
|
@@ -24415,7 +24424,7 @@ onStatusChanged: { data: object({
|
|
|
24415
24424
|
kind: "push",
|
|
24416
24425
|
empty: {
|
|
24417
24426
|
percentage: 0,
|
|
24418
|
-
charging: "
|
|
24427
|
+
charging: "unknown",
|
|
24419
24428
|
sleeping: false,
|
|
24420
24429
|
lastUpdated: 0
|
|
24421
24430
|
}
|
|
@@ -34123,13 +34132,30 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
34123
34132
|
async onRestoreDevices(savedDevices) {
|
|
34124
34133
|
const restored = /* @__PURE__ */ new Set();
|
|
34125
34134
|
const failures = [];
|
|
34135
|
+
const liveStableIds = new Set((await this.ctx.kernel.devices.getAll()).map((device) => device.stableId));
|
|
34126
34136
|
const attemptRestore = async (saved) => {
|
|
34127
34137
|
if (restored.has(saved.id)) return;
|
|
34138
|
+
if (liveStableIds.has(saved.stableId)) {
|
|
34139
|
+
this.ctx.logger.debug("Restore row already live — not dialled again", { tags: {
|
|
34140
|
+
deviceId: saved.id,
|
|
34141
|
+
stableId: saved.stableId
|
|
34142
|
+
} });
|
|
34143
|
+
restored.add(saved.id);
|
|
34144
|
+
return;
|
|
34145
|
+
}
|
|
34128
34146
|
const Class = this.deviceClasses[saved.type];
|
|
34129
34147
|
if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
|
|
34130
34148
|
if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
|
|
34131
34149
|
await this.healSavedConfig(saved, savedDevices);
|
|
34132
|
-
|
|
34150
|
+
try {
|
|
34151
|
+
await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
|
|
34152
|
+
} catch (err) {
|
|
34153
|
+
if (!(await this.ctx.kernel.devices.getAll()).some((device) => device.stableId === saved.stableId)) throw err;
|
|
34154
|
+
this.ctx.logger.debug("Restore row came to life during its own dial — kept", { tags: {
|
|
34155
|
+
deviceId: saved.id,
|
|
34156
|
+
stableId: saved.stableId
|
|
34157
|
+
} });
|
|
34158
|
+
}
|
|
34133
34159
|
restored.add(saved.id);
|
|
34134
34160
|
};
|
|
34135
34161
|
const topLevel = savedDevices.filter((saved) => saved.parentDeviceId === null);
|
|
@@ -231781,13 +231807,15 @@ var reolinkDebugActions = defineCustomActions({ debugRawRead: customAction(RawRe
|
|
|
231781
231807
|
* passato da `adapterStatus: 1` a `0`, con `chargeStatus: 0` da entrambe le
|
|
231782
231808
|
* parti — cioè `chargeStatus` da solo NON distingue i due stati.
|
|
231783
231809
|
*
|
|
231784
|
-
*
|
|
231785
|
-
*
|
|
231786
|
-
*
|
|
231787
|
-
* `
|
|
231788
|
-
*
|
|
231810
|
+
* Due campi assenti NON sono `'none'`: `'none'` è un negativo che il firmware
|
|
231811
|
+
* ha detto ("nessun adattatore, cella ferma"), l'assenza di entrambi è
|
|
231812
|
+
* `'unknown'` — lo stesso confine che `percentage` ha chiuso diventando
|
|
231813
|
+
* `nullable` (D315). Il chiamante (`mapBatteryInfo`) non arriva qui con un
|
|
231814
|
+
* push che non parla di alimentazione: `reportsPowerSource` lo filtra prima e
|
|
231815
|
+
* conserva la convinzione precedente.
|
|
231789
231816
|
*/
|
|
231790
231817
|
function deriveChargingSource(adapterStatus, chargeStatus) {
|
|
231818
|
+
if (adapterStatus === void 0 && chargeStatus === void 0) return "unknown";
|
|
231791
231819
|
const adapter = (adapterStatus ?? "").toLowerCase();
|
|
231792
231820
|
const charge = (chargeStatus ?? "").toLowerCase();
|
|
231793
231821
|
if (adapter.includes("solar")) return "solar";
|
|
@@ -236926,7 +236954,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
236926
236954
|
mapBatteryInfo(info) {
|
|
236927
236955
|
return {
|
|
236928
236956
|
percentage: typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.percentage ?? null,
|
|
236929
|
-
charging: reportsPowerSource(info) ? deriveChargingSource(info.adapterStatus, info.chargeStatus) : this.state.battery.charging ?? "
|
|
236957
|
+
charging: reportsPowerSource(info) ? deriveChargingSource(info.adapterStatus, info.chargeStatus) : this.state.battery.charging ?? "unknown",
|
|
236930
236958
|
sleeping: info.sleeping === true || this.sleeping,
|
|
236931
236959
|
lastUpdated: Date.now()
|
|
236932
236960
|
};
|
|
@@ -242983,6 +243011,69 @@ function asDebugSocketFlags(value) {
|
|
|
242983
243011
|
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
242984
243012
|
}
|
|
242985
243013
|
//#endregion
|
|
243014
|
+
//#region src/hub-battery-fields.ts
|
|
243015
|
+
/**
|
|
243016
|
+
* The hub boundary for the per-channel battery power fields.
|
|
243017
|
+
*
|
|
243018
|
+
* The Reolink hub/NVR reports `chargeStatus` and `adapterStatus` as NUMBERS
|
|
243019
|
+
* (0/1/2); the standalone camera path reports the same facts as strings, and
|
|
243020
|
+
* `deriveChargingSource` (`battery-charging.ts`) speaks only the string
|
|
243021
|
+
* vocabulary. This module is the translation, and — the part that was
|
|
243022
|
+
* missing — it NAMES a code it cannot translate instead of dropping the field
|
|
243023
|
+
* silently. A dropped field reads as "the push said nothing about power", so
|
|
243024
|
+
* a new firmware value would have kept the previous belief forever, with not
|
|
243025
|
+
* one log line. The ledger lets the hub warn once per (device, field, code).
|
|
243026
|
+
*/
|
|
243027
|
+
var CHARGE_STATUS_BY_CODE = {
|
|
243028
|
+
0: "none",
|
|
243029
|
+
1: "charging",
|
|
243030
|
+
2: "chargeComplete"
|
|
243031
|
+
};
|
|
243032
|
+
var ADAPTER_STATUS_BY_CODE = {
|
|
243033
|
+
0: "none",
|
|
243034
|
+
1: "dc",
|
|
243035
|
+
2: "solarPanel"
|
|
243036
|
+
};
|
|
243037
|
+
function mapOne(field, value, byCode) {
|
|
243038
|
+
if (value === void 0 || value === null) return {};
|
|
243039
|
+
if (typeof value === "string") return { mapped: value };
|
|
243040
|
+
if (typeof value === "number") {
|
|
243041
|
+
const mapped = byCode[value];
|
|
243042
|
+
if (mapped !== void 0) return { mapped };
|
|
243043
|
+
}
|
|
243044
|
+
return { unmapped: {
|
|
243045
|
+
field,
|
|
243046
|
+
value
|
|
243047
|
+
} };
|
|
243048
|
+
}
|
|
243049
|
+
/** Translate one channel's raw CGI power fields; never throws. */
|
|
243050
|
+
function mapHubBatteryPower(cgi) {
|
|
243051
|
+
const charge = mapOne("chargeStatus", cgi?.["chargeStatus"], CHARGE_STATUS_BY_CODE);
|
|
243052
|
+
const adapter = mapOne("adapterStatus", cgi?.["adapterStatus"], ADAPTER_STATUS_BY_CODE);
|
|
243053
|
+
return {
|
|
243054
|
+
fields: {
|
|
243055
|
+
...charge.mapped !== void 0 ? { chargeStatus: charge.mapped } : {},
|
|
243056
|
+
...adapter.mapped !== void 0 ? { adapterStatus: adapter.mapped } : {}
|
|
243057
|
+
},
|
|
243058
|
+
unmapped: [...charge.unmapped !== void 0 ? [charge.unmapped] : [], ...adapter.unmapped !== void 0 ? [adapter.unmapped] : []]
|
|
243059
|
+
};
|
|
243060
|
+
}
|
|
243061
|
+
/**
|
|
243062
|
+
* Remembers which (device, field, code) triples were already reported, so a
|
|
243063
|
+
* polled read warns once per finding and not once per tick. Unbounded only in
|
|
243064
|
+
* theory: the key space is a few cameras times two fields times the handful of
|
|
243065
|
+
* codes a firmware can invent.
|
|
243066
|
+
*/
|
|
243067
|
+
var UnmappedCodeLedger = class {
|
|
243068
|
+
seen = /* @__PURE__ */ new Set();
|
|
243069
|
+
firstSeen(deviceId, field, value) {
|
|
243070
|
+
const key = `${deviceId}:${field}:${String(value)}`;
|
|
243071
|
+
if (this.seen.has(key)) return false;
|
|
243072
|
+
this.seen.add(key);
|
|
243073
|
+
return true;
|
|
243074
|
+
}
|
|
243075
|
+
};
|
|
243076
|
+
//#endregion
|
|
242986
243077
|
//#region src/reolink-hub.ts
|
|
242987
243078
|
var HUB_DISCOVERY_REFRESH_TIMEOUT_MS = 8e3;
|
|
242988
243079
|
/**
|
|
@@ -243044,6 +243135,8 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
243044
243135
|
/** One-shot: the raw per-channel battery payload is logged once per
|
|
243045
243136
|
* process so the firmware's charge-field naming is on record. */
|
|
243046
243137
|
batteryShapeLogged = false;
|
|
243138
|
+
/** Firmware power codes this hub could not translate — warned once each. */
|
|
243139
|
+
unmappedBatteryCodes = new UnmappedCodeLedger();
|
|
243047
243140
|
/** Channels the last successful discovery refresh listed as present but
|
|
243048
243141
|
* NOT adopted — their pushes are expected drops (debug), not faults. */
|
|
243049
243142
|
knownUnadoptedChannels = /* @__PURE__ */ new Set();
|
|
@@ -243524,10 +243617,21 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
|
|
|
243524
243617
|
const child = all.find((d) => d.id === deviceId);
|
|
243525
243618
|
if (!(child instanceof ReolinkCamera)) continue;
|
|
243526
243619
|
const cgi = info.entries[0];
|
|
243620
|
+
const power = mapHubBatteryPower(cgi);
|
|
243621
|
+
for (const { field, value } of power.unmapped) {
|
|
243622
|
+
if (!this.unmappedBatteryCodes.firstSeen(deviceId, field, value)) continue;
|
|
243623
|
+
this.ctx.logger.warn("Reolink Hub: unmapped battery power code — field dropped", {
|
|
243624
|
+
tags: { deviceId },
|
|
243625
|
+
meta: {
|
|
243626
|
+
channel: Number(chKey),
|
|
243627
|
+
field,
|
|
243628
|
+
value
|
|
243629
|
+
}
|
|
243630
|
+
});
|
|
243631
|
+
}
|
|
243527
243632
|
child.applyHubBatteryInfo({
|
|
243528
243633
|
batteryPercent: info.batteryLevel,
|
|
243529
|
-
...
|
|
243530
|
-
...mapHubAdapterStatus(cgi?.["adapterStatus"]) !== void 0 ? { adapterStatus: mapHubAdapterStatus(cgi?.["adapterStatus"]) } : {}
|
|
243634
|
+
...power.fields
|
|
243531
243635
|
});
|
|
243532
243636
|
}
|
|
243533
243637
|
} catch (err) {
|
|
@@ -243968,18 +244072,6 @@ function discoveredStatusFor(d) {
|
|
|
243968
244072
|
* chargeStatus: 0 none · 1 charging · 2 charge complete
|
|
243969
244073
|
* adapterStatus: 0 none · 1 DC adapter · 2 solar panel
|
|
243970
244074
|
*/
|
|
243971
|
-
function mapHubChargeStatus(value) {
|
|
243972
|
-
if (typeof value === "string") return value;
|
|
243973
|
-
if (value === 0) return "none";
|
|
243974
|
-
if (value === 1) return "charging";
|
|
243975
|
-
if (value === 2) return "chargeComplete";
|
|
243976
|
-
}
|
|
243977
|
-
function mapHubAdapterStatus(value) {
|
|
243978
|
-
if (typeof value === "string") return value;
|
|
243979
|
-
if (value === 0) return "none";
|
|
243980
|
-
if (value === 1) return "dc";
|
|
243981
|
-
if (value === 2) return "solarPanel";
|
|
243982
|
-
}
|
|
243983
244075
|
//#endregion
|
|
243984
244076
|
//#region src/creation-schema.ts
|
|
243985
244077
|
/**
|