@apocaliss92/nodedreame 1.11.6 → 1.11.10

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 CHANGED
@@ -88,7 +88,7 @@ module.exports = __toCommonJS(index_exports);
88
88
 
89
89
  // src/support/version.ts
90
90
  var LIBRARY_NAME = "nodedreame";
91
- var LIBRARY_VERSION = "1.11.6";
91
+ var LIBRARY_VERSION = "1.11.10";
92
92
 
93
93
  // src/transport/errors.ts
94
94
  var DreameError = class extends Error {
@@ -387,6 +387,11 @@ var CachedPropsResponseSchema = import_zod.z.object({
387
387
  msg: import_zod.z.string().nullish(),
388
388
  data: import_zod.z.array(CachedPropEntrySchema).optional()
389
389
  }).passthrough();
390
+ var BatchDeviceDataResponseSchema = import_zod.z.object({
391
+ code: import_zod.z.number().optional(),
392
+ msg: import_zod.z.string().nullish(),
393
+ data: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
394
+ }).passthrough();
390
395
  var SendCommandResponseSchema = import_zod.z.object({
391
396
  code: import_zod.z.number().optional(),
392
397
  msg: import_zod.z.string().nullish(),
@@ -647,6 +652,29 @@ async function callAction(base, action, opts = {}) {
647
652
  const res = await sendCommand({ ...base, ...opts, method: "action", params });
648
653
  return res.data?.result ?? res.result ?? res;
649
654
  }
655
+ async function getBatchDeviceDatas(base, props, opts = {}) {
656
+ const ctx = base.ctx ?? RequestContext.from({ ...base, host: base.apiHost });
657
+ const signal = opts.signal ?? base.signal;
658
+ const timeoutMs = opts.timeoutMs ?? base.timeoutMs;
659
+ const raw = await httpPostJsonBody({
660
+ ctx,
661
+ path: "/dreame-user-iot/iotuserdata/getDeviceData",
662
+ accessToken: base.session.accessToken,
663
+ body: { did: base.did, model: props },
664
+ context: "batch device data",
665
+ ...signal !== void 0 ? { signal } : {},
666
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
667
+ });
668
+ const parsed = BatchDeviceDataResponseSchema.parse(raw);
669
+ if (parsed.code !== void 0 && parsed.code !== 0) {
670
+ throw new DreameApiError(
671
+ `batch device data rejected: code=${parsed.code} msg=${parsed.msg ?? "?"}`,
672
+ 200,
673
+ parsed
674
+ );
675
+ }
676
+ return parsed.data ?? {};
677
+ }
650
678
  function extractResultArray(res, context) {
651
679
  const raw = Array.isArray(res.data?.result) ? res.data.result : Array.isArray(res.result) ? res.result : null;
652
680
  if (raw === null) {
@@ -2151,6 +2179,43 @@ function parseFrame(inflated) {
2151
2179
  return { header, tail };
2152
2180
  }
2153
2181
 
2182
+ // src/models/vacuum/map/segment-types.ts
2183
+ var SEGMENT_TYPE_CODE_TO_NAME = {
2184
+ 0: "Room",
2185
+ 1: "Living Room",
2186
+ 2: "Primary Bedroom",
2187
+ 3: "Study",
2188
+ 4: "Kitchen",
2189
+ 5: "Dining Hall",
2190
+ 6: "Bathroom",
2191
+ 7: "Balcony",
2192
+ 8: "Corridor",
2193
+ 9: "Utility Room",
2194
+ 10: "Closet",
2195
+ 11: "Meeting Room",
2196
+ 12: "Office",
2197
+ 13: "Fitness Area",
2198
+ 14: "Recreation Area",
2199
+ 15: "Secondary Bedroom"
2200
+ };
2201
+ function safeBase64ToUtf8(s) {
2202
+ try {
2203
+ return Buffer.from(s, "base64").toString("utf8");
2204
+ } catch {
2205
+ return null;
2206
+ }
2207
+ }
2208
+ function resolveSegmentName(type, index, customName) {
2209
+ if (type !== void 0 && type !== 0 && SEGMENT_TYPE_CODE_TO_NAME[type] !== void 0) {
2210
+ const base = SEGMENT_TYPE_CODE_TO_NAME[type];
2211
+ return index !== void 0 && index > 0 ? `${base} ${index + 1}` : base;
2212
+ }
2213
+ if (typeof customName === "string" && customName.length > 0) {
2214
+ return safeBase64ToUtf8(customName);
2215
+ }
2216
+ return null;
2217
+ }
2218
+
2154
2219
  // src/models/vacuum/map/pixel-grid.ts
2155
2220
  function classifyPixelFsm1(byte) {
2156
2221
  if (byte === 0) {
@@ -2296,7 +2361,9 @@ function collectSegments(layers, dim, tail) {
2296
2361
  };
2297
2362
  segs.push({
2298
2363
  id,
2299
- name: meta?.name ? safeBase64ToUtf8(meta.name) : null,
2364
+ // Mirror the donor: a room-type code (≠0) yields a localized default name
2365
+ // (e.g. "Kitchen"), else the user's custom base64 name, else null.
2366
+ name: resolveSegmentName(meta?.type, meta?.index, meta?.name),
2300
2367
  bbox,
2301
2368
  centroid,
2302
2369
  neighbours: meta?.nei_id ?? [],
@@ -2307,13 +2374,6 @@ function collectSegments(layers, dim, tail) {
2307
2374
  }
2308
2375
  return segs;
2309
2376
  }
2310
- function safeBase64ToUtf8(s) {
2311
- try {
2312
- return Buffer.from(s, "base64").toString("utf8");
2313
- } catch {
2314
- return null;
2315
- }
2316
- }
2317
2377
 
2318
2378
  // src/models/vacuum/map/path.ts
2319
2379
  var PATH_TYPE_FROM_OP = {
@@ -4109,7 +4169,8 @@ var TASK_OPCODE = {
4109
4169
  ALL_AREA: 100,
4110
4170
  EDGE: 101,
4111
4171
  ZONE: 102,
4112
- SPOT: 103
4172
+ SPOT: 103,
4173
+ SET_CURRENT_MAP: 200
4113
4174
  };
4114
4175
  function buildResumePayload() {
4115
4176
  return { m: "a", p: 0, o: 5 };
@@ -4126,6 +4187,9 @@ function buildEdgePayload(contourIds) {
4126
4187
  function buildSpotPayload(spotAreaIds) {
4127
4188
  return { m: "a", p: 0, o: TASK_OPCODE.SPOT, d: { area: [...spotAreaIds] } };
4128
4189
  }
4190
+ function buildSetCurrentMapPayload(mapIndex) {
4191
+ return { m: "a", p: 0, o: TASK_OPCODE.SET_CURRENT_MAP, d: { idx: mapIndex } };
4192
+ }
4129
4193
  function buildGetConsumablePayload() {
4130
4194
  return { m: "g", t: "CMS" };
4131
4195
  }
@@ -4887,11 +4951,15 @@ var DEFAULT_HEIGHT = 1200;
4887
4951
  var DEFAULT_PADDING = 50;
4888
4952
  var BACKGROUND = "#f5f5f0";
4889
4953
  var MAP_BOUNDARY = "#006400";
4890
- var MOWING_PATH = "#ffa500";
4954
+ var MOWING_PATH = "#32cd32";
4891
4955
  var NAV_PATH = "#b4b4b4";
4892
4956
  var OBSTACLE_STROKE = "#ff4d00";
4893
4957
  var OBSTACLE_FILL = "#ff4d0065";
4894
4958
  var TEXT_COLOR = "#000000";
4959
+ var ZONE_LABEL_COLOR = "#3c3c3c";
4960
+ function escapeXml(s) {
4961
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
4962
+ }
4895
4963
  var ZONE_COLORS = [
4896
4964
  ["#a4d291c8", "#86be73"],
4897
4965
  ["#a0c8dcc8", "#82aac8"],
@@ -5014,7 +5082,6 @@ function renderMowerSvg(map, opts = {}) {
5014
5082
  return lines.join("\n");
5015
5083
  }
5016
5084
  const bounds = calculateBounds(allPoints);
5017
- const multiZone = map.zones.length > 1;
5018
5085
  for (const navPath of map.paths) {
5019
5086
  const dashed = svgPathFromSegments(
5020
5087
  [navPath.path],
@@ -5031,9 +5098,6 @@ function renderMowerSvg(map, opts = {}) {
5031
5098
  }
5032
5099
  }
5033
5100
  map.zones.forEach((zone, i) => {
5034
- if (!multiZone) {
5035
- return;
5036
- }
5037
5101
  const palette = ZONE_COLORS[i % ZONE_COLORS.length];
5038
5102
  const [fill, outline] = palette ?? ZONE_COLORS[0] ?? ["#cccccc", "#888888"];
5039
5103
  const poly = svgPolygon(zone.path, bounds, width, height, padding, fill, outline);
@@ -5042,7 +5106,7 @@ function renderMowerSvg(map, opts = {}) {
5042
5106
  }
5043
5107
  });
5044
5108
  map.zones.forEach((zone, i) => {
5045
- const outline = multiZone ? ZONE_COLORS[i % ZONE_COLORS.length]?.[1] ?? MAP_BOUNDARY : MAP_BOUNDARY;
5109
+ const outline = ZONE_COLORS[i % ZONE_COLORS.length]?.[1] ?? MAP_BOUNDARY;
5046
5110
  const boundary = svgPathFromSegments(
5047
5111
  [zone.path],
5048
5112
  bounds,
@@ -5086,6 +5150,22 @@ function renderMowerSvg(map, opts = {}) {
5086
5150
  lines.push(poly);
5087
5151
  }
5088
5152
  }
5153
+ for (const zone of map.zones) {
5154
+ if (zone.name.length === 0 || zone.path.length === 0) {
5155
+ continue;
5156
+ }
5157
+ let sumX = 0;
5158
+ let sumY = 0;
5159
+ for (const p of zone.path) {
5160
+ sumX += p.x;
5161
+ sumY += p.y;
5162
+ }
5163
+ const centroid = { x: sumX / zone.path.length, y: sumY / zone.path.length };
5164
+ const { px, py } = coordToPixel(centroid, bounds, width, height, padding);
5165
+ lines.push(
5166
+ `<text x="${px}" y="${py}" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="${ZONE_LABEL_COLOR}" text-anchor="middle" dominant-baseline="middle">${escapeXml(zone.name)}</text>`
5167
+ );
5168
+ }
5089
5169
  lines.push("</svg>");
5090
5170
  return lines.join("\n");
5091
5171
  }
@@ -5116,7 +5196,10 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
5116
5196
  capabilities: input.capabilities ?? new MowerCapabilityResolver().resolve(input.device.model)
5117
5197
  });
5118
5198
  this.#caps = getMowerCapabilities(input.device.model);
5119
- this.#fetchBatch = input.getBatchDeviceDatas ?? null;
5199
+ this.#fetchBatch = input.getBatchDeviceDatas ?? ((did, props) => getBatchDeviceDatas(
5200
+ { session: this.currentSession(), region: this.region, did },
5201
+ props
5202
+ ));
5120
5203
  }
5121
5204
  /** Rich, mower-specific capability record. */
5122
5205
  get mowerCapabilities() {
@@ -5295,6 +5378,20 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
5295
5378
  }
5296
5379
  return this.#sendTask(buildSpotPayload(spotAreaIds.map((s) => Math.trunc(s))));
5297
5380
  }
5381
+ /**
5382
+ * Switch the mower's ACTIVE map (2:50 o:200). Takes a map *id* (as carried by
5383
+ * {@link MowerMap.availableMaps}/`currentMapId`) and resolves it to the
5384
+ * firmware's map *index* via the last-fetched map; falls back to treating the
5385
+ * id as the index when no map has been fetched yet. Mirrors the donor
5386
+ * `set_current_map`. Capability-gated on `canMap`.
5387
+ */
5388
+ async setCurrentMap(mapId) {
5389
+ this.#requireCap(this.#caps.canMap, "setCurrentMap", "map switching");
5390
+ const id = Math.trunc(mapId);
5391
+ const entry = this.#lastMap?.availableMaps.find((m) => m.mapId === id);
5392
+ const index = entry?.mapIndex ?? id;
5393
+ return this.#sendTask(buildSetCurrentMapPayload(index));
5394
+ }
5298
5395
  // -- CMS consumables ----------------------------------------------------
5299
5396
  /**
5300
5397
  * Read the raw CMS consumable counters `[blade, brush, robot]` (minutes used),