@apocaliss92/nodedreame 1.11.4 → 1.11.6
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.cjs +119 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -8
- package/dist/index.d.ts +48 -8
- package/dist/index.js +117 -79
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -75,8 +75,10 @@ __export(index_exports, {
|
|
|
75
75
|
getVacuumCapabilities: () => getVacuumCapabilities,
|
|
76
76
|
isDreameConsumableKey: () => isDreameConsumableKey,
|
|
77
77
|
mowerConsumableIndex: () => mowerConsumableIndex,
|
|
78
|
+
mowerFaultSeverity: () => mowerFaultSeverity,
|
|
78
79
|
parseMowerConsumables: () => parseMowerConsumables,
|
|
79
80
|
parseMowerHeartbeat: () => parseMowerHeartbeat,
|
|
81
|
+
parseMowingProgress: () => parseMowingProgress,
|
|
80
82
|
renderMowerSvg: () => renderMowerSvg,
|
|
81
83
|
renderVacuumPng: () => renderVacuumPng,
|
|
82
84
|
resolveCapabilities: () => resolveCapabilities,
|
|
@@ -86,7 +88,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
86
88
|
|
|
87
89
|
// src/support/version.ts
|
|
88
90
|
var LIBRARY_NAME = "nodedreame";
|
|
89
|
-
var LIBRARY_VERSION = "1.11.
|
|
91
|
+
var LIBRARY_VERSION = "1.11.6";
|
|
90
92
|
|
|
91
93
|
// src/transport/errors.ts
|
|
92
94
|
var DreameError = class extends Error {
|
|
@@ -2857,18 +2859,27 @@ function decodeVacuumMap(input, opts = {}) {
|
|
|
2857
2859
|
const pixelGrid = inflated.subarray(pixelStart, pixelEnd);
|
|
2858
2860
|
const canDecodePixels = header.frameType === "I" && pixelGrid.length === header.width * header.height;
|
|
2859
2861
|
const layers = canDecodePixels ? decodePixelGridFsm1(pixelGrid, header.width, header.height) : [];
|
|
2860
|
-
const segments = canDecodePixels ? collectSegments(layers, dimensions, tail) : [];
|
|
2861
2862
|
const paths = parsePathTr(tail.tr ?? "");
|
|
2862
2863
|
const obstacles = parseObstacles(tail.ai_obstacle ?? []);
|
|
2863
2864
|
let geometry = parseTailGeometry(tail);
|
|
2864
|
-
|
|
2865
|
+
let segTail = tail;
|
|
2866
|
+
const outerHasNames = Object.values(tail.seg_inf ?? {}).some(
|
|
2867
|
+
(m) => typeof m?.name === "string" && m.name.length > 0
|
|
2868
|
+
);
|
|
2869
|
+
if ((!isGeometryComplete(geometry) || !outerHasNames) && typeof tail.rism === "string" && tail.rism.length > 0) {
|
|
2865
2870
|
try {
|
|
2866
2871
|
const innerInflated = unwrapEnvelope(tail.rism);
|
|
2867
2872
|
const { tail: innerTail } = parseFrame(innerInflated);
|
|
2868
|
-
|
|
2873
|
+
if (!isGeometryComplete(geometry)) {
|
|
2874
|
+
geometry = coalesceGeometry(geometry, parseTailGeometry(innerTail));
|
|
2875
|
+
}
|
|
2876
|
+
if (!outerHasNames && innerTail.seg_inf) {
|
|
2877
|
+
segTail = mergeSegInfNames(tail, innerTail.seg_inf);
|
|
2878
|
+
}
|
|
2869
2879
|
} catch {
|
|
2870
2880
|
}
|
|
2871
2881
|
}
|
|
2882
|
+
const segments = canDecodePixels ? collectSegments(layers, dimensions, segTail) : [];
|
|
2872
2883
|
const cleanedArea = typeof tail.decmap === "string" ? parseCleanedAreaOverlay(tail.decmap) : null;
|
|
2873
2884
|
return {
|
|
2874
2885
|
mapId: header.mapId,
|
|
@@ -2892,6 +2903,18 @@ function applyVacuumPFrame(prev, pframe, opts = {}) {
|
|
|
2892
2903
|
const merged = typeof prev === "string" || typeof pframe === "string" ? mergePFrameEnvelope(prev, pframe, opts.prev, opts.pframe) : mergePFrame(prev, pframe);
|
|
2893
2904
|
return { buffer: merged, data: decodeVacuumMap(merged) };
|
|
2894
2905
|
}
|
|
2906
|
+
function mergeSegInfNames(tail, innerSegInf) {
|
|
2907
|
+
const outer = tail.seg_inf ?? {};
|
|
2908
|
+
const ids = /* @__PURE__ */ new Set([...Object.keys(outer), ...Object.keys(innerSegInf)]);
|
|
2909
|
+
const merged = {};
|
|
2910
|
+
for (const id of ids) {
|
|
2911
|
+
const o = outer[id] ?? {};
|
|
2912
|
+
const inner = innerSegInf[id] ?? {};
|
|
2913
|
+
const name = typeof o.name === "string" && o.name.length > 0 ? o.name : inner.name;
|
|
2914
|
+
merged[id] = { ...inner, ...o, ...name !== void 0 ? { name } : {} };
|
|
2915
|
+
}
|
|
2916
|
+
return { ...tail, seg_inf: merged };
|
|
2917
|
+
}
|
|
2895
2918
|
function mergeDimensions(header, tail) {
|
|
2896
2919
|
const left = tail.origin?.[0] ?? header.left;
|
|
2897
2920
|
const top = tail.origin?.[1] ?? header.top;
|
|
@@ -2913,78 +2936,6 @@ function pose(x, y, angle, absent) {
|
|
|
2913
2936
|
// src/models/vacuum/map/render.ts
|
|
2914
2937
|
var import_pngjs = require("pngjs");
|
|
2915
2938
|
var SCHEMES = {
|
|
2916
|
-
"dreame-light": {
|
|
2917
|
-
floor: [210, 222, 235, 255],
|
|
2918
|
-
wall: [60, 60, 60, 255],
|
|
2919
|
-
carpet: [180, 150, 120, 200],
|
|
2920
|
-
segSat: 0.45,
|
|
2921
|
-
segVal: 0.95,
|
|
2922
|
-
path: [60, 110, 180, 235],
|
|
2923
|
-
robotBody: [40, 70, 110, 255],
|
|
2924
|
-
robotHeading: [255, 255, 255, 255],
|
|
2925
|
-
charger: [40, 160, 80, 255],
|
|
2926
|
-
noGoFill: [220, 60, 60, 70],
|
|
2927
|
-
noGoBorder: [200, 40, 40, 220],
|
|
2928
|
-
noMopFill: [60, 120, 220, 60],
|
|
2929
|
-
noMopBorder: [40, 90, 200, 200],
|
|
2930
|
-
virtualWall: [200, 40, 40, 230],
|
|
2931
|
-
obstacle: [230, 150, 30, 255],
|
|
2932
|
-
label: [40, 40, 40, 255]
|
|
2933
|
-
},
|
|
2934
|
-
"dreame-dark": {
|
|
2935
|
-
floor: [40, 46, 56, 255],
|
|
2936
|
-
wall: [15, 15, 18, 255],
|
|
2937
|
-
carpet: [80, 66, 52, 200],
|
|
2938
|
-
segSat: 0.5,
|
|
2939
|
-
segVal: 0.62,
|
|
2940
|
-
path: [120, 170, 230, 235],
|
|
2941
|
-
robotBody: [120, 170, 230, 255],
|
|
2942
|
-
robotHeading: [20, 24, 30, 255],
|
|
2943
|
-
charger: [60, 200, 110, 255],
|
|
2944
|
-
noGoFill: [220, 70, 70, 80],
|
|
2945
|
-
noGoBorder: [230, 70, 70, 220],
|
|
2946
|
-
noMopFill: [70, 130, 230, 70],
|
|
2947
|
-
noMopBorder: [80, 140, 235, 210],
|
|
2948
|
-
virtualWall: [230, 70, 70, 235],
|
|
2949
|
-
obstacle: [240, 180, 60, 255],
|
|
2950
|
-
label: [225, 230, 240, 255]
|
|
2951
|
-
},
|
|
2952
|
-
"mijia-light": {
|
|
2953
|
-
floor: [225, 232, 240, 255],
|
|
2954
|
-
wall: [70, 78, 92, 255],
|
|
2955
|
-
carpet: [188, 162, 132, 200],
|
|
2956
|
-
segSat: 0.35,
|
|
2957
|
-
segVal: 0.98,
|
|
2958
|
-
path: [80, 130, 200, 230],
|
|
2959
|
-
robotBody: [30, 100, 200, 255],
|
|
2960
|
-
robotHeading: [255, 255, 255, 255],
|
|
2961
|
-
charger: [40, 170, 90, 255],
|
|
2962
|
-
noGoFill: [225, 70, 70, 70],
|
|
2963
|
-
noGoBorder: [205, 45, 45, 220],
|
|
2964
|
-
noMopFill: [70, 130, 225, 60],
|
|
2965
|
-
noMopBorder: [50, 100, 205, 200],
|
|
2966
|
-
virtualWall: [205, 45, 45, 230],
|
|
2967
|
-
obstacle: [235, 160, 35, 255],
|
|
2968
|
-
label: [45, 52, 64, 255]
|
|
2969
|
-
},
|
|
2970
|
-
tasshack: {
|
|
2971
|
-
floor: [200, 210, 225, 255],
|
|
2972
|
-
wall: [80, 80, 95, 255],
|
|
2973
|
-
carpet: [176, 148, 118, 205],
|
|
2974
|
-
segSat: 0.5,
|
|
2975
|
-
segVal: 0.9,
|
|
2976
|
-
path: [50, 100, 170, 235],
|
|
2977
|
-
robotBody: [35, 60, 100, 255],
|
|
2978
|
-
robotHeading: [255, 255, 255, 255],
|
|
2979
|
-
charger: [35, 150, 75, 255],
|
|
2980
|
-
noGoFill: [215, 55, 55, 75],
|
|
2981
|
-
noGoBorder: [195, 35, 35, 220],
|
|
2982
|
-
noMopFill: [55, 115, 215, 65],
|
|
2983
|
-
noMopBorder: [35, 85, 195, 205],
|
|
2984
|
-
virtualWall: [195, 35, 35, 230],
|
|
2985
|
-
obstacle: [225, 145, 25, 255],
|
|
2986
|
-
label: [35, 42, 54, 255]
|
|
2987
|
-
},
|
|
2988
2939
|
// Flat-design: soft neutral floor, bright low-saturation segment fills, thin
|
|
2989
2940
|
// mid-grey walls, a single vivid accent for the path.
|
|
2990
2941
|
flat: {
|
|
@@ -3081,7 +3032,7 @@ function segmentColor(id, pal) {
|
|
|
3081
3032
|
}
|
|
3082
3033
|
function renderVacuumPng(map, opts = {}) {
|
|
3083
3034
|
const scale = Math.max(1, Math.trunc(opts.scale ?? 1));
|
|
3084
|
-
const pal = SCHEMES[opts.colorScheme ?? "
|
|
3035
|
+
const pal = SCHEMES[opts.colorScheme ?? "flat"] ?? SCHEMES.flat;
|
|
3085
3036
|
const w = Math.max(1, map.dimensions.width * scale);
|
|
3086
3037
|
const h = Math.max(1, map.dimensions.height * scale);
|
|
3087
3038
|
const png = new import_pngjs.PNG({ width: w, height: h });
|
|
@@ -3124,8 +3075,12 @@ function renderVacuumPng(map, opts = {}) {
|
|
|
3124
3075
|
const byType = opts.colorObstaclesByType !== false;
|
|
3125
3076
|
for (const ob of map.obstacles) {
|
|
3126
3077
|
const p = worldToPx(ob.x, ob.y, dim, scale);
|
|
3127
|
-
const
|
|
3128
|
-
|
|
3078
|
+
const r = Math.max(2, scale * 2);
|
|
3079
|
+
if (byType) {
|
|
3080
|
+
drawObstacleShape(png, p.x, p.y, r, ob.type, obstacleColor(ob.type));
|
|
3081
|
+
} else {
|
|
3082
|
+
drawDiamond(png, p.x, p.y, r, pal.obstacle);
|
|
3083
|
+
}
|
|
3129
3084
|
}
|
|
3130
3085
|
}
|
|
3131
3086
|
if (opts.showCharger !== false && map.dock !== null) {
|
|
@@ -3286,6 +3241,32 @@ function obstacleColor(type) {
|
|
|
3286
3241
|
const hue = Math.abs(Math.trunc(type)) * 47 % 360;
|
|
3287
3242
|
return hsvToRgba(hue, 0.85, 0.95);
|
|
3288
3243
|
}
|
|
3244
|
+
function drawSquare(png, cx, cy, r, color) {
|
|
3245
|
+
fillRect(png, cx - r, cy - r, cx + r, cy + r, color);
|
|
3246
|
+
}
|
|
3247
|
+
function drawTriangle(png, cx, cy, r, color) {
|
|
3248
|
+
for (let dy = -r; dy <= r; dy += 1) {
|
|
3249
|
+
const w = Math.round((dy + r) / (2 * r) * r);
|
|
3250
|
+
for (let dx = -w; dx <= w; dx += 1) {
|
|
3251
|
+
setPixel(png, cx + dx, cy + dy, color);
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
function drawObstacleShape(png, cx, cy, r, type, color) {
|
|
3256
|
+
switch (Math.abs(Math.trunc(type)) % 4) {
|
|
3257
|
+
case 0:
|
|
3258
|
+
drawDiamond(png, cx, cy, r, color);
|
|
3259
|
+
return;
|
|
3260
|
+
case 1:
|
|
3261
|
+
drawSquare(png, cx, cy, r, color);
|
|
3262
|
+
return;
|
|
3263
|
+
case 2:
|
|
3264
|
+
drawTriangle(png, cx, cy, r, color);
|
|
3265
|
+
return;
|
|
3266
|
+
default:
|
|
3267
|
+
drawDisk(png, cx, cy, r, color);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3289
3270
|
var FURNITURE_LINE = [150, 110, 200, 235];
|
|
3290
3271
|
function paintFurnitureZone(png, points, dim, scale) {
|
|
3291
3272
|
if (points.length < 2) return;
|
|
@@ -4270,6 +4251,12 @@ var MowerFault = /* @__PURE__ */ ((MowerFault2) => {
|
|
|
4270
4251
|
MowerFault2[MowerFault2["TopCoverOpen"] = 73] = "TopCoverOpen";
|
|
4271
4252
|
return MowerFault2;
|
|
4272
4253
|
})(MowerFault || {});
|
|
4254
|
+
function mowerFaultSeverity(code) {
|
|
4255
|
+
if (code === null) return "info";
|
|
4256
|
+
if (code >= 1 && code <= 30 || code === 37 || code === 73) return "error";
|
|
4257
|
+
if (code >= 31 && code <= 36 || code >= 38 && code <= 45) return "warning";
|
|
4258
|
+
return "info";
|
|
4259
|
+
}
|
|
4273
4260
|
|
|
4274
4261
|
// src/models/mower/decode.ts
|
|
4275
4262
|
function isRecord2(v) {
|
|
@@ -4480,6 +4467,37 @@ function parseMowerHeartbeat(value) {
|
|
|
4480
4467
|
const taskSubState = mainState === HEARTBEAT_MAIN_STATE_MOWING ? MOWER_TASK_SUBSTATES[subStateRaw - HEARTBEAT_SUBSTATE_BASE] ?? null : null;
|
|
4481
4468
|
return { rawBattery, mainState, subStateRaw, taskSubState };
|
|
4482
4469
|
}
|
|
4470
|
+
var POSE_SENTINEL = 206;
|
|
4471
|
+
function poseBytes(value) {
|
|
4472
|
+
if (!Array.isArray(value)) return null;
|
|
4473
|
+
const bytes = [];
|
|
4474
|
+
for (const b of value) {
|
|
4475
|
+
if (typeof b !== "number") return null;
|
|
4476
|
+
bytes.push(b);
|
|
4477
|
+
}
|
|
4478
|
+
return bytes;
|
|
4479
|
+
}
|
|
4480
|
+
function parseMowingProgress(value) {
|
|
4481
|
+
const bytes = poseBytes(value);
|
|
4482
|
+
if (bytes === null || bytes.length < 11) return null;
|
|
4483
|
+
const last = bytes[bytes.length - 1];
|
|
4484
|
+
const head = bytes[0];
|
|
4485
|
+
let offset = null;
|
|
4486
|
+
if (head !== POSE_SENTINEL && last === POSE_SENTINEL) {
|
|
4487
|
+
offset = 0;
|
|
4488
|
+
} else if (head === POSE_SENTINEL && last === POSE_SENTINEL && bytes.length >= 33) {
|
|
4489
|
+
offset = 22;
|
|
4490
|
+
}
|
|
4491
|
+
if (offset === null || offset + 10 > bytes.length) return null;
|
|
4492
|
+
const rawPercent = (bytes[offset + 2] ?? 0) | (bytes[offset + 3] ?? 0) << 8;
|
|
4493
|
+
const total = (bytes[offset + 4] ?? 0) | (bytes[offset + 5] ?? 0) << 8 | (bytes[offset + 6] ?? 0) << 16;
|
|
4494
|
+
const finish = (bytes[offset + 7] ?? 0) | (bytes[offset + 8] ?? 0) << 8 | (bytes[offset + 9] ?? 0) << 16;
|
|
4495
|
+
return {
|
|
4496
|
+
progressPercent: rawPercent ? Math.min(100, rawPercent / 100) : 0,
|
|
4497
|
+
currentAreaSqm: finish / 100,
|
|
4498
|
+
totalAreaSqm: total / 100
|
|
4499
|
+
};
|
|
4500
|
+
}
|
|
4483
4501
|
|
|
4484
4502
|
// src/models/mower/capabilities.ts
|
|
4485
4503
|
var FALLBACK2 = {
|
|
@@ -5179,6 +5197,25 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
|
|
|
5179
5197
|
get coverageTargetPct() {
|
|
5180
5198
|
return this.task?.coverageTarget ?? null;
|
|
5181
5199
|
}
|
|
5200
|
+
/**
|
|
5201
|
+
* Live mowing PROGRESS decoded from the POSE_COVERAGE (1:4) task block —
|
|
5202
|
+
* `{progressPercent, currentAreaSqm, totalAreaSqm}` — or null when 1:4 carries
|
|
5203
|
+
* no task block (idle / pose-only frame). This is the byte-accurate progress
|
|
5204
|
+
* the Dreamehome app shows (vs {@link coverageTargetPct}, the task TARGET).
|
|
5205
|
+
*/
|
|
5206
|
+
get mowingProgress() {
|
|
5207
|
+
return parseMowingProgress(
|
|
5208
|
+
this.getProperty(MOWER_PROP.POSE_COVERAGE.siid, MOWER_PROP.POSE_COVERAGE.piid)?.value
|
|
5209
|
+
);
|
|
5210
|
+
}
|
|
5211
|
+
/** Mowing completion percent (0..100) from {@link mowingProgress}, or null. */
|
|
5212
|
+
get mowingProgressPct() {
|
|
5213
|
+
return this.mowingProgress?.progressPercent ?? null;
|
|
5214
|
+
}
|
|
5215
|
+
/** Severity of the current DEVICE_CODE (2:2) — info / warning / error. */
|
|
5216
|
+
get faultSeverity() {
|
|
5217
|
+
return mowerFaultSeverity(this.faultRaw);
|
|
5218
|
+
}
|
|
5182
5219
|
/** Parsed per-zone control status (2:56), or null. */
|
|
5183
5220
|
get controlStatus() {
|
|
5184
5221
|
return parseControlStatus(
|
|
@@ -5358,6 +5395,7 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
|
|
|
5358
5395
|
/** Props worth seeding on start() / polling — exported for the facade. */
|
|
5359
5396
|
static DEFAULT_PROPS = [
|
|
5360
5397
|
MOWER_PROP.HEARTBEAT,
|
|
5398
|
+
MOWER_PROP.POSE_COVERAGE,
|
|
5361
5399
|
MOWER_PROP.STATUS,
|
|
5362
5400
|
MOWER_PROP.DEVICE_CODE,
|
|
5363
5401
|
MOWER_PROP.BATTERY,
|
|
@@ -6017,8 +6055,10 @@ function createClientDumper(client, options) {
|
|
|
6017
6055
|
getVacuumCapabilities,
|
|
6018
6056
|
isDreameConsumableKey,
|
|
6019
6057
|
mowerConsumableIndex,
|
|
6058
|
+
mowerFaultSeverity,
|
|
6020
6059
|
parseMowerConsumables,
|
|
6021
6060
|
parseMowerHeartbeat,
|
|
6061
|
+
parseMowingProgress,
|
|
6022
6062
|
renderMowerSvg,
|
|
6023
6063
|
renderVacuumPng,
|
|
6024
6064
|
resolveCapabilities,
|