@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.d.cts CHANGED
@@ -2178,6 +2178,14 @@ declare class MowerDevice extends BaseDevice {
2178
2178
  startMowingEdges(contourIds: number[][]): Promise<unknown>;
2179
2179
  /** Spot mowing (2:50 o:103). */
2180
2180
  startMowingSpots(spotAreaIds: number[]): Promise<unknown>;
2181
+ /**
2182
+ * Switch the mower's ACTIVE map (2:50 o:200). Takes a map *id* (as carried by
2183
+ * {@link MowerMap.availableMaps}/`currentMapId`) and resolves it to the
2184
+ * firmware's map *index* via the last-fetched map; falls back to treating the
2185
+ * id as the index when no map has been fetched yet. Mirrors the donor
2186
+ * `set_current_map`. Capability-gated on `canMap`.
2187
+ */
2188
+ setCurrentMap(mapId: number): Promise<unknown>;
2181
2189
  /**
2182
2190
  * Read the raw CMS consumable counters `[blade, brush, robot]` (minutes used),
2183
2191
  * via the SCHEDULING_TASK (2:50) custom-action getter `{m:'g',t:'CMS'}`. This
package/dist/index.d.ts CHANGED
@@ -2178,6 +2178,14 @@ declare class MowerDevice extends BaseDevice {
2178
2178
  startMowingEdges(contourIds: number[][]): Promise<unknown>;
2179
2179
  /** Spot mowing (2:50 o:103). */
2180
2180
  startMowingSpots(spotAreaIds: number[]): Promise<unknown>;
2181
+ /**
2182
+ * Switch the mower's ACTIVE map (2:50 o:200). Takes a map *id* (as carried by
2183
+ * {@link MowerMap.availableMaps}/`currentMapId`) and resolves it to the
2184
+ * firmware's map *index* via the last-fetched map; falls back to treating the
2185
+ * id as the index when no map has been fetched yet. Mirrors the donor
2186
+ * `set_current_map`. Capability-gated on `canMap`.
2187
+ */
2188
+ setCurrentMap(mapId: number): Promise<unknown>;
2181
2189
  /**
2182
2190
  * Read the raw CMS consumable counters `[blade, brush, robot]` (minutes used),
2183
2191
  * via the SCHEDULING_TASK (2:50) custom-action getter `{m:'g',t:'CMS'}`. This
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/support/version.ts
2
2
  var LIBRARY_NAME = "nodedreame";
3
- var LIBRARY_VERSION = "1.11.6";
3
+ var LIBRARY_VERSION = "1.11.10";
4
4
 
5
5
  // src/transport/errors.ts
6
6
  var DreameError = class extends Error {
@@ -299,6 +299,11 @@ var CachedPropsResponseSchema = z.object({
299
299
  msg: z.string().nullish(),
300
300
  data: z.array(CachedPropEntrySchema).optional()
301
301
  }).passthrough();
302
+ var BatchDeviceDataResponseSchema = z.object({
303
+ code: z.number().optional(),
304
+ msg: z.string().nullish(),
305
+ data: z.record(z.string(), z.unknown()).optional()
306
+ }).passthrough();
302
307
  var SendCommandResponseSchema = z.object({
303
308
  code: z.number().optional(),
304
309
  msg: z.string().nullish(),
@@ -559,6 +564,29 @@ async function callAction(base, action, opts = {}) {
559
564
  const res = await sendCommand({ ...base, ...opts, method: "action", params });
560
565
  return res.data?.result ?? res.result ?? res;
561
566
  }
567
+ async function getBatchDeviceDatas(base, props, opts = {}) {
568
+ const ctx = base.ctx ?? RequestContext.from({ ...base, host: base.apiHost });
569
+ const signal = opts.signal ?? base.signal;
570
+ const timeoutMs = opts.timeoutMs ?? base.timeoutMs;
571
+ const raw = await httpPostJsonBody({
572
+ ctx,
573
+ path: "/dreame-user-iot/iotuserdata/getDeviceData",
574
+ accessToken: base.session.accessToken,
575
+ body: { did: base.did, model: props },
576
+ context: "batch device data",
577
+ ...signal !== void 0 ? { signal } : {},
578
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
579
+ });
580
+ const parsed = BatchDeviceDataResponseSchema.parse(raw);
581
+ if (parsed.code !== void 0 && parsed.code !== 0) {
582
+ throw new DreameApiError(
583
+ `batch device data rejected: code=${parsed.code} msg=${parsed.msg ?? "?"}`,
584
+ 200,
585
+ parsed
586
+ );
587
+ }
588
+ return parsed.data ?? {};
589
+ }
562
590
  function extractResultArray(res, context) {
563
591
  const raw = Array.isArray(res.data?.result) ? res.data.result : Array.isArray(res.result) ? res.result : null;
564
592
  if (raw === null) {
@@ -2063,6 +2091,43 @@ function parseFrame(inflated) {
2063
2091
  return { header, tail };
2064
2092
  }
2065
2093
 
2094
+ // src/models/vacuum/map/segment-types.ts
2095
+ var SEGMENT_TYPE_CODE_TO_NAME = {
2096
+ 0: "Room",
2097
+ 1: "Living Room",
2098
+ 2: "Primary Bedroom",
2099
+ 3: "Study",
2100
+ 4: "Kitchen",
2101
+ 5: "Dining Hall",
2102
+ 6: "Bathroom",
2103
+ 7: "Balcony",
2104
+ 8: "Corridor",
2105
+ 9: "Utility Room",
2106
+ 10: "Closet",
2107
+ 11: "Meeting Room",
2108
+ 12: "Office",
2109
+ 13: "Fitness Area",
2110
+ 14: "Recreation Area",
2111
+ 15: "Secondary Bedroom"
2112
+ };
2113
+ function safeBase64ToUtf8(s) {
2114
+ try {
2115
+ return Buffer.from(s, "base64").toString("utf8");
2116
+ } catch {
2117
+ return null;
2118
+ }
2119
+ }
2120
+ function resolveSegmentName(type, index, customName) {
2121
+ if (type !== void 0 && type !== 0 && SEGMENT_TYPE_CODE_TO_NAME[type] !== void 0) {
2122
+ const base = SEGMENT_TYPE_CODE_TO_NAME[type];
2123
+ return index !== void 0 && index > 0 ? `${base} ${index + 1}` : base;
2124
+ }
2125
+ if (typeof customName === "string" && customName.length > 0) {
2126
+ return safeBase64ToUtf8(customName);
2127
+ }
2128
+ return null;
2129
+ }
2130
+
2066
2131
  // src/models/vacuum/map/pixel-grid.ts
2067
2132
  function classifyPixelFsm1(byte) {
2068
2133
  if (byte === 0) {
@@ -2208,7 +2273,9 @@ function collectSegments(layers, dim, tail) {
2208
2273
  };
2209
2274
  segs.push({
2210
2275
  id,
2211
- name: meta?.name ? safeBase64ToUtf8(meta.name) : null,
2276
+ // Mirror the donor: a room-type code (≠0) yields a localized default name
2277
+ // (e.g. "Kitchen"), else the user's custom base64 name, else null.
2278
+ name: resolveSegmentName(meta?.type, meta?.index, meta?.name),
2212
2279
  bbox,
2213
2280
  centroid,
2214
2281
  neighbours: meta?.nei_id ?? [],
@@ -2219,13 +2286,6 @@ function collectSegments(layers, dim, tail) {
2219
2286
  }
2220
2287
  return segs;
2221
2288
  }
2222
- function safeBase64ToUtf8(s) {
2223
- try {
2224
- return Buffer.from(s, "base64").toString("utf8");
2225
- } catch {
2226
- return null;
2227
- }
2228
- }
2229
2289
 
2230
2290
  // src/models/vacuum/map/path.ts
2231
2291
  var PATH_TYPE_FROM_OP = {
@@ -4021,7 +4081,8 @@ var TASK_OPCODE = {
4021
4081
  ALL_AREA: 100,
4022
4082
  EDGE: 101,
4023
4083
  ZONE: 102,
4024
- SPOT: 103
4084
+ SPOT: 103,
4085
+ SET_CURRENT_MAP: 200
4025
4086
  };
4026
4087
  function buildResumePayload() {
4027
4088
  return { m: "a", p: 0, o: 5 };
@@ -4038,6 +4099,9 @@ function buildEdgePayload(contourIds) {
4038
4099
  function buildSpotPayload(spotAreaIds) {
4039
4100
  return { m: "a", p: 0, o: TASK_OPCODE.SPOT, d: { area: [...spotAreaIds] } };
4040
4101
  }
4102
+ function buildSetCurrentMapPayload(mapIndex) {
4103
+ return { m: "a", p: 0, o: TASK_OPCODE.SET_CURRENT_MAP, d: { idx: mapIndex } };
4104
+ }
4041
4105
  function buildGetConsumablePayload() {
4042
4106
  return { m: "g", t: "CMS" };
4043
4107
  }
@@ -4799,11 +4863,15 @@ var DEFAULT_HEIGHT = 1200;
4799
4863
  var DEFAULT_PADDING = 50;
4800
4864
  var BACKGROUND = "#f5f5f0";
4801
4865
  var MAP_BOUNDARY = "#006400";
4802
- var MOWING_PATH = "#ffa500";
4866
+ var MOWING_PATH = "#32cd32";
4803
4867
  var NAV_PATH = "#b4b4b4";
4804
4868
  var OBSTACLE_STROKE = "#ff4d00";
4805
4869
  var OBSTACLE_FILL = "#ff4d0065";
4806
4870
  var TEXT_COLOR = "#000000";
4871
+ var ZONE_LABEL_COLOR = "#3c3c3c";
4872
+ function escapeXml(s) {
4873
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
4874
+ }
4807
4875
  var ZONE_COLORS = [
4808
4876
  ["#a4d291c8", "#86be73"],
4809
4877
  ["#a0c8dcc8", "#82aac8"],
@@ -4926,7 +4994,6 @@ function renderMowerSvg(map, opts = {}) {
4926
4994
  return lines.join("\n");
4927
4995
  }
4928
4996
  const bounds = calculateBounds(allPoints);
4929
- const multiZone = map.zones.length > 1;
4930
4997
  for (const navPath of map.paths) {
4931
4998
  const dashed = svgPathFromSegments(
4932
4999
  [navPath.path],
@@ -4943,9 +5010,6 @@ function renderMowerSvg(map, opts = {}) {
4943
5010
  }
4944
5011
  }
4945
5012
  map.zones.forEach((zone, i) => {
4946
- if (!multiZone) {
4947
- return;
4948
- }
4949
5013
  const palette = ZONE_COLORS[i % ZONE_COLORS.length];
4950
5014
  const [fill, outline] = palette ?? ZONE_COLORS[0] ?? ["#cccccc", "#888888"];
4951
5015
  const poly = svgPolygon(zone.path, bounds, width, height, padding, fill, outline);
@@ -4954,7 +5018,7 @@ function renderMowerSvg(map, opts = {}) {
4954
5018
  }
4955
5019
  });
4956
5020
  map.zones.forEach((zone, i) => {
4957
- const outline = multiZone ? ZONE_COLORS[i % ZONE_COLORS.length]?.[1] ?? MAP_BOUNDARY : MAP_BOUNDARY;
5021
+ const outline = ZONE_COLORS[i % ZONE_COLORS.length]?.[1] ?? MAP_BOUNDARY;
4958
5022
  const boundary = svgPathFromSegments(
4959
5023
  [zone.path],
4960
5024
  bounds,
@@ -4998,6 +5062,22 @@ function renderMowerSvg(map, opts = {}) {
4998
5062
  lines.push(poly);
4999
5063
  }
5000
5064
  }
5065
+ for (const zone of map.zones) {
5066
+ if (zone.name.length === 0 || zone.path.length === 0) {
5067
+ continue;
5068
+ }
5069
+ let sumX = 0;
5070
+ let sumY = 0;
5071
+ for (const p of zone.path) {
5072
+ sumX += p.x;
5073
+ sumY += p.y;
5074
+ }
5075
+ const centroid = { x: sumX / zone.path.length, y: sumY / zone.path.length };
5076
+ const { px, py } = coordToPixel(centroid, bounds, width, height, padding);
5077
+ lines.push(
5078
+ `<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>`
5079
+ );
5080
+ }
5001
5081
  lines.push("</svg>");
5002
5082
  return lines.join("\n");
5003
5083
  }
@@ -5028,7 +5108,10 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
5028
5108
  capabilities: input.capabilities ?? new MowerCapabilityResolver().resolve(input.device.model)
5029
5109
  });
5030
5110
  this.#caps = getMowerCapabilities(input.device.model);
5031
- this.#fetchBatch = input.getBatchDeviceDatas ?? null;
5111
+ this.#fetchBatch = input.getBatchDeviceDatas ?? ((did, props) => getBatchDeviceDatas(
5112
+ { session: this.currentSession(), region: this.region, did },
5113
+ props
5114
+ ));
5032
5115
  }
5033
5116
  /** Rich, mower-specific capability record. */
5034
5117
  get mowerCapabilities() {
@@ -5207,6 +5290,20 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
5207
5290
  }
5208
5291
  return this.#sendTask(buildSpotPayload(spotAreaIds.map((s) => Math.trunc(s))));
5209
5292
  }
5293
+ /**
5294
+ * Switch the mower's ACTIVE map (2:50 o:200). Takes a map *id* (as carried by
5295
+ * {@link MowerMap.availableMaps}/`currentMapId`) and resolves it to the
5296
+ * firmware's map *index* via the last-fetched map; falls back to treating the
5297
+ * id as the index when no map has been fetched yet. Mirrors the donor
5298
+ * `set_current_map`. Capability-gated on `canMap`.
5299
+ */
5300
+ async setCurrentMap(mapId) {
5301
+ this.#requireCap(this.#caps.canMap, "setCurrentMap", "map switching");
5302
+ const id = Math.trunc(mapId);
5303
+ const entry = this.#lastMap?.availableMaps.find((m) => m.mapId === id);
5304
+ const index = entry?.mapIndex ?? id;
5305
+ return this.#sendTask(buildSetCurrentMapPayload(index));
5306
+ }
5210
5307
  // -- CMS consumables ----------------------------------------------------
5211
5308
  /**
5212
5309
  * Read the raw CMS consumable counters `[blade, brush, robot]` (minutes used),