@apocaliss92/nodedreame

中文文档
1.12.5发布于 2026/09/26MITTypeScriptESM + CJS近 30 天下载 2413

用于 Dreame 真空和割草机的节点.js/TypeScript 客户端

中文使用文档
文档目录21 节
  1. 状态
  2. 使用量
  3. 真空
  4. 诚实的警告
  5. 汽车
  6. 诚实的警告
  7. 地图
  8. 真空图-巴布亚新几内亚
  9. 摩尔地图 → SVG
  10. 归属
  11. 连通性
  12. WiFi 信号
  13. 相机和视频流
  14. 如何运作(高级)
  15. 快开始
  16. 相机动作(控制平面)
  17. 双向对讲机(回声)
  18. 对象检测
  19. 诊断堆(只读)
  20. 安装
  21. 许可证

点心

用于Dreame机器人真空和割草机的Node.js/TypeScript客户端通过Dreamehome云.

工作正在进行中。 统一,事件驱动库——不是UI.

状态

discoverDevices() 返回键入 VacuumDevice 页:1 dreame.vacuum.* 模型 键入 MowerDevice 页:1 dreame.mower.* 模型,每个模型都有解码状态 获取器和能力命令。 除此之外:

  • 地图 ——活式真空图解码(I/P-帧流)+PNG渲染,割草机图+SVG.
  • 连通性 ——每台设备在线/链接类型/经纪人/公司软件/系列视图 (单位:千美元)连通性) (中文(简体) ).
  • WiFi 信号 ——机器人的WiFi覆盖热映射,在机器人那里取样 用于当前符号读取的姿势(WiFi 信号) (中文(简体) ).
  • 相机视频( V) (X40/X50级)——自主冷起动"连通视觉" RTMP流与 H.264+AAC 帧输出,相机动作(驱动/预设/声调/) 和双向对讲机 (单位:千美元)相机和视频流) (中文(简体) ).

盖下:

  • OAuth 密码赠出登录, 并主动更新符号( 共享会话) 过期前刷新~100s,每个设备的 MQTT 推为 以透明的方式重新认证新标志).
  • 每台设备 MQTT 推取 持久重联 - 它与备份重接 在意外的下降中重建连接 刷新点 A 类 get_properties 民意调查倒计时只在推倒时进行 一旦重新连接就停止.
  • 所有云和 MQTT 回复均经过验证 zod 在边界,所以 错误的响应以明显的错误快速失败, 而不是传播 undefined。 。 。 .

使用量

import { Nodreame } from '@apocaliss92/nodedreame';

const client = new Nodreame({
  username: process.env.DREAME_USERNAME!,
  password: process.env.DREAME_PASSWORD!,
  region: 'eu',
});

await client.login();
const devices = await client.discoverDevices();

for (const device of devices) {
  console.log(device.deviceId, device.model, device.name);

  // Live-read a couple of MIoT properties (siid.piid are model-specific).
  await device.refreshProperties([{ siid: 2, piid: 1 }]);
  console.log('state:', device.getProperty(2, 1)?.value);

  // React to pushed updates.
  device.on('stateChanged', (e) => console.log('changed', e.changes));
}

// The shared session auto-refreshes ~100s before expiry; every device's MQTT
// push is re-authenticated with the new token transparently.

await client.close(); // closes all pushes, clears all timers

每个手柄上都有通用的MIOT原始相: refreshProperties 编号 : getProperty (缓存) / setProperty 编号 : callAction。 。 。 . 摩尔startMowing/附表)处理和活地地图后期到达.

真空

对于 dreame.vacuum.* 型号, discoverDevices() 返回 a VacuumDevice 联合国 用于添加打入、解码状态获取器和 在原始的MIOT原始的上方有能力命令.

import { Nodreame, VacuumDevice, SuctionLevel } from '@apocaliss92/nodedreame';

const client = new Nodreame({
  username: process.env.DREAME_USERNAME!,
  password: process.env.DREAME_PASSWORD!,
  region: 'eu',
});

await client.login();
const devices = await client.discoverDevices();

const vacuum = devices.find((d): d is VacuumDevice => d instanceof VacuumDevice);
if (vacuum) {
  // Seed the cache with the vacuum's known properties (one live read).
  await vacuum.refreshProperties([...VacuumDevice.DEFAULT_PROPS]);

  // Typed, decoded state. Each getter returns null until the matching
  // property has landed (via the seed read above or a pushed update).
  console.log('status:', vacuum.status); // MiotState | null
  console.log('battery:', vacuum.battery); // number | null (%)
  console.log('suction:', vacuum.suction); // SuctionLevel | null
  console.log('water:', vacuum.water); // WaterVolume | null
  console.log('docked:', vacuum.isDocked);
  console.log('faults:', vacuum.faults); // number[]

  // Capability-gated commands (all async).
  await vacuum.setSuction(SuctionLevel.Max);
  await vacuum.startCleaning(); // begins a clean (NOT the lifecycle start())
  // await vacuum.pause();
  // await vacuum.stop();
  // await vacuum.dock();         // return to dock / charge
  // await vacuum.locate();       // make the robot beep
  // await vacuum.cleanSegments([1, 2]); // per-room, gated by canCleanPerRoom
}

await client.close();

startCleaning()没有 start()。 。 。 . start() 是继承的生命周期 打开 MQTT 推力的方法; 清除命令是 startCleaning()。 。 。 . 所有指挥方法都是 async。 。 。 .

诚实的警告

  • 返回状态获取器 null 直到数据着陆. 打电话 refreshProperties([...VacuumDevice.DEFAULT_PROPS]) (或等被推倒) 在读取前更新; 未种子获取器是 null,以及一个外程生 值还解码为 null (原始整数仍可通过 *Raw 获得者,例如:. vacuum.suctionRaw) (中文(简体) ).
  • r2538z 能力是假定的,没有经过核实. 用户的 dreame.vacuum.r2538z 能力记录从其 r2532a (X50) (中文(简体) ) 师兄,所以 vacuum.vacuumCapabilities.verified === false。处理其特点 旗帜是最佳假说,直到确认在设备上.
  • 清末写作通过建筑安全. setCleaningMode() 写入 简单 CLEAN_MODE_SETTING 属性(siid 2 piid 6); 未加工 0x1400- 装模作样 bitfield(siid 4 piid 23)在此仅读取,从未直接写入.
  • 一些动作映射是假设的. pause编号 :stop编号 :locate编号 :clearWarning 在r2532a兄弟身上有线核查; startCleaning编号 :dock 编辑 目标清洁有效载荷cleanSegments编号 :cleanZones编号 :cleanSpot) 已移植 从Tashack开始 还没有对所有模型进行现场验证.
  • 还没有地图生成状态 . 每个房间/当前区域的数据和现场地图都来了 从映射层(第五阶段);它们不在第三阶段暴露.

汽车

对于 dreame.mower.* 型号, discoverDevices() 返回 a MowerDevice 联合国 普通手柄的子类),带有已打入、已解码的状态获取器和 能力命令,镜像真空表面.

import { Nodreame, MowerDevice } from '@apocaliss92/nodedreame';

const client = new Nodreame({
  username: process.env.DREAME_USERNAME!,
  password: process.env.DREAME_PASSWORD!,
  region: 'eu',
});

await client.login();
const devices = await client.discoverDevices();

const mower = devices.find((d): d is MowerDevice => d instanceof MowerDevice);
if (mower) {
  // Seed the cache with the mower's known properties (one live read).
  await mower.refreshProperties([...MowerDevice.DEFAULT_PROPS]);

  // Typed, decoded state. Each getter returns null until the matching
  // property has landed (via the seed read above or a pushed update).
  console.log('status:', mower.status); // MowerStatus | null
  console.log('battery:', mower.battery); // number | null (%)
  console.log('charging:', mower.charging); // MowerChargingStatus | null
  console.log('docked:', mower.isDocked); // boolean
  console.log('mowing:', mower.isMowing); // boolean
  console.log('task:', mower.task); // MowerTaskDescriptor | null (2:50)
  console.log('coverage target %:', mower.coverageTargetPct); // number | null
  console.log('control action:', mower.controlAction); // MowerControlAction | null

  // Capability-gated commands (all async).
  await mower.startMowing(); // begins mowing (NOT the lifecycle start())
  // await mower.pause();
  // await mower.stop();
  // await mower.dock();   // return to dock / charge
  // await mower.resume(); // resume after a pause (continueControl opcode)
  // Targeted starts (gated by the model's capability flags):
  // await mower.startMowingAllArea(mapId);   // whole map
  // await mower.startMowingZones([1, 3]);    // selected zones
  // await mower.startMowingEdges([[1, 0]]);  // edge / contour pairs
  // await mower.startMowingSpots([5]);       // spot areas
}

await client.close();

startMowing()没有 start()。 。 。 . start() 是继承的生命周期 打开 MQTT 推力的方法; 剪切命令是 startMowing()全部数据 命令方法为 async。 。 。 .

诚实的警告

  • 返回状态获取器 null 直到数据着陆. 打电话 refreshProperties([...MowerDevice.DEFAULT_PROPS]) (或等被推倒) 在读取前更新; 未种子获取器是 null,以及一个外程生 值还解码为 null (原始整数仍可通过 *Raw 获得者,例如:. mower.statusRaw, (中文(简体) ), mower.chargingRaw, (中文(简体) ), mower.taskStatusRaw) (中文(简体) ).
  • dreame.mower.p2255 (Dreame A1)能力是假设的,没有核实. 捐助方的一体化没有每个模型的割草机能力矩阵,因此 从指挥表面看,有目标地升起旗帜是一种保守的假设; mower.mowerCapabilities.verified === false 直到确认在设备上. 不支持的目标开始抛出 DreameError。 。 。 .
  • 进步是覆盖的尺度,而不是地图轨道. coverageTargetPct 显示列表任务描述符的覆盖目标( q)d.o, 2:50) 字节精确的姿势/覆盖轨道几何和活图来自地图 在此不解码.

错误类别和核心域类型也被导出,以便消费者能够捕捉 和类型云失败 :

import {
  DreameError,
  DreameAuthError,
  DreameApiError,
  DreameDeviceOfflineError,
  DreameTransportError,
} from '@apocaliss92/nodedreame';
import type { DreameSession, DreameDevice, MiotProp } from '@apocaliss92/nodedreame';

地图

两个设备家庭都解码了他们的安装设备地图,并将其制作成图像。 这个 二进制/JSON解码器、开放源码软件签名接取器和每个中间步骤 保持私人状态——通过设备手柄和(可选的) 两个出炉器.

真空图-巴布亚新几内亚

VacuumDevice.getMap() 解决已保存/ 启用的地图块、 解密和充气 二进制信封,解析 27 字节标题和 fsm:1 像素网格,以及 返回结构 VacuumMap ——分会/会所,清洁道,AI 屏障、虚拟墙、禁行区/禁行区、潜行区、每室墙和 清理区域覆盖. renderVacuumPng(map) 把它给巴布亚新几内亚人 Buffer 通过 pngjs。 。 。 .

import { renderVacuumPng } from '@apocaliss92/nodedreame';
import type { VacuumMap } from '@apocaliss92/nodedreame';
import { writeFile } from 'node:fs/promises';

// `filename` is the OSS object name advertised on the map PATH push (siid 6,
// piid 3); resolve it from a `mapInfo` push before calling getMap().
const map: VacuumMap = await vacuum.getMap({ filename });
const png = renderVacuumPng(map); // Buffer (optional { scale } upscale)
await writeFile('map.png', png);

// The most-recently-decoded map is cached, and the active room id is derived:
vacuum.lastMap; // VacuumMap | null
vacuum.currentSegmentId; // number | null (id of the active segment)

活地地图数据需要一个醒地机器人. 沉睡的真空无所新鲜 鲍勃. getMap() 解码单一框架; 连续活框 P-框架 流线 (随着机器人的移动而合并三角框)是一个文件 后续行动—— applyVacuumPFrame 合并原始船只,并经过单位测试, 这样的工作是添加剂,而不是重写.

摩尔地图 → SVG

MowerDevice.getMap() 重新组合批次 MAP.* 编号 : M_PATH.* JSON块 把他们描述成 MowerMap 禁区、禁区、禁区, 导航路径、轮廓、割草路径和地图边界. mower.mapSvg() (或免费的) renderMowerSvg(map))使SVG具有确定性 取出几何的字符串.

import { renderMowerSvg } from '@apocaliss92/nodedreame';
import type { MowerMap } from '@apocaliss92/nodedreame';
import { writeFile } from 'node:fs/promises';

const map: MowerMap = await mower.getMap();
const svg = await mower.mapSvg(); // or renderMowerSvg(map)
await writeFile('map.svg', svg);

同样的清醒机器人警告. 没有新鲜货. 此外,混凝土 有记录的活批量云端点 后续行动:其道路在捐助方一体化中模糊不清,尚未 已恢复,所以默认的拾取者将抛出getMap() 然后,他们否认真理。 地图 剖析器和 SVG 渲染器已完全运出并测试单元 对批次 固定装置,以及 MowerDevice 接受注入的批量取取取器接合,所以 已经知道路径的呼叫者可以驱动今天的活地图.

归属

真空映射解码器从 马拉德/节点-梦想; 割草机地图 解析器和 SVG 渲染器 antondaubert/冷冻机两者 麻省理工学院——见 LICENSE。 。 。 .

连通性

每个 DreameDevice 从 listDevices() (和后面的记录) discoverDevices())搭载了蒸馏接通视图——所有 梦云实际报:

import { listDevices } from '@apocaliss92/nodedreame';

for (const d of await listDevices({ session, region: 'eu' })) {
  console.log(d.name, d.connectivity);
  // {
  //   online: true,
  //   connectionType: 'WIFI',            // or 'BLE' (mowers)
  //   mac, broker: { host, port },       // assigned MQTT broker (bindDomain)
  //   region, cloudVendor, firmwareVersion, serialNumber, subModel,
  //   battery, statusCode
  // }
}

parseConnectivity(rawRecord) 用于解析您已经持有的记录. 注:云录有 没有 暴露无线甚高频/ SSID/ 本地IP—— 这些 仅是本地公司的软件。 对于信号强度,使用下面的WiFi地图.

WiFi 信号

机器人没有RSSI的活性属性;其唯一的信号数据是a WiFi 覆盖 地图 它在清洁时会建起 点头取出 上次存储 映射( 无) 机器人运动——应用程序显示的同样内容),解码每个细胞的信号 级别,并且——由于地图头带有机器人的姿势——报告信号在 机器人现在的位置.

const bars = await vacuum.getCurrentSignal();       // 0–4 (0 = unreached), or null
const png  = await vacuum.getWifiSignalImage();     // Buffer: heatmap PNG (ready to show)
const map  = await vacuum.getWifiSignalMap();       // full data:
//   map.dimensions {left,top,width,height,gridSize}
//   map.robot / map.dock  (poses, mm world frame)
//   map.cells            (Uint8Array, raw nibble per cell)
//   map.signalAt(x, y)   -> bars 1–4 / 0 unreached / null (no data / off-map)
//   map.currentSignal    -> bars at the robot pose

每个呼叫问题 requestWMap (地图服务行动) 6/4以( 重) 缩写上 储存的地图、民意测验 PropWifiMap (单位:千美元)6/15用于 OSS 对象,并通过 正常的OSS地图管道。 信号级别 : 10 未触及, 11– 说吧 –14 =: 1–4条栏. 如果设备从未建立 WiFi 映射, 则丢弃 .

相机和视频流

相机真空(X40/X50级,Link Visual/Aliyun后端), 点心 自动冷启动活流 没有Dreamehome应用软件,以及 任何消费者(ffmpeg、scripted、camstack)都可以使用H.264 + AAC框架 使用时.

如何运作(高级)

DreameCameraController.open()          DreameCameraStream            consumer
─────────────────────────────         ──────────────────           ────────
1. getAccessCodeLaunch (prime)   ┐
2. initCameraSdk (boot agent)    │  MIoT actions on siid 10001
3. startMonitor  ── code -1 ─────┤  over the device's MQTT channel
4. verifyAccessCode(sha256 PIN)  │  (the camera has a per-session
5. startMonitor (retry) ── ok ───┘  privacy gate: PIN is mandatory)
6. keep-alive loop (~10s)                    │
7. stream/query (Aliyun) → RTMP relay URL ───┤
                                             ▼
                              LvRtmpClient connects to the relay
                              (private-mode RTMP), demuxes FLV →
                              emits: videoAccessUnit (H.264 Annex-B),
                                     audioFrame (AAC/ADTS), audioInfo
                                             │
                                             ▼  (scrypted wraps these as RFC4571;
                                                camstack consumes frames directly)

这个 访问码门 是关键设备突变:一空 startMonitor 返回时 code:-1; 您必须首先 verifyAccessCode 与配对PIN的SHA-256, 接下来 startMonitor 被接受,阿里云中继薄荷则使用RTMP URL。 整个 顺序,保持活性, 和拆解由 DreameCameraController。 。 。 .

快开始

const controller = await vacuum.createCameraController({ accessCode: '0000' });
const { rtmpUrl } = await controller.open();   // cold-start → live relay URL (H.264+AAC)
// … hand rtmpUrl to ffmpeg, or use DreameCameraStream for frames …
await controller.close();                      // stops keep-alive + releases the monitor

框架级管线(凸起/被撕裂所依托):

import { DreameCameraStream } from '@apocaliss92/nodedreame';

const stream = new DreameCameraStream({ controller });
stream.on('videoAccessUnit', (au) => { /* au.data = H.264 Annex-B, au.isKeyframe */ });
stream.on('audioFrame', (buf) => { /* AAC ADTS */ });
await stream.start();   // cold-start + connect + emit frames
// … later …
await stream.stop();

相机动作(控制平面)

全部开始 DreameCameraController,大多数门开到开口的溪流:

方法效果
driveDirection('forward'|'left'|'right'|'turnAround'|'stop') 编号 : drive(spdv, spdw)远程驱动机器人( 保持时发送~ 1 Hz; 相机固定, 机器人移动)
returnToDock() • 妇女 locate()发送回家/ 哔声以定位
spotClean() • 妇女 startPersonFollow() 编号 : stopPersonFollow() • 妇女 findPet() • 妇女 goToPoint(sp, tp)工作模式行动
stopWork()通用停站(跟随/即地清扫/巡航)
playPetSound('meow'|'bark'|'footsteps'|'purring'|'tickTock') 编号 : playSound(id)播放声音剪辑
setFillLight(level) 编号 : setFillLightAuto(full)充光亮度/ 全光亮亮亮度
takePhoto()设备侧快照( 上载到云)
startIntercom() 编号 : stopIntercom()双向音频会话( 控制)
runVacuumAction('startClean'|'pauseClean'|'stopClean'|'dockWash'|'autoEmpty')共同的全机器人行动

双向对讲机(回声)

麦克风上行链路是 RTMP 类型 8 音频 在同一播放会话上向上推 (G.711 A-law, 8 kHz). 在溪流上:

await stream.startTalk();                 // MIoT intercom start + wait for TalkReady
stream.sendTalkPcm(pcm16le8kMono);        // encodes to A-law + pushes upstream
await stream.stopTalk();

对象检测

机器人自己跟随的人10001/110和障碍10001/112框为 驱动作为相机服务属性;用 parsePersonFollow 编号 : parseObstacleData (订阅设备的) propertyChanged) (中文(简体) ).

诊断堆(只读)

nodedreame 可以记录设备在运行和导出时暴露的内容 匿名 JSON 您可以附加在 GitHub 上, 帮助映射无文件 代码(如割草机) taskStatus 2/3/10/13号文件。 垃圾箱很严格 只读 —— 说 它从不发出命令 也从不唤醒机器人 它只订阅 设备的事件流( e)on编号 :off),读取缓存(getProperty),以及 取出云影来refreshFromCache) (中文(简体) ).

import { Nodreame, createDumper } from '@apocaliss92/nodedreame';

const client = new Nodreame({ username, password, region: 'eu' });
await client.login();
const [device] = await client.discoverDevices();

const dumper = createDumper(device);
await dumper.start(); // hooks the live stream + periodic cloud-shadow read
// ...operate the device normally for a few minutes...
await dumper.stop();

console.log(dumper.exportJson()); // pretty, anonymized — safe to share
await client.close();

它捕捉到的东西:每个财产 独特的价值集 unmapped 旗帜 (值与无已知的enum相匹配——最高值信号),MIoT事件,a 静态命令/能力目录。 设备/账号 代号, MAC、序列、Wi-Fi/IP、GPS、房间名称和任何自定义设备名称(全部) [redacted]) (中文(简体) ). firmware编号 :region 省略( 不被设备浮出水面) 手柄尚未打开).

立即将整个账户用 createClientDumper(client),返回一个 每个发现装置的倾卸器:

import { createClientDumper } from '@apocaliss92/nodedreame';

const dumpers = createClientDumper(client);
await Promise.all(dumpers.map((d) => d.start()));
// ...observe...
await Promise.all(dumpers.map((d) => d.stop()));
const dumps = dumpers.map((d) => d.exportJson());

将导出 JSON 共享到库中 表格来标记新代码并将其折叠到库中.

安装

npm install @apocaliss92/nodedreame

许可证

麻省理工学院. 前端口从 塔斯夏克/德雷姆真空, (中文(简体) ), antondaubert/冷冻机,以及 马拉德/节点-梦想; 见 LICENSE。 。 。 .

版本与发布记录

@apocaliss92/nodedreame 共发布 32 个版本,首次发布于 2026/06/05;过去 12 个月发布了 32 个版本。

版本发布日期距今
1.12.5latest2026/09/261 个月前
1.12.42026/09/161 个月前
1.12.32026/09/161 个月前
1.12.22026/09/161 个月前
1.12.12026/09/161 个月前
1.12.02026/09/062 个月前

关于 @apocaliss92/nodedreame 的常见问题

@apocaliss92/nodedreame 是什么?

用于 Dreame 真空和割草机的节点.js/TypeScript 客户端

如何安装 @apocaliss92/nodedreame?

在项目目录运行 npm install @apocaliss92/nodedreame,或使用 pnpm add / yarn add / bun add。

@apocaliss92/nodedreame 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@apocaliss92/nodedreame 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@apocaliss92/nodedreame 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@apocaliss92/nodedreame 可以商用吗?

@apocaliss92/nodedreame 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@apocaliss92/nodedreame 的下载量有多少?

根据 npm 官方下载统计,@apocaliss92/nodedreame 在 2026/10/07 – 2026/11/05 这 30 天内被下载 2,413 次,日均约 80 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@apocaliss92/nodedreame 还在维护吗?

最新版本 1.12.5 发布于 2026/09/26(1 个月前);至今共发布 32 个版本,其中过去 12 个月发布了 32 个;首次发布于 2026/06/05。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。