@apocaliss92/nodedreame 1.11.11 → 1.12.1

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.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { fetch } from 'undici';
2
2
  import { z } from 'zod';
3
+ import { EventEmitter } from 'node:events';
3
4
 
4
5
  /** Public name of this library. */
5
6
  declare const LIBRARY_NAME = "nodedreame";
@@ -36,6 +37,47 @@ declare class DreameTransportError extends DreameError {
36
37
  /** Supported Dreame cloud regions. */
37
38
  type DreameRegion = 'eu' | 'us' | 'cn' | 'ru' | 'sg' | 'in' | 'de' | 'tw';
38
39
 
40
+ /**
41
+ * Connectivity / diagnostic info distilled from a device-list record.
42
+ *
43
+ * Scope note: the Dreame cloud device record exposes reachability + identity +
44
+ * the assigned MQTT broker, but NOT Wi-Fi RSSI / SSID / local IP — those are
45
+ * native-firmware (miIO-style) details the cloud API does not return for these
46
+ * vacuums. So `signalStrength`/`ssid`/`ip` are intentionally absent here; this
47
+ * surfaces everything the cloud actually provides.
48
+ */
49
+ /** The device's assigned MQTT broker endpoint (from `bindDomain`). */
50
+ interface BrokerEndpoint {
51
+ readonly host: string;
52
+ readonly port: number | null;
53
+ }
54
+ interface DeviceConnectivity {
55
+ /** Whether the cloud considers the device online (LWT/online flag). */
56
+ readonly online: boolean;
57
+ /** Physical link type reported by the cloud (e.g. `"WIFI"`), or null. */
58
+ readonly connectionType: string | null;
59
+ /** Device MAC address, or null. */
60
+ readonly mac: string | null;
61
+ /** Assigned MQTT broker (`bindDomain` split into host/port), or null. */
62
+ readonly broker: BrokerEndpoint | null;
63
+ /** Cloud region the device is bound to (e.g. `"eu"`), or null. */
64
+ readonly region: string | null;
65
+ /** Cloud media/IoT vendor (e.g. `"ali"`), or null. */
66
+ readonly cloudVendor: string | null;
67
+ /** Firmware version (`ver`), or null. */
68
+ readonly firmwareVersion: string | null;
69
+ /** Serial number (`sn`), or null. */
70
+ readonly serialNumber: string | null;
71
+ /** Sub-model code, or null. */
72
+ readonly subModel: string | null;
73
+ /** Battery percentage 0–100 from the cloud snapshot, or null. */
74
+ readonly battery: number | null;
75
+ /** Latest MIoT status int from the cloud snapshot, or null. */
76
+ readonly statusCode: number | null;
77
+ }
78
+ /** Parse the connectivity view from a raw device-list record. */
79
+ declare function parseConnectivity(raw: Record<string, unknown>): DeviceConnectivity;
80
+
39
81
  interface DreameSession {
40
82
  accessToken: string;
41
83
  refreshToken?: string | undefined;
@@ -55,6 +97,8 @@ interface DreameDevice {
55
97
  firmwareVersion?: string;
56
98
  serialNumber?: string;
57
99
  cloudState?: DreameCloudState;
100
+ /** Connectivity/diagnostic view distilled from the cloud record. */
101
+ connectivity?: DeviceConnectivity;
58
102
  }
59
103
  /** Cloud-cached subset of device state distilled from the device-list response. */
60
104
  interface DreameCloudState {
@@ -932,6 +976,268 @@ declare class VacuumCapabilityResolver implements CapabilityResolver {
932
976
  resolve(model: string): DeviceCapabilities;
933
977
  }
934
978
 
979
+ /**
980
+ * MIoT surface of the Dreame camera service (SIID 10001), reversed byte-exact
981
+ * from the r2538z (X50) React-Native plugin `Monitor` model. Every camera
982
+ * control the app issues is a MIoT `action` on this service; the values below
983
+ * are the verbatim aiid/piid maps from the bundle.
984
+ */
985
+ /** The camera/monitor service id. Same across the X40/X50 vacuum class. */
986
+ declare const MONITOR_SIID = 10001;
987
+ /** Action instance ids on {@link MONITOR_SIID}. */
988
+ declare const MONITOR_AIID: {
989
+ /** Stream lifecycle + keep-alive (monitor start/end, keep_alive). */
990
+ readonly CAMERA_OPERATE: 1;
991
+ /** Two-way intercom start/end. */
992
+ readonly VOICE_OPERATE: 2;
993
+ /** Generic property-scoped operations. */
994
+ readonly PROPERTY_OPERATE: 3;
995
+ /** Privacy access-code gate (launch/verify/set/reset). */
996
+ readonly ACCESS_CODE_OPERATE: 4;
997
+ /** Video-vendor SDK init / switch. */
998
+ readonly VIDEO_VENDOR: 7;
999
+ };
1000
+ /** Property instance ids on {@link MONITOR_SIID}. */
1001
+ declare const MONITOR_PIID: {
1002
+ readonly MONITOR_STATUS: 1;
1003
+ readonly MONITOR_AUDIO_STATUS: 2;
1004
+ readonly MONITOR_RECORD_STATUS: 4;
1005
+ readonly TAKE_PHOTO: 5;
1006
+ readonly KEEP_ALIVE: 6;
1007
+ readonly MONITOR_FAULTS: 7;
1008
+ readonly FILL_LIGHT_SET: 9;
1009
+ readonly CAMERA_LIGHT_SWITCH: 10;
1010
+ readonly VIDEO_VENDOR_STATUS: 11;
1011
+ readonly GET_PROPERTY: 99;
1012
+ readonly PERSON_DATA: 110;
1013
+ readonly VIDEO_OBSTACLE_SWITCH: 111;
1014
+ readonly VIDEO_OBSTACLE_DATA: 112;
1015
+ readonly MONITOR_TASK_STATUS: 103;
1016
+ readonly UPLOAD_STATUS: 1003;
1017
+ readonly GET_ACCESS_CODE: 1100;
1018
+ readonly SET_ACCESS_CODE: 1101;
1019
+ readonly VERIFY_ACCESS_CODE: 1102;
1020
+ readonly MONITOR_REST_MODE: 1103;
1021
+ readonly SET_ACCESS_HOT_STATUS: 1104;
1022
+ readonly MONITOR_SPACE: 2003;
1023
+ };
1024
+ /**
1025
+ * The `token` field the `startMonitor` action expects, keyed by vendor. Aliyun
1026
+ * LinkVisual uses the literal `"alify"`; Tencent uses `"tx"`. (Agora passes an
1027
+ * RTC access token instead and is not modelled here.)
1028
+ */
1029
+ declare const MONITOR_VENDOR_TOKEN: Record<'ali' | 'tx', string>;
1030
+ /**
1031
+ * The vacuum work service (SIID 4). The camera has no gimbal — "PTZ" and the
1032
+ * camera-adjacent controls (remote drive, go-to-point, person-follow) drive the
1033
+ * robot itself through this service.
1034
+ */
1035
+ declare const VACUUM_SIID = 4;
1036
+ declare const VACUUM_MOVE: {
1037
+ /** Remote-drive state property (set_properties), sent ~1 Hz while held. */
1038
+ readonly REMOTE_STATE_PIID: 15;
1039
+ /** Work-mode action instance (go-to-point, cruise, person-follow start). */
1040
+ readonly WORK_AIID: 1;
1041
+ /** Work-mode stop action instance (person-follow stop). */
1042
+ readonly STOP_AIID: 2;
1043
+ /** piid carrying the work-mode selector value in the WORK action. */
1044
+ readonly MODE_PIID: 1;
1045
+ /** piid carrying the point-info JSON in the WORK action. */
1046
+ readonly POINT_PIID: 10;
1047
+ /** Work-mode selector values. */
1048
+ readonly MODE_SPOT: 23;
1049
+ readonly MODE_CRUISE: 22;
1050
+ readonly MODE_PERSON_FOLLOW: 26;
1051
+ readonly MODE_SPOT_CLEAN: 20;
1052
+ /** Auto-switch setter property (fill-light auto/full toggle lives here). */
1053
+ readonly AUTO_SWITCH_PIID: 50;
1054
+ };
1055
+ /** Pet-teasing sound clips playable via {@link VOICE_PLAY_SOUND_AIID}. */
1056
+ declare const PET_SOUNDS: {
1057
+ readonly meow: 684;
1058
+ readonly bark: 685;
1059
+ readonly footsteps: 686;
1060
+ readonly purring: 687;
1061
+ readonly tickTock: 688;
1062
+ };
1063
+ type PetSound = keyof typeof PET_SOUNDS;
1064
+ /** Common whole-robot actions (usable as camera presets). */
1065
+ declare const VACUUM_ACTIONS: {
1066
+ readonly startClean: {
1067
+ readonly siid: 2;
1068
+ readonly aiid: 1;
1069
+ };
1070
+ readonly pauseClean: {
1071
+ readonly siid: 2;
1072
+ readonly aiid: 2;
1073
+ };
1074
+ readonly stopClean: {
1075
+ readonly siid: 4;
1076
+ readonly aiid: 2;
1077
+ };
1078
+ readonly dockWash: {
1079
+ readonly siid: 4;
1080
+ readonly aiid: 4;
1081
+ };
1082
+ readonly autoEmpty: {
1083
+ readonly siid: 15;
1084
+ readonly aiid: 1;
1085
+ };
1086
+ };
1087
+ type VacuumActionKey = keyof typeof VACUUM_ACTIONS;
1088
+
1089
+ /**
1090
+ * Per-session correlation token, exactly as the app's `Monitor.updateSession()`:
1091
+ * `MD5(accountId + "_" + epochMillis)`. Pass an explicit `now` for deterministic
1092
+ * tests. `accountId` is the Dreame account uid.
1093
+ */
1094
+ declare function makeMonitorSession(accountId: string, now?: number): string;
1095
+ /** SHA-256 hex of a privacy access code, as `Monitor.verifyAccessCode` sends it. */
1096
+ declare function hashAccessCode(code: string): string;
1097
+ /**
1098
+ * Remote-drive value (siid 4 / piid 15), sent ~1 Hz while a direction is held.
1099
+ * `spdv` = forward speed (200 fwd, 0 stop), `spdw` = turn (45 left, -45 right,
1100
+ * 180 turn-around, 0 straight). Pass an explicit `now` for deterministic tests.
1101
+ */
1102
+ declare function remoteDriveValue(spdv: number, spdw: number, now?: number): string;
1103
+ /** Named remote-drive directions, mapping to (spdv, spdw). */
1104
+ declare const DRIVE_DIRECTIONS: {
1105
+ readonly forward: readonly [200, 0];
1106
+ readonly left: readonly [0, 45];
1107
+ readonly right: readonly [0, -45];
1108
+ readonly turnAround: readonly [0, 180];
1109
+ readonly stop: readonly [0, 0];
1110
+ };
1111
+ type DriveDirection = keyof typeof DRIVE_DIRECTIONS;
1112
+
1113
+ /**
1114
+ * MIoT device transport the controller drives — satisfied by a nodedreame device
1115
+ * handle (VacuumDevice). `setProperty` is optional so callAction-only fakes still
1116
+ * satisfy the interface; the robot-drive helpers require it.
1117
+ */
1118
+ interface MonitorActionCaller {
1119
+ callAction(siid: number, aiid: number, input: unknown[]): Promise<unknown>;
1120
+ setProperty?(write: {
1121
+ siid: number;
1122
+ piid: number;
1123
+ value: unknown;
1124
+ }): Promise<unknown>;
1125
+ }
1126
+ /** Minimal relay minter — satisfied by {@link DreameVideoSession}. */
1127
+ interface RelayMinter {
1128
+ getStreamInfo(iotId: string, opts?: {
1129
+ encrypted?: boolean;
1130
+ wakeTimeoutMs?: number;
1131
+ pollMs?: number;
1132
+ }): Promise<{
1133
+ relayUrl: string | null;
1134
+ }>;
1135
+ }
1136
+ interface DreameCameraControllerInput {
1137
+ /** MIoT action transport (the device handle). */
1138
+ readonly device: MonitorActionCaller;
1139
+ /** Aliyun relay minter (a {@link DreameVideoSession}). */
1140
+ readonly relay: RelayMinter;
1141
+ /** LinkVisual channel id for this device (`iotId`). */
1142
+ readonly iotId: string;
1143
+ /** Dreame account uid — seeds the per-session correlation token. */
1144
+ readonly accountId: string;
1145
+ /** Video vendor. Only `ali` mints an RTMP relay; defaults to `ali`. */
1146
+ readonly vendor?: 'ali' | 'tx';
1147
+ /** Privacy access code (PIN) set at pairing, if any. Required to wake a coded camera. */
1148
+ readonly accessCode?: string;
1149
+ /** Aliyun region code the device reports as `area`; defaults to `'4'`. */
1150
+ readonly area?: string;
1151
+ /** Keep-alive cadence in ms (app default 10s). */
1152
+ readonly keepAliveIntervalMs?: number;
1153
+ /** Max time to wait for the relay to come up while the device wakes. */
1154
+ readonly wakeTimeoutMs?: number;
1155
+ /** Reported when a keep-alive tick fails (the stream is likely dying). */
1156
+ readonly onKeepAliveError?: (err: unknown) => void;
1157
+ /** Optional line logger (nodelink-style). Defaults to no-op. */
1158
+ readonly log?: (line: string) => void;
1159
+ }
1160
+ /** A live camera stream: the pullable relay URL plus the device's per-stream key. */
1161
+ interface CameraStreamHandle {
1162
+ /** Plain RTMP relay URL (H.264 + AAC). Single stream; re-open for another consumer. */
1163
+ readonly rtmpUrl: string;
1164
+ /** Device-generated per-session encryption key echoed by `startMonitor`. */
1165
+ readonly encryptionKey: string | null;
1166
+ }
1167
+ declare class DreameCameraController {
1168
+ #private;
1169
+ constructor(input: DreameCameraControllerInput);
1170
+ /** True while a monitor session is active (between {@link open} and {@link close}). */
1171
+ get isOpen(): boolean;
1172
+ /**
1173
+ * Run the full cold-start sequence and return a live relay URL. Starts the
1174
+ * keep-alive loop; the caller MUST {@link close} when done to release the
1175
+ * device. Throws if the monitor cannot be started or no relay comes up.
1176
+ */
1177
+ open(): Promise<CameraStreamHandle>;
1178
+ /**
1179
+ * Mint a FRESH single-use RTMP relay URL for the already-open monitor. Each
1180
+ * consumer needs its own relay URL, so call this once per ffmpeg/consumer.
1181
+ * Throws if the stream is not open.
1182
+ */
1183
+ mintRelayUrl(): Promise<string>;
1184
+ /** Start the two-way intercom session (control plane only; see module docs on mic uplink). */
1185
+ startIntercom(opts?: {
1186
+ needRecordSound?: boolean;
1187
+ videoCall?: boolean;
1188
+ }): Promise<unknown>;
1189
+ /** Stop the two-way intercom session. */
1190
+ stopIntercom(): Promise<unknown>;
1191
+ /** Set the fill-light brightness (40–100 = manual %, below 40 / 101 = auto). */
1192
+ setFillLight(value: number): Promise<unknown>;
1193
+ /**
1194
+ * Device-side snapshot: the robot captures a still and uploads it to cloud
1195
+ * storage (read the result back from the `uploadStatus` property). Prefer a
1196
+ * frame-grab from the live stream for an instant image.
1197
+ */
1198
+ takePhoto(): Promise<unknown>;
1199
+ /**
1200
+ * Remote-drive the robot ("PTZ" substitute — the camera is fixed, the robot
1201
+ * moves). `spdv` = forward speed, `spdw` = turn; send repeatedly (~1 Hz) while
1202
+ * a direction is held, then `stop`. Only valid while the stream is open.
1203
+ */
1204
+ drive(spdv: number, spdw: number): Promise<unknown>;
1205
+ /** Remote-drive by named direction (forward/left/right/turnAround/stop). */
1206
+ driveDirection(direction: DriveDirection): Promise<unknown>;
1207
+ /** Start person-follow mode (robot tracks a detected person). */
1208
+ startPersonFollow(): Promise<unknown>;
1209
+ /**
1210
+ * Universal stop for the current work mode — stops person-follow, spot-clean,
1211
+ * go-to-point, cruise, etc. (siid 4 / aiid 2).
1212
+ */
1213
+ stopWork(): Promise<unknown>;
1214
+ /** Stop person-follow mode (alias for {@link stopWork}). */
1215
+ stopPersonFollow(): Promise<unknown>;
1216
+ /** Spot-clean the robot's current position (WorkMode SpotClean). */
1217
+ spotClean(): Promise<unknown>;
1218
+ /** Play one of the robot's sound clips (e.g. pet sounds) by id. */
1219
+ playSound(soundId: number): Promise<unknown>;
1220
+ /** Play a named pet-teasing sound (meow/bark/footsteps/purring/tickTock). */
1221
+ playPetSound(sound: PetSound): Promise<unknown>;
1222
+ /** Run a common whole-robot action (startClean/pauseClean/stopClean/dockWash/autoEmpty). */
1223
+ runVacuumAction(action: VacuumActionKey): Promise<unknown>;
1224
+ /** Find-pet: cruise the home looking for the pet. */
1225
+ findPet(): Promise<unknown>;
1226
+ /** Send the robot to a map point (`spoint` current, `tpoint` targets). */
1227
+ goToPoint(spoint: number[][], tpoint: number[][]): Promise<unknown>;
1228
+ /** Toggle the fill light between auto (`full=false`) and full-on (`full=true`). */
1229
+ setFillLightAuto(full: boolean): Promise<unknown>;
1230
+ /**
1231
+ * Send the robot back to its dock to charge ("go home"). Works regardless of
1232
+ * stream state — usable as a standalone command or a PTZ "home" preset.
1233
+ */
1234
+ returnToDock(): Promise<unknown>;
1235
+ /** Locate the robot — it beeps. Works regardless of stream state. */
1236
+ locate(): Promise<unknown>;
1237
+ /** Stop the keep-alive loop and release the monitor on the device. Idempotent. */
1238
+ close(): Promise<void>;
1239
+ }
1240
+
935
1241
  /**
936
1242
  * Public types for the live-map decoder.
937
1243
  *
@@ -1246,6 +1552,19 @@ interface VacuumMap {
1246
1552
  */
1247
1553
  cleanedArea: MapCleanedAreaOverlay | null;
1248
1554
  }
1555
+ /**
1556
+ * Optional decode hints. The AES key is per-blob (it arrives appended to
1557
+ * the OSS object name with a comma, or via the OLD_MAP_DATA multiplexed
1558
+ * format); the IV is per-model (lives in Tasshack's `device_info` table)
1559
+ * and must be supplied here since node-dreame doesn't ship a per-model
1560
+ * IV lookup.
1561
+ */
1562
+ interface VacuumMapDecodeOptions {
1563
+ /** AES-256-CBC key — 32 ASCII hex chars from `sha256(<rawKey>)[0:32]`. */
1564
+ key?: string;
1565
+ /** AES-256-CBC IV — 16 ASCII bytes, model-specific. */
1566
+ iv?: string;
1567
+ }
1249
1568
 
1250
1569
  /**
1251
1570
  * Raster PNG renderer for a decoded `VacuumMap`.
@@ -1307,6 +1626,34 @@ interface RenderVacuumPngOptions {
1307
1626
  }
1308
1627
  declare function renderVacuumPng(map: VacuumMap, opts?: RenderVacuumPngOptions): Buffer;
1309
1628
 
1629
+ interface WifiSignalMap {
1630
+ readonly dimensions: MapDimensions;
1631
+ /** Robot pose from the frame header, or null when absent. */
1632
+ readonly robot: MapPose | null;
1633
+ /** Charging-dock pose from the frame header, or null when absent. */
1634
+ readonly dock: MapPose | null;
1635
+ /** Raw per-cell nibble (`pixel & 15`), row-major (width*height). */
1636
+ readonly cells: Uint8Array;
1637
+ /** Signal bars (1–4) at a world point, 0 = unreached, or null = no Wi-Fi data / off-map. */
1638
+ signalAt(worldX: number, worldY: number): number | null;
1639
+ /** Signal bars at the robot's own position (or null when unknown). */
1640
+ readonly currentSignal: number | null;
1641
+ }
1642
+ /** Decode a Wi-Fi map blob (raw string/Buffer or already-inflated) into signal cells + geometry. */
1643
+ declare function decodeWifiSignalMap(input: string | Buffer, opts?: VacuumMapDecodeOptions): WifiSignalMap;
1644
+ interface RenderWifiSignalPngOptions {
1645
+ /** Upscale factor (each map cell → scale×scale px). Default 4. */
1646
+ scale?: number;
1647
+ /** Draw a marker at the robot position. Default true. */
1648
+ markRobot?: boolean;
1649
+ }
1650
+ /**
1651
+ * Render a simple Wi-Fi coverage heatmap PNG (weakest→strongest blue, unreached
1652
+ * grey, no-data transparent), with an optional robot marker — a ready-to-show
1653
+ * image for consumers.
1654
+ */
1655
+ declare function renderWifiSignalPng(map: WifiSignalMap, opts?: RenderWifiSignalPngOptions): Buffer;
1656
+
1310
1657
  /**
1311
1658
  * Resolve a Dreame OSS object name (advertised via the PATH push,
1312
1659
  * `siid 6 piid 3`) to a signed download URL and fetch the blob.
@@ -1399,6 +1746,26 @@ interface VacuumGetMapInput {
1399
1746
  /** Caller-supplied AbortSignal. */
1400
1747
  signal?: AbortSignal;
1401
1748
  }
1749
+ /** Input to {@link VacuumDevice.getWifiSignalMap} / {@link VacuumDevice.getCurrentSignal}. */
1750
+ interface WifiSignalMapInput {
1751
+ /** Request the last stored map first (default true). `false` = read what's advertised. */
1752
+ requestFresh?: boolean;
1753
+ /** Poll interval (ms) while waiting for the OSS object to be advertised. Default 2500. */
1754
+ pollMs?: number;
1755
+ /** Max wait (ms) for the map to become available. Default 20000. */
1756
+ maxWaitMs?: number;
1757
+ /** Inject the signed-blob fetcher (tests pass a fake). */
1758
+ fetcher?: OssFetcherLike;
1759
+ /** Optional AES key/iv for an encrypted blob (hex). */
1760
+ key?: string;
1761
+ iv?: string;
1762
+ /** Override the API host (defaults from the device region). */
1763
+ host?: string;
1764
+ /** Per-request timeout override in ms. */
1765
+ timeoutMs?: number;
1766
+ /** Caller-supplied AbortSignal. */
1767
+ signal?: AbortSignal;
1768
+ }
1402
1769
  /** A typed Dreame-vacuum handle (state + capability-gated commands). */
1403
1770
  declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
1404
1771
  #private;
@@ -1639,6 +2006,29 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
1639
2006
  * `applyVacuumPFrame` merge primitive ships separately for that work.
1640
2007
  */
1641
2008
  getMap(input: VacuumGetMapInput): Promise<VacuumMap>;
2009
+ /**
2010
+ * Fetch the robot's Wi-Fi signal map — the last stored coverage heatmap, with
2011
+ * NO robot movement. Requests it (map service `wifiMap` action, siid 6 aiid 4),
2012
+ * polls `PropWifiMap` (6/15) for the OSS object it advertises, fetches + decodes
2013
+ * it into a per-cell signal grid + geometry + robot/dock poses. The signal at
2014
+ * any world point (incl. the robot's own position) is a lookup — see
2015
+ * {@link WifiSignalMap.signalAt} / {@link WifiSignalMap.currentSignal}.
2016
+ *
2017
+ * Pass `requestFresh: false` to skip the request and read whatever is already
2018
+ * advertised. Throws if no Wi-Fi map exists yet (never run on this device).
2019
+ */
2020
+ getWifiSignalMap(input?: WifiSignalMapInput): Promise<WifiSignalMap>;
2021
+ /**
2022
+ * Render the Wi-Fi signal map as a simple PNG heatmap image (weakest→strongest,
2023
+ * unreached grey, robot marker) — ready to display. Convenience over
2024
+ * {@link getWifiSignalMap} + {@link renderWifiSignalPng}.
2025
+ */
2026
+ getWifiSignalImage(input?: WifiSignalMapInput & RenderWifiSignalPngOptions): Promise<Buffer>;
2027
+ /**
2028
+ * The one-call "current Wi-Fi signal": fetches the signal map and returns the
2029
+ * bars (1–4, 0 = unreached) at the robot's own position, or null when unknown.
2030
+ */
2031
+ getCurrentSignal(input?: WifiSignalMapInput): Promise<number | null>;
1642
2032
  /** Drop the P-frame merge base so the next I-frame re-seeds the stream. */
1643
2033
  resetMapStream(): void;
1644
2034
  /**
@@ -1655,6 +2045,37 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
1655
2045
  * frame on every push during a live clean.
1656
2046
  */
1657
2047
  fetchLatestMapStreaming(opts?: Omit<VacuumGetMapInput, 'filename'>): Promise<VacuumMap | null>;
2048
+ /**
2049
+ * Build a {@link DreameCameraController} for this device's camera, wired to
2050
+ * both planes it needs: the MIoT action transport (this device handle) and a
2051
+ * fresh Aliyun {@link DreameVideoSession} for the relay. Call
2052
+ * `controller.open()` to autonomously cold-start the stream and get a pullable
2053
+ * RTMP URL, then `controller.close()` to release it.
2054
+ *
2055
+ * The camera's per-session privacy gate means a coded camera needs its PIN:
2056
+ * pass `accessCode` (the code set at pairing). `iotId` is resolved from the
2057
+ * device's cloud video profile when not supplied.
2058
+ */
2059
+ createCameraController(opts?: {
2060
+ /** LinkVisual channel id; resolved from the cloud profile when omitted. */
2061
+ iotId?: string;
2062
+ /** Privacy PIN set at pairing (required to wake a coded camera). */
2063
+ accessCode?: string;
2064
+ /** Video vendor; defaults to `ali` (the only vendor that mints an RTMP relay). */
2065
+ vendor?: 'ali' | 'tx';
2066
+ /** Aliyun region code reported as `area`; defaults to `'4'`. */
2067
+ area?: string;
2068
+ /** Keep-alive cadence in ms (default 10s). */
2069
+ keepAliveIntervalMs?: number;
2070
+ /** Max time to wait for the relay while the device wakes. */
2071
+ wakeTimeoutMs?: number;
2072
+ /** Reported when a keep-alive tick fails. */
2073
+ onKeepAliveError?: (err: unknown) => void;
2074
+ /** Optional nodelink-style line logger. */
2075
+ log?: (line: string) => void;
2076
+ /** Reuse an existing relay minter (e.g. a shared {@link DreameVideoSession}). */
2077
+ relay?: RelayMinter;
2078
+ }): Promise<DreameCameraController>;
1658
2079
  /** Props worth seeding on start() / polling — exported for the facade. */
1659
2080
  static readonly DEFAULT_PROPS: readonly [{
1660
2081
  readonly siid: 2;
@@ -2305,13 +2726,13 @@ declare const DeviceDumpSchema: z.ZodObject<{
2305
2726
  type: z.ZodOptional<z.ZodString>;
2306
2727
  }, "strip", z.ZodTypeAny, {
2307
2728
  model: string;
2308
- type?: string | undefined;
2309
2729
  region?: string | undefined;
2730
+ type?: string | undefined;
2310
2731
  firmware?: string | undefined;
2311
2732
  }, {
2312
2733
  model: string;
2313
- type?: string | undefined;
2314
2734
  region?: string | undefined;
2735
+ type?: string | undefined;
2315
2736
  firmware?: string | undefined;
2316
2737
  }>;
2317
2738
  observations: z.ZodObject<{
@@ -2469,8 +2890,8 @@ declare const DeviceDumpSchema: z.ZodObject<{
2469
2890
  libraryVersion: string;
2470
2891
  device: {
2471
2892
  model: string;
2472
- type?: string | undefined;
2473
2893
  region?: string | undefined;
2894
+ type?: string | undefined;
2474
2895
  firmware?: string | undefined;
2475
2896
  };
2476
2897
  observations: {
@@ -2516,8 +2937,8 @@ declare const DeviceDumpSchema: z.ZodObject<{
2516
2937
  libraryVersion: string;
2517
2938
  device: {
2518
2939
  model: string;
2519
- type?: string | undefined;
2520
2940
  region?: string | undefined;
2941
+ type?: string | undefined;
2521
2942
  firmware?: string | undefined;
2522
2943
  };
2523
2944
  observations: {
@@ -2629,4 +3050,700 @@ declare function createDumper(target: DumperDevice, options?: DumperOptions): Du
2629
3050
  */
2630
3051
  declare function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[];
2631
3052
 
2632
- export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, AUTO_SWITCH_JSON_KEY, type AiDetectionRaw, type AutoSwitchKey, type AutoSwitchRaw, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableSpec, type CreateDeviceArgs, DefaultCapabilityResolver, type DeviceCapabilities, type DeviceDump, type DeviceEvent, type DreameAiFeature, DreameApiError, DreameAuthError, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, type DumperOptions, LIBRARY_NAME, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, MOWER_TASK_SUBSTATES, type MapBoundingBox, type MapCleanedAreaOverlay, type MapColorScheme, type MapDimensions, type MapFrameType, type MapLayer, type MapLayerType, type MapLowLyingArea, type MapObstacle, type MapPath, type MapPathType, type MapPoint, type MapPose, type MapRestrictedArea, type MapRoom, type MapRoomWall, type MapRun, type MapSegment, type MapStorey, type MapVirtualWall, type MapWallsInfo, type MiotAction, MiotError, type MiotProp, MiotState, type MowerAvailableMap, type MowerCapabilities, MowerCapabilityResolver, MowerChargingStatus, type MowerConsumableKey, type MowerConsumableReading, type MowerContour, MowerControlAction, type MowerControlState, MowerDevice, type MowerDeviceInput, MowerFault, type MowerFaultSeverity, type MowerHeartbeat, type MowerMap, type MowerMapBoundary, type MowerMowPath, type MowerMowingProgress, type MowerPathEntry, type MowerPoint, type MowerSpotArea, MowerStatus, type MowerTaskDescriptor, MowerTaskStatus, type MowerTaskSubState, type MowerZone, Nodreame, type NodreameDeps, type NodreameEvents, type NodreameOptions, type OssFetchInput, type OssFetcherLike, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type StateChangedEvent, SuctionLevel, TaskStatus, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, WaterVolume, consumableSpec, createClientDumper, createDumper, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, encodeAiFeatureWrite, encodeAutoSwitchWrite, extractMowerConsumableValues, getMowerCapabilities, getVacuumCapabilities, isAuthRefusedError, isDreameConsumableKey, mowerConsumableIndex, mowerFaultSeverity, parseMowerConsumables, parseMowerHeartbeat, parseMowingProgress, renderMowerSvg, renderVacuumPng, resolveCapabilities, supportedAutoSwitchKeys };
3053
+ /**
3054
+ * Public types for the video control-plane. The Dreamehome cloud fronts several
3055
+ * camera backends ("vendors"); a device advertises which ones it supports and
3056
+ * which one it is currently provisioned on. This module models only the
3057
+ * control-plane (token + session negotiation); the media transport per vendor
3058
+ * is built on top of it.
3059
+ */
3060
+ /** Camera backend a device streams through. `tx` = Tencent, `ali` = Aliyun LinkVisual. */
3061
+ type VideoVendor = 'tx' | 'ali';
3062
+ /** Short-lived token minted by `tx/user/accesstoken`, used by the video endpoints. */
3063
+ interface VideoAccessToken {
3064
+ /** Opaque bearer for the third-video service (distinct from the account token). */
3065
+ readonly token: string;
3066
+ /** Numeric video-service user id, as a string. */
3067
+ readonly userId: string | null;
3068
+ /** Epoch-ms at which {@link token} expires (converted from the cloud's epoch-seconds). */
3069
+ readonly expiresAt: number;
3070
+ }
3071
+ /**
3072
+ * A device's video profile, derived from its cloud record. Tells you whether the
3073
+ * device has a camera, which vendors it supports, which one it is on now, and —
3074
+ * when on Aliyun — its LinkVisual `iotId` (the id every LinkVisual call needs).
3075
+ */
3076
+ interface DeviceVideoProfile {
3077
+ /** Dreame device id (the `did`). */
3078
+ readonly did: string;
3079
+ /** Model code, e.g. `dreame.vacuum.r2538z`. */
3080
+ readonly model: string;
3081
+ /** Human product name, e.g. `X50 Ultra Complete`. */
3082
+ readonly displayName: string | null;
3083
+ /** True when the device exposes a camera stream (`permit === "video"`). */
3084
+ readonly videoCapable: boolean;
3085
+ /** The vendor the device is currently provisioned on (`''` in the record → null). */
3086
+ readonly currentVendor: VideoVendor | null;
3087
+ /** Vendors the device can use, in cloud-declared order. */
3088
+ readonly supportedVendors: readonly VideoVendor[];
3089
+ /** True when the cloud picks the vendor dynamically at session time. */
3090
+ readonly dynamicVendor: boolean;
3091
+ /** Aliyun LinkVisual device id, when the device is on (or has been on) Aliyun. */
3092
+ readonly iotId: string | null;
3093
+ /** Whether the device is online. */
3094
+ readonly online: boolean;
3095
+ }
3096
+
3097
+ /** Shared inputs for every video control-plane call. Mirrors `listDevices`. */
3098
+ interface VideoRequestInput {
3099
+ session: DreameSession;
3100
+ region: DreameRegion;
3101
+ ctx?: RequestContext;
3102
+ country?: string;
3103
+ lang?: string;
3104
+ apiHost?: string;
3105
+ fetchImpl?: FetchImpl;
3106
+ signal?: AbortSignal;
3107
+ timeoutMs?: number;
3108
+ }
3109
+ /**
3110
+ * Mint the short-lived third-video access token. Required as a body field by the
3111
+ * device-scoped video calls (getIdentity, getP2PInfo, …).
3112
+ */
3113
+ declare function getVideoAccessToken(input: VideoRequestInput): Promise<VideoAccessToken>;
3114
+ /**
3115
+ * Fetch the Aliyun authCode blob (`getAuthCodeV3`). This opaque hex string is the
3116
+ * material exchanged with Aliyun IoT to obtain a LinkVisual identity — the first
3117
+ * step of the Aliyun media path.
3118
+ */
3119
+ declare function getAliyunAuthCode(input: VideoRequestInput): Promise<string>;
3120
+ /**
3121
+ * Fetch the account's video `familyId` (used to scope some vendor calls). This
3122
+ * endpoint requires the third-video access token in the body — pass one via
3123
+ * `videoToken`, otherwise a fresh one is minted with {@link getVideoAccessToken}.
3124
+ */
3125
+ declare function getVideoFamilyId(input: VideoRequestInput & {
3126
+ videoToken?: string;
3127
+ }): Promise<string>;
3128
+ /**
3129
+ * Resolve a device's video profile (vendor, supported vendors, Aliyun iotId,
3130
+ * capability) from its cloud record. `did` is the Dreame device id.
3131
+ */
3132
+ declare function getDeviceVideoProfile(input: VideoRequestInput & {
3133
+ did: string;
3134
+ }): Promise<DeviceVideoProfile>;
3135
+
3136
+ /**
3137
+ * Why this device cannot open a camera channel — or `null` when it can.
3138
+ *
3139
+ * The camera path needs one thing from the cloud: the `iotId`, which is the
3140
+ * CHANNEL id `startMonitor` addresses (for both vendors — see
3141
+ * `startMonitorParams`). When it is absent the call cannot proceed, and for a
3142
+ * long time that was reported as a single sentence covering four different
3143
+ * situations:
3144
+ *
3145
+ * "device has no LinkVisual iotId; not a camera device or not provisioned"
3146
+ *
3147
+ * A CamStack operator met it on a robot whose camera they had just enabled in
3148
+ * the Dreame app (2026-09-16). Working out which of the four they were in
3149
+ * meant reading this library's source — the sentence names a MISSING FIELD,
3150
+ * not a cause, and the profile sitting right next to it already knows whether
3151
+ * the device is a camera at all, whether it is online, and which vendors it is
3152
+ * on and supports.
3153
+ *
3154
+ * The four states want four different actions:
3155
+ *
3156
+ * - not a camera → wrong device; nothing done here will help
3157
+ * - offline → wait; the binding is published BY the device
3158
+ * - camera, online, no channel → open it once in the vendor app, which is
3159
+ * what creates the binding
3160
+ * - channel present → not this function's business
3161
+ *
3162
+ * Pure, so it is testable against captured profiles without a cloud.
3163
+ */
3164
+ declare function explainNoCameraChannel(profile: DeviceVideoProfile): string | null;
3165
+
3166
+ /** A device record as returned by `device/info` (`data` = single record). */
3167
+ declare const DeviceInfoRecordSchema: z.ZodObject<{
3168
+ did: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3169
+ model: z.ZodOptional<z.ZodString>;
3170
+ customName: z.ZodOptional<z.ZodString>;
3171
+ vendor: z.ZodOptional<z.ZodString>;
3172
+ online: z.ZodOptional<z.ZodBoolean>;
3173
+ lwt: z.ZodOptional<z.ZodNumber>;
3174
+ property: z.ZodOptional<z.ZodString>;
3175
+ bindDomain: z.ZodOptional<z.ZodString>;
3176
+ deviceInfo: z.ZodOptional<z.ZodObject<{
3177
+ model: z.ZodOptional<z.ZodString>;
3178
+ displayName: z.ZodOptional<z.ZodString>;
3179
+ permit: z.ZodOptional<z.ZodString>;
3180
+ videoDynamicVendor: z.ZodOptional<z.ZodBoolean>;
3181
+ defaultVendors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
3182
+ productId: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3183
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
3184
+ model: z.ZodOptional<z.ZodString>;
3185
+ displayName: z.ZodOptional<z.ZodString>;
3186
+ permit: z.ZodOptional<z.ZodString>;
3187
+ videoDynamicVendor: z.ZodOptional<z.ZodBoolean>;
3188
+ defaultVendors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
3189
+ productId: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3190
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
3191
+ model: z.ZodOptional<z.ZodString>;
3192
+ displayName: z.ZodOptional<z.ZodString>;
3193
+ permit: z.ZodOptional<z.ZodString>;
3194
+ videoDynamicVendor: z.ZodOptional<z.ZodBoolean>;
3195
+ defaultVendors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
3196
+ productId: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3197
+ }, z.ZodTypeAny, "passthrough">>>;
3198
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
3199
+ did: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3200
+ model: z.ZodOptional<z.ZodString>;
3201
+ customName: z.ZodOptional<z.ZodString>;
3202
+ vendor: z.ZodOptional<z.ZodString>;
3203
+ online: z.ZodOptional<z.ZodBoolean>;
3204
+ lwt: z.ZodOptional<z.ZodNumber>;
3205
+ property: z.ZodOptional<z.ZodString>;
3206
+ bindDomain: z.ZodOptional<z.ZodString>;
3207
+ deviceInfo: z.ZodOptional<z.ZodObject<{
3208
+ model: z.ZodOptional<z.ZodString>;
3209
+ displayName: z.ZodOptional<z.ZodString>;
3210
+ permit: z.ZodOptional<z.ZodString>;
3211
+ videoDynamicVendor: z.ZodOptional<z.ZodBoolean>;
3212
+ defaultVendors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
3213
+ productId: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3214
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
3215
+ model: z.ZodOptional<z.ZodString>;
3216
+ displayName: z.ZodOptional<z.ZodString>;
3217
+ permit: z.ZodOptional<z.ZodString>;
3218
+ videoDynamicVendor: z.ZodOptional<z.ZodBoolean>;
3219
+ defaultVendors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
3220
+ productId: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3221
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
3222
+ model: z.ZodOptional<z.ZodString>;
3223
+ displayName: z.ZodOptional<z.ZodString>;
3224
+ permit: z.ZodOptional<z.ZodString>;
3225
+ videoDynamicVendor: z.ZodOptional<z.ZodBoolean>;
3226
+ defaultVendors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
3227
+ productId: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3228
+ }, z.ZodTypeAny, "passthrough">>>;
3229
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
3230
+ did: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3231
+ model: z.ZodOptional<z.ZodString>;
3232
+ customName: z.ZodOptional<z.ZodString>;
3233
+ vendor: z.ZodOptional<z.ZodString>;
3234
+ online: z.ZodOptional<z.ZodBoolean>;
3235
+ lwt: z.ZodOptional<z.ZodNumber>;
3236
+ property: z.ZodOptional<z.ZodString>;
3237
+ bindDomain: z.ZodOptional<z.ZodString>;
3238
+ deviceInfo: z.ZodOptional<z.ZodObject<{
3239
+ model: z.ZodOptional<z.ZodString>;
3240
+ displayName: z.ZodOptional<z.ZodString>;
3241
+ permit: z.ZodOptional<z.ZodString>;
3242
+ videoDynamicVendor: z.ZodOptional<z.ZodBoolean>;
3243
+ defaultVendors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
3244
+ productId: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3245
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
3246
+ model: z.ZodOptional<z.ZodString>;
3247
+ displayName: z.ZodOptional<z.ZodString>;
3248
+ permit: z.ZodOptional<z.ZodString>;
3249
+ videoDynamicVendor: z.ZodOptional<z.ZodBoolean>;
3250
+ defaultVendors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
3251
+ productId: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3252
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
3253
+ model: z.ZodOptional<z.ZodString>;
3254
+ displayName: z.ZodOptional<z.ZodString>;
3255
+ permit: z.ZodOptional<z.ZodString>;
3256
+ videoDynamicVendor: z.ZodOptional<z.ZodBoolean>;
3257
+ defaultVendors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
3258
+ productId: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
3259
+ }, z.ZodTypeAny, "passthrough">>>;
3260
+ }, z.ZodTypeAny, "passthrough">>;
3261
+ type DeviceInfoRecord = z.infer<typeof DeviceInfoRecordSchema>;
3262
+
3263
+ /**
3264
+ * Derive a {@link DeviceVideoProfile} from a raw device record. Pure — no I/O —
3265
+ * so it is unit-testable against captured records. `videoCapable` is true when
3266
+ * the cloud marks the device `permit: "video"` or lists any supported vendor.
3267
+ */
3268
+ declare function toVideoProfile(record: DeviceInfoRecord): DeviceVideoProfile;
3269
+
3270
+ /**
3271
+ * Aliyun API Gateway ("x-ca") request signing, HmacSHA1. Used by the LinkVisual
3272
+ * video endpoints the Dreamehome app talks to (living-account + api-iot hosts).
3273
+ *
3274
+ * The string to sign is:
3275
+ * METHOD\n Accept\n Content-MD5\n Content-Type\n Date\n <signed-headers-block> <resource>
3276
+ * where the signed-headers block is `Name:Value\n` for each header named in
3277
+ * `X-Ca-Signature-Headers`, sorted case-insensitively, and `resource` is the
3278
+ * path plus, for form bodies, the `?`-joined sorted form params.
3279
+ *
3280
+ * Verified byte-for-byte against a captured connect.json request (see tests).
3281
+ */
3282
+ interface SignedRequest {
3283
+ headers: Record<string, string>;
3284
+ body: string;
3285
+ }
3286
+ interface SignInput {
3287
+ method: 'POST' | 'GET';
3288
+ host: string;
3289
+ path: string;
3290
+ appKey: string;
3291
+ appSecret: string;
3292
+ /** JSON payload → sent as the raw body with a Content-MD5. */
3293
+ json?: unknown;
3294
+ /** Form fields → sent as application/x-www-form-urlencoded and folded into the resource. */
3295
+ form?: Record<string, string>;
3296
+ /** Extra x-ca headers to sign (e.g. `{ 'X-Ca-Stage': 'RELEASE' }`). */
3297
+ extraCaHeaders?: Record<string, string>;
3298
+ /** Overrides for deterministic tests. */
3299
+ nonce?: string;
3300
+ timestamp?: number;
3301
+ date?: string;
3302
+ }
3303
+ /** Build the x-ca headers + body for a request. */
3304
+ declare function signApiGatewayRequest(input: SignInput): SignedRequest;
3305
+
3306
+ /** LinkVisual live-stream descriptor returned by /vision/customer/stream/query. */
3307
+ interface StreamInfo {
3308
+ /** Connection strategies the cloud offers, e.g. ["Direct","Relay","NAT"]. */
3309
+ typeList: string[];
3310
+ /** RTMP relay URL. Plain (playable directly) when requested with relayEncrypted:false. */
3311
+ relayUrl: string | null;
3312
+ /** AES key/iv for the relay payload — present (non-empty) only when relayEncrypted:true. */
3313
+ relayDecryptKey: {
3314
+ iv: string;
3315
+ key: string;
3316
+ } | null;
3317
+ /** P2P (Direct/NAT) parameters: STUN/TURN + the WebSocket signaling URL. */
3318
+ p2pInfo: {
3319
+ stunUrl?: string;
3320
+ alterStunUrl?: string;
3321
+ signalUrl?: string;
3322
+ portDetectStunUrlList?: string[];
3323
+ } | null;
3324
+ supportVisionPtz?: boolean;
3325
+ }
3326
+ interface StreamQueryInput {
3327
+ /** Aliyun IoT region id, e.g. `eu-central-1`. */
3328
+ regionId: string;
3329
+ /** Short-lived IoT identity token (from createSessionByAuthCode). */
3330
+ iotToken: string;
3331
+ /** LinkVisual device id. */
3332
+ iotId: string;
3333
+ /**
3334
+ * When false (default) the relay RTMP carries a PLAIN (unencrypted) FLV, so it
3335
+ * can be handed straight to ffmpeg. When true the payload is AES-encrypted and
3336
+ * `relayDecryptKey` is returned.
3337
+ */
3338
+ relayEncrypted?: boolean;
3339
+ /** 0 = HD main stream (default). */
3340
+ streamType?: number;
3341
+ appKey?: string;
3342
+ appSecret?: string;
3343
+ fetchImpl?: FetchImpl;
3344
+ signal?: AbortSignal;
3345
+ }
3346
+ /**
3347
+ * Query the LinkVisual live stream for a device. Returns the relay RTMP URL and
3348
+ * P2P parameters. Note: each returned relay token is single-use — request a
3349
+ * fresh one per consumer.
3350
+ */
3351
+ declare function streamQuery(input: StreamQueryInput): Promise<StreamInfo>;
3352
+
3353
+ /** Aliyun OpenAccount session from loginbyoauth. */
3354
+ interface OaSession {
3355
+ sid: string;
3356
+ refreshToken: string;
3357
+ openId: string;
3358
+ }
3359
+ /** Aliyun IoT session (the iotToken used by every LinkVisual call). */
3360
+ interface IotSession {
3361
+ iotToken: string;
3362
+ refreshToken: string;
3363
+ identityId: string;
3364
+ /** Epoch-ms at which iotToken expires. */
3365
+ expiresAt: number;
3366
+ }
3367
+ interface LoginByOauthInput {
3368
+ authCode: string;
3369
+ regionId: string;
3370
+ deviceId?: string;
3371
+ utdid?: string;
3372
+ fetchImpl?: FetchImpl;
3373
+ }
3374
+ /** Exchange the Dreame authCode for an Aliyun OpenAccount session (step 2). */
3375
+ declare function loginByOauth(input: LoginByOauthInput): Promise<OaSession>;
3376
+ interface CreateIotSessionInput {
3377
+ /** The OpenAccount `sid` from {@link loginByOauth}. */
3378
+ sid: string;
3379
+ regionId: string;
3380
+ fetchImpl?: FetchImpl;
3381
+ }
3382
+ /** Exchange the OA `sid` for an Aliyun IoT session / iotToken (step 3). */
3383
+ declare function createIotSession(input: CreateIotSessionInput): Promise<IotSession>;
3384
+
3385
+ /** Aliyun IoT region id per Dreame region. Only `eu-central-1` is verified live. */
3386
+ declare const ALIYUN_REGION_ID: Record<DreameRegion, string>;
3387
+
3388
+ interface DreameVideoSessionInput {
3389
+ /** An authenticated Dreame account session (from `Nodreame`). */
3390
+ session: DreameSession;
3391
+ region: DreameRegion;
3392
+ fetchImpl?: FetchImpl;
3393
+ }
3394
+ /**
3395
+ * High-level video session for a Dreame account. Establishes and caches the
3396
+ * Aliyun IoT identity (iotToken), and mints fresh LinkVisual RTMP stream URLs
3397
+ * on demand. Each returned relay URL is single-use — call {@link getStreamInfo}
3398
+ * (or {@link getRtmpUrl}) once per consumer.
3399
+ *
3400
+ * The plain (unencrypted) relay URL is a standard RTMP stream: hand it straight
3401
+ * to ffmpeg / scrypted / camstack. No local muxing or RTSP server required here.
3402
+ */
3403
+ declare class DreameVideoSession {
3404
+ #private;
3405
+ constructor(input: DreameVideoSessionInput);
3406
+ /** The resolved Aliyun IoT region id (e.g. `eu-central-1`). */
3407
+ get regionId(): string;
3408
+ /** Mint (or reuse) a valid Aliyun IoT session, refreshing before expiry. */
3409
+ ensureIotSession(): Promise<IotSession>;
3410
+ /**
3411
+ * Mint a fresh LinkVisual stream descriptor for a device. `encrypted:false`
3412
+ * (default) yields a plain RTMP relay URL directly playable by ffmpeg.
3413
+ *
3414
+ * A camera that has been idle reports "device offline" until the query wakes
3415
+ * its video agent via the LinkVisual cloud. This transparently retries on that
3416
+ * (up to `wakeTimeoutMs`, default 30s) so a single call returns a live stream;
3417
+ * pass `wakeTimeoutMs: 0` to fail fast instead.
3418
+ */
3419
+ getStreamInfo(iotId: string, opts?: {
3420
+ encrypted?: boolean;
3421
+ wakeTimeoutMs?: number;
3422
+ pollMs?: number;
3423
+ }): Promise<StreamInfo>;
3424
+ /**
3425
+ * Convenience: mint a fresh plain RTMP relay URL for a device, or `null` when
3426
+ * the device is not currently streaming (no relay available).
3427
+ */
3428
+ getRtmpUrl(iotId: string): Promise<string | null>;
3429
+ }
3430
+
3431
+ /**
3432
+ * Parsers for the robot's on-device AI detections, pushed as camera-service
3433
+ * properties (subscribe to `propertyChanged` for SIID 10001):
3434
+ * - piid 110 (`personFollow` / PERSON_DATA): a tracked-person bounding box
3435
+ * - piid 112 (`videoObstacleData`): a list of obstacle boxes
3436
+ *
3437
+ * The overlay for obstacle boxes is gated on `set_properties siid 28 piid 13 = 1`.
3438
+ * Payloads are JSON strings; these parsers are lenient (return `null` on garbage)
3439
+ * and normalize timestamps (the device reports nanoseconds).
3440
+ */
3441
+ /** A normalized detection box. Coordinates are passed through as-reported. */
3442
+ interface DetectionBox {
3443
+ readonly x: number;
3444
+ readonly y: number;
3445
+ readonly w: number;
3446
+ readonly h: number;
3447
+ /** Optional label/class if the payload carries one. */
3448
+ readonly label?: string;
3449
+ /** Optional confidence if the payload carries one. */
3450
+ readonly score?: number;
3451
+ }
3452
+ interface PersonFollowDetection {
3453
+ /** Detection time in epoch milliseconds (converted from the device's ns). */
3454
+ readonly timestampMs: number;
3455
+ readonly box: DetectionBox | null;
3456
+ }
3457
+ interface ObstacleDetection {
3458
+ readonly timestampMs: number;
3459
+ readonly boxes: readonly DetectionBox[];
3460
+ }
3461
+ /** Parse a `personFollow` (piid 110) payload into a normalized detection. */
3462
+ declare function parsePersonFollow(value: unknown): PersonFollowDetection | null;
3463
+ /** Parse a `videoObstacleData` (piid 112) payload into normalized boxes. */
3464
+ declare function parseObstacleData(value: unknown): ObstacleDetection | null;
3465
+
3466
+ type AmfValue = number | boolean | string | null | undefined | AmfValue[] | {
3467
+ [key: string]: AmfValue;
3468
+ };
3469
+
3470
+ declare const LV_STATUS: {
3471
+ readonly REQUEST_AUDIO_TYPE: "NetStream.RequestAudioType";
3472
+ readonly FORCE_IFRAME_START: "NetStream.ForceIFrame.Start";
3473
+ readonly PING_START: "NetStream.Ping.Start";
3474
+ readonly PING_RESPONSE: "NetStream.Ping.Response";
3475
+ readonly PRE_CONNECT: "NetStream.PreConnect";
3476
+ readonly KEEPALIVE: "NetStream.Keepalive";
3477
+ readonly SERVER_INFO: "NetStream.ServerInfo";
3478
+ readonly TRANSFER_DATA: "NetStream.TransferData";
3479
+ readonly PLAY_START: "NetStream.Play.Start";
3480
+ /** Server signals the talk (intercom uplink) channel is ready. */
3481
+ readonly TALK_READY: "NetStream.TalkReady";
3482
+ };
3483
+ /**
3484
+ * FLV audio tag header byte for the intercom uplink: G.711 A-law, 8 kHz, 16-bit,
3485
+ * mono — `(7<<4)|(0<<2)|(1<<1)|0` = 0x72. Verified against liblvmedia SetTalkInfo.
3486
+ */
3487
+ declare const TALK_AUDIO_HEADER_G711A = 114;
3488
+ interface StatusEvent {
3489
+ code: string;
3490
+ level: string;
3491
+ description: string;
3492
+ fields: {
3493
+ [key: string]: AmfValue;
3494
+ };
3495
+ transactionId: number;
3496
+ }
3497
+ interface VideoAccessUnitEvent {
3498
+ data: Buffer;
3499
+ isKeyframe: boolean;
3500
+ videoType: 'H264';
3501
+ microseconds: number;
3502
+ time?: number;
3503
+ }
3504
+ interface AudioInfoEvent {
3505
+ codec: 'aac' | 'g711a' | 'g711u' | 'unknown';
3506
+ sampleRate: number;
3507
+ channels: number;
3508
+ }
3509
+ interface LvRtmpClientOptions {
3510
+ /** relayUrl from stream/query, e.g. rtmp://host:8000/live?token=..&session=../<key>_0 */
3511
+ url: string;
3512
+ /** Appended to app/tcUrl as "&cacheDuration=N" when not already present (the app always sends 3000). */
3513
+ cacheDurationMs?: number;
3514
+ /** Send the onStatus NetStream.RequestAudioType {audio_type:"1"} right after play (app does). */
3515
+ requestAudioType?: boolean;
3516
+ /** Send NetStream.Ping.Start after the first server message and then periodically (app does). */
3517
+ ping?: boolean;
3518
+ pingIntervalMs?: number;
3519
+ /** Also send NetStream.ForceIFrame.Start once playing (the app sends it when it starts an MP4 record). */
3520
+ forceIFrameOnStart?: boolean;
3521
+ connectTimeoutMs?: number;
3522
+ log?: (line: string) => void;
3523
+ }
3524
+ interface Endpoint {
3525
+ host: string;
3526
+ port: number;
3527
+ app: string;
3528
+ tcUrl: string;
3529
+ playpath: string;
3530
+ }
3531
+ /**
3532
+ * Split like librtmp/ffmpeg do: playpath = last path segment, app = everything between host and it
3533
+ * (query string included). Verified against the app's connect/play in both pcaps.
3534
+ */
3535
+ declare function parseRelayUrl(url: string, cacheDurationMs: number): Endpoint;
3536
+ type Events = {
3537
+ connected: [];
3538
+ status: [StatusEvent];
3539
+ command: [string, AmfValue[]];
3540
+ metadata: [{
3541
+ [key: string]: AmfValue;
3542
+ }];
3543
+ audio: [{
3544
+ timestamp: number;
3545
+ body: Buffer;
3546
+ }];
3547
+ video: [{
3548
+ timestamp: number;
3549
+ body: Buffer;
3550
+ }];
3551
+ videoAccessUnit: [VideoAccessUnitEvent];
3552
+ audioFrame: [Buffer];
3553
+ audioInfo: [AudioInfoEvent];
3554
+ error: [Error];
3555
+ close: [];
3556
+ };
3557
+ declare class LvRtmpClient extends EventEmitter<Events> {
3558
+ private readonly opts;
3559
+ private readonly endpoint;
3560
+ private socket;
3561
+ private handshakeBuf;
3562
+ private handshakeDone;
3563
+ private c1Time;
3564
+ private reader;
3565
+ private transactionId;
3566
+ private bytesIn;
3567
+ private bytesAcked;
3568
+ private ackWindow;
3569
+ private pingTimer;
3570
+ private firstMessageSeen;
3571
+ private avcConfig;
3572
+ private aacConfig;
3573
+ private closed;
3574
+ constructor(options: LvRtmpClientOptions);
3575
+ /** Open TCP, write C0+C1 and the pipelined connect/play/RequestAudioType. Resolves once written. */
3576
+ connect(): Promise<void>;
3577
+ close(): void;
3578
+ /** onStatus encoder — byte-exact replica of liblvmedia IOT_RTMP_SendMessage (csid 3, fmt 1, ts 0). */
3579
+ buildStatus(code: string, extra?: readonly [string, string]): Buffer;
3580
+ sendStatus(code: string, extra?: readonly [string, string]): void;
3581
+ forceIFrame(): void;
3582
+ private buildC0C1;
3583
+ private buildConnect;
3584
+ private buildPlay;
3585
+ /** Exposed for the byte-exact self-test against the pcap. */
3586
+ buildOpeningSequence(): Buffer[];
3587
+ /**
3588
+ * Push one intercom (talk-back) audio frame UPSTREAM to the device — an RTMP
3589
+ * type-8 audio message on the talk chunk stream (csid 4, msid 1), exactly as
3590
+ * liblvmedia's `IOT_RTMP_SendAudio` does. `payload` is the raw codec audio
3591
+ * (default framing = G.711 A-law); `tsMs` is a running millisecond timestamp.
3592
+ * Only meaningful after the server sends {@link LV_STATUS.TALK_READY}.
3593
+ */
3594
+ sendAudioFrame(payload: Buffer, tsMs: number, header?: number): void;
3595
+ private write;
3596
+ private sendAck;
3597
+ private sendPingResponse;
3598
+ private startPinging;
3599
+ private onData;
3600
+ private onHandshake;
3601
+ private dispatch;
3602
+ private onUserControl;
3603
+ private onCommand;
3604
+ private onDataMessage;
3605
+ private onAudio;
3606
+ private onVideo;
3607
+ private fail;
3608
+ private onClose;
3609
+ }
3610
+
3611
+ /**
3612
+ * End-to-end camera media pipeline: autonomously cold-start the LinkVisual
3613
+ * monitor, connect to the resulting RTMP relay, and re-emit demuxed elementary
3614
+ * frames — H.264 access units (Annex-B, SPS/PPS prepended on keyframes) and AAC
3615
+ * (ADTS) — plus lifecycle events.
3616
+ *
3617
+ * This is the reusable frame feed for any external consumer (scrypted's RFC4571
3618
+ * muxer, camstack, a file writer). It owns the {@link DreameCameraController}
3619
+ * lifecycle: {@link start} opens the monitor and connects; {@link stop} closes
3620
+ * the relay socket and releases the monitor on the device.
3621
+ */
3622
+
3623
+ /** The subset of {@link LvRtmpClient} the pipeline drives — injectable for tests. */
3624
+ interface FrameSource {
3625
+ connect(): Promise<void>;
3626
+ close(): void;
3627
+ on(event: 'videoAccessUnit', cb: (e: VideoAccessUnitEvent) => void): void;
3628
+ on(event: 'audioFrame', cb: (buf: Buffer) => void): void;
3629
+ on(event: 'audioInfo', cb: (info: AudioInfoEvent) => void): void;
3630
+ on(event: 'status', cb: (e: StatusEvent) => void): void;
3631
+ on(event: 'error', cb: (err: Error) => void): void;
3632
+ on(event: 'close', cb: () => void): void;
3633
+ /** Push an intercom (talk-back) audio frame upstream. */
3634
+ sendAudioFrame(payload: Buffer, tsMs: number, header?: number): void;
3635
+ }
3636
+ /** Factory for the RTMP frame source; defaults to a real {@link LvRtmpClient}. */
3637
+ type FrameSourceFactory = (url: string) => FrameSource;
3638
+ interface DreameCameraStreamEvents {
3639
+ /** Emitted once the relay is connected; carries the (single-use) RTMP URL. */
3640
+ started: [{
3641
+ rtmpUrl: string;
3642
+ }];
3643
+ /** One H.264 access unit (Annex-B). */
3644
+ videoAccessUnit: [VideoAccessUnitEvent];
3645
+ /** One audio frame (AAC in ADTS, or raw G.711). */
3646
+ audioFrame: [Buffer];
3647
+ /** Audio format, emitted once known. */
3648
+ audioInfo: [AudioInfoEvent];
3649
+ /** Emitted after the pipeline is fully torn down. */
3650
+ stopped: [];
3651
+ error: [Error];
3652
+ [event: string]: unknown[];
3653
+ }
3654
+ interface DreameCameraStreamInput {
3655
+ /** A controller for the target camera (not yet opened). */
3656
+ controller: DreameCameraController;
3657
+ /** Override the RTMP client (tests inject a fake). */
3658
+ clientFactory?: FrameSourceFactory;
3659
+ /** Forwarded to {@link LvRtmpClient}: send NetStream.RequestAudioType after play. */
3660
+ requestAudioType?: boolean;
3661
+ /** Forwarded to {@link LvRtmpClient}: send periodic NetStream.Ping. */
3662
+ ping?: boolean;
3663
+ /** Optional line logger for the RTMP client. */
3664
+ log?: (line: string) => void;
3665
+ }
3666
+ declare class DreameCameraStream extends TypedEmitter<DreameCameraStreamEvents> {
3667
+ #private;
3668
+ constructor(input: DreameCameraStreamInput);
3669
+ /** True while frames are flowing. */
3670
+ get isRunning(): boolean;
3671
+ /**
3672
+ * Cold-start the monitor, connect to the relay, and begin emitting frames.
3673
+ * Resolves once connected. Throws (and cleans up) if the monitor or relay
3674
+ * connection fails.
3675
+ */
3676
+ start(): Promise<{
3677
+ rtmpUrl: string;
3678
+ }>;
3679
+ /** Close the relay connection and release the monitor. Idempotent. */
3680
+ stop(): Promise<void>;
3681
+ /**
3682
+ * Open the two-way intercom: send the MIoT start, then resolve once the relay
3683
+ * signals the talk channel is ready (so it's safe to {@link sendTalkPcm}).
3684
+ * Rejects if not ready within `timeoutMs`.
3685
+ */
3686
+ startTalk(opts?: {
3687
+ needRecordSound?: boolean;
3688
+ videoCall?: boolean;
3689
+ timeoutMs?: number;
3690
+ }): Promise<void>;
3691
+ /** Resolve when the talk channel is ready (or reject on timeout). */
3692
+ whenTalkReady(timeoutMs?: number): Promise<void>;
3693
+ /**
3694
+ * Encode 16-bit LE mono PCM (8 kHz) to G.711 A-law and push it upstream.
3695
+ * `tsMs` defaults to a running clock advanced by the frame duration.
3696
+ */
3697
+ sendTalkPcm(pcm16le: Buffer, tsMs?: number): void;
3698
+ /** Push raw A-law audio upstream (advancing the running talk clock). */
3699
+ sendTalkAlaw(alaw: Buffer, tsMs?: number): void;
3700
+ /** Close the two-way intercom (MIoT stop). */
3701
+ stopTalk(): Promise<void>;
3702
+ }
3703
+
3704
+ /**
3705
+ * G.711 A-law codec (ITU-T G.711) — the intercom uplink format for Dreame
3706
+ * cameras (8 kHz, mono, 16-bit PCM in). Pure functions, no state.
3707
+ */
3708
+ /** Encode one 16-bit signed PCM sample to an A-law byte. */
3709
+ declare function pcm16ToALaw(sample: number): number;
3710
+ /**
3711
+ * Encode a buffer of little-endian 16-bit PCM samples to A-law bytes
3712
+ * (one output byte per input sample).
3713
+ */
3714
+ declare function pcm16leToALaw(pcm: Buffer): Buffer;
3715
+
3716
+ interface AvcConfig {
3717
+ sps: Buffer[];
3718
+ pps: Buffer[];
3719
+ nalLengthSize: number;
3720
+ }
3721
+ interface AacConfig {
3722
+ objectType: number;
3723
+ samplingFrequencyIndex: number;
3724
+ channelConfiguration: number;
3725
+ }
3726
+ /** Parse an AVCDecoderConfigurationRecord (FLV AVC sequence header payload). */
3727
+ declare function parseAvcConfig(rec: Buffer): AvcConfig;
3728
+ /** Length-prefixed NALUs -> Annex-B. Prepends SPS/PPS when `prependParams` is set (for keyframes). */
3729
+ declare function avccToAnnexB(data: Buffer, cfg: AvcConfig, prependParams: boolean): Buffer;
3730
+ declare function parseAacConfig(asc: Buffer): AacConfig;
3731
+ declare function aacSampleRate(cfg: AacConfig): number;
3732
+ /** Wrap one raw AAC frame in a 7-byte ADTS header. */
3733
+ declare function aacToAdts(raw: Buffer, cfg: AacConfig): Buffer;
3734
+
3735
+ interface ListDevicesInput {
3736
+ session: DreameSession;
3737
+ region: DreameRegion;
3738
+ ctx?: RequestContext;
3739
+ country?: string;
3740
+ lang?: string;
3741
+ apiHost?: string;
3742
+ fetchImpl?: FetchImpl;
3743
+ signal?: AbortSignal;
3744
+ timeoutMs?: number;
3745
+ }
3746
+ /** Enumerate the devices visible to the authenticated account (incl. shared). */
3747
+ declare function listDevices(input: ListDevicesInput): Promise<DreameDevice[]>;
3748
+
3749
+ export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, ALIYUN_REGION_ID, AUTO_SWITCH_JSON_KEY, type AacConfig, type AiDetectionRaw, type AudioInfoEvent, type AutoSwitchKey, type AutoSwitchRaw, type AvcConfig, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type BrokerEndpoint, type CameraStreamHandle, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableSpec, type CreateDeviceArgs, DRIVE_DIRECTIONS, DefaultCapabilityResolver, type DetectionBox, type DeviceCapabilities, type DeviceConnectivity, type DeviceDump, type DeviceEvent, type DeviceVideoProfile, type DreameAiFeature, DreameApiError, DreameAuthError, DreameCameraController, type DreameCameraControllerInput, DreameCameraStream, type DreameCameraStreamEvents, type DreameCameraStreamInput, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, DreameVideoSession, type DreameVideoSessionInput, type DriveDirection, type DumperOptions, type FrameSource, type FrameSourceFactory, type IotSession, LIBRARY_NAME, LV_STATUS, type ListDevicesInput, LvRtmpClient, type LvRtmpClientOptions, MONITOR_AIID, MONITOR_PIID, MONITOR_SIID, MONITOR_VENDOR_TOKEN, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, MOWER_TASK_SUBSTATES, type MapBoundingBox, type MapCleanedAreaOverlay, type MapColorScheme, type MapDimensions, type MapFrameType, type MapLayer, type MapLayerType, type MapLowLyingArea, type MapObstacle, type MapPath, type MapPathType, type MapPoint, type MapPose, type MapRestrictedArea, type MapRoom, type MapRoomWall, type MapRun, type MapSegment, type MapStorey, type MapVirtualWall, type MapWallsInfo, type MiotAction, MiotError, type MiotProp, MiotState, type MonitorActionCaller, type MowerAvailableMap, type MowerCapabilities, MowerCapabilityResolver, MowerChargingStatus, type MowerConsumableKey, type MowerConsumableReading, type MowerContour, MowerControlAction, type MowerControlState, MowerDevice, type MowerDeviceInput, MowerFault, type MowerFaultSeverity, type MowerHeartbeat, type MowerMap, type MowerMapBoundary, type MowerMowPath, type MowerMowingProgress, type MowerPathEntry, type MowerPoint, type MowerSpotArea, MowerStatus, type MowerTaskDescriptor, MowerTaskStatus, type MowerTaskSubState, type MowerZone, Nodreame, type NodreameDeps, type NodreameEvents, type NodreameOptions, type OaSession, type ObstacleDetection, type OssFetchInput, type OssFetcherLike, PET_SOUNDS, type PersonFollowDetection, type PetSound, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RelayMinter, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type RenderWifiSignalPngOptions, type StateChangedEvent, type StatusEvent, type StreamInfo, type StreamQueryInput, SuctionLevel, TALK_AUDIO_HEADER_G711A, TaskStatus, VACUUM_ACTIONS, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, VACUUM_MOVE, VACUUM_SIID, type VacuumActionKey, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, type VideoAccessToken, type VideoAccessUnitEvent, type VideoRequestInput, type VideoVendor, WaterVolume, type WifiSignalMap, type WifiSignalMapInput, aacSampleRate, aacToAdts, avccToAnnexB, consumableSpec, createClientDumper, createDumper, createIotSession, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, decodeWifiSignalMap, encodeAiFeatureWrite, encodeAutoSwitchWrite, explainNoCameraChannel, extractMowerConsumableValues, getAliyunAuthCode, getDeviceVideoProfile, getMowerCapabilities, getVacuumCapabilities, getVideoAccessToken, getVideoFamilyId, hashAccessCode, isAuthRefusedError, isDreameConsumableKey, listDevices, loginByOauth, makeMonitorSession, mowerConsumableIndex, mowerFaultSeverity, parseAacConfig, parseAvcConfig, parseConnectivity, parseMowerConsumables, parseMowerHeartbeat, parseMowingProgress, parseObstacleData, parsePersonFollow, parseRelayUrl, pcm16ToALaw, pcm16leToALaw, remoteDriveValue, renderMowerSvg, renderVacuumPng, renderWifiSignalPng, resolveCapabilities, signApiGatewayRequest, streamQuery, supportedAutoSwitchKeys, toVideoProfile };