@mega-yfue/eufy-sdk 0.5.0-beta.7 → 0.5.0-beta.8

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.
@@ -835,11 +835,14 @@ export declare class EufyMega extends EventEmitter {
835
835
  */
836
836
  setProperty(sn: string, name: string, value: boolean | number | string): Promise<void>;
837
837
  /**
838
- * Restart a HomeBase.
839
- *
840
- * **HomeBases only** — restart is a hub operation, so a non-HomeBase serial (a camera, an NVR)
841
- * throws rather than doing nothing. The hub drops its connection and returns after a minute or two,
842
- * so everything behind it is briefly offline. Verified on real hardware.
838
+ * Restart a station: a HomeBase, or a camera-family device that is its own station.
839
+ *
840
+ * The restart is `RESTART_HUB` on the station's broadcast channel, so it reaches whatever owns the
841
+ * P2P session. A standalone camera or doorbell owns its own session and is the station it restarts.
842
+ * A camera attached to a HomeBase is not — the same frame would restart its HomeBase — so its serial
843
+ * throws, as does any non-camera serial, rather than doing nothing. The device drops its connection
844
+ * and returns after a minute or two; a HomeBase takes everything behind it offline meanwhile.
845
+ * Verified on real hardware against a HomeBase and a standalone indoor camera.
843
846
  */
844
847
  reboot(sn: string): Promise<void>;
845
848
  /**
package/dist/index.js CHANGED
@@ -16249,6 +16249,21 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
16249
16249
  this.send(this.connectAddress, RequestMessageType.DATA, data);
16250
16250
  return true;
16251
16251
  }
16252
+ /**
16253
+ * The level-1 twin of {@link sendRawLevel2Bytes}: the plaintext `payload` sealed AES-128-ECB under the
16254
+ * level-1 key, signCode 1 — the seal of the level-1 media start. Returns `false` when not connected.
16255
+ */
16256
+ sendRawLevel1Bytes(payload, channel, outerCmd) {
16257
+ if (!this.connectAddress)
16258
+ return false;
16259
+ const data = Buffer.concat([
16260
+ buildCommandHeader(this.seqNumber, outerCmd),
16261
+ buildRawCommandPayload(encryptP2PData(paddingP2PData(payload), this.level1Key), channel, 1)
16262
+ ]);
16263
+ this.seqNumber = this.seqNumber + 1 & 65535;
16264
+ this.send(this.connectAddress, RequestMessageType.DATA, data);
16265
+ return true;
16266
+ }
16252
16267
  /**
16253
16268
  * Stop the realtime media stream (`CMD_STOP_REALTIME_MEDIA`, 1004) on a camera `channel`, in the shape
16254
16269
  * its runtime topology takes, as {@link startLiveMedia} selects its start:
@@ -21197,14 +21212,20 @@ var P2PCommandRouter = class _P2PCommandRouter {
21197
21212
  }
21198
21213
  }
21199
21214
  /**
21200
- * Restart a HomeBase. `RESTART_HUB` (1034) is a station-scalar on the broadcast channel 255: a
21201
- * level-2 frame whose body is `[u32 value][account_id padded]` — the same shape as the hub
21202
- * alarm-volume control. ✅ Wire-confirmed byte-exact from a capture of the app's own Restart
21203
- * (2026-08-03) and HW-tested: the captured frame carried value `0` and rebooted the hub. Replays
21204
- * like every other level-2 control, so a single dropped datagram doesn't lose it.
21215
+ * Restart the station that owns `sn`'s P2P session. `RESTART_HUB` (1034) is a station-scalar on the
21216
+ * broadcast channel 255 whose body is `[u32 value][account_id padded]`, value `0`. ✅ Wire-confirmed
21217
+ * byte-exact as a level-2 frame from a capture of the app's own HomeBase Restart (2026-08-03).
21218
+ *
21219
+ * The seal follows the session, as {@link sendBySessionLevel} defines it: a keyed session takes the
21220
+ * level-2 frame, a keyless one — a standalone camera, which never negotiates a key — the same body
21221
+ * sealed level-1. Both are replayed {@link DIRECT_CMD_SENDS}× at 200ms, as every other unacknowledged
21222
+ * control here is.
21205
21223
  */
21206
21224
  async rebootStation(sn) {
21207
- await this.replayLevel2Send(sn, `reboot ${sn}`, ({ session, accountId }) => session.sendRawLevel2Bytes(buildDirectBinaryBody(0, accountId), 255, P2P_ENVELOPE.RESTART_HUB, 8));
21225
+ await this.sendBySessionLevel(sn, {
21226
+ l2: (resolved) => this.replayLevel2Send(sn, `reboot ${sn}`, ({ session, accountId }) => session.sendRawLevel2Bytes(buildDirectBinaryBody(0, accountId), 255, P2P_ENVELOPE.RESTART_HUB, 8), resolved),
21227
+ l1: (resolved) => this.replayLevel2Send(sn, `reboot ${sn}`, ({ session, accountId }) => session.sendRawLevel1Bytes(buildDirectBinaryBody(0, accountId), 255, P2P_ENVELOPE.RESTART_HUB), resolved)
21228
+ });
21208
21229
  }
21209
21230
  /**
21210
21231
  * A {@link MediaProvider} bound to one serial — resolves the session then calls `p2p/media`.
@@ -27310,16 +27331,20 @@ var EufyMega = class extends EventEmitter9 {
27310
27331
  await this.commandSinkFor(sn).dispatch(cmd);
27311
27332
  }
27312
27333
  /**
27313
- * Restart a HomeBase.
27334
+ * Restart a station: a HomeBase, or a camera-family device that is its own station.
27314
27335
  *
27315
- * **HomeBases only** — restart is a hub operation, so a non-HomeBase serial (a camera, an NVR)
27316
- * throws rather than doing nothing. The hub drops its connection and returns after a minute or two,
27317
- * so everything behind it is briefly offline. Verified on real hardware.
27336
+ * The restart is `RESTART_HUB` on the station's broadcast channel, so it reaches whatever owns the
27337
+ * P2P session. A standalone camera or doorbell owns its own session and is the station it restarts.
27338
+ * A camera attached to a HomeBase is not — the same frame would restart its HomeBase — so its serial
27339
+ * throws, as does any non-camera serial, rather than doing nothing. The device drops its connection
27340
+ * and returns after a minute or two; a HomeBase takes everything behind it offline meanwhile.
27341
+ * Verified on real hardware against a HomeBase and a standalone indoor camera.
27318
27342
  */
27319
27343
  async reboot(sn) {
27320
27344
  const ctx = await this.commandContext(sn);
27321
- if (!isHomeBase({ deviceType: ctx.deviceType, model: ctx.model })) {
27322
- throw new Error(`reboot: ${sn} is not a HomeBase (deviceType ${ctx.deviceType ?? "?"}, model ${ctx.model ?? "?"}) \u2014 restart is a hub-only operation`);
27345
+ const standaloneCamera = ctx.codec === "camera" && this.p2p.stationKeyOf(sn) === sn;
27346
+ if (!isHomeBase({ deviceType: ctx.deviceType, model: ctx.model }) && !standaloneCamera) {
27347
+ throw new Error(`reboot: ${sn} is neither a HomeBase nor a standalone camera (deviceType ${ctx.deviceType ?? "?"}, model ${ctx.model ?? "?"}) \u2014 restart addresses the station that owns the P2P session`);
27323
27348
  }
27324
27349
  await this.p2p.rebootStation(sn);
27325
27350
  }