@mega-yfue/eufy-sdk 0.2.0-beta.11 → 0.2.0-beta.13

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/index.js CHANGED
@@ -3972,12 +3972,32 @@ function decodePowerSource(raw) {
3972
3972
  function recordSetting(param, value, ctx) {
3973
3973
  return setStationScalar(param, value, ctx.channel);
3974
3974
  }
3975
- var MAINS_CAMERA_MODELS = ["T8425", "T8419"];
3975
+ var MAINS_CAMERA_MODELS = ["T8425", "T8419", "T8410"];
3976
3976
  var notMainsCamera = (ctx) => {
3977
3977
  const model = (ctx.model ?? "").toUpperCase();
3978
3978
  return !MAINS_CAMERA_MODELS.some((prefix) => model.startsWith(prefix));
3979
3979
  };
3980
- var BATTERY_MEMBERS = {
3980
+ var CELL_PARAMS = [
3981
+ BATTERY_PARAM.BATTERY,
3982
+ BATTERY_PARAM.BATTERY_STATUS,
3983
+ BATTERY_PARAM.BATTERY_TEMP,
3984
+ BATTERY_PARAM.BATTERY_HEALTH,
3985
+ BATTERY_PARAM.SOLAR_INTENSITY,
3986
+ BATTERY_PARAM.SOLAR_CONNECT_24H,
3987
+ BATTERY_PARAM.BATTERY_POWER_DATAS
3988
+ ];
3989
+ function cellGated(members) {
3990
+ const gated = Object.entries(members).map(([name, m]) => {
3991
+ const param = m.param;
3992
+ if (typeof param !== "number" || !CELL_PARAMS.includes(param))
3993
+ return [name, m];
3994
+ const own = m.available;
3995
+ const available = own ? (ctx) => notMainsCamera(ctx) && own(ctx) : notMainsCamera;
3996
+ return [name, { ...m, available }];
3997
+ });
3998
+ return Object.fromEntries(gated);
3999
+ }
4000
+ var BATTERY_MEMBERS = cellGated({
3981
4001
  /**
3982
4002
  * The headline percentage, and this capability's detection evidence: reporting 1101 is what proves a
3983
4003
  * device is battery-powered, which is also the fact `MediaProvider` reads to decide a stream needs a
@@ -3990,7 +4010,6 @@ var BATTERY_MEMBERS = {
3990
4010
  unit: "%",
3991
4011
  kind: "percent",
3992
4012
  provenance: "verified",
3993
- available: notMainsCamera,
3994
4013
  description: "Battery level 0-100 (verified: param 1101)."
3995
4014
  },
3996
4015
  /**
@@ -4003,7 +4022,6 @@ var BATTERY_MEMBERS = {
4003
4022
  type: "bool",
4004
4023
  kind: "boolean",
4005
4024
  provenance: "apk",
4006
- available: notMainsCamera,
4007
4025
  coerce: (v) => {
4008
4026
  const n = Number(v);
4009
4027
  return n !== 0 && n !== 2;
@@ -4174,6 +4192,11 @@ var BATTERY_MEMBERS = {
4174
4192
  * Reported, so it stays in the schema and answers through `getProperty` — but given no typed getter:
4175
4193
  * the payload's fields have never been decoded, and a getter would hand back an opaque blob typed as
4176
4194
  * though it meant something.
4195
+ *
4196
+ * `unexposed` is NOT a substitute for the cell gate. It suppresses the fluent GETTER; `propertiesOf`
4197
+ * filters `writeOnly` and `available` and deliberately not `unexposed`, because a schema entry
4198
+ * reachable through `getProperty` is the whole point of the mark. So a cell param needs
4199
+ * {@link CELL_PARAMS} either way, or a mains camera publishes "battery power history" and answers it.
4177
4200
  */
4178
4201
  batteryPowerStats: {
4179
4202
  param: BATTERY_PARAM.BATTERY_POWER_DATAS,
@@ -4199,7 +4222,7 @@ var BATTERY_MEMBERS = {
4199
4222
  unexposed: true,
4200
4223
  description: "Raw GET_CAMERA_INFO value (1103), meaning unevidenced \u2014 read live as the constant 5 on a T8170 at 92% and a T8171 at 27%, so it is NOT a low-battery flag."
4201
4224
  }
4202
- };
4225
+ });
4203
4226
  var BATTERY = {
4204
4227
  capability: "battery",
4205
4228
  description: "Battery level, charging, health, temperature, and solar input.",
@@ -10093,6 +10116,16 @@ function getCapabilityModule(cap) {
10093
10116
  return BY_CAP.get(cap);
10094
10117
  }
10095
10118
  var CAPABILITY_MODULES = Object.fromEntries(MODULES.map((m) => [m.capability, m]));
10119
+ function claimedParams(caps) {
10120
+ const claimed = /* @__PURE__ */ new Set();
10121
+ for (const cap of caps)
10122
+ for (const spec of getCapabilityModule(cap)?.properties ?? []) {
10123
+ claimed.add(spec.paramType);
10124
+ for (const alias of spec.readAliases ?? [])
10125
+ claimed.add(alias.paramType);
10126
+ }
10127
+ return claimed;
10128
+ }
10096
10129
  function mergeProperties(caps, ctx) {
10097
10130
  const seen = /* @__PURE__ */ new Set();
10098
10131
  const merged = [];
@@ -10375,6 +10408,11 @@ var MODEL_REGISTRY = {
10375
10408
  T8210: { codec: "camera", caps: ["doorbell", "battery"], name: "Video Doorbell" },
10376
10409
  // Confirmed against a real owned unit (named "Doorbell"): Video Doorbell Dual.
10377
10410
  T8214: { codec: "camera", caps: ["doorbell", "battery"], name: "Video Doorbell Dual" },
10411
+ // Confirmed against a real owned unit: the mains-powered Wired Doorbell 2K. No `battery` row
10412
+ // member on purpose — this model is wired, so the codec baseline plus inference is the whole
10413
+ // truth for power. Without this row it classified as a plain camera, so the doorbell capability
10414
+ // never appeared and consumers got no doorbell event entity or ring trigger.
10415
+ T8200: { codec: "camera", caps: ["doorbell"], name: "Wired Doorbell 2K" },
10378
10416
  // Cameras observed on real owned hardware (inspect-device sweep). Names from the app's own
10379
10417
  // model-family constants (scripts/data/app_model_registry.json); caps mirror what the device
10380
10418
  // reports. T8171 has no family entry in that dump, so it keeps the raw T-code.
@@ -12384,6 +12422,11 @@ var Device = class _Device {
12384
12422
  * different wire ids across device families still resolves to one named value.
12385
12423
  */
12386
12424
  specByParam;
12425
+ /**
12426
+ * Params a resolved capability declares that this device's schema does not carry — withheld by a
12427
+ * member's gate, so not named from the param dictionary either. See {@link Device.applyParams}.
12428
+ */
12429
+ withheld = /* @__PURE__ */ new Set();
12387
12430
  /** Which param namespace this device's ids live in (clean DPs vs security P2P). */
12388
12431
  namespace;
12389
12432
  /** The record's `device_name` as stated, before the {@link modelName} fallback is applied. */
@@ -12450,6 +12493,7 @@ var Device = class _Device {
12450
12493
  byParam.set(a.paramType, { spec: p, invert: a.invert ?? false });
12451
12494
  }
12452
12495
  this.specByParam = byParam;
12496
+ this.withheld = new Set([...claimedParams(resolved.capabilities)].filter((pt) => !byParam.has(pt)));
12453
12497
  this.namespace = namespaceForCodec(resolved.codec);
12454
12498
  this.installAccessors();
12455
12499
  }
@@ -12612,6 +12656,16 @@ var Device = class _Device {
12612
12656
  * Apply a raw param map (cloud record or P2P notification). Known params update their named
12613
12657
  * property; unrecognised params are retained as `unknown_<paramType>` so nothing is lost.
12614
12658
  *
12659
+ * Naming precedence: this device's own `PropertySpec` (curated), then the param dictionary for its
12660
+ * namespace, then the `unknown_<paramType>` passthrough. The dictionary def is consulted even where a
12661
+ * spec exists, because `encoding` lives there.
12662
+ *
12663
+ * A param a resolved capability's gate WITHHELD takes the passthrough instead of its dictionary name.
12664
+ * The gate decided the read does not describe this device, the dictionary names it what the member
12665
+ * would have, and republishing it there hands a caller a reading indistinguishable from one the device
12666
+ * really answered. A capability that never resolved withholds nothing: a param arriving before its
12667
+ * capability is still the device's own, and keeps its dictionary name.
12668
+ *
12615
12669
  * @param params param_type → raw value.
12616
12670
  * @param ts observation time (epoch ms); defaults to `Date.now()`.
12617
12671
  * @returns the list of property names whose value changed.
@@ -12624,7 +12678,7 @@ var Device = class _Device {
12624
12678
  continue;
12625
12679
  const hit = this.specByParam.get(pt);
12626
12680
  const spec = hit?.spec;
12627
- const def = paramDef(this.namespace, pt);
12681
+ const def = this.withheld.has(pt) ? void 0 : paramDef(this.namespace, pt);
12628
12682
  const name = spec ? spec.name : def ? def.name : `${UNKNOWN_PARAM_PREFIX}${pt}`;
12629
12683
  const value = def?.encoding ? decodeEncoded(rawVal, def.encoding) : spec ? coerce(spec, rawVal, hit.invert, this.logger) : def ? coerceByType(def.type, rawVal, def.name, this.logger) : String(rawVal);
12630
12684
  const prev = this.state.get(name);
@@ -26700,6 +26754,7 @@ export {
26700
26754
  buildRealtimeInit,
26701
26755
  captureSnapshotFromShared,
26702
26756
  cellAtPoint,
26757
+ claimedParams,
26703
26758
  clamp,
26704
26759
  classify,
26705
26760
  classifyDevice,