@mega-yfue/eufy-sdk 0.2.0-beta.21 → 0.2.0-beta.23

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
@@ -3230,7 +3230,14 @@ function describeBound(modules, bound, ctx) {
3230
3230
  else
3231
3231
  undescribedActions.push(name);
3232
3232
  }
3233
- out.push({ capability: m.capability, accessor, reads: reads2, actions, undescribedActions, events: emitsOf(m) });
3233
+ out.push({
3234
+ capability: m.capability,
3235
+ accessor,
3236
+ reads: reads2,
3237
+ actions,
3238
+ undescribedActions,
3239
+ events: emitsOf(m, reads2, ctx)
3240
+ });
3234
3241
  }
3235
3242
  return out;
3236
3243
  }
@@ -3249,8 +3256,22 @@ function readDescriptor(name, m, descriptors, ctx) {
3249
3256
  description: m.description
3250
3257
  };
3251
3258
  }
3252
- function emitsOf(m) {
3253
- return [.../* @__PURE__ */ new Set([...(m.events ?? []).map((e) => e.emit), ...m.emits ?? []])];
3259
+ function emitsOf(m, reads2, ctx) {
3260
+ const installed = new Set(reads2.map((r) => r.accessor));
3261
+ const claimed = (m.events ?? []).filter((e) => holds(e.claim, installed, ctx)).map((e) => e.emit);
3262
+ return [.../* @__PURE__ */ new Set([...claimed, ...m.emits ?? []])];
3263
+ }
3264
+ function holds(claim, installed, ctx) {
3265
+ if (!claim)
3266
+ return true;
3267
+ if (claim.codecs && (ctx?.codec === void 0 || !claim.codecs.includes(ctx.codec)))
3268
+ return false;
3269
+ if (claim.reads?.some((name) => !installed.has(name)))
3270
+ return false;
3271
+ if (claim.homeBaseAttached !== void 0 && ctx?.homeBaseAttached !== void 0) {
3272
+ return ctx.homeBaseAttached === claim.homeBaseAttached;
3273
+ }
3274
+ return true;
3254
3275
  }
3255
3276
 
3256
3277
  // dist/model/capabilities/video.js
@@ -3755,6 +3776,9 @@ var AiDetectType = {
3755
3776
  vehicle: 4,
3756
3777
  pet: 8
3757
3778
  };
3779
+ var CAMERA_AI_CLAIM = { codecs: ["camera"] };
3780
+ var VEHICLE_CLAIM = { ...CAMERA_AI_CLAIM, reads: ["aiDetectType"] };
3781
+ var DOG_CLAIM = { ...CAMERA_AI_CLAIM, homeBaseAttached: true };
3758
3782
  var SENSITIVITY_SCALES = [
3759
3783
  /** Standalone PIR sensor: five steps counting DOWN. */
3760
3784
  {
@@ -4146,21 +4170,24 @@ var MOTION = {
4146
4170
  events: [
4147
4171
  { source: "push", match: DoorbellPushEvent.MOTION_DETECTION, emit: "motion" },
4148
4172
  { source: "push", match: CusPushEvent.MOTION_SENSOR_PIR, emit: "motion" },
4149
- { source: "push", match: IndoorPushEvent.CRYING_DETECTION, emit: "cryingDetected" },
4150
- { source: "push", match: IndoorPushEvent.SOUND_DETECTION, emit: "soundDetected" },
4151
- { source: "push", match: DoorbellPushEvent.VEHICLE_DETECTION, emit: "vehicleDetected" },
4152
- { source: "push", match: HB3PairedDevicePushEvent.DOG_DETECTION, emit: "dogDetected" },
4173
+ { source: "push", match: IndoorPushEvent.CRYING_DETECTION, emit: "cryingDetected", claim: CAMERA_AI_CLAIM },
4174
+ { source: "push", match: IndoorPushEvent.SOUND_DETECTION, emit: "soundDetected", claim: CAMERA_AI_CLAIM },
4175
+ { source: "push", match: IndoorPushEvent.PET_DETECTION, emit: "petDetection", claim: CAMERA_AI_CLAIM },
4176
+ { source: "push", match: DoorbellPushEvent.VEHICLE_DETECTION, emit: "vehicleDetected", claim: VEHICLE_CLAIM },
4177
+ { source: "push", match: HB3PairedDevicePushEvent.DOG_DETECTION, emit: "dogDetected", claim: DOG_CLAIM },
4153
4178
  {
4154
4179
  source: "push",
4155
4180
  match: HB3PairedDevicePushEvent.DOG_LICK_DETECTION,
4156
4181
  emit: "dogDetected",
4157
- payload: { kind: "lick" }
4182
+ payload: { kind: "lick" },
4183
+ claim: DOG_CLAIM
4158
4184
  },
4159
4185
  {
4160
4186
  source: "push",
4161
4187
  match: HB3PairedDevicePushEvent.DOG_POOP_DETECTION,
4162
4188
  emit: "dogDetected",
4163
- payload: { kind: "poop" }
4189
+ payload: { kind: "poop" },
4190
+ claim: DOG_CLAIM
4164
4191
  }
4165
4192
  ]
4166
4193
  };
@@ -6830,10 +6857,16 @@ var DOORBELL = {
6830
6857
  properties: propertiesOf(DOORBELL_MEMBERS),
6831
6858
  /** Doorbells self-report no single unambiguous param; the model name is the reliable signal. */
6832
6859
  detection: { modelHints: [/doorbell/i] },
6833
- /** Inbound FCM doorbell events (`DoorbellPushEvent`): ring press, pet, package delivered/taken. */
6860
+ /**
6861
+ * Inbound FCM doorbell events (`DoorbellPushEvent`): ring press and the package trio.
6862
+ *
6863
+ * Pet (3106) is NOT here. The id is declared identically in the doorbell, indoor and HB3-paired
6864
+ * vocabularies, so it belongs to the camera-wide `motion` module that every camera binds; claiming
6865
+ * it here as well would make it a contested id that a doorbell — which has both capabilities —
6866
+ * matches twice, emitting one push as two events.
6867
+ */
6834
6868
  events: [
6835
6869
  { source: "push", match: DoorbellPushEvent.PRESS_DOORBELL, emit: "doorbellPress" },
6836
- { source: "push", match: DoorbellPushEvent.PET_DETECTION, emit: "petDetection" },
6837
6870
  { source: "push", match: DoorbellPushEvent.PACKAGE_DELIVERED, emit: "packageDelivered" },
6838
6871
  { source: "push", match: DoorbellPushEvent.PACKAGE_TAKEN, emit: "packageTaken" },
6839
6872
  { source: "push", match: DoorbellPushEvent.PACKAGE_STRANDED, emit: "packageStranded" }
@@ -8326,6 +8359,25 @@ var CARPET_STRATEGIES = ["autoRaise", "avoid", "ignore"];
8326
8359
  var CARPET_STRATEGY = { 0: "autoRaise", 1: "avoid", 2: "ignore" };
8327
8360
  var CLEAN_EXTENTS = ["normal", "narrow", "quick"];
8328
8361
  var CLEAN_EXTENT = { 0: "normal", 1: "narrow", 2: "quick" };
8362
+ function cleanParamWireValue(names, name) {
8363
+ const found = Object.entries(names).find(([, candidate]) => candidate === name);
8364
+ if (found === void 0)
8365
+ throw new Error(`clean param: ${name} is not a value this setting takes`);
8366
+ return Number(found[0]);
8367
+ }
8368
+ function writeCleanSetting(p, wrapper, inner, value) {
8369
+ p.sub(wrapper, (w) => {
8370
+ if (value !== 0)
8371
+ w.int(inner, value);
8372
+ });
8373
+ }
8374
+ function encodeCleanParam(cleanType, cleanExtent, mopLevel) {
8375
+ return rawDp((w) => w.sub(CLEAN_PARAM_FIELD.CONFIGURED, (p) => {
8376
+ writeCleanSetting(p, CLEAN_PARAM_FIELD.CLEAN_TYPE, CLEAN_PARAM_FIELD.VALUE, cleanParamWireValue(CLEAN_TYPE, cleanType));
8377
+ writeCleanSetting(p, CLEAN_PARAM_FIELD.CLEAN_EXTENT, CLEAN_PARAM_FIELD.VALUE, cleanParamWireValue(CLEAN_EXTENT, cleanExtent));
8378
+ writeCleanSetting(p, CLEAN_PARAM_FIELD.MOP_MODE, CLEAN_PARAM_FIELD.MOP_LEVEL, cleanParamWireValue(MOP_LEVEL, mopLevel));
8379
+ }));
8380
+ }
8329
8381
  function decodeCleanParamValue(raw, codec, field, inner) {
8330
8382
  if (typeof raw !== "string" || !codec)
8331
8383
  return void 0;
@@ -10005,6 +10057,49 @@ var VACUUM_CLEAN_MEMBERS = {
10005
10057
  * WATCHED: run on a T2351, it started the named scene.
10006
10058
  */
10007
10059
  startScene: method(({ sink }) => (sceneId) => sink.dispatch(aiotDp(VACUUM_DP.MODE_CTRL, encodeSceneClean(sceneId))), "Run a saved cleaning scene by its id (ModeCtrlRequest method 24 over DP 152).", isAiotVacuum),
10060
+ /**
10061
+ * State the cleaning settings a run uses — `CleanParamRequest.clean_param` over DP 154.
10062
+ *
10063
+ * The write counterpart of {@link VACUUM_CLEAN_MEMBERS.cleanType},
10064
+ * {@link VACUUM_CLEAN_MEMBERS.cleanExtent} and {@link VACUUM_CLEAN_MEMBERS.mopLevel}: one message
10065
+ * carries all three, so they are set together rather than through three setters that would each send
10066
+ * the same message with the other two silent.
10067
+ *
10068
+ * **The evidence, and its limit.** The frame is field 1 of `CleanParamRequest`, which carries the
10069
+ * very `CleanParam` this module decodes out of field 1 of the reports a live T2351 sends — the
10070
+ * field numbers, the single-field wrappers and the `mop_mode.level` scale are all read off that
10071
+ * capture, and `encodeCleanParam` writes what `decodeCleanParamValue` reads. What is NOT captured is
10072
+ * the write direction itself. It ships as a method rather than an unverified write because the
10073
+ * hazard that rule answers does not arise here: DP 154 is the robot's own settings report, so a frame
10074
+ * it does not accept leaves those three reads unchanged, where a wrong fire-and-forget command would
10075
+ * look exactly like success.
10076
+ *
10077
+ * Suction is not here. It has its own data point and its own capability — a `fan` field exists in
10078
+ * this message and is deliberately not written, for the same reason it is not read.
10079
+ */
10080
+ setCleanParam: {
10081
+ ...method(({ sink }) => (cleanType, cleanExtent, mopLevel) => sink.dispatch(aiotDp(VACUUM_DP.CLEAN_PARAM, encodeCleanParam(cleanType, cleanExtent, mopLevel))), "Set the cleaning type, extent and mop water level together (CleanParamRequest.clean_param over DP 154).", isAiotVacuum),
10082
+ args: [
10083
+ {
10084
+ name: "cleanType",
10085
+ kind: "enum",
10086
+ values: VACUUM_CLEAN_TYPES,
10087
+ description: "What the robot does with a surface."
10088
+ },
10089
+ {
10090
+ name: "cleanExtent",
10091
+ kind: "enum",
10092
+ values: CLEAN_EXTENTS,
10093
+ description: "How far past the mapped edge a job reaches. Wire order, not app order."
10094
+ },
10095
+ {
10096
+ name: "mopLevel",
10097
+ kind: "enum",
10098
+ values: MOP_LEVELS,
10099
+ description: "How much water the mop lays down. Only meaningful for a clean type that mops."
10100
+ }
10101
+ ]
10102
+ },
10008
10103
  /**
10009
10104
  * Clean the named rooms of a named map (ModeCtrlRequest method 1 over DP 152).
10010
10105
  *
@@ -13043,7 +13138,8 @@ var Device = class _Device {
13043
13138
  details: describeCapabilities(this.actionMap, {
13044
13139
  codec: this.codec,
13045
13140
  model: this.model,
13046
- capabilities: new Set(this.capabilities)
13141
+ capabilities: new Set(this.capabilities),
13142
+ homeBaseAttached: this.stationSn !== this.sn
13047
13143
  })
13048
13144
  };
13049
13145
  }
@@ -27059,45 +27155,79 @@ var SolixClient = class _SolixClient {
27059
27155
  import { EventEmitter as EventEmitter10 } from "node:events";
27060
27156
  var SOLIX_METER_FIELD_NAMES = {
27061
27157
  168: "meterPowerL1",
27158
+ // Anker Modbus: primary_phase_1_active_power
27062
27159
  169: "meterPowerL2",
27160
+ // Anker Modbus: primary_phase_2_active_power
27063
27161
  170: "meterPowerL3",
27162
+ // Anker Modbus: primary_phase_3_active_power
27064
27163
  171: "meterPowerTotal",
27164
+ // Anker Modbus: primary_total_active_power
27065
27165
  172: "meterVoltageL1",
27166
+ // Anker Modbus: primary_phase_1_voltage
27066
27167
  173: "meterVoltageL2",
27168
+ // Anker Modbus: primary_phase_2_voltage
27067
27169
  174: "meterVoltageL3",
27170
+ // Anker Modbus: primary_phase_3_voltage
27068
27171
  175: "meterCurrentL1",
27172
+ // Anker Modbus: primary_phase_1_current
27069
27173
  176: "meterCurrentL2",
27174
+ // Anker Modbus: primary_phase_2_current
27070
27175
  177: "meterCurrentL3",
27176
+ // Anker Modbus: primary_phase_3_current
27071
27177
  179: "meterImportEnergy",
27178
+ // Anker Modbus: primary_total_forward_active_energy
27072
27179
  180: "meterExportEnergy"
27180
+ // Anker Modbus: primary_total_reverse_active_energy
27073
27181
  };
27074
27182
  var SOLIX_METER_PRODUCT_PREFIXES = ["AE1X0"];
27075
27183
  var SOLIX_SOLARBANK_PRODUCT_PREFIX = "AE10";
27076
27184
  var SOLIX_SOLARBANK_FIELD_NAMES = {
27077
27185
  171: "photovoltaicPower",
27078
- // total PV input across the strings
27186
+ // total PV input across the strings — Anker Modbus: pv_power
27079
27187
  172: "batteryPower",
27188
+ // signed net pack power — Anker Modbus: battery_charging_power − battery_discharging_power
27080
27189
  188: "chargePower",
27190
+ // Anker Modbus: battery_charging_power
27081
27191
  173: "dischargePower",
27192
+ // Anker Modbus: battery_discharging_power
27082
27193
  174: "acPlugPower",
27194
+ // AC plug, signed — Anker Modbus: ac_grid_output_power
27083
27195
  175: "socketPower",
27084
- // the unit's own on-board AC socket (an appliance plugged into the Solarbank)
27196
+ // the unit's own on-board AC socket (an appliance plugged into the Solarbank) — no Anker register
27085
27197
  196: "gridInputPower",
27198
+ // Anker Modbus: grid_import_power
27086
27199
  197: "homeLoadPower",
27200
+ // Anker Modbus: load_power
27087
27201
  198: "pv1Power",
27088
- // the four PV-string inputs (0 when a string is unused / dark)
27202
+ // the four PV-string inputs (0 when a string is unused / dark); Anker exposes only a pv_power total
27089
27203
  199: "pv2Power",
27090
27204
  200: "pv3Power",
27091
27205
  201: "pv4Power"
27092
27206
  };
27093
27207
  var SOLIX_STATE_FIELD_NAMES = {
27094
27208
  169: "mode",
27095
- // current operating (EMS) mode (1 custom, 2 self-consumption, 4 rapid charge, 7 smart, 8 dynamic tariff)
27209
+ // current operating (EMS) mode, AE103 numbering: 1 custom, 2 self-consumption, 4 rapid charge, 7 smart, 8 dynamic tariff (NOT the Modbus SOLIX_MODBUS_EMS_MODES numbering)
27096
27210
  170: "maxLoad"
27097
27211
  // configured max home load (W) — matches get_site_device_param max_load
27098
27212
  // NOTE `0xab` is grid-in/out-related power but its exact meaning is not yet pinned, so it stays raw
27099
27213
  // `state_ab` (a diagnostic a consumer can watch) rather than being asserted under a guessed name.
27100
27214
  };
27215
+ var SOLIX_MODBUS_EMS_MODES = {
27216
+ 0: "selfConsumption",
27217
+ // Self-Consumption Mode (Anker: self_consumption, 0x8006 BIT0)
27218
+ 1: "timeOfUse",
27219
+ // Time Of Use Mode (Anker: tou_mode, BIT1)
27220
+ 3: "thirdPartyControl",
27221
+ // Third-Party Controlled (Anker: third_party_control, BIT5)
27222
+ 4: "custom",
27223
+ // Custom Mode (Anker: custom_mode, BIT2)
27224
+ 5: "socketOverlay",
27225
+ // Socket Overlay Mode (Anker: socket_overlay_mode, BIT4)
27226
+ 6: "smart",
27227
+ // Smart Mode (Anker: smart_mode, BIT3)
27228
+ 7: "dynamicTariff"
27229
+ // Dynamic Tariff Mode (Anker: dynamic_pricing, BIT6)
27230
+ };
27101
27231
  function solixStateReadings(frame) {
27102
27232
  const out = {};
27103
27233
  for (const [tag2, value] of frame.fields) {
@@ -27660,6 +27790,7 @@ export {
27660
27790
  SMOKE_MEMBERS,
27661
27791
  SOLIX_ENERGY_METER_MEMBERS,
27662
27792
  SOLIX_LOCAL_KEY_HEX,
27793
+ SOLIX_MODBUS_EMS_MODES,
27663
27794
  STATE_EVENT_FIELDS,
27664
27795
  STATION_CHANNEL3 as STATION_CHANNEL,
27665
27796
  STATION_CHUNK_BYTES,