@apocaliss92/nodedreame 1.11.5 → 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 +118 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -1
- package/dist/index.d.ts +45 -1
- package/dist/index.js +116 -6
- 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;
|
|
@@ -3052,8 +3075,12 @@ function renderVacuumPng(map, opts = {}) {
|
|
|
3052
3075
|
const byType = opts.colorObstaclesByType !== false;
|
|
3053
3076
|
for (const ob of map.obstacles) {
|
|
3054
3077
|
const p = worldToPx(ob.x, ob.y, dim, scale);
|
|
3055
|
-
const
|
|
3056
|
-
|
|
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
|
+
}
|
|
3057
3084
|
}
|
|
3058
3085
|
}
|
|
3059
3086
|
if (opts.showCharger !== false && map.dock !== null) {
|
|
@@ -3214,6 +3241,32 @@ function obstacleColor(type) {
|
|
|
3214
3241
|
const hue = Math.abs(Math.trunc(type)) * 47 % 360;
|
|
3215
3242
|
return hsvToRgba(hue, 0.85, 0.95);
|
|
3216
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
|
+
}
|
|
3217
3270
|
var FURNITURE_LINE = [150, 110, 200, 235];
|
|
3218
3271
|
function paintFurnitureZone(png, points, dim, scale) {
|
|
3219
3272
|
if (points.length < 2) return;
|
|
@@ -4198,6 +4251,12 @@ var MowerFault = /* @__PURE__ */ ((MowerFault2) => {
|
|
|
4198
4251
|
MowerFault2[MowerFault2["TopCoverOpen"] = 73] = "TopCoverOpen";
|
|
4199
4252
|
return MowerFault2;
|
|
4200
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
|
+
}
|
|
4201
4260
|
|
|
4202
4261
|
// src/models/mower/decode.ts
|
|
4203
4262
|
function isRecord2(v) {
|
|
@@ -4408,6 +4467,37 @@ function parseMowerHeartbeat(value) {
|
|
|
4408
4467
|
const taskSubState = mainState === HEARTBEAT_MAIN_STATE_MOWING ? MOWER_TASK_SUBSTATES[subStateRaw - HEARTBEAT_SUBSTATE_BASE] ?? null : null;
|
|
4409
4468
|
return { rawBattery, mainState, subStateRaw, taskSubState };
|
|
4410
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
|
+
}
|
|
4411
4501
|
|
|
4412
4502
|
// src/models/mower/capabilities.ts
|
|
4413
4503
|
var FALLBACK2 = {
|
|
@@ -5107,6 +5197,25 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
|
|
|
5107
5197
|
get coverageTargetPct() {
|
|
5108
5198
|
return this.task?.coverageTarget ?? null;
|
|
5109
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
|
+
}
|
|
5110
5219
|
/** Parsed per-zone control status (2:56), or null. */
|
|
5111
5220
|
get controlStatus() {
|
|
5112
5221
|
return parseControlStatus(
|
|
@@ -5286,6 +5395,7 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
|
|
|
5286
5395
|
/** Props worth seeding on start() / polling — exported for the facade. */
|
|
5287
5396
|
static DEFAULT_PROPS = [
|
|
5288
5397
|
MOWER_PROP.HEARTBEAT,
|
|
5398
|
+
MOWER_PROP.POSE_COVERAGE,
|
|
5289
5399
|
MOWER_PROP.STATUS,
|
|
5290
5400
|
MOWER_PROP.DEVICE_CODE,
|
|
5291
5401
|
MOWER_PROP.BATTERY,
|
|
@@ -5945,8 +6055,10 @@ function createClientDumper(client, options) {
|
|
|
5945
6055
|
getVacuumCapabilities,
|
|
5946
6056
|
isDreameConsumableKey,
|
|
5947
6057
|
mowerConsumableIndex,
|
|
6058
|
+
mowerFaultSeverity,
|
|
5948
6059
|
parseMowerConsumables,
|
|
5949
6060
|
parseMowerHeartbeat,
|
|
6061
|
+
parseMowingProgress,
|
|
5950
6062
|
renderMowerSvg,
|
|
5951
6063
|
renderVacuumPng,
|
|
5952
6064
|
resolveCapabilities,
|