@apocaliss92/nodedreame 1.2.0 → 1.3.0

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/support/version.ts","../src/transport/errors.ts","../src/transport/crypto.ts","../src/auth/config.ts","../src/transport/headers.ts","../src/transport/fetch.ts","../src/transport/http.ts","../src/transport/schemas.ts","../src/auth/dreame-account.ts","../src/cloud/devices.ts","../src/cloud/commands.ts","../src/transport/mqtt-push.ts","../src/transport/typed-emitter.ts","../src/device/capability.ts","../src/device/base-device.ts","../src/models/vacuum/properties.ts","../src/models/vacuum/enums.ts","../src/models/_shared/decode.ts","../src/models/vacuum/decode.ts","../src/models/vacuum/capabilities.ts","../src/models/vacuum/map/envelope.ts","../src/models/vacuum/map/header.ts","../src/models/vacuum/map/tail.ts","../src/models/vacuum/map/pixel-grid.ts","../src/models/vacuum/map/path.ts","../src/models/vacuum/map/field-utils.ts","../src/models/vacuum/map/obstacles.ts","../src/models/vacuum/map/geometry.ts","../src/models/vacuum/map/cleaned-area.ts","../src/models/vacuum/map/decode.ts","../src/models/vacuum/map/render.ts","../src/models/vacuum/map/oss-fetch.ts","../src/models/vacuum/vacuum-device.ts","../src/models/mower/properties.ts","../src/models/mower/enums.ts","../src/models/mower/decode.ts","../src/models/mower/capabilities.ts","../src/models/mower/map/parser.ts","../src/models/mower/map/render.ts","../src/models/mower/mower-device.ts","../src/api/nodreame.ts"],"sourcesContent":["// Public API surface of nodedreame.\n// Phase 1 exports only the error classes and core domain types; transport,\n// cloud and model internals stay private to keep the published surface small\n// and stable. The high-level facade is added in Phase 2.\n\nexport { LIBRARY_NAME } from './support/version.js';\n\n// Stable public surface for Phase 1: error classes + core types. The full\n// facade (login/discoverDevices) is added in Phase 2 — we intentionally do\n// not export transport internals here to keep the published API small.\n\nexport {\n DreameError,\n DreameAuthError,\n DreameApiError,\n DreameDeviceOfflineError,\n DreameTransportError,\n} from './transport/errors.js';\n\nexport type { DreameRegion } from './auth/config.js';\nexport type {\n DreameSession,\n DreameDevice,\n DreameCloudState,\n MiotProp,\n MiotAction,\n PropertyWrite,\n PropertyResult,\n} from './cloud/types.js';\n\n// --- Phase 2: high-level facade + generic device handle ------------------\nexport { Nodreame } from './api/nodreame.js';\nexport type { NodreameDeps, CreateDeviceArgs, NodreameEvents } from './api/nodreame.js';\nexport { BaseDevice } from './device/base-device.js';\nexport type { BaseDeviceEvents } from './device/base-device.js';\nexport { DefaultCapabilityResolver, resolveCapabilities } from './device/capability.js';\nexport type { CapabilityResolver, DeviceCapabilities } from './device/capability.js';\nexport type {\n NodreameOptions,\n PropertyState,\n PropertyChangedEvent,\n StateChangedEvent,\n DeviceEvent,\n} from './api/types.js';\n\n// --- Phase 3: vacuum model ------------------------------------------------\n// Public vacuum surface only: the typed VacuumDevice handle, its value enums,\n// and the capability records/resolver. Internal siid/piid/aiid property maps,\n// the model factory's property tables, and the decode helpers stay private.\nexport { VacuumDevice } from './models/vacuum/vacuum-device.js';\nexport type { CleanOpts, VacuumGetMapInput } from './models/vacuum/vacuum-device.js';\n// Fetcher-injection seam for VacuumDevice.getMap: the interface a custom\n// signed-blob fetcher implements, plus its input shape. The concrete\n// OssFetcher class and the decode internals stay private.\nexport type { OssFetcherLike, OssFetchInput } from './models/vacuum/map/index.js';\nexport {\n MiotState,\n ChargingStatus,\n SuctionLevel,\n WaterVolume,\n CleaningMode,\n MiotError,\n TaskStatus,\n} from './models/vacuum/enums.js';\nexport {\n getVacuumCapabilities,\n VacuumCapabilityResolver,\n MODEL_CAPABILITIES as VACUUM_MODEL_CAPABILITIES,\n} from './models/vacuum/capabilities.js';\nexport type { VacuumCapabilities } from './models/vacuum/capabilities.js';\n\n// --- Phase 4: mower model -------------------------------------------------\n// Public mower surface only: the typed MowerDevice handle, its value enums,\n// the capability records/resolver, and the parsed task/control types. Internal\n// siid/piid/aiid maps, opcode payload builders, decode helpers, and the\n// deviceClassFor factory stay private.\nexport { MowerDevice } from './models/mower/mower-device.js';\n// Batch-fetch injection seam for MowerDevice.getMap: the fetcher signature and\n// the construction input that carries it. The opcode/decode internals and the\n// device-class factory stay private.\nexport type { BatchDeviceDataFetcher, MowerDeviceInput } from './models/mower/mower-device.js';\nexport {\n MowerStatus,\n MowerChargingStatus,\n MowerControlAction,\n MowerTaskStatus,\n MowerFault,\n} from './models/mower/enums.js';\nexport {\n getMowerCapabilities,\n MowerCapabilityResolver,\n MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES,\n} from './models/mower/capabilities.js';\nexport type { MowerCapabilities } from './models/mower/capabilities.js';\nexport type { MowerTaskDescriptor, MowerControlState } from './models/mower/decode.js';\n\n// --- Phase 5: maps --------------------------------------------------------\n// Public map surface: the structured model types + the two renderers. The\n// binary/JSON decoders, the OSS signed-blob fetcher, and every intermediate\n// decode step (envelope/header/pixel-grid/path/obstacles/geometry/cleaned-area/\n// merge, mower chunk reassembly + parser) stay PRIVATE — consumers obtain maps\n// via VacuumDevice.getMap()/currentSegmentId/lastMap and MowerDevice.getMap()/\n// mapSvg(), all reachable through the already-exported device handles.\nexport { renderVacuumPng } from './models/vacuum/map/render.js';\nexport type { RenderVacuumPngOptions } from './models/vacuum/map/render.js';\nexport type {\n VacuumMap,\n MapDimensions,\n MapBoundingBox,\n MapPoint,\n MapPose,\n MapRun,\n MapLayer,\n MapLayerType,\n MapSegment,\n MapPath,\n MapPathType,\n MapObstacle,\n MapFrameType,\n MapVirtualWall,\n MapRestrictedArea,\n MapLowLyingArea,\n MapWallsInfo,\n MapStorey,\n MapRoom,\n MapRoomWall,\n MapCleanedAreaOverlay,\n} from './models/vacuum/map/types.js';\n\nexport { renderMowerSvg } from './models/mower/map/render.js';\nexport type { RenderMowerSvgOptions } from './models/mower/map/render.js';\nexport type {\n MowerMap,\n MowerPoint,\n MowerZone,\n MowerSpotArea,\n MowerPathEntry,\n MowerContour,\n MowerMapBoundary,\n MowerMowPath,\n MowerAvailableMap,\n} from './models/mower/map/types.js';\n","/** Public name of this library. */\nexport const LIBRARY_NAME = 'nodedreame';\n","export class DreameError extends Error {\n constructor(\n message: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = 'DreameError';\n }\n}\n\nexport class DreameAuthError extends DreameError {\n constructor(\n message: string,\n public readonly status?: number,\n cause?: unknown,\n ) {\n super(message, cause);\n this.name = 'DreameAuthError';\n }\n}\n\nexport class DreameApiError extends DreameError {\n constructor(\n message: string,\n public readonly status: number,\n public readonly body?: unknown,\n cause?: unknown,\n ) {\n super(message, cause);\n this.name = 'DreameApiError';\n }\n}\n\n/**\n * Thrown when the cloud returns code 80001. The literal `msg` claims the\n * device is offline — that interpretation is FREQUENTLY WRONG. 80001 means\n * the cloud's HTTP-side waiter for the device's MQTT ACK gave up after ~8s.\n * It does NOT prove the action failed to reach the device. The MQTT\n * subscription is the source of truth for reachability. The only true\n * positive is a genuinely unreachable device (powered off / network lost /\n * rebooting) — in which case no MQTT echo arrives either.\n */\nexport class DreameDeviceOfflineError extends DreameApiError {\n constructor(message: string, status: number, body?: unknown) {\n super(message, status, body);\n this.name = 'DreameDeviceOfflineError';\n }\n}\n\nexport class DreameTransportError extends DreameError {\n constructor(message: string, cause?: unknown) {\n super(message, cause);\n this.name = 'DreameTransportError';\n }\n}\n","import { createCipheriv, createHash, randomBytes } from 'node:crypto';\n\n/**\n * App-global secrets extracted from the Dreamehome Flutter binary. These\n * rotate with new app versions — if login starts failing with no obvious\n * cause, re-extract from the latest APK. Last verified against\n * `dreame-meta: cv=i_829`.\n */\nconst PASSWORD_SALT = 'RAylYC%fmSKp7%Tq';\nconst RLC_AES_KEY = 'EETjszu*XI5znHsI';\n\n/**\n * Salted MD5 of the plaintext password, lower-case hex. Sent on the wire\n * instead of the cleartext password.\n */\nexport function hashPassword(plaintext: string): string {\n return createHash('md5')\n .update(plaintext + PASSWORD_SALT)\n .digest('hex');\n}\n\n/**\n * Compute the value of the `dreame-rlc` request header: AES-128-ECB (PKCS7\n * padding) of `<region>|<lang>|<country>` with the static app key, output as\n * lowercase hex.\n */\nexport function buildRlcHeader(region: string, lang: string, country: string): string {\n const plaintext = `${region}|${lang}|${country}`;\n const cipher = createCipheriv('aes-128-ecb', Buffer.from(RLC_AES_KEY, 'utf8'), null);\n const out = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n return out.toString('hex');\n}\n\n/** Random MQTT client id matching the format used by the Dreamehome app. */\nexport function randomMqttClientId(): string {\n return 'p_' + randomBytes(8).toString('hex');\n}\n\n/**\n * Random positive 31-bit integer for the `id` field of a sendCommand\n * envelope. Wide enough that concurrent fan-out won't collide.\n */\nexport function randomRequestId(): number {\n return Math.floor(Math.random() * 0x7fffffff) + 1;\n}\n","/** Supported Dreame cloud regions. */\nexport type DreameRegion = 'eu' | 'us' | 'cn' | 'ru' | 'sg' | 'in' | 'de' | 'tw';\n\n/** All regions, in canonical order. Used for table-completeness checks. */\nexport const ALL_REGIONS: readonly DreameRegion[] = [\n 'eu',\n 'us',\n 'cn',\n 'ru',\n 'sg',\n 'in',\n 'de',\n 'tw',\n];\n\n/** Hostname (with non-standard port) of the Dreame auth + API server, per region. */\nexport const REGION_HOSTS: Record<DreameRegion, string> = {\n eu: 'eu.iot.dreame.tech:13267',\n us: 'us.iot.dreame.tech:13267',\n cn: 'cn.iot.dreame.tech:13267',\n ru: 'ru.iot.dreame.tech:13267',\n sg: 'sg.iot.dreame.tech:13267',\n in: 'in.iot.dreame.tech:13267',\n de: 'eu.iot.dreame.tech:13267',\n tw: 'cn.iot.dreame.tech:13267',\n};\n\n/** Default `country` form-field per region (ISO-3166 alpha-2). */\nexport const REGION_DEFAULT_COUNTRY: Record<DreameRegion, string> = {\n eu: 'GB',\n us: 'US',\n cn: 'CN',\n ru: 'RU',\n sg: 'SG',\n in: 'IN',\n de: 'DE',\n tw: 'TW',\n};\n\n/** Default UI language per region (ISO-639-1). */\nexport const REGION_DEFAULT_LANG: Record<DreameRegion, string> = {\n eu: 'en',\n us: 'en',\n cn: 'zh',\n ru: 'ru',\n sg: 'en',\n in: 'en',\n de: 'de',\n tw: 'zh',\n};\n\n/**\n * App-level OAuth2 CLIENT credentials (a Basic header) baked into the public\n * Dreamehome app binary — NOT a user secret, password, or per-account token.\n * These identify the app to the OAuth endpoint and are identical for every\n * install; they rotate only when Dreame ships a new app release (last verified\n * against cv=i_829). Not sensitive — safe to commit. (SAST note: this is a\n * shipped public client id/secret, not a leaked credential.)\n */\nexport const OAUTH_BASIC_AUTH = 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=';\n\n/** App-version fingerprint. Update if a new app version starts requiring it. */\nexport const APP_META = 'cv=i_829';\n\n/** User-Agent the Flutter Dreamehome app sends. */\nexport const APP_USER_AGENT = 'Dart/3.2 (dart:io)';\n\n/** Tenant id — Dreame brand. */\nexport const TENANT_DREAME = '000000';\n/** Tenant id — Mova brand. */\nexport const TENANT_MOVA = '000002';\n\n/**\n * Literal value placed in the `from` field of every command envelope. Dreame's\n * cloud ignores it — the original Flutter app sends \"XXXXXX\" verbatim.\n */\nexport const COMMAND_FROM_FIELD = 'XXXXXX';\n\n/** `iotComPrefix` path component (/dreame-iot-com-<n>/) for Dreame. */\nexport const IOT_COM_PREFIX_DREAME = 10000;\n/** `iotComPrefix` for Mova brand requests (untested). */\nexport const IOT_COM_PREFIX_MOVA = 20000;\n\n/** Standard HTTP content types used by the Dreame backend. */\nexport const CONTENT_TYPE_JSON = 'application/json';\nexport const CONTENT_TYPE_FORM = 'application/x-www-form-urlencoded';\n","import type { DreameRegion } from '../auth/config.js';\nimport {\n APP_META,\n APP_USER_AGENT,\n CONTENT_TYPE_FORM,\n OAUTH_BASIC_AUTH,\n TENANT_DREAME,\n} from '../auth/config.js';\nimport { buildRlcHeader } from './crypto.js';\n\n/**\n * Compose the static request headers the Dreame backend expects on every\n * call. Pass `accessToken` after login; omit (or pass `null`) for the\n * unauthenticated login request itself.\n */\nexport function buildHeaders(opts: {\n region: DreameRegion;\n country: string;\n lang: string;\n accessToken?: string | null;\n contentType?: string;\n}): Record<string, string> {\n const auth = opts.accessToken ? `bearer ${opts.accessToken}` : 'bearer';\n return {\n 'user-agent': APP_USER_AGENT,\n authorization: OAUTH_BASIC_AUTH,\n 'content-type': opts.contentType ?? CONTENT_TYPE_FORM,\n 'dreame-auth': auth,\n 'dreame-meta': APP_META,\n 'dreame-rlc': buildRlcHeader(opts.region, opts.lang, opts.country),\n 'tenant-id': TENANT_DREAME,\n };\n}\n","import { fetch as undiciFetch } from 'undici';\n\n/**\n * Narrow fetch-shaped function type backed by undici. Defined here so all\n * callers share one declaration — no DOM lib globals, no casts.\n *\n * We re-export `FetchImpl` from this module as the canonical definition;\n * `transport/http.ts` re-exports it from here so downstream modules have a\n * single import path.\n */\nexport type FetchImpl = typeof undiciFetch;\n\n/**\n * Default HTTP fetch implementation. We use undici explicitly (rather than the\n * Node global `fetch`) so the library works identically on Node ≥20 and so the\n * exact client behavior is pinned to a known version.\n */\nexport const defaultFetch: FetchImpl = undiciFetch;\n","import type { DreameRegion } from '../auth/config.js';\nimport {\n CONTENT_TYPE_JSON,\n REGION_DEFAULT_COUNTRY,\n REGION_DEFAULT_LANG,\n REGION_HOSTS,\n} from '../auth/config.js';\nimport { buildHeaders } from './headers.js';\nimport { defaultFetch, type FetchImpl } from './fetch.js';\nimport {\n DreameApiError,\n DreameAuthError,\n DreameDeviceOfflineError,\n DreameTransportError,\n} from './errors.js';\n\n/** Re-export so other modules can import FetchImpl from a single location. */\nexport type { FetchImpl };\n\n/** Cloud response code meaning \"device didn't ACK; may be offline\" (often a false negative). */\nconst CODE_DEVICE_OFFLINE = 80001;\n\n/** Default per-request timeout — fetch() is otherwise unbounded. */\nconst DEFAULT_TIMEOUT_MS = 30000;\n\nexport interface RequestContextOpts {\n region: DreameRegion;\n country?: string;\n lang?: string;\n host?: string;\n fetchImpl?: FetchImpl;\n}\n\nexport interface RequestContextInput {\n region: DreameRegion;\n country?: string | undefined;\n lang?: string | undefined;\n /** Override host. Some callers spell this `authHost`/`apiHost` — pass that here. */\n host?: string | undefined;\n fetchImpl?: FetchImpl | undefined;\n}\n\nexport class RequestContext {\n readonly region: DreameRegion;\n readonly country: string;\n readonly lang: string;\n readonly host: string;\n readonly fetchImpl: FetchImpl;\n\n constructor(opts: RequestContextOpts) {\n this.region = opts.region;\n this.country = opts.country ?? REGION_DEFAULT_COUNTRY[opts.region];\n this.lang = opts.lang ?? REGION_DEFAULT_LANG[opts.region];\n this.host = opts.host ?? REGION_HOSTS[opts.region];\n this.fetchImpl = opts.fetchImpl ?? defaultFetch;\n }\n\n static from(input: RequestContextInput): RequestContext {\n return new RequestContext({\n region: input.region,\n ...(input.country !== undefined ? { country: input.country } : {}),\n ...(input.lang !== undefined ? { lang: input.lang } : {}),\n ...(input.host !== undefined ? { host: input.host } : {}),\n ...(input.fetchImpl !== undefined ? { fetchImpl: input.fetchImpl } : {}),\n });\n }\n\n /** `https://<host><path>` — pass a path with a leading slash. */\n url(path: string): string {\n return `https://${this.host}${path}`;\n }\n\n /** Build the static Dreame headers, optionally with a bearer token + content-type. */\n headers(\n opts: { accessToken?: string | null; contentType?: string } = {},\n ): Record<string, string> {\n return buildHeaders({\n region: this.region,\n country: this.country,\n lang: this.lang,\n ...(opts.accessToken !== undefined ? { accessToken: opts.accessToken } : {}),\n ...(opts.contentType !== undefined ? { contentType: opts.contentType } : {}),\n });\n }\n}\n\n/** Minimal common shape every Dreame JSON response carries. */\nexport interface BaseResponse {\n code?: number;\n msg?: string;\n}\n\nexport async function httpPostJson<T extends BaseResponse>(input: {\n ctx: RequestContext;\n url: string;\n headers: Record<string, string>;\n body: string | URLSearchParams;\n context: string;\n errorClass?: typeof DreameApiError | typeof DreameAuthError;\n skipCodeCheck?: boolean;\n timeoutMs?: number;\n signal?: AbortSignal;\n}): Promise<T> {\n const Err = input.errorClass ?? DreameApiError;\n const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const signal = composeSignals(input.signal, timeoutMs);\n\n let res: Response;\n try {\n res = await input.ctx.fetchImpl(input.url, {\n method: 'POST',\n headers: input.headers,\n body: input.body,\n ...(signal !== undefined ? { signal } : {}),\n });\n } catch (err) {\n if (isAbortError(err)) {\n throw new DreameTransportError(\n `request to ${input.url} aborted after ${timeoutMs}ms or by caller`,\n err,\n );\n }\n throw new DreameTransportError(`network error contacting ${input.url}`, err);\n }\n\n const text = await res.text();\n let parsed: T | null = null;\n if (text) {\n try {\n parsed = JSON.parse(text) as T;\n } catch {\n // leave parsed null\n }\n }\n\n if (!res.ok) {\n throw new Err(\n `${input.context} failed: ${res.status} ${text.slice(0, 200)}`,\n res.status,\n parsed,\n );\n }\n if (!parsed) {\n throw new Err(`${input.context} response was not JSON (status ${res.status})`, res.status);\n }\n\n if (!input.skipCodeCheck && parsed.code !== undefined && parsed.code !== 0) {\n if (parsed.code === CODE_DEVICE_OFFLINE) {\n throw new DreameDeviceOfflineError(\n `device offline: ${parsed.msg ?? 'timeout'}`,\n res.status,\n parsed,\n );\n }\n throw new Err(\n `${input.context} rejected: code=${parsed.code} msg=${parsed.msg ?? '?'}`,\n res.status,\n parsed,\n );\n }\n\n return parsed;\n}\n\nexport async function httpPostJsonBody<T extends BaseResponse>(input: {\n ctx: RequestContext;\n path: string;\n accessToken?: string;\n body: unknown;\n context: string;\n timeoutMs?: number;\n signal?: AbortSignal;\n}): Promise<T> {\n return httpPostJson<T>({\n ctx: input.ctx,\n url: input.ctx.url(input.path),\n headers: input.ctx.headers({\n ...(input.accessToken !== undefined ? { accessToken: input.accessToken } : {}),\n contentType: CONTENT_TYPE_JSON,\n }),\n body: JSON.stringify(input.body),\n context: input.context,\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n });\n}\n\nfunction composeSignals(\n callerSignal: AbortSignal | undefined,\n timeoutMs: number,\n): AbortSignal | undefined {\n if (timeoutMs <= 0) {\n return callerSignal;\n }\n const timeoutSignal = AbortSignal.timeout(timeoutMs);\n if (!callerSignal) {\n return timeoutSignal;\n }\n return AbortSignal.any([callerSignal, timeoutSignal]);\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof Error && (err.name === 'AbortError' || err.name === 'TimeoutError');\n}\n","import { z } from 'zod';\n\n/** OAuth token endpoint response. Permissive: Dreame may add fields. */\nexport const OAuthTokenResponseSchema = z\n .object({\n access_token: z.string().optional(),\n refresh_token: z.string().optional(),\n expires_in: z.number().optional(),\n token_type: z.string().optional(),\n uid: z.union([z.string(), z.number()]).optional(),\n region: z.string().optional(),\n country: z.string().optional(),\n lang: z.string().optional(),\n tenant_id: z.string().optional(),\n error: z.string().optional(),\n error_description: z.string().optional(),\n code: z.number().optional(),\n msg: z.string().optional(),\n })\n .passthrough();\nexport type OAuthTokenResponse = z.infer<typeof OAuthTokenResponseSchema>;\n\n/** Single raw device record from the device-list endpoint. */\nexport const RawDeviceSchema = z\n .object({\n did: z.union([z.string(), z.number()]).optional(),\n model: z.string().optional(),\n customName: z.string().optional(),\n deviceName: z.string().optional(),\n mac: z.string().optional(),\n online: z.boolean().optional(),\n lwt: z.number().optional(),\n bindDomain: z.string().optional(),\n master: z.boolean().optional(),\n })\n .passthrough();\nexport type RawDevice = z.infer<typeof RawDeviceSchema>;\n\nexport const DeviceListResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().optional(),\n data: z\n .object({\n page: z\n .object({ records: z.array(RawDeviceSchema).optional() })\n .partial()\n .optional(),\n records: z.array(RawDeviceSchema).optional(),\n })\n .passthrough()\n .optional(),\n records: z.array(RawDeviceSchema).optional(),\n })\n .passthrough();\nexport type DeviceListResponse = z.infer<typeof DeviceListResponseSchema>;\n\n/**\n * Per-property result. Lenient by design: every known field is OPTIONAL and\n * extra keys pass through. This validates the STRUCTURE (each element must be\n * an object) without rejecting real cloud responses whose exact shape we have\n * not fully observed — the command path is not exercised by the live e2e.\n */\nexport const PropertyResultSchema = z\n .object({\n siid: z.number().optional(),\n piid: z.number().optional(),\n value: z.unknown().optional(),\n code: z.number().optional(),\n })\n .passthrough();\n\n/**\n * Single cloud-shadow entry from `dreame-user-iot/iotstatus/props`. The cloud\n * returns the last-known value as a STRING (e.g. `\"100\"`, `\"true\"`); the value\n * is widened to `unknown` and coerced by the command layer. `updateDate` is the\n * epoch-ms the cloud last saw that value (used for cache-age reporting).\n */\nexport const CachedPropEntrySchema = z\n .object({\n key: z.string(),\n value: z.unknown().optional(),\n updateDate: z.number().optional(),\n })\n .passthrough();\nexport type CachedPropEntry = z.infer<typeof CachedPropEntrySchema>;\n\n/**\n * Cloud-shadow read response (`iotstatus/props`). `data` is an ARRAY of\n * per-property entries (NOT the nested `data.result` shape `sendCommand`\n * returns). Permissive: missing props are simply absent; on an error code\n * `data` may be absent entirely.\n */\nexport const CachedPropsResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().optional(),\n data: z.array(CachedPropEntrySchema).optional(),\n })\n .passthrough();\nexport type CachedPropsResponse = z.infer<typeof CachedPropsResponseSchema>;\n\nexport const SendCommandResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().optional(),\n data: z.object({ result: z.unknown().optional() }).passthrough().optional(),\n result: z.unknown().optional(),\n })\n .passthrough();\nexport type SendCommandResponse = z.infer<typeof SendCommandResponseSchema>;\n\n/** Raw MQTT envelope as received from the broker, before flattening. */\nexport const RawMqttEventSchema = z\n .object({\n id: z.number().optional(),\n did: z.union([z.string(), z.number()]).optional(),\n data: z\n .object({\n id: z.number().optional(),\n method: z.string().optional(),\n params: z.unknown().optional(),\n })\n .passthrough()\n .optional(),\n })\n .passthrough();\nexport type RawMqttEvent = z.infer<typeof RawMqttEventSchema>;\n","import type { DreameRegion } from './config.js';\nimport type { DreameSession } from '../cloud/types.js';\nimport { DreameAuthError } from '../transport/errors.js';\nimport { hashPassword } from '../transport/crypto.js';\nimport { httpPostJson, RequestContext, type BaseResponse } from '../transport/http.js';\nimport { OAuthTokenResponseSchema } from '../transport/schemas.js';\nimport type { FetchImpl } from '../transport/http.js';\n\nexport interface LoginInput {\n email: string;\n password: string;\n region: DreameRegion;\n /** ISO-3166 alpha-2. Defaults from region. */\n country?: string;\n /** ISO-639-1. Defaults from region. */\n lang?: string;\n /** Override host (advanced — for testing). */\n authHost?: string;\n /** Inject a fetch impl for testing. */\n fetchImpl?: FetchImpl;\n}\n\nexport interface RefreshInput {\n refreshToken: string;\n region: DreameRegion;\n country?: string;\n lang?: string;\n authHost?: string;\n fetchImpl?: FetchImpl;\n}\n\nfunction ctxFromInput(input: {\n region: DreameRegion;\n country?: string;\n lang?: string;\n authHost?: string;\n fetchImpl?: FetchImpl;\n}): RequestContext {\n return RequestContext.from({ ...input, host: input.authHost });\n}\n\n/** Authenticate against the Dreame native cloud (OAuth2 password grant). */\nexport async function login(input: LoginInput): Promise<DreameSession> {\n const ctx = ctxFromInput(input);\n const body = new URLSearchParams({\n grant_type: 'password',\n scope: 'all',\n platform: 'IOS',\n type: 'account',\n username: input.email,\n password: hashPassword(input.password),\n country: ctx.country,\n lang: ctx.lang,\n });\n return postForToken(ctx, body);\n}\n\n/** Exchange a refresh token for a fresh access token. Refresh ~100s before expiry. */\nexport async function refresh(input: RefreshInput): Promise<DreameSession> {\n const ctx = ctxFromInput(input);\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: input.refreshToken,\n });\n return postForToken(ctx, body);\n}\n\nasync function postForToken(ctx: RequestContext, body: URLSearchParams): Promise<DreameSession> {\n // The OAuth response is validated/narrowed by `OAuthTokenResponseSchema.parse`\n // below; the HTTP layer only needs the minimal `BaseResponse` shape here. The\n // zod-inferred `OAuthTokenResponse` cannot be the generic directly because its\n // passthrough widening makes `code` `number | undefined`, which is\n // incompatible with `BaseResponse.code?: number` under exactOptionalPropertyTypes.\n const raw = await httpPostJson<BaseResponse>({\n ctx,\n url: ctx.url('/dreame-auth/oauth/token'),\n headers: ctx.headers(),\n body,\n context: 'auth',\n errorClass: DreameAuthError,\n skipCodeCheck: true, // OAuth uses HTTP status + top-level error fields, not parsed.code\n });\n\n const data = OAuthTokenResponseSchema.parse(raw);\n\n if (data.error || data.error_description) {\n throw new DreameAuthError(\n `auth failed: ${data.error ?? '?'} — ${data.error_description ?? 'no description'}`,\n );\n }\n if (!data.access_token) {\n // Redacted: never stringify the full payload — it carries refresh_token and\n // other secrets. Surface only non-secret diagnostic fields.\n throw new DreameAuthError(\n `auth response missing access_token: uid=${data.uid ?? '?'} error=${data.error ?? '?'} code=${data.code ?? '?'}`,\n );\n }\n\n const expiresIn = typeof data.expires_in === 'number' ? data.expires_in : 7200;\n const uid = data.uid !== undefined ? String(data.uid) : '';\n if (!uid) {\n throw new DreameAuthError('auth response missing uid');\n }\n\n const session: DreameSession = {\n accessToken: data.access_token,\n uid,\n expiresAt: Date.now() + expiresIn * 1000,\n region: ctx.region,\n };\n if (typeof data.refresh_token === 'string') {\n session.refreshToken = data.refresh_token;\n }\n return session;\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type { DreameCloudState, DreameDevice, DreameSession } from './types.js';\nimport {\n httpPostJsonBody,\n RequestContext,\n type BaseResponse,\n type FetchImpl,\n} from '../transport/http.js';\nimport { DeviceListResponseSchema, type RawDevice } from '../transport/schemas.js';\n\nexport interface ListDevicesInput {\n session: DreameSession;\n region: DreameRegion;\n ctx?: RequestContext;\n country?: string;\n lang?: string;\n apiHost?: string;\n fetchImpl?: FetchImpl;\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\n/** Enumerate the devices visible to the authenticated account (incl. shared). */\nexport async function listDevices(input: ListDevicesInput): Promise<DreameDevice[]> {\n const ctx = input.ctx ?? RequestContext.from({ ...input, host: input.apiHost });\n\n // The response is validated/narrowed by `DeviceListResponseSchema.parse`\n // below; the HTTP layer only needs the minimal `BaseResponse` shape. The\n // zod-inferred `DeviceListResponse` cannot be the generic directly because\n // its passthrough widening makes `code` `number | undefined`, which is\n // incompatible with `BaseResponse.code?: number` under exactOptionalPropertyTypes.\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: '/dreame-user-iot/iotuserbind/device/listV2',\n accessToken: input.session.accessToken,\n body: {\n sharedStatus: 1,\n current: 1,\n size: 100,\n lang: ctx.lang,\n timestamp: Date.now(),\n },\n context: 'device list',\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n });\n\n const parsed = DeviceListResponseSchema.parse(raw);\n const records = parsed.data?.page?.records ?? parsed.data?.records ?? parsed.records ?? [];\n return records.map(toDevice);\n}\n\nfunction toDevice(rawIn: RawDevice): DreameDevice {\n const raw = rawIn as Record<string, unknown>;\n const did = String(rawIn.did ?? '');\n const model = String(rawIn.model ?? '');\n const name = String(rawIn.customName || rawIn.deviceName || model || did);\n\n let lwtFlag = 0;\n if (typeof raw['property'] === 'string') {\n try {\n const p = JSON.parse(raw['property']) as { lwt?: number };\n if (p && typeof p.lwt === 'number') {\n lwtFlag = p.lwt;\n }\n } catch {\n // ignore malformed JSON\n }\n }\n const online = rawIn.online === true || rawIn.lwt === 1 || lwtFlag === 1;\n\n const device: DreameDevice = {\n did,\n model,\n name,\n online,\n raw,\n cloudState: parseCloudState(raw),\n };\n if (rawIn.mac) {\n device.mac = String(rawIn.mac);\n }\n if (typeof raw['ver'] === 'string') {\n device.firmwareVersion = raw['ver'];\n }\n if (typeof raw['sn'] === 'string') {\n device.serialNumber = raw['sn'];\n }\n return device;\n}\n\nfunction parseCloudState(raw: Record<string, unknown>): DreameCloudState {\n const latestStatus = typeof raw['latestStatus'] === 'number' ? raw['latestStatus'] : null;\n const battery = typeof raw['battery'] === 'number' ? raw['battery'] : null;\n const featureCode2 = typeof raw['featureCode2'] === 'number' ? raw['featureCode2'] : null;\n let videoActive: boolean | null = null;\n if (typeof raw['videoStatus'] === 'string') {\n try {\n const v = JSON.parse(raw['videoStatus']) as { operType?: string; status?: number };\n if (v?.operType === 'end' || v?.status === 0) {\n videoActive = false;\n } else if (v?.operType || v?.status) {\n videoActive = true;\n }\n } catch {\n // ignore malformed JSON\n }\n }\n return { latestStatus, battery, videoActive, featureCode2 };\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type {\n DreameSession,\n MiotProp,\n MiotAction,\n PropertyWrite,\n PropertyResult,\n} from './types.js';\nimport { COMMAND_FROM_FIELD, IOT_COM_PREFIX_DREAME } from '../auth/config.js';\nimport { randomRequestId } from '../transport/crypto.js';\nimport { DreameApiError } from '../transport/errors.js';\nimport {\n httpPostJsonBody,\n RequestContext,\n type BaseResponse,\n type FetchImpl,\n} from '../transport/http.js';\nimport { z } from 'zod';\nimport {\n SendCommandResponseSchema,\n CachedPropsResponseSchema,\n PropertyResultSchema,\n type SendCommandResponse,\n} from '../transport/schemas.js';\n\ninterface SendCommandInput {\n session: DreameSession;\n region: DreameRegion;\n did: string;\n /** MIoT method: `get_properties`, `set_properties`, `action`. */\n method: string;\n /**\n * For `get_properties`/`set_properties`: an ARRAY of property descriptors.\n * For `action`: a single OBJECT (NOT an array — Dreame surfaces the wrong\n * shape as a misleading code 80001 \"device offline\" error).\n */\n params: unknown;\n ctx?: RequestContext;\n country?: string;\n lang?: string;\n apiHost?: string;\n iotComPrefix?: number;\n fetchImpl?: FetchImpl;\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\n/** Low-level dispatch to `/device/sendCommand`. Caller owns the `params` shape. */\nexport async function sendCommand(input: SendCommandInput): Promise<SendCommandResponse> {\n const ctx = input.ctx ?? RequestContext.from({ ...input, host: input.apiHost });\n const prefix = input.iotComPrefix ?? IOT_COM_PREFIX_DREAME;\n const id = randomRequestId();\n\n // The response is validated/narrowed by `SendCommandResponseSchema.parse`\n // below; the HTTP layer only needs the minimal `BaseResponse` shape. The\n // zod-inferred `SendCommandResponse` cannot be the generic directly because\n // its passthrough widening makes `code` `number | undefined`, which is\n // incompatible with `BaseResponse.code?: number` under exactOptionalPropertyTypes.\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `/dreame-iot-com-${prefix}/device/sendCommand`,\n accessToken: input.session.accessToken,\n body: {\n did: input.did,\n id,\n data: {\n did: input.did,\n id,\n method: input.method,\n params: input.params,\n from: COMMAND_FROM_FIELD,\n },\n },\n context: input.method,\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n });\n return SendCommandResponseSchema.parse(raw);\n}\n\nexport interface CommonInput {\n session: DreameSession;\n region: DreameRegion;\n did: string;\n ctx?: RequestContext;\n country?: string;\n lang?: string;\n apiHost?: string;\n /**\n * Inject a fetch implementation. Placed here (on `CommonInput`) so tests can\n * pass a mock via the first argument without any casts. Do NOT put this on\n * `CallOptions` — it belongs on the base/donor argument only.\n */\n fetchImpl?: FetchImpl;\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\nexport interface CallOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n // fetchImpl intentionally NOT here — inject it via CommonInput instead.\n}\n\n/** Read one or more MIoT properties from a device. */\nexport async function getProperties(\n base: CommonInput,\n props: MiotProp[],\n opts: CallOptions = {},\n): Promise<PropertyResult[]> {\n const params = props.map((p) => ({ did: base.did, siid: p.siid, piid: p.piid }));\n const res = await sendCommand({ ...base, ...opts, method: 'get_properties', params });\n return extractResultArray(res, 'get_properties');\n}\n\n/**\n * Read the CLOUD-CACHED (shadow) values of one or more MIoT properties WITHOUT\n * waking the device. Hits the `dreame-user-iot/iotstatus/props` endpoint (the\n * account-auth path, NOT the `device/sendCommand` envelope), which returns the\n * cloud's last-known values for a standby/offline robot and never 80001s for an\n * idle device — exactly what the Dreamehome app shows for a sleeping robot.\n *\n * `keys` MUST be the comma-joined `\"siid.piid\"` STRING (an array yields code\n * 10001; an empty value yields 10007). Each `data[]` entry's `value` arrives as\n * a STRING; it is coerced back to number/boolean (else kept as a string) so the\n * existing typed getters (which expect numbers) decode it unchanged.\n */\nexport async function getCachedProperties(\n base: CommonInput,\n props: MiotProp[],\n opts: CallOptions = {},\n): Promise<PropertyResult[]> {\n const ctx = base.ctx ?? RequestContext.from({ ...base, host: base.apiHost });\n const keys = props.map((p) => `${p.siid}.${p.piid}`).join(',');\n const signal = opts.signal ?? base.signal;\n const timeoutMs = opts.timeoutMs ?? base.timeoutMs;\n\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: '/dreame-user-iot/iotstatus/props',\n accessToken: base.session.accessToken,\n body: { did: base.did, keys },\n context: 'cached properties',\n ...(signal !== undefined ? { signal } : {}),\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n\n const parsed = CachedPropsResponseSchema.parse(raw);\n // The HTTP layer already rejects code !== 0; this is a defensive backstop so\n // the contract (code === 0 ⇒ success) holds even if that check is bypassed.\n if (parsed.code !== undefined && parsed.code !== 0) {\n throw new DreameApiError(\n `cached properties rejected: code=${parsed.code} msg=${parsed.msg ?? '?'}`,\n 200,\n parsed,\n );\n }\n return (parsed.data ?? []).flatMap((entry) => {\n const [siidStr, piidStr] = entry.key.split('.');\n const siid = Number(siidStr);\n const piid = Number(piidStr);\n if (!Number.isFinite(siid) || !Number.isFinite(piid)) {\n return [];\n }\n const result: PropertyResult = { siid, piid, value: coerceShadowValue(entry.value) };\n if (entry.updateDate !== undefined) {\n result.updateDate = entry.updateDate;\n }\n return [result];\n });\n}\n\n/**\n * Coerce a cloud-shadow value (delivered as a string) back to the type the\n * typed getters expect: a numeric string → number, `\"true\"`/`\"false\"` →\n * boolean, anything else (incl. already-typed values) is returned unchanged.\n */\nfunction coerceShadowValue(value: unknown): unknown {\n if (typeof value !== 'string') {\n return value;\n }\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n // Number('') === 0 and Number('1,2') === NaN — only coerce a clean numeric\n // token, leaving fault-list strings like \"18,107\" intact.\n if (value.trim() !== '' && Number.isFinite(Number(value))) {\n return Number(value);\n }\n return value;\n}\n\n/** Write one or more MIoT properties to a device. */\nexport async function setProperties(\n base: CommonInput,\n writes: PropertyWrite[],\n opts: CallOptions = {},\n): Promise<PropertyResult[]> {\n const params = writes.map((p) => ({\n did: base.did,\n siid: p.siid,\n piid: p.piid,\n value: p.value,\n }));\n const res = await sendCommand({ ...base, ...opts, method: 'set_properties', params });\n return extractResultArray(res, 'set_properties');\n}\n\n/**\n * Invoke a single MIoT action. Unlike property calls, `params` is a single\n * OBJECT, not an array — an array here surfaces as a misleading code 80001.\n */\nexport async function callAction(\n base: CommonInput,\n action: MiotAction,\n opts: CallOptions = {},\n): Promise<unknown> {\n const params = {\n did: base.did,\n siid: action.siid,\n aiid: action.aiid,\n in: action.in ?? [],\n };\n const res = await sendCommand({ ...base, ...opts, method: 'action', params });\n return res.data?.result ?? res.result ?? res;\n}\n\n/**\n * Fetch batched device data (the mower's vector map: `MAP.*` / `M_PATH.*`\n * chunk keys) for a set of property groups.\n *\n * ⚠️ ENDPOINT GAP — LIVE PATH UNRECOVERED. The concrete batch-fetch endpoint\n * path is OBFUSCATED in the donor (`antondaubert/dreame-mower` builds it from\n * an encoded `_api_strings` table — entries `[23]/[26]/[44]` — that we have not\n * decoded). The donor's `cloud_base` resolves to a `/dreame-iot-com-<prefix>/…`\n * route we cannot reproduce byte-exact without that table, and we will NOT\n * invent a wrong URL. Recovering the real path is a documented follow-up; until\n * then this function will THROW if invoked against the live cloud.\n *\n * The map PARSER (`parseBatchMapData`) is the shipped deliverable and is fully\n * unit-tested with synthetic batch fixtures. `MowerDevice.getMap` consumes an\n * INJECTED {@link BatchDeviceDataFetcher} seam, so tests (and any caller that\n * already knows the path) drive it with no dependency on this stub.\n *\n * @throws DreameApiError always — the endpoint path is not yet recovered.\n */\nexport async function getBatchDeviceDatas(\n base: CommonInput,\n props: string[],\n): Promise<Record<string, unknown>> {\n // Deliberately unreachable in production until the endpoint is recovered. The\n // request is described in the error so the gap is self-documenting at runtime.\n return Promise.reject(\n new DreameApiError(\n `getBatchDeviceDatas(did=${base.did}, props=${JSON.stringify(props)}): live ` +\n 'batch-fetch endpoint path is not yet recovered (obfuscated in the donor). ' +\n 'Inject a BatchDeviceDataFetcher into MowerDevice instead, or supply the ' +\n 'resolved path once known.',\n 0,\n ),\n );\n}\n\nfunction extractResultArray(res: SendCommandResponse, context: string): PropertyResult[] {\n const raw = Array.isArray(res.data?.result)\n ? res.data.result\n : Array.isArray(res.result)\n ? res.result\n : null;\n if (raw === null) {\n throw new DreameApiError(\n `${context}: response did not contain a result array — ${JSON.stringify(res).slice(0, 200)}`,\n 200,\n res,\n );\n }\n // Validate STRUCTURE leniently (array of objects, known fields optional)\n // rather than casting unknown[] straight to PropertyResult[].\n const parsed = z.array(PropertyResultSchema).safeParse(raw);\n if (!parsed.success) {\n throw new DreameApiError(\n `${context}: result array contained a non-object element — ${JSON.stringify(raw).slice(0, 200)}`,\n 200,\n res,\n );\n }\n return parsed.data;\n}\n","import mqtt from 'mqtt';\nimport type { DreameRegion } from '../auth/config.js';\nimport type { DreameDevice, DreameSession } from '../cloud/types.js';\nimport { DreameTransportError } from './errors.js';\nimport { randomMqttClientId } from './crypto.js';\nimport { TypedEmitter } from './typed-emitter.js';\nimport { RawMqttEventSchema } from './schemas.js';\n\n/** A single property update pushed by the device. */\nexport interface PropertyChange {\n did: string;\n siid: number;\n piid: number;\n value: unknown;\n}\n\n/** MIoT event push (`method: \"event_occured\"` — yes, the typo is on the wire). */\nexport interface EventOccuredPush {\n did: string;\n siid: number;\n eiid: number;\n arguments: unknown[];\n}\n\n/** Untyped k/v push on `method: \"props\"` (OTA progress/state, etc.). */\nexport interface PropsPush {\n did: string;\n params: Record<string, unknown>;\n}\n\n/** Saved-map catalogue push on `_sync.update_vacuum_mapinfo`. */\nexport interface MapInfoPush {\n did: string;\n maps: Map<number, readonly number[]>;\n activeMapId: number | null;\n savedMapIds: readonly number[];\n}\n\n/**\n * The subset of `mqtt.MqttClient` this module uses. Declaring it lets tests\n * inject a fake broker without depending on a live TLS socket.\n */\nexport interface MqttLikeClient {\n on(event: 'connect', cb: () => void): this;\n on(event: 'close', cb: () => void): this;\n on(event: 'error', cb: (err: Error) => void): this;\n on(event: 'message', cb: (topic: string, payload: Buffer) => void): this;\n once(event: 'connect', cb: () => void): this;\n once(event: 'error', cb: (err: Error) => void): this;\n removeListener(event: string, cb: (...args: never[]) => void): this;\n subscribe(topic: string, opts: { qos: 0 | 1 | 2 }, cb: (err?: Error) => void): void;\n end(force: boolean, opts: Record<string, never>, cb: () => void): void;\n}\n\nexport interface ConnectOptions {\n username: string;\n password: string;\n clientId: string;\n protocolVersion: 4;\n reconnectPeriod: number;\n connectTimeout: number;\n rejectUnauthorized: boolean;\n clean: boolean;\n}\n\n/** Factory matching `mqtt.connect`'s shape we rely on (injectable for tests). */\nexport type MqttConnectFn = (url: string, opts: ConnectOptions) => MqttLikeClient;\n\nfunction defaultConnect(url: string, opts: ConnectOptions): MqttLikeClient {\n // mqtt.connect returns a MqttClient which structurally satisfies MqttLikeClient.\n // We verify this with a local type assertion through the interface rather than\n // a raw cast, keeping the boundary explicit.\n const client = mqtt.connect(url, opts);\n // MqttClient implements the full MqttLikeClient contract; assign through the\n // interface to surface any future structural mismatch as a compile error.\n const typed: MqttLikeClient = client;\n return typed;\n}\n\nexport type DreamePushEvents = {\n properties: [PropertyChange[]];\n event: [EventOccuredPush];\n props: [PropsPush];\n mapInfo: [MapInfoPush];\n connect: [];\n close: [];\n error: [Error];\n};\n\nexport interface DreamePushInput {\n device: DreameDevice;\n session: DreameSession;\n region: DreameRegion;\n /** Injectable mqtt.connect (defaults to the real one). */\n connect?: MqttConnectFn;\n /** Backoff before a reconnect attempt after an unexpected drop. Default 5000. */\n reconnectBackoffMs?: number;\n /**\n * Whether the TLS layer must reject a server certificate that does not chain\n * to a trusted public CA. Defaults to `false`: Dreame brokers present\n * device-specific certificates that are NOT chained to a public CA, so strict\n * verification fails against the real fleet. Override to `true` only when\n * pointing at a broker with a properly chained certificate (e.g. a test rig).\n */\n rejectUnauthorized?: boolean;\n}\n\n/**\n * Durable per-device MQTT subscription. Unlike the donor, this:\n * - reconnects with backoff after an unexpected drop, then resubscribes;\n * - on `refreshSession(newSession)` closes the old client and reopens with\n * the new access token (the broker validates the token at CONNECT only and\n * cannot be re-authed on a live client);\n * - never auto-reconnects after an explicit `close()`.\n */\nexport class DreamePush extends TypedEmitter<DreamePushEvents> {\n readonly #device: DreameDevice;\n #session: DreameSession;\n readonly #region: DreameRegion;\n readonly #connect: MqttConnectFn;\n readonly #backoffMs: number;\n readonly #rejectUnauthorized: boolean;\n readonly #topic: string;\n #client: MqttLikeClient | null = null;\n #closed = false;\n #tearingDown = false;\n #reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Per-instance bound close handler. Stored so we can pass the EXACT same\n * function reference to both `client.on('close', …)` and\n * `client.removeListener('close', …)` during teardown — a throwaway arrow\n * would be a different reference and would NOT be removed.\n */\n readonly #onClose: () => void;\n\n constructor(input: DreamePushInput) {\n super();\n this.#device = input.device;\n this.#session = input.session;\n this.#region = input.region;\n this.#connect = input.connect ?? defaultConnect;\n this.#backoffMs = input.reconnectBackoffMs ?? 5000;\n this.#rejectUnauthorized = input.rejectUnauthorized ?? false;\n this.#topic = buildStatusTopic(this.#device, this.#session.uid, this.#region);\n // Bind once so the reference is stable across register/remove calls.\n this.#onClose = (): void => {\n if (this.#tearingDown) {\n // We caused this close (teardown or refresh). Do NOT reconnect.\n return;\n }\n this.emit('close');\n this.#scheduleReconnect();\n };\n }\n\n get topic(): string {\n return this.#topic;\n }\n\n /** Open the connection and resolve when subscribed. */\n async open(): Promise<void> {\n if (this.#closed) {\n throw new DreameTransportError('subscription is closed');\n }\n if (this.#client) {\n return;\n }\n await this.#connectAndSubscribe();\n }\n\n /**\n * Swap to a freshly-refreshed session: close the current client and reopen\n * with the new access token. Safe to call while connected or disconnected.\n */\n async refreshSession(session: DreameSession): Promise<void> {\n this.#session = session;\n if (this.#closed) {\n return;\n }\n // Cancel any reconnect armed by a prior drop. Without this, a timer armed\n // before refresh fires AFTER refresh reconnects, spawning a second live\n // client and orphaning the first (mirror what close() does).\n if (this.#reconnectTimer) {\n clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = null;\n }\n await this.#teardownClient();\n await this.#connectAndSubscribe();\n }\n\n /** Tear down permanently. Closed subscriptions cannot be reopened. */\n async close(): Promise<void> {\n this.#closed = true;\n if (this.#reconnectTimer) {\n clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = null;\n }\n await this.#teardownClient();\n }\n\n async #connectAndSubscribe(): Promise<void> {\n const client = this.#connect(brokerUrl(this.#device), {\n username: this.#session.uid,\n password: this.#session.accessToken,\n clientId: randomMqttClientId(),\n protocolVersion: 4,\n reconnectPeriod: 0, // we drive reconnect ourselves\n connectTimeout: 15000,\n rejectUnauthorized: this.#rejectUnauthorized,\n clean: true,\n });\n this.#client = client;\n\n client.on('message', (_topic, payload) => this.#handleMessage(payload));\n client.on('error', (err) => this.emit('error', err));\n // Use the stored bound reference so teardown can reliably remove it.\n client.on('close', this.#onClose);\n\n await new Promise<void>((resolve, reject) => {\n const onConnect = (): void => {\n client.removeListener('error', onError);\n client.subscribe(this.#topic, { qos: 0 }, (err) => {\n if (err) {\n reject(new DreameTransportError(`mqtt subscribe failed: ${err.message}`, err));\n return;\n }\n this.emit('connect');\n resolve();\n });\n };\n const onError = (err: Error): void => {\n client.removeListener('connect', onConnect);\n reject(new DreameTransportError(`mqtt connect failed: ${err.message}`, err));\n };\n client.once('connect', onConnect);\n client.once('error', onError);\n });\n }\n\n #scheduleReconnect(): void {\n if (this.#closed || this.#reconnectTimer) {\n return;\n }\n this.#client = null;\n this.#reconnectTimer = setTimeout(() => {\n this.#reconnectTimer = null;\n // Skip if we were closed or a client was already (re)established in the\n // meantime (e.g. by refreshSession) — never spawn a duplicate connection.\n if (this.#closed || this.#client) {\n return;\n }\n void this.#connectAndSubscribe().catch((err: unknown) => {\n this.emit('error', err instanceof Error ? err : new DreameTransportError(String(err)));\n this.#scheduleReconnect();\n });\n }, this.#backoffMs);\n }\n\n async #teardownClient(): Promise<void> {\n const client = this.#client;\n this.#client = null;\n if (!client) {\n return;\n }\n // Signal to #onClose that this close event is intentional — do not reconnect.\n this.#tearingDown = true;\n // Remove the stored bound reference. This is the exact function object\n // registered in #connectAndSubscribe, so removeListener works correctly.\n client.removeListener('close', this.#onClose);\n await new Promise<void>((resolve) => {\n client.end(false, {}, () => resolve());\n });\n this.#tearingDown = false;\n }\n\n #handleMessage(payload: Buffer): void {\n let raw: unknown;\n try {\n raw = JSON.parse(payload.toString('utf8'));\n } catch (err) {\n this.emit('error', new DreameTransportError('invalid mqtt payload', err));\n return;\n }\n const result = RawMqttEventSchema.safeParse(raw);\n if (!result.success) {\n this.emit('error', new DreameTransportError('unexpected mqtt envelope'));\n return;\n }\n const envelope = result.data;\n const did = String(envelope.did ?? this.#device.did);\n const method = envelope.data?.method;\n const params = envelope.data?.params;\n\n if (method === 'properties_changed' && Array.isArray(params)) {\n const changes = parsePropertyChanges(did, params);\n if (changes.length > 0) {\n this.emit('properties', changes);\n }\n return;\n }\n if (method === 'event_occured' && isObject(params)) {\n const ev = parseEventOccured(did, params);\n if (ev) {\n this.emit('event', ev);\n }\n return;\n }\n if (method === 'props' && isObject(params)) {\n this.emit('props', { did, params });\n return;\n }\n if (method === '_sync.update_vacuum_mapinfo' && isObject(params)) {\n const mi = parseMapInfo(did, params);\n if (mi) {\n this.emit('mapInfo', mi);\n }\n return;\n }\n }\n}\n\n// --- Pure helpers (exported for unit tests) ------------------------------\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nexport function brokerUrl(device: DreameDevice): string {\n const bindDomain = (device.raw['bindDomain'] as string | undefined) ?? '';\n if (!bindDomain) {\n throw new DreameTransportError(`device ${device.did} has no bindDomain — cannot connect MQTT`);\n }\n return `mqtts://${bindDomain}`;\n}\n\nexport function buildStatusTopic(device: DreameDevice, uid: string, region: DreameRegion): string {\n // Trailing slash is mandatory — the broker matches it literally.\n return `/status/${device.did}/${uid}/${device.model}/${region}/`;\n}\n\nexport function parsePropertyChanges(fallbackDid: string, params: unknown[]): PropertyChange[] {\n const changes: PropertyChange[] = [];\n for (const p of params) {\n if (!isObject(p)) {\n continue;\n }\n const siid = p['siid'];\n const piid = p['piid'];\n if (typeof siid === 'number' && typeof piid === 'number') {\n const did = p['did'];\n changes.push({\n did: typeof did === 'string' || typeof did === 'number' ? String(did) : fallbackDid,\n siid,\n piid,\n value: p['value'],\n });\n }\n }\n return changes;\n}\n\nexport function parseEventOccured(\n fallbackDid: string,\n params: Record<string, unknown>,\n): EventOccuredPush | null {\n const siid = params['siid'];\n const eiid = params['eiid'];\n if (typeof siid !== 'number' || typeof eiid !== 'number') {\n return null;\n }\n const did = params['did'];\n const args = params['arguments'];\n return {\n did: typeof did === 'string' || typeof did === 'number' ? String(did) : fallbackDid,\n siid,\n eiid,\n arguments: Array.isArray(args) ? args : [],\n };\n}\n\nexport function parseMapInfo(\n fallbackDid: string,\n params: Record<string, unknown>,\n): MapInfoPush | null {\n const inner = params['map_info'];\n if (typeof inner !== 'string') {\n return null;\n }\n let decoded: unknown;\n try {\n decoded = JSON.parse(inner);\n } catch {\n return null;\n }\n if (!isObject(decoded)) {\n return null;\n }\n const maps = new Map<number, readonly number[]>();\n for (const [key, value] of Object.entries(decoded)) {\n const id = Number(key);\n if (!Number.isFinite(id) || !Array.isArray(value)) {\n continue;\n }\n maps.set(\n id,\n value.filter((v): v is number => typeof v === 'number'),\n );\n }\n let activeMapId: number | null = null;\n for (const [id, tok] of [...maps].sort((a, b) => a[0] - b[0])) {\n if (tok.length > 1 || (tok.length === 1 && tok[0] !== 0)) {\n activeMapId = id;\n break;\n }\n }\n const savedMapIds = Object.freeze([...maps.keys()].sort((a, b) => a - b));\n const did = params['did'];\n return {\n did: typeof did === 'string' || typeof did === 'number' ? String(did) : fallbackDid,\n maps,\n activeMapId,\n savedMapIds,\n };\n}\n","import { EventEmitter } from 'node:events';\n\n/** A strongly-typed wrapper over Node's EventEmitter. */\nexport class TypedEmitter<Events extends Record<string, unknown[]>> {\n readonly #emitter = new EventEmitter();\n\n on<K extends keyof Events & string>(event: K, listener: (...args: Events[K]) => void): this {\n this.#emitter.on(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n once<K extends keyof Events & string>(event: K, listener: (...args: Events[K]) => void): this {\n this.#emitter.once(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n off<K extends keyof Events & string>(event: K, listener: (...args: Events[K]) => void): this {\n this.#emitter.off(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n emit<K extends keyof Events & string>(event: K, ...args: Events[K]): boolean {\n return this.#emitter.emit(event, ...args);\n }\n\n removeAllListeners(): this {\n this.#emitter.removeAllListeners();\n return this;\n }\n}\n","/**\n * A read-only set of capability tokens a device model supports. Phase 2 ships\n * only the shape + a no-op default; P3/P4 populate per-model tables behind the\n * same {@link CapabilityResolver} interface.\n */\nexport interface DeviceCapabilities {\n readonly model: string;\n /** Whether the model is known to support a capability token. */\n has(token: string): boolean;\n /** All known capability tokens for the model. */\n list(): readonly string[];\n}\n\n/** Resolves the capability set for a device model string. */\nexport interface CapabilityResolver {\n resolve(model: string): DeviceCapabilities;\n}\n\n/** Immutable capability set backed by a token set. */\nclass CapabilitySet implements DeviceCapabilities {\n readonly model: string;\n readonly #tokens: ReadonlySet<string>;\n\n constructor(model: string, tokens: ReadonlySet<string>) {\n this.model = model;\n this.#tokens = tokens;\n }\n\n has(token: string): boolean {\n return this.#tokens.has(token);\n }\n\n list(): readonly string[] {\n return [...this.#tokens];\n }\n}\n\n/**\n * Default resolver: every model resolves to an EMPTY capability set. This is a\n * deliberate no-op scaffold — device-family resolvers (vacuum/mower) override\n * `resolve` in later phases while keeping this interface stable.\n */\nexport class DefaultCapabilityResolver implements CapabilityResolver {\n resolve(model: string): DeviceCapabilities {\n return new CapabilitySet(model, new Set<string>());\n }\n}\n\nconst sharedDefault = new DefaultCapabilityResolver();\n\n/** Convenience: resolve capabilities via the shared default resolver. */\nexport function resolveCapabilities(model: string): DeviceCapabilities {\n return sharedDefault.resolve(model);\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type {\n DreameDevice,\n DreameSession,\n MiotProp,\n PropertyResult,\n PropertyWrite,\n} from '../cloud/types.js';\nimport {\n callAction as defaultCallAction,\n getProperties as defaultGetProperties,\n getCachedProperties as defaultGetCachedProperties,\n setProperties as defaultSetProperties,\n type CommonInput,\n} from '../cloud/commands.js';\nimport { DreamePush, type PropertyChange } from '../transport/mqtt-push.js';\nimport { DreameTransportError } from '../transport/errors.js';\nimport { TypedEmitter } from '../transport/typed-emitter.js';\nimport { DefaultCapabilityResolver, type DeviceCapabilities } from './capability.js';\nimport type {\n DeviceEvent,\n PropertyChangedEvent,\n PropertyState,\n StateChangedEvent,\n} from '../api/types.js';\n\n/**\n * The slice of {@link DreamePush} that {@link BaseDevice} depends on. Declaring\n * it as an interface lets tests inject a fake emitter with no casts; the real\n * `DreamePush` structurally satisfies it.\n */\nexport interface PushLike {\n on(event: 'properties', cb: (changes: PropertyChange[]) => void): this;\n on(\n event: 'event',\n cb: (ev: { did: string; siid: number; eiid: number; arguments: unknown[] }) => void,\n ): this;\n on(event: 'connect', cb: () => void): this;\n on(event: 'close', cb: () => void): this;\n on(event: 'error', cb: (err: Error) => void): this;\n open(): Promise<void>;\n close(): Promise<void>;\n refreshSession(session: DreameSession): Promise<void>;\n}\n\n/** Injectable collaborators — defaults wire the real transport + command layer. */\nexport interface BaseDeviceDeps {\n createPush(device: DreameDevice, session: DreameSession, region: DreameRegion): PushLike;\n getProperties(base: CommonInput, props: MiotProp[]): Promise<PropertyResult[]>;\n /** Read the cloud-cached (shadow) values WITHOUT waking the device. */\n getCachedProperties(base: CommonInput, props: MiotProp[]): Promise<PropertyResult[]>;\n setProperties(base: CommonInput, writes: PropertyWrite[]): Promise<PropertyResult[]>;\n callAction(\n base: CommonInput,\n action: { siid: number; aiid: number; in?: unknown[] },\n ): Promise<unknown>;\n}\n\n/** Default deps using the real `DreamePush` and `src/cloud/commands.ts`. */\nexport function defaultBaseDeviceDeps(): BaseDeviceDeps {\n return {\n createPush: (device, session, region) => new DreamePush({ device, session, region }),\n getProperties: (base, props) => defaultGetProperties(base, props),\n getCachedProperties: (base, props) => defaultGetCachedProperties(base, props),\n setProperties: (base, writes) => defaultSetProperties(base, writes),\n callAction: (base, action) => defaultCallAction(base, action),\n };\n}\n\nexport type BaseDeviceEvents = {\n propertyChanged: [PropertyChangedEvent];\n stateChanged: [StateChangedEvent];\n event: [DeviceEvent];\n error: [Error];\n};\n\nexport interface BaseDeviceInput {\n device: DreameDevice;\n region: DreameRegion;\n /** Always reads the LATEST session — the facade owns the variable. */\n sessionRef: () => DreameSession;\n deps?: BaseDeviceDeps;\n /** Eager-seed the cache on `start()`. Default true. */\n fetchInitialValues?: boolean;\n /** Properties to seed when `fetchInitialValues` is true. Default `[]`. */\n initialProps?: MiotProp[];\n /** Poll interval (ms) while MQTT is down. Default 30000; 0 disables. */\n pollIntervalMs?: number;\n /** Capability set (defaults to the no-op resolver). */\n capabilities?: DeviceCapabilities;\n}\n\nconst DEFAULT_POLL_INTERVAL_MS = 30000;\n\n/**\n * A device-type-agnostic live handle for one Dreame device.\n *\n * Generic over its event map so subclasses can WIDEN the typed emitter with\n * their own events (e.g. the vacuum's `'map'` event) without a banned cast.\n * `Events` is constrained to extend {@link BaseDeviceEvents} so every internal\n * `emit(...)` call stays type-safe, and it defaults to {@link BaseDeviceEvents}\n * so a bare `BaseDevice` reference behaves exactly as before.\n */\nexport class BaseDevice<\n Events extends BaseDeviceEvents = BaseDeviceEvents,\n> extends TypedEmitter<Events> {\n readonly #device: DreameDevice;\n readonly #region: DreameRegion;\n readonly #sessionRef: () => DreameSession;\n readonly #deps: BaseDeviceDeps;\n readonly #fetchInitial: boolean;\n readonly #initialProps: MiotProp[];\n readonly #pollIntervalMs: number;\n readonly #capabilities: DeviceCapabilities;\n readonly #cache = new Map<string, PropertyState>();\n #push: PushLike | null = null;\n #mqttHealthy = false;\n #pollTimer: ReturnType<typeof setInterval> | null = null;\n #closed = false;\n\n constructor(input: BaseDeviceInput) {\n super();\n this.#device = input.device;\n this.#region = input.region;\n this.#sessionRef = input.sessionRef;\n this.#deps = input.deps ?? defaultBaseDeviceDeps();\n this.#fetchInitial = input.fetchInitialValues ?? true;\n this.#initialProps = input.initialProps ?? [];\n this.#pollIntervalMs = input.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.#capabilities =\n input.capabilities ?? new DefaultCapabilityResolver().resolve(input.device.model);\n }\n\n get deviceId(): string {\n return this.#device.did;\n }\n\n get model(): string {\n return this.#device.model;\n }\n\n get name(): string {\n return this.#device.name;\n }\n\n get capabilities(): DeviceCapabilities {\n return this.#capabilities;\n }\n\n /** Snapshot of all cached property states. */\n get properties(): readonly PropertyState[] {\n return [...this.#cache.values()];\n }\n\n /** Read a cached property, or `undefined` if never observed. */\n getProperty(siid: number, piid: number): PropertyState | undefined {\n return this.#cache.get(key(siid, piid));\n }\n\n #base(): CommonInput {\n return { session: this.#sessionRef(), region: this.#region, did: this.#device.did };\n }\n\n /** The region this handle is bound to. Subclasses use it to build map fetches. */\n protected get region(): DreameRegion {\n return this.#region;\n }\n\n /** Latest session snapshot. Subclasses use it for out-of-band fetches (maps). */\n protected currentSession(): DreameSession {\n return this.#sessionRef();\n }\n\n /** Open the push, wire events, optionally seed the cache. */\n async start(): Promise<void> {\n const push = this.#deps.createPush(this.#device, this.#sessionRef(), this.#region);\n this.#push = push;\n push.on('properties', (changes) => this.#onProperties(changes));\n push.on('event', (ev) =>\n this.emit('event', {\n deviceId: ev.did || this.#device.did,\n siid: ev.siid,\n eiid: ev.eiid,\n arguments: ev.arguments,\n }),\n );\n push.on('error', (err) => this.emit('error', err));\n push.on('connect', () => this.#onMqttUp());\n push.on('close', () => this.#onMqttDown());\n await push.open();\n this.#onMqttUp();\n if (this.#fetchInitial && this.#initialProps.length > 0) {\n await this.refreshProperties(this.#initialProps);\n }\n }\n\n /** Live-read properties, update the cache, return the raw results. */\n async refreshProperties(props: MiotProp[]): Promise<PropertyResult[]> {\n this.#assertOpen();\n const results = await this.#deps.getProperties(this.#base(), props);\n this.#seedFromResults(results);\n return results;\n }\n\n /**\n * Read the CLOUD-CACHED (shadow) values of `props` WITHOUT waking the device,\n * update the cache, and emit `propertyChanged`/`stateChanged` just like\n * {@link refreshProperties} — but sourced from the cloud shadow endpoint, so\n * it works for standby/offline robots (and never surfaces a false 80001).\n */\n async refreshCachedProperties(props: MiotProp[]): Promise<PropertyResult[]> {\n this.#assertOpen();\n const results = await this.#deps.getCachedProperties(this.#base(), props);\n this.#seedFromResults(results);\n return results;\n }\n\n /** Mirror a PropertyResult[] into the cache + emit the change events. */\n #seedFromResults(results: PropertyResult[]): void {\n const changes: PropertyChange[] = [];\n for (const r of results) {\n if (typeof r.siid === 'number' && typeof r.piid === 'number') {\n changes.push({ did: this.#device.did, siid: r.siid, piid: r.piid, value: r.value });\n }\n }\n if (changes.length > 0) {\n this.#onProperties(changes);\n }\n }\n\n /** Write a property to the device. */\n async setProperty(write: PropertyWrite): Promise<PropertyResult[]> {\n this.#assertOpen();\n return this.#deps.setProperties(this.#base(), [write]);\n }\n\n /** Invoke a MIoT action on the device. */\n async callAction(siid: number, aiid: number, input: unknown[] = []): Promise<unknown> {\n this.#assertOpen();\n return this.#deps.callAction(this.#base(), { siid, aiid, in: input });\n }\n\n /** Guard against use after {@link close}. */\n #assertOpen(): void {\n if (this.#closed) {\n throw new DreameTransportError('device handle is closed');\n }\n }\n\n /** Propagate a refreshed session to the underlying push. */\n async applySession(session: DreameSession): Promise<void> {\n if (this.#push) {\n await this.#push.refreshSession(session);\n }\n }\n\n /** Tear down: stop polling, close the push. */\n async close(): Promise<void> {\n this.#closed = true;\n this.#stopPolling();\n const push = this.#push;\n this.#push = null;\n if (push) {\n await push.close();\n }\n this.removeAllListeners();\n }\n\n #onProperties(changes: PropertyChange[]): void {\n const emitted: PropertyChangedEvent[] = [];\n const now = Date.now();\n for (const c of changes) {\n const k = key(c.siid, c.piid);\n const previous = this.#cache.get(k);\n this.#cache.set(k, { siid: c.siid, piid: c.piid, value: c.value, updatedAt: now });\n const ev: PropertyChangedEvent = {\n deviceId: this.#device.did,\n siid: c.siid,\n piid: c.piid,\n value: c.value,\n previousValue: previous ? previous.value : null,\n };\n emitted.push(ev);\n this.emit('propertyChanged', ev);\n }\n if (emitted.length > 0) {\n this.emit('stateChanged', { deviceId: this.#device.did, changes: emitted });\n }\n }\n\n #onMqttUp(): void {\n this.#mqttHealthy = true;\n this.#stopPolling();\n }\n\n #onMqttDown(): void {\n this.#mqttHealthy = false;\n this.#startPolling();\n }\n\n #startPolling(): void {\n if (this.#closed || this.#pollTimer || this.#pollIntervalMs <= 0) {\n return;\n }\n if (this.#initialProps.length === 0) {\n return;\n }\n this.#pollTimer = setInterval(() => {\n if (this.#mqttHealthy) {\n this.#stopPolling();\n return;\n }\n void this.refreshProperties(this.#initialProps).catch((err: unknown) => {\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n }, this.#pollIntervalMs);\n }\n\n #stopPolling(): void {\n if (this.#pollTimer) {\n clearInterval(this.#pollTimer);\n this.#pollTimer = null;\n }\n }\n}\n\nfunction key(siid: number, piid: number): string {\n return `${siid}.${piid}`;\n}\n","/**\n * Vacuum MIoT property + action maps (ported from malard/node-dreame\n * src/spec/{vacuum,battery,settings,consumable}-props.ts, MIT — attribution\n * retained). P3 subset only (state/commands/consumables); map/dock/schedule\n * tables land in later phases. VERIFIED/ASSUMED annotations preserved.\n */\n\n/** Vacuum service properties (siid 2 = state machine, siid 4 = task cluster). */\nexport const VACUUM_PROP = {\n /** VERIFIED r2532a — MiotState enum. */\n STATE: { siid: 2, piid: 1 } as const,\n /** VERIFIED r2532a — single-value error/fault code (see MiotError). */\n ERROR: { siid: 2, piid: 2 } as const,\n /** VERIFIED r2449a — writable CleaningMode (plain 0..3). SAFE write path. */\n CLEAN_MODE_SETTING: { siid: 2, piid: 6 } as const,\n /** VERIFIED r2532a — multi-value comma-separated fault-list mirror. */\n FAULTS_STR: { siid: 4, piid: 18 } as const,\n /** VERIFIED r2532a — Dreame \"task status\" enum (TaskStatus). */\n TASK_STATUS: { siid: 4, piid: 1 } as const,\n /** ASSUMED Tasshack types.py:573 — job runtime (minutes). */\n CLEANING_TIME: { siid: 4, piid: 2 } as const,\n /** ASSUMED Tasshack types.py:574 — area cleaned (m²). */\n CLEANED_AREA: { siid: 4, piid: 3 } as const,\n /** ASSUMED Tasshack types.py:575 — suction enum (SuctionLevel). */\n SUCTION_LEVEL: { siid: 4, piid: 4 } as const,\n /** ASSUMED Tasshack types.py:576 — water flow during cleaning (WaterVolume). */\n WATER_VOLUME: { siid: 4, piid: 5 } as const,\n /** ASSUMED Tasshack types.py:1479 — relocation state machine (0/1/10/11). */\n RELOCATION_STATUS: { siid: 4, piid: 20 } as const,\n /**\n * VERIFIED r2449a — packed (0x1400 capability mask | CleaningMode low-2-bits).\n * READ-ONLY here: do NOT write directly (drops mask → bricks next clean).\n * Use CLEAN_MODE_SETTING (siid 2 piid 6) to change clean mode.\n */\n CLEANING_MODE: { siid: 4, piid: 23 } as const,\n /** VERIFIED r2532a — Child Lock boolean. */\n CHILD_LOCK: { siid: 4, piid: 27 } as const,\n /** VERIFIED r2532a 2026-05-03 — task progress percentage 0..100. */\n TASK_PROGRESS_PCT: { siid: 4, piid: 63 } as const,\n /** VERIFIED r2532a — mop-drying progress (minutes ticking during MopDrying). */\n DRYING_PROGRESS: { siid: 4, piid: 64 } as const,\n} as const;\n\n/**\n * Vacuum-service actions. `callAction` builds the single-OBJECT params shape\n * (an array surfaces a misleading code 80001). START_CUSTOM carries the\n * segment/zone/spot payload as in-params (piid 1 = mode, piid 10 = JSON).\n */\nexport const VACUUM_ACTION = {\n /** ASSUMED Tasshack — start cleaning. */\n START: { siid: 2, aiid: 1 } as const,\n /** VERIFIED r2532a — pause (code 0 while idle). */\n PAUSE: { siid: 2, aiid: 2 } as const,\n /** ASSUMED Tasshack — return to dock / charge. */\n CHARGE: { siid: 3, aiid: 1 } as const,\n /** ASSUMED Tasshack — START_CUSTOM (segment/zone/spot; needs in payload). */\n START_CUSTOM: { siid: 4, aiid: 1 } as const,\n /** VERIFIED r2532a — stop (code 0 while idle). */\n STOP: { siid: 4, aiid: 2 } as const,\n /** VERIFIED r2532a 2026-05-02 — clear warning (code 0). */\n CLEAR_WARNING: { siid: 4, aiid: 3 } as const,\n /** ASSUMED Tasshack — start dock mop wash. */\n START_WASHING: { siid: 4, aiid: 4 } as const,\n /** VERIFIED r2532a 2026-05-02 — locate (robot beeps, code 0). */\n LOCATE: { siid: 7, aiid: 1 } as const,\n /** ASSUMED Tasshack — manual auto-empty trigger. */\n START_AUTO_EMPTY: { siid: 15, aiid: 1 } as const,\n} as const;\n\n/** Battery service (siid 3). */\nexport const BATTERY_PROP = {\n /** VERIFIED r2532a — battery percentage. */\n LEVEL: { siid: 3, piid: 1 } as const,\n /** VERIFIED r2532a — charging status (ChargingStatus). */\n CHARGING_STATUS: { siid: 3, piid: 2 } as const,\n} as const;\n\n/** Settings service — P3 needs voice volume only. */\nexport const SETTINGS_PROP = {\n /** ASSUMED Tasshack types.py:642 — voice volume 0-100. */\n VOLUME: { siid: 7, piid: 1 } as const,\n} as const;\n\n/** Consumables — % remaining (VERIFIED r2532a; note filter % is on piid 1). */\nexport const CONSUMABLE_PROP = {\n MAIN_BRUSH_LEFT: { siid: 9, piid: 2 } as const,\n SIDE_BRUSH_LEFT: { siid: 10, piid: 2 } as const,\n FILTER_LEFT: { siid: 11, piid: 1 } as const,\n} as const;\n\n/** START_CUSTOM mode ints (siid 4 piid 1 in-param). Tasshack device.py. */\nexport const CUSTOM_CLEAN_MODE = {\n SEGMENT: 18,\n ZONE: 19,\n SPOT: 20,\n} as const;\n","/**\n * Vacuum MIoT enum definitions (ported from malard/node-dreame\n * src/spec/enums.ts, MIT — attribution retained). Only the state/command\n * enums P3 needs are ported here; dock/schedule enums land in later phases.\n * Annotations (VERIFIED <date> / ASSUMED from Tasshack) are preserved.\n */\n\n/**\n * VERIFIED on r2532a 2026-05-02 — MIoT STATE (siid 2 piid 1). Sourced from\n * the device's keyDefine v8 JSON. 19 values verified live across a full\n * cleaning task; sub-mode: value 1 = vacuum-only, value 12 = vacuum+mop.\n */\nexport enum MiotState {\n Cleaning = 1,\n Standby = 2,\n Paused = 3,\n PausedAlt = 4,\n ReturningToCharge = 5,\n Charging = 6,\n Mopping = 7,\n MopDrying = 8,\n MopCleaning = 9,\n ReturningToWash = 10,\n Mapping = 11,\n CleaningAlt = 12,\n ChargingComplete = 13,\n Updating = 14,\n CallToClean = 15,\n AutoRepairBase = 16,\n ReturnInstallMop = 17,\n ReturnRemoveMop = 18,\n WaterSupplyDrainTest = 19,\n CleanMopRefillWater = 20,\n PausedCleaning = 21,\n AutoEmptying = 22,\n RemoteCleaning = 23,\n IntelligentCharging = 24,\n SecondCleaning = 25,\n Following = 26,\n PartialCleaning = 27,\n ReturnToEmpty = 28,\n WaitingForTask = 29,\n CleanWashboardBase = 30,\n // ASSUMED from Tasshack (DreameVacuumState) — codes 31-38 not yet live-verified.\n ReturningToDrain = 31,\n Draining = 32,\n AutoWaterDraining = 33,\n Emptying = 34,\n DustBagDrying = 35,\n DustBagDryingPaused = 36,\n HeadingToExtraCleaning = 37,\n ExtraCleaning = 38,\n FindingPetPaused = 95,\n FindingPet = 96,\n ShortcutRunning = 97,\n CameraMonitoring = 98,\n CameraMonitoringPaused = 99,\n InitialDeepClean = 101,\n // ASSUMED from Tasshack (DreameVacuumState) — codes 102-108.\n InitialDeepCleanPaused = 102,\n Sanitizing = 103,\n SanitizingWithDry = 104,\n ChangingMop = 105,\n ChangingMopPaused = 106,\n FloorMaintaining = 107,\n FloorMaintainingPaused = 108,\n}\n\n/** VERIFIED on r2532a 2026-05-02 — ChargingStatus (siid 3 piid 2). */\nexport enum ChargingStatus {\n Charging = 1,\n Discharging = 2,\n Returning = 5,\n}\n\n/** VERIFIED on r2532a 2026-05-02 — SuctionLevel (siid 4 piid 4). X50 labels. */\nexport enum SuctionLevel {\n Quiet = 0,\n Standard = 1,\n Intense = 2,\n Max = 3,\n}\n\n/** ASSUMED from Tasshack — WaterVolume (siid 4 piid 5). NOT YET verified on r2532a. */\nexport enum WaterVolume {\n Low = 1,\n Medium = 2,\n High = 3,\n}\n\n/**\n * VERIFIED on r2449a 2026-05-21 — plain 0..3 value space written via\n * CLEAN_MODE_SETTING (siid 2 piid 6). The raw CLEANING_MODE field (siid 4\n * piid 23) packs this in its low 2 bits OR'd with a 0x1400 capability mask;\n * prefer CLEAN_MODE_SETTING to avoid the bitfield trap.\n */\nexport enum CleaningMode {\n Sweeping = 0,\n Mopping = 1,\n SweepAndMop = 2,\n MopAfterSweep = 3,\n}\n\n/**\n * Error / fault value space for ERROR (siid 2 piid 2) and FAULTS_STR\n * (siid 4 piid 18). This is the FULL Tasshack `DreameVacuumErrorCode` table\n * (types.py) — every documented integer is mapped. Members whose names were\n * VERIFIED live on r2532a keep their verified labels even where the donor uses\n * a different label for the same integer (annotated inline); the rest carry the\n * donor's documented name. UNKNOWN (-1) is intentionally omitted — an unknown\n * code falls through the cast-free lookup and surfaces as the raw number.\n */\nexport enum MiotError {\n Clear = 0, // donor NO_ERROR\n WheelRotationAnomaly = 1, // donor DROP (VERIFIED label kept)\n Cliff = 2,\n Bumper = 3,\n Gesture = 4,\n BumperRepeat = 5,\n DropRepeat = 6,\n OpticalFlow = 7,\n Box = 8,\n TankBox = 9,\n WaterBoxEmpty = 10,\n BoxFull = 11,\n Brush = 12,\n SideBrush = 13,\n Fan = 14,\n LeftWheelMotor = 15,\n RightWheelMotor = 16,\n TurnSuffocate = 17,\n RobotLifted = 18, // donor FORWARD_SUFFOCATE (VERIFIED label kept)\n ChargerGet = 19,\n BatteryLow = 20,\n ChargeFault = 21,\n BatteryPercentageAnomaly = 22, // donor BATTERY_PERCENTAGE\n Heart = 23,\n CameraOcclusion = 24,\n Move = 25,\n FlowShielding = 26,\n InfraredShielding = 27,\n ChargeNoElectric = 28,\n BatteryFault = 29,\n FanSpeedError = 30,\n LeftWheelSpeed = 31,\n RightWheelSpeed = 32,\n Bmi055Acce = 33,\n Bmi055Gyro = 34,\n Xv7001 = 35,\n LeftMagnet = 36,\n RightMagnet = 37,\n FlowError = 38,\n InfraredFault = 39,\n CameraFault = 40,\n StrongMagnet = 41,\n WaterPump = 42,\n Rtc = 43,\n AutoKeyTrig = 44,\n P3v3 = 45,\n CameraIdle = 46,\n Blocked = 47,\n LdsError = 48,\n LdsBumper = 49,\n WaterPump2 = 50,\n FilterBlocked = 51,\n Edge = 54,\n Carpet = 55,\n Laser = 56,\n Edge2 = 57,\n Ultrasonic = 58,\n NoGoZone = 59,\n Route = 61,\n Route2 = 62,\n Blocked2 = 63,\n Blocked3 = 64,\n Restricted = 65,\n Restricted2 = 66,\n Restricted3 = 67,\n TaskComplete = 68, // donor REMOVE_MOP (VERIFIED label kept)\n MopRemoved = 69,\n MopRemoved2 = 70,\n MopPadStopRotate = 71,\n MopPadStopRotate2 = 72,\n ManualMopInstallRequired = 74, // donor MOP_INSTALL_FAILED (VERIFIED label kept)\n LowBatteryTurnOff = 75,\n DirtyTankNotInstalled = 76,\n RobotInHiddenRoom = 78,\n LdsFailedToLift = 79,\n RobotStuck = 80,\n RobotStuckRepeat = 81,\n SlipperyFloor = 82,\n UnknownError = 84,\n CheckMopInstall = 85,\n DirtyWaterTankFull = 86,\n RetractableLegStuck = 88,\n InternalError = 89,\n RobotStuck2 = 90,\n RobotStuckOnTables = 91,\n RobotStuckOnPassage = 92,\n RobotStuckOnThreshold = 93,\n RobotStuckOnLowLyingArea = 94,\n RobotStuckOnRamp = 95,\n RobotStuckOnObstacle = 96,\n RobotStuckOnPet = 97,\n RobotStuckOnSlipperySurface = 98,\n RobotStuckOnCarpet = 99,\n BinFull = 101,\n BinOpen = 102,\n BinOpen2 = 103,\n BinFull2 = 104,\n WastewaterTankFull = 105, // donor WATER_TANK (VERIFIED label kept)\n DirtyWaterTank = 106,\n CleanWaterTankEmpty = 107, // donor WATER_TANK_DRY (VERIFIED label kept)\n DirtyWaterTank2 = 108,\n DirtyWaterTankBlocked = 109,\n DirtyWaterTankPump = 110,\n MopPad = 111,\n WetMopPad = 112,\n WashboardFilterNeedsCleaning = 114, // donor CLEAN_MOP_PAD (VERIFIED label kept)\n CleanTankLevel = 116,\n StationDisconnected = 117,\n DirtyTankLevel = 118,\n WashboardLevel = 119,\n MopPadsMissing = 120, // donor NO_MOP_IN_STATION (VERIFIED label kept)\n DustBagFull = 121,\n UnknownWarning = 122,\n SelfTestFailed = 123,\n WashboardNotWorking = 124,\n DrainageFailed = 125,\n MopNotDetected = 126,\n MopHolderError = 127,\n DockError = 128,\n WashFailed = 129,\n RobotStuckOnCurtain = 200,\n EdgeMopStopRotate = 201,\n EdgeMopDetached = 202,\n ChassisLiftMalfunction = 203,\n InternalError2 = 207,\n MopCoverError = 209,\n RollerMopError = 210,\n OnboardWaterTankEmpty = 213,\n OnboardDirtyWaterTankFull = 214,\n MopNotInstalled = 215,\n RollerMopError2 = 218,\n FluffingRollerError = 222,\n MopCoverError2 = 223,\n BlockedByObstacle = 226,\n ReturnToChargeFailed = 1000,\n}\n\n/**\n * TASK_STATUS (siid 4 piid 1). Codes 1/2/3/6/12/14 carry the labels VERIFIED\n * live on r2532a 2026-05-02 — these are KEPT verbatim because they were\n * observed against real task transitions and differ from the donor's labels for\n * the same integers (donor: 1=AUTO_CLEANING, 2=ZONE_CLEANING, 3=SEGMENT_CLEANING,\n * 6=AUTO_CLEANING_PAUSED, 12=MOPPING_PAUSED, 14=ZONE_MOPPING_PAUSED). The\n * remaining members are ASSUMED from Tasshack `DreameVacuumTaskStatus`\n * (types.py) — added so documented codes resolve to a name. Unknown codes\n * surface as raw via {@link VacuumDevice.taskStatusRaw}.\n */\nexport enum TaskStatus {\n // ASSUMED from Tasshack\n Completed = 0,\n // VERIFIED live\n InterruptedOrPaused = 1,\n Active = 2,\n Transitioning = 3,\n // ASSUMED from Tasshack\n SpotCleaning = 4,\n FastMapping = 5,\n // VERIFIED live\n OnDockIdle = 6,\n // ASSUMED from Tasshack\n ZoneCleaningPaused = 7,\n SegmentCleaningPaused = 8,\n SpotCleaningPaused = 9,\n MapCleaningPaused = 10,\n DockingPaused = 11,\n // VERIFIED live\n TransientPauseEdge = 12,\n // ASSUMED from Tasshack\n SegmentMoppingPaused = 13,\n // VERIFIED live\n NeedsIntervention = 14,\n // ASSUMED from Tasshack\n AutoMoppingPaused = 15,\n AutoDockingPaused = 16,\n SegmentDockingPaused = 17,\n ZoneDockingPaused = 18,\n CruisingPath = 20,\n CruisingPathPaused = 21,\n CruisingPoint = 22,\n CruisingPointPaused = 23,\n SummonCleanPaused = 24,\n ReturningInstallMop = 25,\n ReturningRemoveMop = 26,\n StationCleaning = 27,\n PetFinding = 30,\n AutoCleaningWashingPaused = 31,\n AreaCleaningWashingPaused = 32,\n CustomCleaningWashingPaused = 33,\n}\n","/**\n * Cast-free decode primitives shared by the vacuum and mower model layers.\n * `enumLookup` was a private helper in vacuum-device.ts (P3); promoted here so\n * the mower reuses it verbatim with no banned cast. The narrower relies on\n * `members.find(...)` being `E | undefined`, so no `as` is needed to turn a raw\n * number back into an enum literal.\n */\n\n/** Coerce a raw MIoT value to a number, or null. */\nexport function asNum(value: unknown): number | null {\n return typeof value === 'number' ? value : null;\n}\n\n/**\n * Returns a narrower: a raw number (or null) maps to the matching ENUM-typed\n * member, or null when not a member. Cast-free.\n */\nexport function enumLookup<E extends number>(\n members: readonly E[],\n): (n: number | null) => E | null {\n const set = new Set<number>(members);\n return (n) => (n !== null && set.has(n) ? (members.find((m) => m === n) ?? null) : null);\n}\n","/**\n * Cast-free decode helpers for vacuum state (ported from node-dreame\n * src/vacuum/state.ts, MIT). The donor's `asEnum` used `num as T`; this\n * port replaces it with a numeric-membership check so callers can narrow\n * with no banned cast.\n */\n\n// asNum now lives in the shared module so vacuum + mower share one cast-free\n// implementation. parseFaultList / enumMembers stay vacuum-specific below.\nexport { asNum } from '../_shared/decode.js';\n\n/** Numeric members of a TS numeric enum object (ascending insertion order). */\nexport function enumMembers(enumObj: Record<string, unknown>): number[] {\n return Object.values(enumObj).filter((v): v is number => typeof v === 'number');\n}\n\n/**\n * Parse the multi-value FAULTS_STR (siid 4 piid 18) into an array of fault\n * ints. Comma-separated when multiple conditions are latched; the single\n * case is the int as a string. Empty / \"0\" / whitespace → []. A bare\n * non-zero number is accepted as a one-element list.\n */\nexport function parseFaultList(value: unknown): number[] {\n if (typeof value !== 'string') {\n if (typeof value === 'number' && value !== 0) {\n return [value];\n }\n return [];\n }\n const trimmed = value.trim();\n if (trimmed === '' || trimmed === '0') {\n return [];\n }\n const codes: number[] = [];\n for (const part of trimmed.split(',')) {\n const n = Number.parseInt(part.trim(), 10);\n if (Number.isFinite(n) && n !== 0) {\n codes.push(n);\n }\n }\n return codes;\n}\n","/**\n * Per-model vacuum capability records (ported from malard/node-dreame\n * src/capabilities.ts, MIT — attribution retained) plus a resolver that\n * adapts the rich record into the generic CapabilityResolver the BaseDevice\n * expects. r2538z is added mirroring r2532a (X50 sibling), marked unverified\n * until live-confirmed.\n */\nimport { SuctionLevel, WaterVolume } from './enums.js';\nimport type { CapabilityResolver, DeviceCapabilities } from '../../device/capability.js';\n\n/** Rich vacuum capability record. `canX` = behaviour, `hasX` = hardware. */\nexport interface VacuumCapabilities {\n model: string;\n /** true when the record came from the curated table (false = fallback / assumed). */\n verified: boolean;\n canMop: boolean;\n canAutoInstallMop: boolean;\n hasSideBrush: boolean;\n hasCamera: boolean;\n hasCarpetSensor: boolean;\n hasAiObstacleDetection: boolean;\n canAutoEmpty: boolean;\n canMopWash: boolean;\n canMopDry: boolean;\n canHeatMopWater: boolean;\n hasDetergentReservoir: boolean;\n canCleanPerRoom: boolean;\n supportsVirtualWalls: boolean;\n supportsNoGoZones: boolean;\n hasChildLock: boolean;\n supportsMultiFloor: boolean;\n /** Decodes/fetches the binary live/saved map (envelope → pixel grid → segments). */\n canMap: boolean;\n supportedSuctionLevels: readonly SuctionLevel[];\n supportedWaterVolumes: readonly WaterVolume[];\n}\n\nconst FALLBACK: Omit<VacuumCapabilities, 'model'> = {\n verified: false,\n canMop: false,\n canAutoInstallMop: false,\n hasSideBrush: true,\n hasCamera: false,\n hasCarpetSensor: false,\n hasAiObstacleDetection: false,\n canAutoEmpty: false,\n canMopWash: false,\n canMopDry: false,\n canHeatMopWater: false,\n hasDetergentReservoir: false,\n canCleanPerRoom: false,\n supportsVirtualWalls: false,\n supportsNoGoZones: false,\n hasChildLock: false,\n supportsMultiFloor: false,\n // Map decoding is model-agnostic (binary frame format is identical across\n // the lineup); the fallback record leaves it false so an unknown model does\n // not advertise a feature we have not verified its firmware exposes.\n canMap: false,\n supportedSuctionLevels: [\n SuctionLevel.Quiet,\n SuctionLevel.Standard,\n SuctionLevel.Intense,\n SuctionLevel.Max,\n ],\n supportedWaterVolumes: [WaterVolume.Low, WaterVolume.Medium, WaterVolume.High],\n};\n\n/** Full-feature X50-family record body (r2532a verified; r2538z assumed sibling). */\nconst X50_FAMILY: Omit<VacuumCapabilities, 'model' | 'verified'> = {\n canMop: true,\n canAutoInstallMop: true,\n hasSideBrush: true,\n hasCamera: true,\n hasCarpetSensor: true,\n hasAiObstacleDetection: true,\n canAutoEmpty: true,\n canMopWash: true,\n canMopDry: true,\n canHeatMopWater: true,\n hasDetergentReservoir: true,\n canCleanPerRoom: true,\n supportsVirtualWalls: true,\n supportsNoGoZones: true,\n hasChildLock: true,\n supportsMultiFloor: true,\n canMap: true,\n supportedSuctionLevels: [\n SuctionLevel.Quiet,\n SuctionLevel.Standard,\n SuctionLevel.Intense,\n SuctionLevel.Max,\n ],\n supportedWaterVolumes: [WaterVolume.Low, WaterVolume.Medium, WaterVolume.High],\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {\n Object.freeze(obj);\n for (const v of Object.values(obj)) {\n deepFreeze(v);\n }\n }\n return obj;\n}\n\nexport const MODEL_CAPABILITIES: Readonly<Record<string, VacuumCapabilities>> = {\n /** Dreame X40 Ultra Complete — VERIFIED r2449a (2026-05-21). */\n 'dreame.vacuum.r2449a': { model: 'dreame.vacuum.r2449a', verified: true, ...X50_FAMILY },\n /** Dreame X50 Ultra Complete — VERIFIED r2532a (firmware 4.3.9_2199, 2026-05-02). */\n 'dreame.vacuum.r2532a': { model: 'dreame.vacuum.r2532a', verified: true, ...X50_FAMILY },\n /**\n * Dreame r2538z — the user's device. ASSUMED from the r2532a (X50) sibling:\n * same dock-side feature set is the working hypothesis. Marked verified:false\n * until confirmed live (e2e). Flip to true + drop this note once verified.\n */\n 'dreame.vacuum.r2538z': { model: 'dreame.vacuum.r2538z', verified: false, ...X50_FAMILY },\n};\n\nfor (const entry of Object.values(MODEL_CAPABILITIES)) {\n deepFreeze(entry);\n}\n\n/** Resolve a model to its rich vacuum capability record (frozen / fallback). */\nexport function getVacuumCapabilities(model: string): VacuumCapabilities {\n const known = MODEL_CAPABILITIES[model];\n if (known) {\n return known;\n }\n return deepFreeze({ model, ...FALLBACK });\n}\n\n/** Map the rich record's booleans → the generic capability token set. */\nfunction tokensFor(c: VacuumCapabilities): Set<string> {\n const tokens = new Set<string>();\n const add = (flag: boolean, token: string): void => {\n if (flag) {\n tokens.add(token);\n }\n };\n add(c.canMop, 'mop');\n add(c.canAutoInstallMop, 'auto-install-mop');\n add(c.hasSideBrush, 'side-brush');\n add(c.hasCamera, 'camera');\n add(c.hasCarpetSensor, 'carpet-sensor');\n add(c.hasAiObstacleDetection, 'ai-obstacle');\n add(c.canAutoEmpty, 'auto-empty');\n add(c.canMopWash, 'mop-wash');\n add(c.canMopDry, 'mop-dry');\n add(c.canHeatMopWater, 'heat-mop-water');\n add(c.hasDetergentReservoir, 'detergent');\n add(c.canCleanPerRoom, 'clean-per-room');\n add(c.supportsVirtualWalls, 'virtual-walls');\n add(c.supportsNoGoZones, 'no-go-zones');\n add(c.hasChildLock, 'child-lock');\n add(c.supportsMultiFloor, 'multi-floor');\n return tokens;\n}\n\nclass VacuumCapabilitySet implements DeviceCapabilities {\n readonly model: string;\n readonly #tokens: ReadonlySet<string>;\n constructor(model: string, tokens: ReadonlySet<string>) {\n this.model = model;\n this.#tokens = tokens;\n }\n has(token: string): boolean {\n return this.#tokens.has(token);\n }\n list(): readonly string[] {\n return [...this.#tokens];\n }\n}\n\n/** Resolver that the BaseDevice accepts via BaseDeviceInput.capabilities. */\nexport class VacuumCapabilityResolver implements CapabilityResolver {\n resolve(model: string): DeviceCapabilities {\n return new VacuumCapabilitySet(model, tokensFor(getVacuumCapabilities(model)));\n }\n}\n","/**\n * Outer-envelope handling for Dreame map blobs.\n *\n * Wire format: URL-safe base64 → optional AES-256-CBC decrypt → zlib\n * inflate → 27-byte binary header + width*height pixel grid + UTF-8\n * JSON tail. This module covers the first three steps; header / tail\n * parsing live in `header.ts` / `tail.ts`.\n */\n\nimport { createDecipheriv, createHash } from 'node:crypto';\nimport * as zlib from 'node:zlib';\nimport type { VacuumMapDecodeOptions } from './types.js';\n\n/** Bytes 0-26 are the fixed-layout binary header. */\nexport const HEADER_SIZE = 27;\n\n/** Sentinel value in `robot.a` / `charger.a` meaning \"absent\". */\nexport const ANGLE_ABSENT = 0x7fff;\n\n/** Frame-type byte values from the header. */\nexport const FRAME_TYPE = {\n I: 73,\n P: 80,\n W: 87,\n} as const;\n\nexport class MapDecodeError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'MapDecodeError';\n }\n}\n\n/**\n * Unwrap a raw MAP_DATA envelope value: URL-safe base64 → optional\n * AES-256-CBC decrypt → zlib inflate. The returned buffer starts with\n * the 27-byte header.\n *\n * If the value contains a comma, everything after the first comma is\n * treated as the per-blob AES key and AES is forced on (matching the\n * outer-envelope convention from Tasshack `map.py:3759-3792`). If `opts.key`\n * and `opts.iv` are also supplied, the embedded key wins.\n */\nexport function unwrapEnvelope(value: string, opts: VacuumMapDecodeOptions = {}): Buffer {\n const commaIdx = value.indexOf(',');\n const b64 = commaIdx >= 0 ? value.slice(0, commaIdx) : value;\n const embeddedKey = commaIdx >= 0 ? value.slice(commaIdx + 1) : null;\n\n const standard = b64.replace(/-/g, '+').replace(/_/g, '/');\n let bytes: Buffer;\n try {\n bytes = Buffer.from(standard, 'base64');\n } catch (err) {\n throw new MapDecodeError('envelope: base64 decode failed', { cause: err });\n }\n if (bytes.length === 0) {\n throw new MapDecodeError('envelope: empty payload after base64 decode');\n }\n\n const key = embeddedKey ?? opts.key ?? null;\n if (key) {\n if (!opts.iv) {\n throw new MapDecodeError(\n 'envelope: AES key supplied but no IV — pass `opts.iv` (16-byte ASCII)',\n );\n }\n bytes = aesCbcDecrypt(bytes, key, opts.iv);\n }\n\n try {\n return zlib.inflateSync(bytes);\n } catch (err) {\n throw new MapDecodeError('envelope: zlib inflate failed', { cause: err });\n }\n}\n\nfunction aesCbcDecrypt(cipher: Buffer, rawKey: string, iv: string): Buffer {\n const keyBytes = Buffer.from(\n createHash('sha256').update(rawKey).digest('hex').slice(0, 32),\n 'utf8',\n );\n const ivBytes = Buffer.from(iv, 'utf8');\n if (ivBytes.length !== 16) {\n throw new MapDecodeError(`envelope: AES IV must be 16 ASCII bytes, got ${ivBytes.length}`);\n }\n try {\n const decipher = createDecipheriv('aes-256-cbc', keyBytes, ivBytes);\n decipher.setAutoPadding(false);\n return Buffer.concat([decipher.update(cipher), decipher.final()]);\n } catch (err) {\n throw new MapDecodeError('envelope: AES decrypt failed', { cause: err });\n }\n}\n","/**\n * 27-byte binary header at the start of every inflated map frame.\n * Layout per `docs/live-map-format.md` §\"27-byte header\".\n */\n\nimport type { MapFrameType } from './types.js';\nimport { FRAME_TYPE, HEADER_SIZE, MapDecodeError } from './envelope.js';\n\nexport interface MapHeader {\n mapId: number;\n frameId: number;\n frameType: MapFrameType;\n robotX: number;\n robotY: number;\n robotA: number;\n chargerX: number;\n chargerY: number;\n chargerA: number;\n gridSize: number;\n width: number;\n height: number;\n left: number;\n top: number;\n}\n\nexport function parseMapHeader(buf: Buffer): MapHeader {\n if (buf.length < HEADER_SIZE) {\n throw new MapDecodeError(`header: need ${HEADER_SIZE} bytes, got ${buf.length}`);\n }\n return {\n mapId: buf.readInt16LE(0),\n frameId: buf.readInt16LE(2),\n frameType: frameTypeFromByte(buf[4]!),\n robotX: buf.readInt16LE(5),\n robotY: buf.readInt16LE(7),\n robotA: buf.readInt16LE(9),\n chargerX: buf.readInt16LE(11),\n chargerY: buf.readInt16LE(13),\n chargerA: buf.readInt16LE(15),\n gridSize: buf.readInt16LE(17),\n width: buf.readInt16LE(19),\n height: buf.readInt16LE(21),\n left: buf.readInt16LE(23),\n top: buf.readInt16LE(25),\n };\n}\n\nfunction frameTypeFromByte(b: number): MapFrameType {\n switch (b) {\n case FRAME_TYPE.I:\n return 'I';\n case FRAME_TYPE.P:\n return 'P';\n case FRAME_TYPE.W:\n return 'W';\n default:\n throw new MapDecodeError(`header: unknown frame_type byte ${b}`);\n }\n}\n","/**\n * JSON tail extraction from an inflated map frame buffer.\n *\n * The tail is UTF-8 JSON immediately after the header + pixel grid;\n * `MapTail` (`./types.ts`) describes the subset of keys we consume.\n *\n * `parseFrame` is the composed seam used by both `decodeVacuumMap`\n * and the rism-recurse path inside it — keeping the\n * \"header → tail\" sequence in one place ensures the two call sites\n * can't drift independently.\n */\n\nimport { HEADER_SIZE, MapDecodeError } from './envelope.js';\nimport { parseMapHeader, type MapHeader } from './header.js';\nimport type { MapTail } from './types.js';\n\nexport function sliceTailText(inflated: Buffer, header: MapHeader): string {\n const start = HEADER_SIZE + header.width * header.height;\n if (inflated.length < start) {\n throw new MapDecodeError(\n `tail: inflated payload shorter than header+pixels (${inflated.length} < ${start})`,\n );\n }\n return inflated.subarray(start).toString('utf8');\n}\n\n/**\n * Narrow a parsed JSON value to the loose `MapTail` shape without a\n * cast: `MapTail` is an all-optional interface with a\n * `[key: string]: unknown` index signature, so any plain non-null\n * object satisfies it once we've confirmed it is a record. A non-object\n * JSON top-level (number, string, array, null) is rejected.\n */\nfunction asMapTail(value: unknown): MapTail {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new MapDecodeError('tail: JSON top-level is not an object');\n }\n const record: Record<string, unknown> = { ...value };\n return record;\n}\n\nexport function parseMapJsonTail(text: string): MapTail {\n if (!text) {\n return {};\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n throw new MapDecodeError('tail: JSON parse failed', { cause: err });\n }\n return asMapTail(parsed);\n}\n\n/**\n * Parse the header + JSON tail from an already-inflated frame buffer.\n *\n * Combines `parseMapHeader` + `sliceTailText` + `parseMapJsonTail`,\n * which the decoder, the rism-recurse path, and (deliberately not)\n * `merge.ts` invoke as a unit. Centralising the sequence keeps the\n * call sites in lockstep — the alternative is to drift independently\n * if one is updated without the others.\n *\n * Pure; doesn't unwrap base64 — call `unwrapEnvelope` first.\n */\nexport function parseFrame(inflated: Buffer): { header: MapHeader; tail: MapTail } {\n const header = parseMapHeader(inflated);\n const tail = parseMapJsonTail(sliceTailText(inflated, header));\n return { header, tail };\n}\n","/**\n * Pixel-grid decoder for fsm:1 (path B / frame-map mode) — the layout\n * r2532a uses. Walks the grid row-by-row and emits run-length\n * `MapLayer` entries the renderer can blit straight onto canvas\n * without tracking individual pixels.\n *\n * The fsm:0 (variant A) layout is documented in the roadmap but isn't\n * exercised by any captured fixture; not implemented.\n */\n\nimport type { MapDimensions, MapLayer, MapRun, MapSegment, MapTail } from './types.js';\n\n/**\n * Pixel-byte classifications for path B (`fsm: 1`).\n *\n * Layout (per roadmap):\n * bits 7-2 = segment_id (top 6 bits, range 0..63)\n * bits 1-0 = meta (low 2 bits)\n *\n * Special segment ids: 63 = WALL, 62 = FLOOR, 61 = UNKNOWN.\n * When the top 6 bits are zero, the low 2 bits encode markers\n * (01 = NEW_SEGMENT, 10 = WALL).\n * The `11` low-bit meta is a CARPET overlay independent of segment id.\n *\n * v1 layer types collapse this down to `wall | floor | segment`.\n * Carpet overlay is dropped for now — extending the v1 contract is a\n * separate decision (see types.ts MapLayerType).\n */\nexport type PixelClass = 'wall' | 'floor' | 'segment' | 'outside';\n\n/**\n * Classify a single pixel byte under fsm:1 (path B) layout.\n *\n * `kind` is the primary mutually-exclusive class. `carpet` is an\n * independent overlay flag (low bits == 11): it can co-occur with any\n * primary class — a carpet floor, a carpet pixel inside a segment, etc.\n * The wall marker (low bits == 10 with high bits zero) and the carpet\n * marker share the low-bits field, so a pixel cannot be both \"wall\n * marker\" and \"carpet\" at the same time.\n */\nexport function classifyPixelFsm1(byte: number): {\n kind: PixelClass;\n segmentId?: number;\n carpet?: true;\n} {\n if (byte === 0) {\n return { kind: 'outside' };\n }\n const seg = byte >> 2;\n const meta = byte & 3;\n const isCarpet = meta === 3;\n\n if (seg === 63) {\n return isCarpet ? { kind: 'wall', carpet: true } : { kind: 'wall' };\n }\n if (seg === 62) {\n return isCarpet ? { kind: 'floor', carpet: true } : { kind: 'floor' };\n }\n if (seg === 61) {\n return { kind: 'outside' };\n }\n if (seg === 0) {\n if (meta === 2) {\n return { kind: 'wall' };\n }\n if (isCarpet) {\n return { kind: 'outside', carpet: true };\n }\n return { kind: 'outside' };\n }\n return isCarpet\n ? { kind: 'segment', segmentId: seg, carpet: true }\n : { kind: 'segment', segmentId: seg };\n}\n\n/**\n * Walk the pixel grid row-by-row and emit run-length layers. Each row\n * resets the run state; runs never cross row boundaries (so the renderer\n * doesn't need to know the width to interpret them).\n *\n * Two kinds of run accumulation happen in parallel:\n * - the primary classification (wall / floor / segment) — one run per\n * contiguous same-class span\n * - the carpet overlay — one run per contiguous carpet span, regardless\n * of underlying primary class (a carpet stripe spanning the boundary\n * between segment 5 and segment 7 produces one carpet run)\n */\nexport function decodePixelGridFsm1(pixels: Buffer, width: number, height: number): MapLayer[] {\n const wallRuns: MapRun[] = [];\n const floorRuns: MapRun[] = [];\n const segmentRuns = new Map<number, MapRun[]>();\n const carpetRuns: MapRun[] = [];\n\n for (let y = 0; y < height; y++) {\n const rowBase = y * width;\n let runStart = -1;\n let runKind: PixelClass = 'outside';\n let runSegmentId = 0;\n let carpetStart = -1;\n for (let x = 0; x <= width; x++) {\n const byte = x < width ? pixels[rowBase + x]! : 0;\n const c =\n x < width ? classifyPixelFsm1(byte) : { kind: 'outside' as const, carpet: undefined };\n const sameAsRun =\n runStart >= 0 &&\n c.kind === runKind &&\n (c.kind !== 'segment' || c.segmentId === runSegmentId);\n if (!sameAsRun) {\n // Emit the previous primary run (if any).\n if (runStart >= 0 && runKind !== 'outside') {\n const length = x - runStart;\n const run: MapRun = [runStart, y, length];\n if (runKind === 'wall') {\n wallRuns.push(run);\n } else if (runKind === 'floor') {\n floorRuns.push(run);\n } else if (runKind === 'segment') {\n let bucket = segmentRuns.get(runSegmentId);\n if (!bucket) {\n bucket = [];\n segmentRuns.set(runSegmentId, bucket);\n }\n bucket.push(run);\n }\n }\n // Start a new primary run.\n if (x < width && c.kind !== 'outside') {\n runStart = x;\n runKind = c.kind;\n runSegmentId = c.kind === 'segment' ? c.segmentId! : 0;\n } else {\n runStart = -1;\n }\n }\n\n // Carpet overlay accumulates independently of primary classification.\n const isCarpet = c.carpet === true;\n if (isCarpet && carpetStart < 0) {\n carpetStart = x;\n } else if (!isCarpet && carpetStart >= 0) {\n carpetRuns.push([carpetStart, y, x - carpetStart]);\n carpetStart = -1;\n }\n }\n }\n\n const layers: MapLayer[] = [];\n if (wallRuns.length > 0) {\n layers.push({ type: 'wall', runs: wallRuns });\n }\n if (floorRuns.length > 0) {\n layers.push({ type: 'floor', runs: floorRuns });\n }\n for (const [id, runs] of [...segmentRuns.entries()].sort(([a], [b]) => a - b)) {\n layers.push({ type: 'segment', segmentId: id, runs });\n }\n if (carpetRuns.length > 0) {\n layers.push({ type: 'carpet', runs: carpetRuns });\n }\n return layers;\n}\n\n/**\n * Project decoded pixel-grid layers into per-segment metadata: bbox\n * and centroid in mm world-frame, plus the optional `seg_inf.<id>`\n * fields (name, neighbours, material, direction). `tail.sa` flags\n * which segments are in the device's current cleaning set.\n */\nexport function collectSegments(\n layers: MapLayer[],\n dim: MapDimensions,\n tail: MapTail,\n): MapSegment[] {\n const activeIds = new Set<number>();\n for (const entry of tail.sa ?? []) {\n if (Array.isArray(entry) && typeof entry[0] === 'number') {\n activeIds.add(entry[0]);\n }\n }\n\n const segs: MapSegment[] = [];\n for (const layer of layers) {\n if (layer.type !== 'segment' || layer.segmentId === undefined) {\n continue;\n }\n const id = layer.segmentId;\n let xMinPx = Infinity;\n let yMinPx = Infinity;\n let xMaxPx = -Infinity;\n let yMaxPx = -Infinity;\n let sumXPx = 0;\n let sumYPx = 0;\n let count = 0;\n for (const [x, y, len] of layer.runs) {\n if (x < xMinPx) {\n xMinPx = x;\n }\n if (y < yMinPx) {\n yMinPx = y;\n }\n if (x + len - 1 > xMaxPx) {\n xMaxPx = x + len - 1;\n }\n if (y > yMaxPx) {\n yMaxPx = y;\n }\n // centroid sum: for a run of `len` pixels starting at `x`, the\n // x-sum is len*x + (0+1+...+len-1) = len*x + len*(len-1)/2\n sumXPx += len * x + (len * (len - 1)) / 2;\n sumYPx += len * y;\n count += len;\n }\n if (count === 0) {\n continue;\n }\n\n const meta = tail.seg_inf?.[String(id)] ?? null;\n const bbox = {\n xMin: dim.left + xMinPx * dim.gridSize,\n yMin: dim.top + yMinPx * dim.gridSize,\n xMax: dim.left + (xMaxPx + 1) * dim.gridSize,\n yMax: dim.top + (yMaxPx + 1) * dim.gridSize,\n };\n const centroid = {\n x: dim.left + (sumXPx / count) * dim.gridSize,\n y: dim.top + (sumYPx / count) * dim.gridSize,\n };\n\n segs.push({\n id,\n name: meta?.name ? safeBase64ToUtf8(meta.name) : null,\n bbox,\n centroid,\n neighbours: meta?.nei_id ?? [],\n floorMaterial: meta?.material ?? null,\n floorDirection: meta?.direction ?? null,\n active: activeIds.has(id),\n });\n }\n return segs;\n}\n\nfunction safeBase64ToUtf8(s: string): string | null {\n try {\n return Buffer.from(s, 'base64').toString('utf8');\n } catch {\n return null;\n }\n}\n","/**\n * Cleaning-path string parser. Wire format: a flat sequence of\n * `<op-letter><x>,<y>` ops in the JSON tail's `tr` field. Op letters\n * are M (mop), W (sweep+mop), S (sweep), L (line moveTo); lowercase\n * `l` is a P-frame line continuation, treated as L after the merge\n * layer concatenates prev+P trs.\n */\n\nimport type { MapPath, MapPathType } from './types.js';\n\nconst PATH_TYPE_FROM_OP: Record<string, MapPathType> = {\n M: 'mop',\n W: 'sweep-and-mop',\n S: 'sweep',\n L: 'line',\n};\n\nconst PATH_OP_REGEX = /([MWSLl])(-?\\d+),(-?\\d+)/g;\n\n/**\n * Parse Dreame's compact path string into typed segments.\n *\n * Each op letter starts a new segment:\n * M = mop, W = sweep+mop, S = sweep, L = line moveTo,\n * l = P-frame line continuation (treated as L per Tasshack map.py:3987).\n *\n * Coordinates are millimetres world-frame for `mop` / `sweep` /\n * `sweep-and-mop` waypoints. **Line ops are RELATIVE deltas** to the\n * preceding absolute waypoint (or the previous point within the same\n * line). The accumulator unwinds them so every surfaced point is\n * absolute world-frame mm — verified live 2026-05-07 against r2532a\n * (without unwinding, 1000s of `line` deltas all cluster around the\n * world origin and render as a tight artifact).\n *\n * When a `line` op appears with no preceding absolute waypoint\n * (e.g. when `tr` starts with `L`/`l` and there's no anchor to\n * accumulate against — rare but possible at the very start of a\n * fresh subscription), the points are emitted literally. Callers\n * downstream of the merge layer don't see this path because P-frame\n * `tr` concatenation always prepends the prior absolute waypoint.\n */\nexport function parsePathTr(tr: string): MapPath[] {\n if (!tr) {\n return [];\n }\n // Locally mutable so we can push as we walk the regex matches; widened\n // to the public `readonly`-decorated MapPath shape on return.\n type MutablePath = { type: MapPathType; points: { x: number; y: number }[] };\n const out: MutablePath[] = [];\n let current: MutablePath | null = null;\n let anchor: { x: number; y: number } | null = null;\n for (const m of tr.matchAll(PATH_OP_REGEX)) {\n const opRaw = m[1]!;\n const xRaw = Number(m[2]);\n const yRaw = Number(m[3]);\n const op = opRaw === 'l' ? 'L' : opRaw;\n const type = PATH_TYPE_FROM_OP[op];\n if (!type) {\n continue;\n }\n if (type === 'line' && anchor !== null) {\n // Accumulate the relative delta against the running anchor.\n const abs: { x: number; y: number } = {\n x: anchor.x + xRaw,\n y: anchor.y + yRaw,\n };\n if (!current || current.type !== type) {\n // New line segment: seed with the anchor itself so the line\n // draws a continuous trace from the preceding waypoint.\n current = { type, points: [{ x: anchor.x, y: anchor.y }, abs] };\n out.push(current);\n } else {\n current.points.push(abs);\n }\n anchor = abs;\n } else {\n // Either an absolute waypoint (S / W / M) or a `line` with no\n // anchor yet — emit literally.\n const pt = { x: xRaw, y: yRaw };\n if (!current || current.type !== type) {\n current = { type, points: [pt] };\n out.push(current);\n } else {\n current.points.push(pt);\n }\n if (type !== 'line') {\n // Absolute waypoint — update the anchor for any following\n // `line` op to accumulate against.\n anchor = pt;\n }\n }\n }\n return out;\n}\n","/**\n * Wire-value coercion helpers used by the map decoder modules.\n *\n * Dreame's tail JSON ships some numeric fields as strings (notably\n * `ai_obstacle` records' position fields and the rect/line corner\n * arrays) and some as numbers. Both forms decode to the same world-\n * frame mm value; these helpers normalise.\n *\n * Both return `null` on non-numeric / non-finite input — callers\n * decide whether to skip the entry or substitute a default.\n */\n\nexport function parseFloatField(v: unknown): number | null {\n if (typeof v === 'number') {\n return Number.isFinite(v) ? v : null;\n }\n if (typeof v === 'string') {\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n\nexport function parseIntField(v: unknown): number | null {\n const n = parseFloatField(v);\n return n === null ? null : Math.trunc(n);\n}\n","/**\n * AI-detected obstacles parser. Wire format: the JSON tail's\n * `ai_obstacle` field is an array of positional records (~14 fields\n * each on r2532a). Field layout per `docs/live-map-format.md`.\n */\n\nimport type { MapObstacle } from './types.js';\nimport { parseFloatField, parseIntField } from './field-utils.js';\n\n/**\n * Decode one `ai_obstacle` entry. Field layout observed on r2532a\n * (2026-05-02 fixtures), 14 positional string fields:\n * [0] x mm [1] y mm [2] type id\n * [3] confidence 0-1 [4] timestamp.usec [5] photo file path\n * [6] photo id [7..10] bbox-like [11..13] unknown small ints\n *\n * We use field 6 as `id` (numeric, fits comfortably in a JS number) and\n * field 4 (timestamp) is preserved via the schema's existing fields.\n * `photoKey` is left null — no AES key has been observed at this layer\n * on the live channel; it likely arrives via a separate cloud lookup.\n */\nexport function parseObstacles(raw: unknown[]): MapObstacle[] {\n const out: MapObstacle[] = [];\n for (const entry of raw) {\n if (!Array.isArray(entry) || entry.length < 6) {\n continue;\n }\n const x = parseFloatField(entry[0]);\n const y = parseFloatField(entry[1]);\n const type = parseIntField(entry[2]);\n const confRaw = parseFloatField(entry[3]);\n if (x === null || y === null || type === null) {\n continue;\n }\n const photoFileName = typeof entry[5] === 'string' ? entry[5] : null;\n const idCandidate = parseIntField(entry[6]);\n const id = idCandidate ?? Math.round((parseFloatField(entry[4]) ?? 0) * 1e6);\n out.push({\n id,\n x,\n y,\n type,\n confidence: confRaw === null ? 0 : Math.round(confRaw * 100),\n photoFileName,\n photoKey: null,\n });\n }\n return out;\n}\n","/**\n * User-defined map geometry — the `vw`, `vws`, `walls_info`,\n * `sneak_areas` / `sneak_areas_end` blocks of the JSON tail.\n *\n * On r2532a fw 4.3.9_2199 (verified 2026-05-07) the live I-frame's\n * top-level tail does NOT carry these blocks; they live inside the\n * embedded saved-map blob (`tail.rism`). `decodeVacuumMap` recurses\n * into rism and merges the inner geometry onto the outer `VacuumMap`,\n * so consumers see \"all geometry for this floor\" regardless of which\n * tail physically carried it.\n */\n\nimport type {\n MapLowLyingArea,\n MapRestrictedArea,\n MapRoom,\n MapRoomWall,\n MapStorey,\n MapTail,\n MapVirtualWall,\n MapWallsInfo,\n} from './types.js';\nimport { parseFloatField } from './field-utils.js';\n\n/**\n * Parse the `vw` (and optional `vws`) blocks — Dreame's user-defined\n * geometry. Tasshack `dev` `map.py:4597-4702` is the canonical\n * reference; we mirror its shape interpretations:\n *\n * `vw` (classic block):\n * - `vw.line`: `[x0,y0,x1,y1]` line segments — virtual walls.\n * - `vw.rect`: `[x0,y0,x1,y1, angle?]` axis-aligned no-go\n * rectangles. Corners get sorted so the bbox is well-formed\n * regardless of wire order.\n * - `vw.mop`: same shape as `vw.rect`, no-mop zones.\n * - `vw.nocpt`: `[x0,y0,x1,y1]` \"do not cross\" no-go rectangles —\n * verified live 2026-05-07 on r2532a as additional no-go zones\n * the user marked in the app. (Note: NOT carpets despite the\n * name; Tasshack `map.py:4668` reads them as no-go rects.)\n *\n * `vws` (X50 threshold block — only present when the user has\n * configured thresholds; absent on older firmware):\n * - `vws.vwsl`: `[x0,y0,x1,y1]` lines. When `vws.npthrsd` is\n * present in the SAME `vws` object, these are *passable*\n * thresholds (`kind: \"threshold\", passable: true`). When\n * `npthrsd` is absent, they're \"virtual\" thresholds\n * (`kind: \"threshold\"` with no `passable` hint).\n * - `vws.npthrsd`: `[x0,y0,x1,y1]` lines — *impassable* thresholds\n * (`kind: \"threshold\", passable: false`). Verified live\n * 2026-05-07 on r2532a fw 4.3.9_2199.\n *\n * `vw.cliff` and `vws.cliff` (line segments) and `vws.ramp` (areas)\n * have been observed empty on r2532a; they're not surfaced here\n * until a fixture exists for the populated form. `vw.addcpt` /\n * `vws.rec_*` recommendation mirrors are also out of scope for now.\n *\n * Returns empty arrays when both blocks are absent — there's no\n * meaningful difference between \"no walls configured\" and \"this\n * frame doesn't carry the field\" at the public-API layer, and the\n * merge layer + rism recurse handle the latter via fallback.\n */\nexport function parseVirtualWalls(\n vw:\n | {\n line?: number[][];\n rect?: number[][];\n mop?: number[][];\n nocpt?: number[][];\n }\n | undefined,\n vws?: { vwsl?: number[][]; npthrsd?: number[][] },\n): { virtualWalls: MapVirtualWall[]; restrictedAreas: MapRestrictedArea[] } {\n const virtualWalls: MapVirtualWall[] = [];\n const restrictedAreas: MapRestrictedArea[] = [];\n\n if (vw) {\n for (const line of vw.line ?? []) {\n const wall = parseLine(line, 'wall');\n if (wall) {\n virtualWalls.push(wall);\n }\n }\n for (const rect of vw.rect ?? []) {\n const area = parseRestrictedArea('noGo', rect);\n if (area) {\n restrictedAreas.push(area);\n }\n }\n for (const rect of vw.mop ?? []) {\n const area = parseRestrictedArea('noMop', rect);\n if (area) {\n restrictedAreas.push(area);\n }\n }\n for (const rect of vw.nocpt ?? []) {\n const area = parseRestrictedArea('noGo', rect);\n if (area) {\n restrictedAreas.push(area);\n }\n }\n }\n\n if (vws) {\n const npthrsdPresent = Array.isArray(vws.npthrsd) && vws.npthrsd.length > 0;\n for (const line of vws.vwsl ?? []) {\n // vwsl semantics flip on the presence of npthrsd in the same block.\n const wall = parseLine(line, 'threshold');\n if (wall) {\n if (npthrsdPresent) {\n wall.passable = true;\n }\n // else: leave `passable` absent — these are \"virtual\" thresholds\n // from older firmware that doesn't split the two.\n virtualWalls.push(wall);\n }\n }\n for (const line of vws.npthrsd ?? []) {\n const wall = parseLine(line, 'threshold');\n if (wall) {\n wall.passable = false;\n virtualWalls.push(wall);\n }\n }\n }\n\n return { virtualWalls, restrictedAreas };\n}\n\nfunction parseLine(raw: unknown, kind: 'wall' | 'threshold'): MapVirtualWall | null {\n if (!Array.isArray(raw) || raw.length < 4) {\n return null;\n }\n const x0 = parseFloatField(raw[0]);\n const y0 = parseFloatField(raw[1]);\n const x1 = parseFloatField(raw[2]);\n const y1 = parseFloatField(raw[3]);\n if (x0 === null || y0 === null || x1 === null || y1 === null) {\n return null;\n }\n // `kind` is omitted from the emitted object when it would be the\n // default (\"wall\"), matching the historical wire-empty case so\n // identity comparisons in existing tests don't break unnecessarily.\n if (kind === 'wall') {\n return { from: { x: x0, y: y0 }, to: { x: x1, y: y1 } };\n }\n return { from: { x: x0, y: y0 }, to: { x: x1, y: y1 }, kind };\n}\n\nfunction parseRestrictedArea(kind: 'noGo' | 'noMop', raw: unknown): MapRestrictedArea | null {\n if (!Array.isArray(raw) || raw.length < 4) {\n return null;\n }\n const a = parseFloatField(raw[0]);\n const b = parseFloatField(raw[1]);\n const c = parseFloatField(raw[2]);\n const d = parseFloatField(raw[3]);\n if (a === null || b === null || c === null || d === null) {\n return null;\n }\n const xMin = Math.min(a, c);\n const xMax = Math.max(a, c);\n const yMin = Math.min(b, d);\n const yMax = Math.max(b, d);\n const area: MapRestrictedArea = {\n kind,\n bbox: { xMin, yMin, xMax, yMax },\n };\n const e = raw.length > 4 ? parseFloatField(raw[4]) : null;\n if (e !== null) {\n area.angle = e;\n }\n return area;\n}\n\n/**\n * Parse the saved-map's per-room wall geometry. Wire shape (verified\n * 2026-05-07 against r2532a fw 4.3.9_2199):\n *\n * ```\n * {\n * version_flag: 3,\n * storeys: [{\n * rooms: [{\n * room_id: 10,\n * walls: [{\n * type: 0, // 0 = solid wall, 1 = opening (observed)\n * beg_pt_x: -8225,\n * beg_pt_y: 9275,\n * end_pt_x: -9025,\n * end_pt_y: 9275,\n * normal_x: 0, // unit-vector pointing into the room\n * normal_y: -1\n * }, …]\n * }, …]\n * }, …]\n * }\n * ```\n *\n * Returns `null` if the wire object is missing or has no storeys —\n * the public field on `VacuumMap` is null in that case rather than an\n * empty `MapWallsInfo`.\n */\nexport function parseWallsInfo(\n raw: NonNullable<MapTail['walls_info']> | undefined,\n): MapWallsInfo | null {\n if (!raw || !Array.isArray(raw.storeys) || raw.storeys.length === 0) {\n return null;\n }\n const storeys: MapStorey[] = [];\n for (const s of raw.storeys) {\n if (!s || !Array.isArray(s.rooms)) {\n continue;\n }\n const rooms: MapRoom[] = [];\n for (const r of s.rooms) {\n if (!r || typeof r.room_id !== 'number' || !Array.isArray(r.walls)) {\n continue;\n }\n const walls: MapRoomWall[] = [];\n for (const w of r.walls) {\n if (\n !w ||\n typeof w.type !== 'number' ||\n typeof w.beg_pt_x !== 'number' ||\n typeof w.beg_pt_y !== 'number' ||\n typeof w.end_pt_x !== 'number' ||\n typeof w.end_pt_y !== 'number' ||\n typeof w.normal_x !== 'number' ||\n typeof w.normal_y !== 'number'\n ) {\n continue;\n }\n walls.push({\n type: w.type,\n from: { x: w.beg_pt_x, y: w.beg_pt_y },\n to: { x: w.end_pt_x, y: w.end_pt_y },\n normal: { x: w.normal_x, y: w.normal_y },\n });\n }\n rooms.push({ roomId: r.room_id, walls });\n }\n storeys.push({ rooms });\n }\n if (storeys.length === 0) {\n return null;\n }\n return {\n versionFlag: typeof raw.version_flag === 'number' ? raw.version_flag : 0,\n storeys,\n };\n}\n\ninterface SneakAreaEntry {\n id?: number;\n type?: number;\n hide?: number;\n roi?: number[];\n ms?: number;\n area?: number;\n}\n\n/**\n * Aggregate of every geometry-bearing field surfaced from the tail.\n * `decodeVacuumMap` parses one of these per tail and the rism-recurse\n * path uses `coalesceGeometry` to fill in fields the outer tail left\n * empty.\n */\nexport interface MapGeometry {\n virtualWalls: MapVirtualWall[];\n restrictedAreas: MapRestrictedArea[];\n lowLyingAreas: MapLowLyingArea[];\n wallsInfo: MapWallsInfo | null;\n}\n\n/**\n * Decode every geometry-bearing field from a tail in one shot.\n *\n * Adding a new geometry block (e.g. `vw.cliff` once we get a fixture)\n * is a one-line edit here — `decodeVacuumMap` and the rism-recurse\n * path automatically pick up the new field via `coalesceGeometry`.\n */\nexport function parseTailGeometry(tail: MapTail): MapGeometry {\n const { virtualWalls, restrictedAreas } = parseVirtualWalls(tail.vw, tail.vws);\n return {\n virtualWalls,\n restrictedAreas,\n lowLyingAreas: parseLowLyingAreas(tail.sneak_areas_end, tail.sneak_areas),\n wallsInfo: parseWallsInfo(tail.walls_info),\n };\n}\n\n/**\n * `true` when every geometry field has at least one entry. Used by\n * the rism-recurse path to skip the inner decode when the outer tail\n * already supplied everything.\n */\nexport function isGeometryComplete(g: MapGeometry): boolean {\n return (\n g.virtualWalls.length > 0 &&\n g.restrictedAreas.length > 0 &&\n g.lowLyingAreas.length > 0 &&\n g.wallsInfo !== null\n );\n}\n\n/**\n * Merge two `MapGeometry` snapshots. `primary` wins on every field\n * that's non-empty there; `fallback` fills the rest. Used by the\n * rism-recurse path: outer tail's geometry as primary, inner saved-\n * map blob's geometry as fallback.\n */\nexport function coalesceGeometry(primary: MapGeometry, fallback: MapGeometry): MapGeometry {\n return {\n virtualWalls: primary.virtualWalls.length > 0 ? primary.virtualWalls : fallback.virtualWalls,\n restrictedAreas:\n primary.restrictedAreas.length > 0 ? primary.restrictedAreas : fallback.restrictedAreas,\n lowLyingAreas:\n primary.lowLyingAreas.length > 0 ? primary.lowLyingAreas : fallback.lowLyingAreas,\n wallsInfo: primary.wallsInfo !== null ? primary.wallsInfo : fallback.wallsInfo,\n };\n}\n\n/**\n * Parse low-clearance \"sneak under furniture\" zones from a tail's\n * `sneak_areas` / `sneak_areas_end` arrays. Verified live 2026-05-07\n * on r2532a fw 4.3.9_2199 (every observed entry was a 4-corner rect,\n * 8 ints in `roi`); Tasshack `dev` `map.py:4776-4809` parses\n * arbitrary even-length polygons, so we surface points as-emitted\n * without coercing to a bounding box.\n *\n * `sneak_areas_end` is preferred when both fields are present in the\n * same tail — it carries the saved `area` field. `sneak_areas` is the\n * live-fly variant.\n */\nexport function parseLowLyingAreas(\n end: SneakAreaEntry[] | undefined,\n live: SneakAreaEntry[] | undefined,\n): MapLowLyingArea[] {\n const source = end && end.length > 0 ? end : live;\n if (!source) {\n return [];\n }\n const out: MapLowLyingArea[] = [];\n for (const entry of source) {\n if (!entry || !Array.isArray(entry.roi) || entry.roi.length < 4 || entry.roi.length % 2 !== 0) {\n continue;\n }\n const points: { x: number; y: number }[] = [];\n let badPoint = false;\n for (let i = 0; i + 1 < entry.roi.length; i += 2) {\n const x = parseFloatField(entry.roi[i]);\n const y = parseFloatField(entry.roi[i + 1]);\n if (x === null || y === null) {\n badPoint = true;\n break;\n }\n points.push({ x, y });\n }\n if (badPoint || points.length === 0) {\n continue;\n }\n const id = typeof entry.id === 'number' ? entry.id : -1;\n const area: MapLowLyingArea = { id, points };\n if (typeof entry.area === 'number') {\n area.area = entry.area;\n }\n out.push(area);\n }\n return out;\n}\n","/**\n * Cleaned-area overlay decoder for the JSON tail's `decmap` field.\n *\n * `decmap` is a recursive map envelope embedded as a base64 string —\n * the same outer-envelope shape as the parent (URL-safe base64 →\n * zlib → 27-byte header + pixel grid + UTF-8 JSON tail) but with a\n * different per-pixel encoding. Inner pixel encoding uses only the\n * low 2 bits (`& 0x03`): `1` → cleaned, `2` → dirty, others ignored.\n *\n * Tasshack reference: `dev` `map.py:5162-5233`.\n */\n\nimport { HEADER_SIZE, unwrapEnvelope } from './envelope.js';\nimport { parseMapHeader, type MapHeader } from './header.js';\nimport type { MapCleanedAreaOverlay, MapRun } from './types.js';\n\n/**\n * Decode the `decmap` recursive blob into a `MapCleanedAreaOverlay`.\n *\n * Returns `null` for any decode failure — the parent decode should\n * never abort because of a malformed inner blob.\n */\nexport function parseCleanedAreaOverlay(decmap: string): MapCleanedAreaOverlay | null {\n if (!decmap) {\n return null;\n }\n let inflated: Buffer;\n try {\n inflated = unwrapEnvelope(decmap);\n } catch {\n return null;\n }\n if (inflated.length < HEADER_SIZE) {\n return null;\n }\n let header: MapHeader;\n try {\n header = parseMapHeader(inflated);\n } catch {\n return null;\n }\n const pixelStart = HEADER_SIZE;\n const pixelEnd = pixelStart + header.width * header.height;\n if (inflated.length < pixelEnd || header.width <= 0 || header.height <= 0) {\n return null;\n }\n const pixels = inflated.subarray(pixelStart, pixelEnd);\n const { cleaned, dirty } = decodeCleanedAreaPixels(pixels, header.width, header.height);\n\n const overlay: MapCleanedAreaOverlay = {\n dimensions: {\n left: header.left,\n top: header.top,\n width: header.width,\n height: header.height,\n gridSize: header.gridSize,\n },\n cleaned,\n dirty,\n };\n\n // Pull out CleanArea from the inner JSON tail when present — opaque\n // to us, useful for downstream stats.\n if (inflated.length > pixelEnd) {\n const tailText = inflated.subarray(pixelEnd).toString('utf8');\n if (tailText) {\n try {\n const innerTail: unknown = JSON.parse(tailText);\n if (\n typeof innerTail === 'object' &&\n innerTail !== null &&\n !Array.isArray(innerTail) &&\n 'CleanArea' in innerTail\n ) {\n overlay.cleanedSegments = innerTail.CleanArea;\n }\n } catch {\n // Inner tail malformed — keep the pixel decoding, drop the stats.\n }\n }\n }\n\n return overlay;\n}\n\nfunction decodeCleanedAreaPixels(\n pixels: Buffer,\n width: number,\n height: number,\n): { cleaned: MapRun[]; dirty: MapRun[] } {\n const cleaned: MapRun[] = [];\n const dirty: MapRun[] = [];\n for (let y = 0; y < height; y++) {\n let cleanStart = -1;\n let dirtyStart = -1;\n const rowOff = y * width;\n for (let x = 0; x < width; x++) {\n const v = pixels[rowOff + x]! & 0x03;\n if (v === 1) {\n if (dirtyStart >= 0) {\n dirty.push([dirtyStart, y, x - dirtyStart]);\n dirtyStart = -1;\n }\n if (cleanStart < 0) {\n cleanStart = x;\n }\n } else if (v === 2) {\n if (cleanStart >= 0) {\n cleaned.push([cleanStart, y, x - cleanStart]);\n cleanStart = -1;\n }\n if (dirtyStart < 0) {\n dirtyStart = x;\n }\n } else {\n if (cleanStart >= 0) {\n cleaned.push([cleanStart, y, x - cleanStart]);\n cleanStart = -1;\n }\n if (dirtyStart >= 0) {\n dirty.push([dirtyStart, y, x - dirtyStart]);\n dirtyStart = -1;\n }\n }\n }\n if (cleanStart >= 0) {\n cleaned.push([cleanStart, y, width - cleanStart]);\n }\n if (dirtyStart >= 0) {\n dirty.push([dirtyStart, y, width - dirtyStart]);\n }\n }\n return { cleaned, dirty };\n}\n","/**\n * Public entry-point for decoding a single map-frame envelope into a\n * `VacuumMap`. Composes the per-concern modules:\n *\n * - `envelope.ts` — base64 / AES / zlib unwrap\n * - `header.ts` — 27-byte binary header\n * - `tail.ts` — UTF-8 JSON tail (+ `parseFrame` seam)\n * - `pixel-grid.ts` — fsm:1 pixel decode + segment collect\n * - `path.ts` — `tr` cleaning-path string parser\n * - `obstacles.ts` — AI-detected obstacle records\n * - `geometry.ts` — `vw` / `vws` / `walls_info` / sneak-zones\n * - `cleaned-area.ts` — recursive `decmap` overlay\n *\n * Public surface: the `decodeVacuumMap` / `applyVacuumPFrame` functions\n * plus `MapDecodeError` (from `envelope.ts`) and the small wire-format\n * constants. Per-concern parsers are also re-exported via the sub-tree\n * barrel for callers that want to feed raw bytes through specific stages.\n *\n * The donor's stateless `MapDecoder` class is adapted here into plain\n * functions — `decode`/`applyPFrame` carried no instance state, so a\n * function form is a clean simplification.\n */\n\nimport type {\n VacuumMap,\n VacuumMapDecodeOptions,\n MapDimensions,\n MapPose,\n MapTail,\n} from './types.js';\nimport { ANGLE_ABSENT, HEADER_SIZE, unwrapEnvelope } from './envelope.js';\nimport type { MapHeader } from './header.js';\nimport { parseFrame } from './tail.js';\nimport { collectSegments, decodePixelGridFsm1 } from './pixel-grid.js';\nimport { parsePathTr } from './path.js';\nimport { parseObstacles } from './obstacles.js';\nimport { coalesceGeometry, isGeometryComplete, parseTailGeometry } from './geometry.js';\nimport { parseCleanedAreaOverlay } from './cleaned-area.js';\nimport { mergePFrame, mergePFrameEnvelope } from './merge.js';\n\n/**\n * Decode a single frame envelope to a `VacuumMap`. Accepts the raw MQTT\n * value (URL-safe base64 string, optionally with `,<aes-key>` suffix)\n * or the inflated bytes directly (`Buffer`). Pure — no IO.\n */\nexport function decodeVacuumMap(\n input: Buffer | string,\n opts: VacuumMapDecodeOptions = {},\n): VacuumMap {\n const inflated = typeof input === 'string' ? unwrapEnvelope(input, opts) : input;\n const { header, tail } = parseFrame(inflated);\n\n const dimensions = mergeDimensions(header, tail);\n const robot = pose(header.robotX, header.robotY, header.robotA, tail.nr === true);\n const dock = pose(header.chargerX, header.chargerY, header.chargerA, tail.nc === true);\n const docked = tail.oc === true;\n\n // Pixel grid in a P-frame is byte-add deltas over a previous I-frame's\n // grid, not absolute classifications — decoding it standalone produces\n // garbage (high-byte deltas masquerade as segment ids). Only run the\n // pixel decoder on I-frames; the merge layer produces a synthetic\n // I-frame for ongoing P-frame state.\n const pixelStart = HEADER_SIZE;\n const pixelEnd = pixelStart + header.width * header.height;\n const pixelGrid = inflated.subarray(pixelStart, pixelEnd);\n const canDecodePixels =\n header.frameType === 'I' && pixelGrid.length === header.width * header.height;\n const layers = canDecodePixels ? decodePixelGridFsm1(pixelGrid, header.width, header.height) : [];\n\n const segments = canDecodePixels ? collectSegments(layers, dimensions, tail) : [];\n const paths = parsePathTr(tail.tr ?? '');\n const obstacles = parseObstacles(tail.ai_obstacle ?? []);\n let geometry = parseTailGeometry(tail);\n // The persistent saved-map blob is embedded inline as `tail.rism`\n // (URL-safe-base64 + zlib + same envelope shape). On r2532a fw\n // 4.3.9_2199 the outer tail's geometry blocks are absent and the\n // geometry lives only in the inner saved-map's tail. Recurse to\n // surface it; if the inner blob fails to decode (corrupt,\n // unexpected shape, missing AES IV, etc.) leave the outer values\n // as-is and swallow the error — geometry decode failure must\n // never break pixel/path/obstacle decode of the outer frame.\n // Recurses one level only — the inner saved-map blob does not\n // carry its own `rism`.\n if (!isGeometryComplete(geometry) && typeof tail.rism === 'string' && tail.rism.length > 0) {\n try {\n const innerInflated = unwrapEnvelope(tail.rism);\n const { tail: innerTail } = parseFrame(innerInflated);\n geometry = coalesceGeometry(geometry, parseTailGeometry(innerTail));\n } catch {\n // intentional — outer frame remains valid even if rism is unreadable\n }\n }\n const cleanedArea = typeof tail.decmap === 'string' ? parseCleanedAreaOverlay(tail.decmap) : null;\n\n return {\n mapId: header.mapId,\n frameId: header.frameId,\n frameType: header.frameType,\n timestamp: tail.timestamp_ms ?? 0,\n rotation: tail.mra ?? 0,\n dimensions,\n robot,\n dock,\n docked,\n layers,\n segments,\n paths,\n obstacles,\n ...geometry,\n cleanedArea,\n };\n}\n\n/**\n * Merge a P-frame onto a previous (I-frame or already-merged) inflated\n * frame buffer and decode the result. Returns both the merged buffer\n * (so callers can chain further merges without re-allocating) and the\n * decoded `VacuumMap`.\n *\n * Throws `OutOfOrderFrameError` when `pframe.frameId !== prev.frameId + 1`.\n *\n * Inputs are envelope strings or already-unwrapped buffers — both\n * sides are normalised internally via `unwrapEnvelope` when given a\n * string, with the matching `VacuumMapDecodeOptions` for the AES key/IV.\n */\nexport function applyVacuumPFrame(\n prev: Buffer | string,\n pframe: Buffer | string,\n opts: { prev?: VacuumMapDecodeOptions; pframe?: VacuumMapDecodeOptions } = {},\n): { buffer: Buffer; data: VacuumMap } {\n const merged =\n typeof prev === 'string' || typeof pframe === 'string'\n ? mergePFrameEnvelope(prev, pframe, opts.prev, opts.pframe)\n : mergePFrame(prev, pframe);\n return { buffer: merged, data: decodeVacuumMap(merged) };\n}\n\nfunction mergeDimensions(header: MapHeader, tail: MapTail): MapDimensions {\n const left = tail.origin?.[0] ?? header.left;\n const top = tail.origin?.[1] ?? header.top;\n return {\n left,\n top,\n width: header.width,\n height: header.height,\n gridSize: header.gridSize,\n };\n}\n\nfunction pose(x: number, y: number, angle: number, absent: boolean): MapPose | null {\n if (absent || angle === ANGLE_ABSENT) {\n return null;\n }\n return { x, y, angle };\n}\n","/**\n * Raster PNG renderer for a decoded `VacuumMap`.\n *\n * node-dreame ships only the structured model (no image); this is NEW. We paint\n * one pixel per grid cell (× an optional integer upscale `scale`) from the\n * run-length `layers`, later layers painting over earlier ones. The background\n * is transparent. Per-segment colours come from a deterministic golden-angle\n * palette so the same room id always renders the same hue.\n *\n * Pure: only `pngjs` + arithmetic. No casts.\n */\n\nimport { PNG } from 'pngjs';\nimport type { VacuumMap, MapLayer, MapRun } from './types.js';\n\nexport interface RenderVacuumPngOptions {\n /** Integer upscale factor (nearest-neighbour). Default 1. */\n scale?: number;\n}\n\ntype Rgba = readonly [number, number, number, number];\n\nconst RGBA: Record<Exclude<MapLayer['type'], 'segment'>, Rgba> = {\n wall: [60, 60, 60, 255],\n floor: [210, 222, 235, 255],\n carpet: [180, 150, 120, 200],\n};\n\n/** HSV (h in degrees, s/v in 0..1) → opaque RGBA. */\nfunction hsvToRgba(h: number, s: number, v: number): Rgba {\n const c = v * s;\n const hp = (((h % 360) + 360) % 360) / 60;\n const x = c * (1 - Math.abs((hp % 2) - 1));\n let r = 0;\n let g = 0;\n let b = 0;\n if (hp < 1) {\n r = c;\n g = x;\n } else if (hp < 2) {\n r = x;\n g = c;\n } else if (hp < 3) {\n g = c;\n b = x;\n } else if (hp < 4) {\n g = x;\n b = c;\n } else if (hp < 5) {\n r = x;\n b = c;\n } else {\n r = c;\n b = x;\n }\n const m = v - c;\n return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255), 255];\n}\n\n/** Deterministic per-segment palette (golden-angle hue spread). */\nfunction segmentColor(id: number): Rgba {\n const hue = (id * 137.508) % 360;\n return hsvToRgba(hue, 0.45, 0.95);\n}\n\nexport function renderVacuumPng(map: VacuumMap, opts: RenderVacuumPngOptions = {}): Buffer {\n const scale = Math.max(1, Math.trunc(opts.scale ?? 1));\n const w = map.dimensions.width * scale;\n const h = map.dimensions.height * scale;\n const png = new PNG({ width: Math.max(1, w), height: Math.max(1, h) });\n png.data.fill(0); // transparent background\n for (const layer of map.layers) {\n paintLayer(png, layer, scale);\n }\n return PNG.sync.write(png);\n}\n\nfunction paintLayer(png: PNG, layer: MapLayer, scale: number): void {\n const color = layer.type === 'segment' ? segmentColor(layer.segmentId ?? 0) : RGBA[layer.type];\n for (const run of layer.runs) {\n paintRun(png, run, color, scale, png.width, png.height);\n }\n}\n\n/** Fill the scale×scale block of every pixel in a `[xPx, yPx, len]` run. */\nfunction paintRun(\n png: PNG,\n run: MapRun,\n color: Rgba,\n scale: number,\n width: number,\n height: number,\n): void {\n const [xPx, yPx, len] = run;\n for (let i = 0; i < len; i += 1) {\n const baseX = (xPx + i) * scale;\n const baseY = yPx * scale;\n for (let dy = 0; dy < scale; dy += 1) {\n const py = baseY + dy;\n if (py < 0 || py >= height) {\n continue;\n }\n for (let dx = 0; dx < scale; dx += 1) {\n const px = baseX + dx;\n if (px < 0 || px >= width) {\n continue;\n }\n const off = (py * width + px) * 4;\n png.data[off] = color[0];\n png.data[off + 1] = color[1];\n png.data[off + 2] = color[2];\n png.data[off + 3] = color[3];\n }\n }\n }\n}\n","/**\n * Resolve a Dreame OSS object name (advertised via the PATH push,\n * `siid 6 piid 3`) to a signed download URL and fetch the blob.\n *\n * Endpoint: `POST /dreame-user-iot/iotfile/getDownloadUrl`\n * Body: `{ did, model, filename, region }`\n * Response: `{ code: 0, data: \"<signed-url-string>\" }` — `data` is the\n * URL string itself, not a nested `{ url }` object.\n *\n * The signed URL is good for ~1 hour (Aliyun signature). We cache it\n * for 30 min by default and on cache hit append `current=<unix_ts>` to\n * bust any intermediate caches (Tasshack convention).\n *\n * v1 only handles live blobs (the I-frame parked in OSS by the device).\n * Permanent saved-map blobs use a different filename mangling — deferred.\n *\n * ADAPTED from node-dreame's `src/map/oss-fetch.ts`: the only changes are\n * import-path rewrites onto our transport (`RequestContext`/`httpPostJson`\n * from `transport/http`, `DreameApiError`/`DreameTransportError` from\n * `transport/errors`, `DreameRegion` from `auth/config`) and typing the\n * injected fetch seam as our `FetchImpl` (undici-backed) rather than the\n * donor's global `typeof fetch` — so the mock type-checks with no cast.\n */\n\nimport type { DreameRegion } from '../../../auth/config.js';\nimport { RequestContext, httpPostJson } from '../../../transport/http.js';\nimport type { FetchImpl } from '../../../transport/fetch.js';\nimport { defaultFetch } from '../../../transport/fetch.js';\nimport { DreameApiError, DreameTransportError } from '../../../transport/errors.js';\n\nconst OSS_DOWNLOAD_PATH = '/dreame-user-iot/iotfile/getDownloadUrl';\nconst DEFAULT_TTL_MS = 30 * 60 * 1000;\n\nexport interface OssFetchInput {\n /** Resolved API host (e.g. `eu.iot.dreame.tech:13267`). */\n host: string;\n /** Bearer token from the active `DreameSession`. */\n accessToken: string;\n /** Region — used for header construction and the body's `region` field. */\n region: DreameRegion;\n /** Optional `country` override for headers (defaults from region). */\n country?: string;\n /** Optional `lang` override for headers (defaults from region). */\n lang?: string;\n /** Device id. */\n did: string;\n /** Device model. */\n model: string;\n /** OSS object name (`ali_dreame/<uid>/<did>/<n>`). */\n filename: string;\n /** Caller-supplied AbortSignal — composed with the HTTP timeout. */\n signal?: AbortSignal;\n /** Per-request timeout override in ms. Pass `0` to disable. */\n timeoutMs?: number;\n}\n\nexport interface OssFetcherOpts {\n /** Cache TTL in ms. Default 30 min. */\n ttlMs?: number;\n /** Inject a clock (for tests). Default `Date.now`. */\n now?: () => number;\n /** Inject fetch (for tests). Default the shared undici-backed `defaultFetch`. */\n fetchImpl?: FetchImpl;\n}\n\ninterface OssResolveResponse {\n code?: number;\n msg?: string;\n data?: string;\n}\n\ninterface CacheEntry {\n url: string;\n expiresAt: number;\n}\n\n/**\n * Minimal fetcher seam consumed by `VacuumDevice.getMap`. Lets callers\n * inject a custom signed-blob fetcher (e.g. a cache-backed or proxied one)\n * without depending on the concrete {@link OssFetcher} class. The real\n * {@link OssFetcher} implements this interface.\n */\nexport interface OssFetcherLike {\n fetchBlob(input: OssFetchInput): Promise<Buffer>;\n}\n\nexport class OssFetcher implements OssFetcherLike {\n private readonly ttlMs: number;\n private readonly now: () => number;\n private readonly fetchImpl: FetchImpl;\n private readonly cache = new Map<string, CacheEntry>();\n /** In-flight fetchBlob calls, keyed `did:filename`. Lets us coalesce concurrent fetches for the same blob. */\n private readonly inflight = new Map<string, Promise<Buffer>>();\n\n constructor(opts: OssFetcherOpts = {}) {\n this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;\n this.now = opts.now ?? Date.now;\n this.fetchImpl = opts.fetchImpl ?? defaultFetch;\n }\n\n /**\n * Resolve `input.filename` to a signed download URL. Uses the cache\n * when fresh; on cache hit returns the cached URL with\n * `current=<unix_ts>` appended (using `&` if the URL already has a\n * query string — Aliyun signed URLs always do).\n */\n async resolveUrl(input: OssFetchInput): Promise<string> {\n const key = `${input.did}:${input.filename}`;\n const now = this.now();\n const cached = this.cache.get(key);\n if (cached && cached.expiresAt > now) {\n const sep = cached.url.includes('?') ? '&' : '?';\n return `${cached.url}${sep}current=${Math.floor(now / 1000)}`;\n }\n\n const ctx = RequestContext.from({ ...input, fetchImpl: this.fetchImpl });\n\n const resp = await httpPostJson<OssResolveResponse>({\n ctx,\n url: ctx.url(OSS_DOWNLOAD_PATH),\n headers: ctx.headers({\n accessToken: input.accessToken,\n contentType: 'application/json',\n }),\n body: JSON.stringify({\n did: input.did,\n model: input.model,\n filename: input.filename,\n region: input.region,\n }),\n context: 'oss download url',\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n });\n\n if (typeof resp.data !== 'string' || resp.data.length === 0) {\n throw new DreameApiError(\n `oss download url response missing data: ${JSON.stringify(resp).slice(0, 200)}`,\n 200,\n resp,\n );\n }\n\n this.cache.set(key, { url: resp.data, expiresAt: now + this.ttlMs });\n return resp.data;\n }\n\n /**\n * Resolve, GET the signed URL, and return the raw bytes.\n *\n * Concurrent calls for the same `(did, filename)` pair are coalesced\n * into a single network request — the second caller awaits the first's\n * Promise instead of issuing a duplicate fetch.\n */\n async fetchBlob(input: OssFetchInput): Promise<Buffer> {\n const key = `${input.did}:${input.filename}`;\n const existing = this.inflight.get(key);\n if (existing) {\n return existing;\n }\n const promise = this.#doFetchBlob(input).finally(() => {\n this.inflight.delete(key);\n });\n this.inflight.set(key, promise);\n return promise;\n }\n\n async #doFetchBlob(input: OssFetchInput): Promise<Buffer> {\n const url = await this.resolveUrl(input);\n let res: Awaited<ReturnType<FetchImpl>>;\n try {\n res = await this.fetchImpl(url, {\n method: 'GET',\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n });\n } catch (err) {\n throw new DreameTransportError(`oss download network error`, err);\n }\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new DreameApiError(\n `oss download failed: ${res.status} ${text.slice(0, 200)}`,\n res.status,\n );\n }\n return Buffer.from(await res.arrayBuffer());\n }\n\n /** Drop all cached URLs. */\n clearCache(): void {\n this.cache.clear();\n }\n}\n","import {\n BaseDevice,\n type BaseDeviceInput,\n type BaseDeviceEvents,\n} from '../../device/base-device.js';\nimport {\n VACUUM_PROP,\n VACUUM_ACTION,\n BATTERY_PROP,\n SETTINGS_PROP,\n CONSUMABLE_PROP,\n CUSTOM_CLEAN_MODE,\n} from './properties.js';\nimport {\n ChargingStatus,\n CleaningMode,\n MiotState,\n SuctionLevel,\n TaskStatus,\n WaterVolume,\n} from './enums.js';\nimport { asNum, parseFaultList } from './decode.js';\nimport { enumLookup } from '../_shared/decode.js';\nimport {\n VacuumCapabilityResolver,\n getVacuumCapabilities,\n type VacuumCapabilities,\n} from './capabilities.js';\nimport { DreameError } from '../../transport/errors.js';\nimport {\n decodeVacuumMap,\n OssFetcher,\n type VacuumMap,\n type OssFetchInput,\n type OssFetcherLike,\n} from './map/index.js';\nimport { REGION_HOSTS } from '../../auth/config.js';\n\n/** Knobs for the segment/zone/spot helpers. Defaults pull from cached state. */\nexport interface CleanOpts {\n repeats?: number;\n fan?: number;\n water?: number;\n}\n\n/**\n * The vacuum widens the {@link BaseDeviceEvents} map with a `'map'` event,\n * emitted by {@link VacuumDevice.getMap} once a fresh map has been decoded.\n * Declaring it here lets `VacuumDevice extends BaseDevice<VacuumDeviceEvents>`\n * type the emitter with no banned cast.\n */\nexport type VacuumDeviceEvents = BaseDeviceEvents & {\n map: [VacuumMap];\n};\n\n/** Input to {@link VacuumDevice.getMap}. */\nexport interface VacuumGetMapInput {\n /** OSS object name advertised via the PATH push (siid 6 piid 3). */\n filename: string;\n /**\n * Inject the signed-blob fetcher (tests pass a fake). Defaults to a fresh\n * {@link OssFetcher}. Typed as {@link OssFetcherLike} so consumers can pass\n * any object exposing a compatible `fetchBlob`.\n */\n fetcher?: OssFetcherLike;\n /** Optional AES key for an encrypted blob (hex). */\n key?: string;\n /** Optional AES IV for an encrypted blob (hex). */\n iv?: string;\n /** Override the API host (defaults from the device region). */\n host?: string;\n /** Per-request timeout override in ms. */\n timeoutMs?: number;\n /** Caller-supplied AbortSignal. */\n signal?: AbortSignal;\n}\n\nconst SUCTION = enumLookup<SuctionLevel>([\n SuctionLevel.Quiet,\n SuctionLevel.Standard,\n SuctionLevel.Intense,\n SuctionLevel.Max,\n]);\nconst WATER = enumLookup<WaterVolume>([WaterVolume.Low, WaterVolume.Medium, WaterVolume.High]);\nconst CLEAN_MODE = enumLookup<CleaningMode>([\n CleaningMode.Sweeping,\n CleaningMode.Mopping,\n CleaningMode.SweepAndMop,\n CleaningMode.MopAfterSweep,\n]);\nconst STATE = enumLookup<MiotState>(\n Object.values(MiotState).filter((v): v is MiotState => typeof v === 'number'),\n);\nconst CHARGING = enumLookup<ChargingStatus>([\n ChargingStatus.Charging,\n ChargingStatus.Discharging,\n ChargingStatus.Returning,\n]);\nconst TASK = enumLookup<TaskStatus>([\n TaskStatus.InterruptedOrPaused,\n TaskStatus.Active,\n TaskStatus.Transitioning,\n TaskStatus.OnDockIdle,\n TaskStatus.TransientPauseEdge,\n TaskStatus.NeedsIntervention,\n]);\n\n/** A typed Dreame-vacuum handle (state + capability-gated commands). */\nexport class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {\n readonly #caps: VacuumCapabilities;\n #lastMap: VacuumMap | null = null;\n\n constructor(input: BaseDeviceInput) {\n super({\n ...input,\n // Inject the vacuum resolver so the inherited generic `capabilities`\n // getter carries the vacuum token set (no banned cast).\n capabilities:\n input.capabilities ?? new VacuumCapabilityResolver().resolve(input.device.model),\n });\n this.#caps = getVacuumCapabilities(input.device.model);\n }\n\n /** Rich, vacuum-specific capability record (booleans + supported enums). */\n get vacuumCapabilities(): VacuumCapabilities {\n return this.#caps;\n }\n\n /**\n * The suction levels this model supports, as a {@link SuctionLevel} enum array\n * (from the capability record). Lets a consumer see which fan speeds are\n * selectable without reaching into {@link vacuumCapabilities}.\n */\n get supportedSuctionLevels(): readonly SuctionLevel[] {\n return this.#caps.supportedSuctionLevels;\n }\n /** The water/mop volumes this model supports, as a {@link WaterVolume} enum array. */\n get supportedWaterVolumes(): readonly WaterVolume[] {\n return this.#caps.supportedWaterVolumes;\n }\n\n #num(siid: number, piid: number): number | null {\n return asNum(this.getProperty(siid, piid)?.value);\n }\n\n // -- typed state --------------------------------------------------------\n get statusRaw(): number | null {\n return this.#num(VACUUM_PROP.STATE.siid, VACUUM_PROP.STATE.piid);\n }\n get status(): MiotState | null {\n return STATE(this.statusRaw);\n }\n get battery(): number | null {\n return this.#num(BATTERY_PROP.LEVEL.siid, BATTERY_PROP.LEVEL.piid);\n }\n get chargingRaw(): number | null {\n return this.#num(BATTERY_PROP.CHARGING_STATUS.siid, BATTERY_PROP.CHARGING_STATUS.piid);\n }\n get charging(): ChargingStatus | null {\n return CHARGING(this.chargingRaw);\n }\n get isCharging(): boolean {\n return this.charging === ChargingStatus.Charging;\n }\n /** Docked => MiotState Charging or ChargingComplete (on the dock). */\n get isDocked(): boolean {\n const s = this.status;\n return s === MiotState.Charging || s === MiotState.ChargingComplete;\n }\n get suctionRaw(): number | null {\n return this.#num(VACUUM_PROP.SUCTION_LEVEL.siid, VACUUM_PROP.SUCTION_LEVEL.piid);\n }\n get suction(): SuctionLevel | null {\n return SUCTION(this.suctionRaw);\n }\n get waterRaw(): number | null {\n return this.#num(VACUUM_PROP.WATER_VOLUME.siid, VACUUM_PROP.WATER_VOLUME.piid);\n }\n get water(): WaterVolume | null {\n return WATER(this.waterRaw);\n }\n /** Reads the SAFE CLEAN_MODE_SETTING (siid 2 piid 6), plain 0-3. */\n get cleaningModeRaw(): number | null {\n return this.#num(VACUUM_PROP.CLEAN_MODE_SETTING.siid, VACUUM_PROP.CLEAN_MODE_SETTING.piid);\n }\n get cleaningMode(): CleaningMode | null {\n return CLEAN_MODE(this.cleaningModeRaw);\n }\n get taskStatusRaw(): number | null {\n return this.#num(VACUUM_PROP.TASK_STATUS.siid, VACUUM_PROP.TASK_STATUS.piid);\n }\n get taskStatus(): TaskStatus | null {\n return TASK(this.taskStatusRaw);\n }\n get errorCode(): number | null {\n return this.#num(VACUUM_PROP.ERROR.siid, VACUUM_PROP.ERROR.piid);\n }\n get faults(): readonly number[] {\n return parseFaultList(\n this.getProperty(VACUUM_PROP.FAULTS_STR.siid, VACUUM_PROP.FAULTS_STR.piid)?.value,\n );\n }\n get taskProgressPct(): number | null {\n return this.#num(VACUUM_PROP.TASK_PROGRESS_PCT.siid, VACUUM_PROP.TASK_PROGRESS_PCT.piid);\n }\n get mainBrushLeftPct(): number | null {\n return this.#num(CONSUMABLE_PROP.MAIN_BRUSH_LEFT.siid, CONSUMABLE_PROP.MAIN_BRUSH_LEFT.piid);\n }\n get sideBrushLeftPct(): number | null {\n return this.#num(CONSUMABLE_PROP.SIDE_BRUSH_LEFT.siid, CONSUMABLE_PROP.SIDE_BRUSH_LEFT.piid);\n }\n get filterLeftPct(): number | null {\n return this.#num(CONSUMABLE_PROP.FILTER_LEFT.siid, CONSUMABLE_PROP.FILTER_LEFT.piid);\n }\n get volume(): number | null {\n return this.#num(SETTINGS_PROP.VOLUME.siid, SETTINGS_PROP.VOLUME.piid);\n }\n\n // -- command helpers ----------------------------------------------------\n #resolveCleanOpts(opts: CleanOpts): { repeats: number; fan: number; water: number } {\n const repeats = Math.max(1, Math.trunc(opts.repeats ?? 1));\n const fan = opts.fan ?? this.suctionRaw ?? SuctionLevel.Standard;\n const water = opts.water ?? this.waterRaw ?? WaterVolume.Medium;\n return { repeats, fan, water };\n }\n\n #startCustom(mode: number, payload: Record<string, unknown>): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.START_CUSTOM.siid, VACUUM_ACTION.START_CUSTOM.aiid, [\n { piid: 1, value: mode },\n { piid: 10, value: JSON.stringify(payload) },\n ]);\n }\n\n #requireCap(flag: boolean, op: string, feature: string): void {\n if (!flag) {\n throw new DreameError(`${op}: model ${this.model} does not support ${feature}`);\n }\n }\n\n // -- no-arg commands ----------------------------------------------------\n /**\n * Start cleaning (MIoT action siid 2 aiid 1). Named `startCleaning` — NOT\n * `start` — because `BaseDevice.start()` is the lifecycle method that opens\n * the MQTT push, and the facade relies on that meaning. Donor `node-dreame`\n * names this `start()` only because its standalone `Vacuum` class owns no\n * lifecycle method to collide with.\n */\n startCleaning(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.START.siid, VACUUM_ACTION.START.aiid, []);\n }\n pause(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.PAUSE.siid, VACUUM_ACTION.PAUSE.aiid, []);\n }\n stop(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.STOP.siid, VACUUM_ACTION.STOP.aiid, []);\n }\n /** Return to the dock / charge (MIoT action CHARGE, siid 3 aiid 1). */\n dock(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.CHARGE.siid, VACUUM_ACTION.CHARGE.aiid, []);\n }\n locate(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.LOCATE.siid, VACUUM_ACTION.LOCATE.aiid, []);\n }\n clearWarning(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.CLEAR_WARNING.siid, VACUUM_ACTION.CLEAR_WARNING.aiid, []);\n }\n async startAutoEmpty(): Promise<unknown> {\n this.#requireCap(this.#caps.canAutoEmpty, 'startAutoEmpty', 'auto-empty');\n return this.callAction(\n VACUUM_ACTION.START_AUTO_EMPTY.siid,\n VACUUM_ACTION.START_AUTO_EMPTY.aiid,\n [],\n );\n }\n\n // -- settings writes ----------------------------------------------------\n /** Type-safe suction write. Validates against the model's supported levels. */\n setSuction(level: SuctionLevel): Promise<unknown> {\n return this.setSuctionRaw(level);\n }\n /**\n * Untyped suction write: validates an arbitrary number against the model's\n * supported levels and rejects with `RangeError` on an invalid input. This\n * is the raw-input entry point so callers (and tests) can exercise validation\n * with no type assertions. `async` so the guard surfaces as a rejection.\n */\n async setSuctionRaw(level: number): Promise<unknown> {\n if (!this.#caps.supportedSuctionLevels.includes(level)) {\n throw new RangeError(`setSuction: unsupported suction level ${String(level)}`);\n }\n return this.setProperty({ ...VACUUM_PROP.SUCTION_LEVEL, value: level });\n }\n /** Type-safe water-volume write. Validates against supported volumes. */\n setWater(volume: WaterVolume): Promise<unknown> {\n return this.setWaterRaw(volume);\n }\n /** Untyped water-volume write: validates an arbitrary number (zero casts). */\n async setWaterRaw(volume: number): Promise<unknown> {\n if (!this.#caps.supportedWaterVolumes.includes(volume)) {\n throw new RangeError(`setWater: unsupported water volume ${String(volume)}`);\n }\n return this.setProperty({ ...VACUUM_PROP.WATER_VOLUME, value: volume });\n }\n /** SAFE clean-mode write — uses CLEAN_MODE_SETTING (siid 2 piid 6), never the 0x1400 bitfield. */\n setCleaningMode(mode: CleaningMode): Promise<unknown> {\n return this.setProperty({ ...VACUUM_PROP.CLEAN_MODE_SETTING, value: mode });\n }\n\n // -- targeted cleaning --------------------------------------------------\n async cleanSegments(ids: number[], opts: CleanOpts = {}): Promise<unknown> {\n // Argument validation precedes capability gating (donor convention): an\n // empty array throws RangeError regardless of model capability.\n if (ids.length === 0) {\n throw new RangeError('cleanSegments: ids must not be empty');\n }\n this.#requireCap(this.#caps.canCleanPerRoom, 'cleanSegments', 'per-room cleaning');\n const { repeats, fan, water } = this.#resolveCleanOpts(opts);\n const selects = ids.map((id) => [id, repeats, fan, water, 1]);\n return this.#startCustom(CUSTOM_CLEAN_MODE.SEGMENT, { selects });\n }\n async cleanZones(\n zones: Array<{ x0: number; y0: number; x1: number; y1: number }>,\n opts: CleanOpts = {},\n ): Promise<unknown> {\n // Argument validation precedes capability gating (donor convention): an\n // empty array throws RangeError regardless of model capability.\n if (zones.length === 0) {\n throw new RangeError('cleanZones: zones must not be empty');\n }\n this.#requireCap(this.#caps.canCleanPerRoom, 'cleanZones', 'per-room cleaning');\n const { repeats, fan, water } = this.#resolveCleanOpts(opts);\n const areas = zones.map((z) => [\n Math.round(z.x0),\n Math.round(z.y0),\n Math.round(z.x1),\n Math.round(z.y1),\n repeats,\n fan,\n water,\n ]);\n return this.#startCustom(CUSTOM_CLEAN_MODE.ZONE, { areas });\n }\n /**\n * Send the robot to clean a single point (the SPOT custom-clean action,\n * mode 20). ASSUMED: Tasshack's \"go to point\" is the same SPOT action — this\n * is exposed as `cleanSpot` because it maps to the spot/custom-clean action,\n * not a distinct go-to. No separate `goTo` is exposed.\n */\n async cleanSpot(point: { x: number; y: number }, opts: CleanOpts = {}): Promise<unknown> {\n this.#requireCap(this.#caps.canCleanPerRoom, 'cleanSpot', 'per-room cleaning');\n const { repeats, fan, water } = this.#resolveCleanOpts(opts);\n const points = [[Math.round(point.x), Math.round(point.y), repeats, fan, water]];\n return this.#startCustom(CUSTOM_CLEAN_MODE.SPOT, { points });\n }\n\n /**\n * Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the\n * robot — reads {@link VacuumDevice.DEFAULT_PROPS} from the cloud-cached\n * endpoint. After it resolves, every typed getter (status/battery/suction/\n * water/cleaningMode/error/charging…) reflects the cached values, so a\n * standby/docked vacuum reports its state exactly as the Dreamehome app does.\n */\n async refreshFromCache(): Promise<void> {\n await this.refreshCachedProperties([...VacuumDevice.DEFAULT_PROPS]);\n }\n\n // -- maps ---------------------------------------------------------------\n /** The most-recently-decoded map, or `null` until {@link getMap} succeeds. */\n get lastMap(): VacuumMap | null {\n return this.#lastMap;\n }\n\n /**\n * The current room/segment id, derived from the most-recently-decoded map's\n * active-segment set (`sa`). `null` when no map has been fetched or no\n * segment is currently active. REPLACES the P3 \"intentionally not exposed\"\n * placeholder — the value now comes from the decoded map layer (P5).\n */\n get currentSegmentId(): number | null {\n return this.#lastMap?.segments.find((s) => s.active)?.id ?? null;\n }\n\n /**\n * Fetch the current saved/live-map blob (OSS), decode it, cache it as\n * {@link lastMap}, emit a `'map'` event, and return the {@link VacuumMap}.\n *\n * Capability-gated on `canMap`. The `filename` is the OSS object name the\n * caller resolves from a `mapInfo`/PATH push. The fetcher is injectable so\n * tests drive it with a synthetic blob and no live network.\n *\n * NOTE: active live-frame P-frame STREAMING (continuous merge orchestration)\n * is a documented follow-up; `getMap` resolves a single frame here. The\n * `applyVacuumPFrame` merge primitive ships separately for that work.\n */\n async getMap(input: VacuumGetMapInput): Promise<VacuumMap> {\n this.#requireCap(this.#caps.canMap, 'getMap', 'map decoding');\n const session = this.currentSession();\n const region = this.region;\n const fetcher = input.fetcher ?? new OssFetcher();\n const fetchInput: OssFetchInput = {\n host: input.host ?? REGION_HOSTS[region],\n accessToken: session.accessToken,\n region,\n did: this.deviceId,\n model: this.model,\n filename: input.filename,\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n };\n const blob = await fetcher.fetchBlob(fetchInput);\n const map = decodeVacuumMap(blob, {\n ...(input.key !== undefined ? { key: input.key } : {}),\n ...(input.iv !== undefined ? { iv: input.iv } : {}),\n });\n this.#lastMap = map;\n this.emit('map', map);\n return map;\n }\n\n /** Props worth seeding on start() / polling — exported for the facade. */\n static readonly DEFAULT_PROPS = [\n VACUUM_PROP.STATE,\n VACUUM_PROP.ERROR,\n VACUUM_PROP.FAULTS_STR,\n VACUUM_PROP.TASK_STATUS,\n VACUUM_PROP.SUCTION_LEVEL,\n VACUUM_PROP.WATER_VOLUME,\n VACUUM_PROP.CLEAN_MODE_SETTING,\n VACUUM_PROP.TASK_PROGRESS_PCT,\n BATTERY_PROP.LEVEL,\n BATTERY_PROP.CHARGING_STATUS,\n CONSUMABLE_PROP.MAIN_BRUSH_LEFT,\n CONSUMABLE_PROP.SIDE_BRUSH_LEFT,\n CONSUMABLE_PROP.FILTER_LEFT,\n SETTINGS_PROP.VOLUME,\n ] as const;\n}\n","/**\n * Mower MIoT property / action / event identifiers and the embedded\n * scheduling-task opcode payloads, reverse-engineered from\n * antondaubert/dreame-mower (const.py + device.py _build_*_task_payload).\n * Only KNOWN/useful ids are ported; the donor's unnamed SERVICE2_PROPERTY_* /\n * SERVICE5_PROPERTY_* are intentionally omitted.\n *\n * Encoding note: the four siid-5 actions (START_MOWING/STOP/DOCK/PAUSE) take NO\n * in-params. Targeted starts + resume are sent as ACTIONS on SCHEDULING_TASK\n * (siid 2 piid 50) whose in-params are `[ <opcodeObject> ]` — a one-element\n * array carrying the raw {m,p,o,d?} object (NOT the vacuum's {piid,value} form).\n */\n\n/** Mower service properties (subset; unnamed reverse-engineered props skipped). */\nexport const MOWER_PROP = {\n /** VERIFIED-by-donor — pose + coverage telemetry (binary). P4 uses progress % only. */\n POSE_COVERAGE: { siid: 1, piid: 4 } as const,\n /** VERIFIED-by-donor — MowerStatus (DeviceStatus). */\n STATUS: { siid: 2, piid: 1 } as const,\n /** VERIFIED-by-donor — device code blob (model/region). Not decoded in P4. */\n DEVICE_CODE: { siid: 2, piid: 2 } as const,\n /** VERIFIED-by-donor — mission task descriptor object {t,d:{...}}; also the action target. */\n SCHEDULING_TASK: { siid: 2, piid: 50 } as const,\n /** VERIFIED-by-donor — mission completion summary (often empty). */\n SCHEDULING_SUMMARY: { siid: 2, piid: 52 } as const,\n /** VERIFIED-by-donor — per-zone control status {status:[[zone,code],...]}. */\n MOWER_CONTROL_STATUS: { siid: 2, piid: 56 } as const,\n /** VERIFIED-by-donor — battery percentage 0-100. */\n BATTERY: { siid: 3, piid: 1 } as const,\n /** VERIFIED-by-donor — MowerChargingStatus. */\n CHARGING_STATUS: { siid: 3, piid: 2 } as const,\n /** VERIFIED-by-donor — task status code (MowerTaskStatus; mostly diagnostic). */\n TASK_STATUS: { siid: 5, piid: 104 } as const,\n} as const;\n\n/** Mower actions (siid 5). No in-params — parity with the donor execute_action. */\nexport const MOWER_ACTION = {\n START_MOWING: { siid: 5, aiid: 1 } as const,\n STOP: { siid: 5, aiid: 2 } as const,\n DOCK: { siid: 5, aiid: 3 } as const,\n PAUSE: { siid: 5, aiid: 4 } as const,\n} as const;\n\n/** Mower events. */\nexport const MOWER_EVENT = {\n /** VERIFIED-by-donor — mission completion (siid 4 eiid 1). */\n MISSION_COMPLETION: { siid: 4, eiid: 1 } as const,\n} as const;\n\n/**\n * Embedded scheduling-task opcodes carried in SCHEDULING_TASK (2:50) action\n * in-params. `m:'a'` = action, `p` = priority, `o` = opcode, `d` = data.\n * RESUME (o:5) is the donor's TASK_PAYLOAD_RESUME (continueControl).\n */\nexport const TASK_OPCODE = {\n RESUME: { m: 'a', p: 0, o: 5 } as const,\n ALL_AREA: 100,\n EDGE: 101,\n ZONE: 102,\n SPOT: 103,\n} as const;\n\n/** A fresh resume opcode object (never share the frozen literal across calls). */\nexport function buildResumePayload(): { m: string; p: number; o: number } {\n return { m: 'a', p: 0, o: 5 };\n}\n\n/** All-area start: o:100, region_id=[mapId], area_id=[]. device.py _build_all_area_task_payload. */\nexport function buildAllAreaPayload(mapId: number): {\n m: string;\n p: number;\n o: number;\n d: { region_id: number[]; area_id: number[] };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.ALL_AREA, d: { region_id: [mapId], area_id: [] } };\n}\n\n/** Zone start: o:102, region=zoneIds. device.py _build_zone_task_payload. */\nexport function buildZonePayload(zoneIds: number[]): {\n m: string;\n p: number;\n o: number;\n d: { region: number[] };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.ZONE, d: { region: [...zoneIds] } };\n}\n\n/** Edge start: o:101, edge=contourIdPairs. device.py _build_edge_task_payload. */\nexport function buildEdgePayload(contourIds: number[][]): {\n m: string;\n p: number;\n o: number;\n d: { edge: number[][] };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.EDGE, d: { edge: contourIds.map((c) => [...c]) } };\n}\n\n/** Spot start: o:103, area=spotAreaIds. device.py _build_spot_task_payload. */\nexport function buildSpotPayload(spotAreaIds: number[]): {\n m: string;\n p: number;\n o: number;\n d: { area: number[] };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.SPOT, d: { area: [...spotAreaIds] } };\n}\n","/**\n * Mower MIoT enum value spaces, reverse-engineered from antondaubert/dreame-mower\n * (const.py DeviceStatus + STATUS_MAPPING + CHARGING_STATUS_MAPPING, and\n * property/mower_control.py + service5.py). The donor is a working HA\n * integration, so these are VERIFIED-by-donor; members annotated ASSUMED are\n * not yet corroborated. The integer is always the source of truth.\n */\n\n/** VERIFIED-by-donor — STATUS_PROPERTY (siid 2 piid 1), const.py DeviceStatus. */\nexport enum MowerStatus {\n NoStatus = 0,\n Mowing = 1,\n Standby = 2,\n Paused = 3,\n PausedDueToErrors = 4,\n ReturningToCharge = 5,\n Charging = 6,\n Mapping = 11,\n ChargingComplete = 13,\n Updating = 14,\n}\n\n/** VERIFIED-by-donor — CHARGING_STATUS_PROPERTY (siid 3 piid 2), CHARGING_STATUS_MAPPING. */\nexport enum MowerChargingStatus {\n NotDocked = 0,\n Charging = 1,\n NotCharging = 2,\n ChargingCompleted = 3,\n ReturnToCharge = 5,\n /** Charging paused: battery temperature too low (donor issue #40). */\n ChargingPausedLowTemperature = 16,\n}\n\n/**\n * VERIFIED-by-donor — per-zone control code in MOWER_CONTROL_STATUS (siid 2\n * piid 56) `status` array entries `[zone_id, code]`. mower_control.py.\n */\nexport enum MowerControlAction {\n /** Zone waiting in a multi-zone session. */\n Queued = -1,\n /** Actively mowing. */\n Continue = 0,\n Completed = 2,\n Pause = 4,\n}\n\n/**\n * Observed TASK_STATUS codes (siid 5 piid 104), service5.py TASK_STATUS_MAPPING.\n * Only `SpotIncomplete` (7) has a confirmed meaning; the donor explicitly marks\n * codes 2/3/10/13 as \"Unknown task status: N\" (observed after RAIN/LOST/\n * LOW_BATTERY events). Those are NOT invented as named members — they surface as\n * raw via {@link MowerDevice.taskStatusRaw}. This is the FULL documented set:\n * the donor names exactly one code.\n */\nexport enum MowerTaskStatus {\n /** \"Task incomplete - spot mowing\". */\n SpotIncomplete = 7,\n}\n\n/**\n * Mower device-code / fault value space for DEVICE_CODE (siid 2 piid 2). This is\n * the FULL donor `BASE_DEVICE_CODES` registry (property/device_code.py), codes\n * 0..73 contiguous. Each integer is documented by the donor with a name +\n * severity (INFO / WARNING / ERROR — noted inline). 0 = NO_DEVICE_CODE (normal\n * operation). Codes not in this table (model-specific extensions) fall through\n * the cast-free lookup and surface as raw via {@link MowerDevice.faultRaw}.\n *\n * Two donor names repeat at different integers; the second occurrence is\n * suffixed (`LidarDirtyWarning` 38, `BatteryOverheatWarning` 42) to keep enum\n * member names unique while preserving the integer source of truth.\n */\nexport enum MowerFault {\n NoDeviceCode = 0, // INFO\n Tilted = 1, // ERROR\n Trapped = 2, // ERROR\n NarrowPathToStation = 3, // ERROR\n LeftWheel = 4, // ERROR\n RightWheel = 5, // ERROR\n LiftMotor = 6, // ERROR\n Cutter = 7, // ERROR\n SidedMotor = 8, // ERROR\n CrashPlate = 9, // ERROR\n Charging = 10, // ERROR\n BatteryOverheat = 11, // ERROR\n LidarCovered = 12, // ERROR\n LidarOverheatWithoutMap = 13, // ERROR\n LidarOverheatWithMap = 14, // ERROR\n LidarOverheat = 15, // ERROR\n LidarDirty = 16, // ERROR\n LidarAbnormal = 17, // ERROR\n LocationWeak = 18, // ERROR\n LocationLost = 19, // ERROR\n Sensor = 20, // ERROR\n InForbiddenArea = 21, // ERROR\n OutOfMap = 22, // ERROR\n EmergencyStop = 23, // ERROR\n BatteryLow = 24, // ERROR\n MapFileCrack = 25, // ERROR\n AwayFromMap = 26, // ERROR\n HumanDetected = 27, // ERROR\n BladeLoss = 28, // ERROR\n StationLoss = 29, // ERROR\n MaintainLoss = 30, // ERROR\n BackChargeFailed = 31, // WARNING\n DockingFailed = 32, // WARNING\n LocatingFailedWithMap = 33, // WARNING\n LocatingFailedWithoutMap = 34, // WARNING\n LocatingAbnormal = 35, // WARNING\n TaskStartFailed = 36, // WARNING\n PathImpassable = 37, // ERROR\n LidarDirtyWarning = 38, // WARNING (donor LIDAR_DIRTY at 38)\n CamDirty = 39, // WARNING\n CamAbnormal = 40, // WARNING\n CamCover = 41, // WARNING\n BatteryOverheatWarning = 42, // WARNING (donor BATTERY_OVERHEAT at 42)\n BatteryTempLow = 43, // WARNING\n AutobuildBorder = 44, // WARNING\n AutobuildSide = 45, // WARNING\n BorderFinish = 46, // INFO\n NewMap = 47, // INFO\n TaskFinish = 48, // INFO\n DestinationNotReachable = 49, // INFO\n TaskStart = 50, // INFO\n CruiseStart = 51, // INFO\n PointAndGoStart = 52, // INFO\n ScheduleStart = 53, // INFO\n BatteryLowReturning = 54, // INFO\n BatteryLowScheduleSuspend = 55, // INFO\n BadWeatherProtecting = 56, // INFO\n RainScheduleInterupted = 57, // INFO\n RainScheduleSuspend = 58, // INFO\n ForzenReturning = 59, // INFO\n FrozenScheduleSuspend = 60, // INFO\n NotDisturbReturning = 61, // INFO\n NotDisturbScheduleSuspend = 62, // INFO\n WorkingScheduleSuspend = 63, // INFO\n RemoteControlingScheduleSuspend = 64, // INFO\n EmergencyStoppedScheduleSuspend = 65, // INFO\n TopCoverOpenScheduleSuspend = 66, // INFO\n FaultModeScheduleSuspend = 67, // INFO\n ScheduleTimeout = 68, // INFO\n StationNotConnectedToWorkingArea = 69, // INFO\n ContinueFromBreakpoint = 70, // INFO\n IdleTimeoutReturning = 71, // INFO\n PauseTimeoutReturning = 72, // INFO\n TopCoverOpen = 73, // ERROR\n}\n","/**\n * Cast-free mower decoders. Re-exports the shared numeric primitives, and adds\n * the two structured-payload parsers the typed getters consume:\n * - parseTaskDescriptor : SCHEDULING_TASK (2:50) {t,d:{...}} -> typed task\n * - parseControlStatus : MOWER_CONTROL_STATUS (2:56) {status:[[zone,code]]}\n * Ported from antondaubert/dreame-mower property/{scheduling,mower_control}.py.\n * The binary pose-track geometry (1:4) is OUT OF SCOPE here (P5).\n */\nimport { MowerControlAction } from './enums.js';\nimport { enumLookup } from '../_shared/decode.js';\n\nexport { asNum, enumLookup } from '../_shared/decode.js';\n\n/** Typed scheduling task descriptor (subset of the donor TaskHandler fields). */\nexport interface MowerTaskDescriptor {\n taskType: string;\n executionActive: boolean;\n /** `d.o` — coverage target % or mode sentinel. */\n coverageTarget: number;\n taskActive: boolean;\n areaId: number[] | null;\n regionId: number[] | null;\n elapsedTime: number | null;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction numArrayOrNull(v: unknown): number[] | null {\n if (!Array.isArray(v)) {\n return null;\n }\n const out: number[] = [];\n for (const n of v) {\n if (typeof n !== 'number') {\n return null;\n }\n out.push(n);\n }\n return out;\n}\n\n/**\n * Parse the SCHEDULING_TASK descriptor (2:50). Required fields `d.exe`,\n * `d.o`, `d.status` must be present (matching the donor's KeyError-bubble);\n * optional area_id/region_id/time default to null. Returns null on any\n * malformed input — no throw, no cast.\n */\nexport function parseTaskDescriptor(value: unknown): MowerTaskDescriptor | null {\n if (!isRecord(value)) {\n return null;\n }\n const t = value['t'];\n const d = value['d'];\n if (typeof t !== 'string' || !isRecord(d)) {\n return null;\n }\n const exe = d['exe'];\n const o = d['o'];\n const status = d['status'];\n if (typeof exe !== 'boolean' || typeof o !== 'number' || typeof status !== 'boolean') {\n return null;\n }\n return {\n taskType: t === 'TASK' ? 'TASK' : 'UNKNOWN',\n executionActive: exe,\n coverageTarget: o,\n taskActive: status,\n areaId: numArrayOrNull(d['area_id']),\n regionId: numArrayOrNull(d['region_id']),\n elapsedTime: typeof d['time'] === 'number' ? d['time'] : null,\n };\n}\n\nconst controlLookup = enumLookup<MowerControlAction>([\n MowerControlAction.Queued,\n MowerControlAction.Continue,\n MowerControlAction.Completed,\n MowerControlAction.Pause,\n]);\n\n/** Map a raw 2:56 entry code to a MowerControlAction, or null. Cast-free. */\nexport function controlActionFor(code: number): MowerControlAction | null {\n return controlLookup(code);\n}\n\n/** Typed mower control status (2:56). */\nexport interface MowerControlState {\n action: MowerControlAction | null;\n statusCode: number | null;\n zones: number[][];\n}\n\n/**\n * Parse MOWER_CONTROL_STATUS (2:56). `status` is `[[zone_id, code], ...]`.\n * Primary action = the actively-mowing zone (code 0), else the first entry.\n * Empty array -> null action. Warn-and-tolerate: an UNKNOWN code does NOT drop\n * the whole structure — the zone is surfaced as-is and its `action` resolves to\n * null (only the primary's action goes null when the primary itself is unknown).\n * Still returns null on a malformed PAIR shape (non-array / <2 / non-numeric).\n * No cast.\n */\nexport function parseControlStatus(value: unknown): MowerControlState | null {\n if (!isRecord(value)) {\n return null;\n }\n const status = value['status'];\n if (!Array.isArray(status)) {\n return null;\n }\n if (status.length === 0) {\n return { action: null, statusCode: null, zones: [] };\n }\n const zones: number[][] = [];\n for (const entry of status) {\n const pair = numArrayOrNull(entry);\n if (pair === null || pair.length < 2) {\n return null;\n }\n // `numArrayOrNull` already guarantees an all-number array and the length\n // check guarantees >=2 elements, so the pair is pushed as-is (no per-index\n // undefined guard needed — that branch would be unreachable / dead).\n zones.push(pair);\n }\n const primary = zones.find((e) => e[1] === 0) ?? zones[0];\n const code = primary?.[1] ?? null;\n return {\n action: code === null ? null : controlActionFor(code),\n statusCode: code,\n zones,\n };\n}\n","/**\n * Per-model mower capability records + a resolver adapting the rich record into\n * the generic CapabilityResolver the BaseDevice expects (mirrors the vacuum\n * capability layer). The donor (antondaubert/dreame-mower) has no per-model\n * capability matrix for mowers, so the targeted-mowing flags are ASSUMED working\n * hypotheses from the donor command surface; verified:false until live-checked.\n */\nimport type { CapabilityResolver, DeviceCapabilities } from '../../device/capability.js';\n\n/** Rich mower capability record. `canX` = behaviour the model accepts. */\nexport interface MowerCapabilities {\n model: string;\n /** true only for live-confirmed records (false = assumed / fallback). */\n verified: boolean;\n /** Zone-selective mowing (2:50 o:102). */\n canMowZones: boolean;\n /** Edge / contour mowing (2:50 o:101). */\n canMowEdges: boolean;\n /** Spot mowing (2:50 o:103). */\n canMowSpots: boolean;\n /** All-area, map-targeted start (2:50 o:100). */\n canMowAllArea: boolean;\n /** Resume after pause (2:50 o:5). Generic to the mower action surface. */\n canResume: boolean;\n /** Has a scheduling task descriptor (2:50). */\n canSchedule: boolean;\n /** Fetches/parses the batched vector map (MAP.* JSON → zones/paths/contours). */\n canMap: boolean;\n}\n\nconst FALLBACK: Omit<MowerCapabilities, 'model'> = {\n verified: false,\n canMowZones: false,\n canMowEdges: false,\n canMowSpots: false,\n canMowAllArea: false,\n canResume: true,\n canSchedule: true,\n // Vector-map parsing is model-agnostic, but an unknown model leaves it false\n // until its firmware is verified to expose the batch device-data surface.\n canMap: false,\n};\n\n/** A1-family body (ASSUMED from the donor command surface; not live-verified). */\nconst A1_FAMILY: Omit<MowerCapabilities, 'model' | 'verified'> = {\n canMowZones: true,\n canMowEdges: true,\n canMowSpots: true,\n canMowAllArea: true,\n canResume: true,\n canSchedule: true,\n canMap: true,\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {\n Object.freeze(obj);\n for (const v of Object.values(obj)) {\n deepFreeze(v);\n }\n }\n return obj;\n}\n\nexport const MODEL_CAPABILITIES: Readonly<Record<string, MowerCapabilities>> = {\n /** Dreame A1 — the user's mower. ASSUMED from the donor command surface. */\n 'dreame.mower.p2255': { model: 'dreame.mower.p2255', verified: false, ...A1_FAMILY },\n};\n\nfor (const entry of Object.values(MODEL_CAPABILITIES)) {\n deepFreeze(entry);\n}\n\n/** Resolve a model to its rich mower capability record (frozen / fallback). */\nexport function getMowerCapabilities(model: string): MowerCapabilities {\n const known = MODEL_CAPABILITIES[model];\n if (known) {\n return known;\n }\n return deepFreeze({ model, ...FALLBACK });\n}\n\n/** Map the rich record's booleans -> the generic capability token set. */\nfunction tokensFor(c: MowerCapabilities): Set<string> {\n const tokens = new Set<string>();\n const add = (flag: boolean, token: string): void => {\n if (flag) {\n tokens.add(token);\n }\n };\n add(c.canMowZones, 'mow-zones');\n add(c.canMowEdges, 'mow-edges');\n add(c.canMowSpots, 'mow-spots');\n add(c.canMowAllArea, 'mow-all-area');\n add(c.canResume, 'resume');\n add(c.canSchedule, 'schedule');\n return tokens;\n}\n\nclass MowerCapabilitySet implements DeviceCapabilities {\n readonly model: string;\n readonly #tokens: ReadonlySet<string>;\n constructor(model: string, tokens: ReadonlySet<string>) {\n this.model = model;\n this.#tokens = tokens;\n }\n has(token: string): boolean {\n return this.#tokens.has(token);\n }\n list(): readonly string[] {\n return [...this.#tokens];\n }\n}\n\n/** Resolver the BaseDevice accepts via BaseDeviceInput.capabilities. */\nexport class MowerCapabilityResolver implements CapabilityResolver {\n resolve(model: string): DeviceCapabilities {\n return new MowerCapabilitySet(model, tokensFor(getMowerCapabilities(model)));\n }\n}\n","/**\n * Parser for mower vector map data from the Dreame batch device-data API.\n *\n * PORT of antondaubert/dreame-mower's `map_data_parser.py`. The batch API\n * returns map data split across numbered keys (`MAP.0`, `MAP.1`, …); these\n * chunks are concatenated in numeric order to form a complete JSON string. The\n * JSON carries polygon-based zone boundaries, navigation paths, contours and\n * metadata. `M_PATH.*` keys carry the mow-path trace the same chunked way.\n *\n * Wire values arrive as `unknown`; every shape is narrowed with type-guards\n * (no `as` casts). Malformed entries are skipped (warn-and-tolerate), mirroring\n * the donor's try/except. The Python `vector_map_to_map_data` bridge and the\n * internal recursive `maps: dict` cache are intentionally NOT ported — the SVG\n * renderer consumes the structured `MowerMap` directly.\n */\nimport type {\n MowerMap,\n MowerZone,\n MowerSpotArea,\n MowerPathEntry,\n MowerContour,\n MowerMapBoundary,\n MowerMowPath,\n MowerAvailableMap,\n MowerPoint,\n} from './types.js';\n\n/** Sentinel pair in M_PATH data marking a path-segment break. */\nconst PATH_SENTINEL_X = 32767;\nconst PATH_SENTINEL_Y = -32768;\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction asNumber(v: unknown, fallback: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : fallback;\n}\n\nfunction asString(v: unknown, fallback: string): string {\n return typeof v === 'string' ? v : fallback;\n}\n\n/** Translate the batch map index into the task-level region identifier. */\nfunction mapIdFromIndex(mapIndex: number): number {\n return mapIndex + 1;\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Reassemble chunked data from the batch API into a single string.\n *\n * PORT of `reassemble_map_chunks`. Keys like `MAP.0`, `MAP.1`, … `MAP.N` are\n * concatenated in NUMERIC order; the `MAP.info` key (metadata) is skipped.\n * Wire values are strings; non-string values are skipped defensively.\n *\n * @param batchData raw `get_batch_device_datas` response map\n * @param prefix key prefix to match, e.g. `'MAP'` or `'M_PATH'`\n * @returns concatenated string, or `null` if no matching keys are found\n */\nexport function reassembleMapChunks(\n batchData: Record<string, unknown>,\n prefix: string,\n): string | null {\n const pattern = new RegExp(`^${escapeRegex(prefix)}\\\\.(\\\\d+)$`);\n const chunks: Array<{ idx: number; value: string }> = [];\n for (const [key, value] of Object.entries(batchData)) {\n const match = pattern.exec(key);\n if (match && typeof value === 'string') {\n const idx = Number(match[1]);\n chunks.push({ idx, value });\n }\n }\n if (chunks.length === 0) {\n return null;\n }\n chunks.sort((a, b) => a.idx - b.idx);\n return chunks.map((c) => c.value).join('');\n}\n\n/** Parse a `MAP.info`/`M_PATH.info` digit string into a split offset (>=0). */\nfunction parseSplitPos(info: unknown): number {\n if (typeof info === 'string' && /^\\d+$/.test(info)) {\n return Number(info);\n }\n return 0;\n}\n\n/**\n * Extract the `value` array from a `{ dataType: 'Map', value: [...] }` block.\n * Returns `[]` unless the block is a Map with an array value, mirroring the\n * donor `_parse_polygon_list`.\n */\nfunction parsePolygonList(dataMap: unknown): readonly unknown[] {\n if (!isRecord(dataMap) || dataMap['dataType'] !== 'Map') {\n return [];\n }\n const value = dataMap['value'];\n return Array.isArray(value) ? value : [];\n}\n\n/** Convert `[{ x, y }, …]` wire points to `MowerPoint[]`, skipping malformed. */\nfunction extractPathCoords(pathList: unknown): MowerPoint[] {\n if (!Array.isArray(pathList)) {\n return [];\n }\n const out: MowerPoint[] = [];\n for (const p of pathList) {\n if (isRecord(p) && typeof p['x'] === 'number' && typeof p['y'] === 'number') {\n out.push({ x: p['x'], y: p['y'] });\n }\n }\n return out;\n}\n\n/**\n * Normalize a contour identifier (string `'1,0'` or two-element array) into a\n * two-integer tuple. Throws on an invalid length — caught by the batch parser.\n */\nexport function extractContourId(raw: unknown): readonly [number, number] {\n if (typeof raw === 'string') {\n const parts = raw.split(',').map((part) => part.trim());\n if (parts.length !== 2) {\n throw new Error(`Invalid contour id: ${raw}`);\n }\n const a = Number(parts[0]);\n const b = Number(parts[1]);\n if (!Number.isFinite(a) || !Number.isFinite(b)) {\n throw new Error(`Invalid contour id: ${raw}`);\n }\n return [Math.trunc(a), Math.trunc(b)];\n }\n if (Array.isArray(raw) && raw.length === 2) {\n const pair: readonly unknown[] = raw;\n const a = pair[0];\n const b = pair[1];\n if (typeof a === 'number' && typeof b === 'number') {\n return [Math.trunc(a), Math.trunc(b)];\n }\n }\n throw new Error(`Invalid contour id: ${JSON.stringify(raw)}`);\n}\n\n/** A `[id, data]` polygon entry on the wire. */\nfunction asEntry(entry: unknown): { id: unknown; data: Record<string, unknown> } | null {\n if (!Array.isArray(entry) || entry.length < 2) {\n return null;\n }\n const tuple: readonly unknown[] = entry;\n const data = tuple[1];\n if (!isRecord(data)) {\n return null;\n }\n return { id: tuple[0], data };\n}\n\nfunction parseZones(list: readonly unknown[], includeTimes: boolean): MowerZone[] {\n const out: MowerZone[] = [];\n for (const raw of list) {\n const entry = asEntry(raw);\n if (!entry) {\n continue;\n }\n const { id, data } = entry;\n out.push({\n zoneId: asNumber(id, 0),\n path: extractPathCoords(data['path']),\n name: asString(data['name'], ''),\n zoneType: asNumber(data['type'], 0),\n shapeType: includeTimes ? asNumber(data['shapeType'], 0) : 0,\n area: includeTimes ? asNumber(data['area'], 0) : 0,\n time: includeTimes ? asNumber(data['time'], 0) : 0,\n etime: includeTimes ? asNumber(data['etime'], 0) : 0,\n });\n }\n return out;\n}\n\nfunction parseSpotAreas(list: readonly unknown[]): MowerSpotArea[] {\n const out: MowerSpotArea[] = [];\n for (const raw of list) {\n const entry = asEntry(raw);\n if (!entry) {\n continue;\n }\n const { id, data } = entry;\n out.push({\n areaId: Math.trunc(asNumber(id, 0)),\n path: extractPathCoords(data['path']),\n name: asString(data['name'], ''),\n shapeType: asNumber(data['shapeType'], 0),\n area: asNumber(data['area'], 0),\n });\n }\n return out;\n}\n\nfunction parsePaths(list: readonly unknown[]): MowerPathEntry[] {\n const out: MowerPathEntry[] = [];\n for (const raw of list) {\n const entry = asEntry(raw);\n if (!entry) {\n continue;\n }\n const { id, data } = entry;\n out.push({\n pathId: asNumber(id, 0),\n path: extractPathCoords(data['path']),\n pathType: asNumber(data['type'], 0),\n });\n }\n return out;\n}\n\nfunction parseContours(list: readonly unknown[]): MowerContour[] {\n const out: MowerContour[] = [];\n for (const raw of list) {\n const entry = asEntry(raw);\n if (!entry) {\n continue;\n }\n const { id, data } = entry;\n out.push({\n contourId: extractContourId(id),\n path: extractPathCoords(data['path']),\n contourType: asNumber(data['type'], 0),\n shapeType: asNumber(data['shapeType'], 0),\n });\n }\n return out;\n}\n\nfunction parseBoundary(raw: unknown): MowerMapBoundary | null {\n if (!isRecord(raw)) {\n return null;\n }\n const { x1, y1, x2, y2 } = raw;\n if (\n typeof x1 !== 'number' ||\n typeof y1 !== 'number' ||\n typeof x2 !== 'number' ||\n typeof y2 !== 'number'\n ) {\n return null;\n }\n return { x1, y1, x2, y2 };\n}\n\n/**\n * Parse a single map JSON string into a `MowerMap`.\n *\n * PORT of `parse_mower_map`. `JSON.parse` may throw on malformed input and\n * `extractContourId` may throw on an invalid contour id — both bubble up to\n * `parseBatchMapData`, which catches and skips (the donor's try/except).\n */\nexport function parseMowerMap(mapJsonStr: string): MowerMap {\n const data: unknown = JSON.parse(mapJsonStr);\n if (!isRecord(data)) {\n throw new Error('Map JSON is not an object');\n }\n\n const mapIndex = asNumber(data['mapIndex'], 0);\n\n return {\n zones: parseZones(parsePolygonList(data['mowingAreas']), true),\n spotAreas: parseSpotAreas(parsePolygonList(data['spotAreas'])),\n forbiddenAreas: parseZones(parsePolygonList(data['forbiddenAreas']), false),\n paths: parsePaths(parsePolygonList(data['paths'])),\n contours: parseContours(parsePolygonList(data['contours'])),\n boundary: parseBoundary(data['boundary']),\n totalArea: asNumber(data['totalArea'], 0),\n name: asString(data['name'], ''),\n mapIndex,\n mapId: mapIdFromIndex(mapIndex),\n mowPaths: [],\n availableMaps: [],\n currentMapId: null,\n lastUpdated: Date.now(),\n };\n}\n\n/**\n * Parse `M_PATH.*` keys into mow-path traces.\n *\n * PORT of `parse_mow_paths`. Reassembles the chunks, applies the `M_PATH.info`\n * split offset, extracts every `[x,y]` pair via regex, splits on the\n * `[32767,-32768]` sentinel into segments, and scales the remaining\n * coordinates ×10 (decimeters → centimeters). Always returns at most one\n * `MowerMowPath` (zone 0), matching the donor.\n */\nexport function parseMowPaths(batchData: Record<string, unknown>): MowerMowPath[] {\n const reassembled = reassembleMapChunks(batchData, 'M_PATH');\n if (!reassembled) {\n return [];\n }\n\n const splitPos = parseSplitPos(batchData['M_PATH.info']);\n let raw = reassembled;\n if (splitPos > 0 && splitPos < raw.length) {\n raw = raw.slice(splitPos);\n }\n\n const trimmed = raw.trim();\n if (trimmed.length === 0 || trimmed === '[]') {\n return [];\n }\n\n const pairPattern = /\\[(-?\\d+),(-?\\d+)\\]/g;\n const rawPairs: Array<readonly [number, number]> = [];\n for (const m of raw.matchAll(pairPattern)) {\n rawPairs.push([Number(m[1]), Number(m[2])]);\n }\n if (rawPairs.length === 0) {\n return [];\n }\n\n const segments: MowerPoint[][] = [];\n let current: MowerPoint[] = [];\n for (const [x, y] of rawPairs) {\n if (x === PATH_SENTINEL_X && y === PATH_SENTINEL_Y) {\n if (current.length > 0) {\n segments.push(current);\n current = [];\n }\n } else {\n current.push({ x: x * 10, y: y * 10 });\n }\n }\n if (current.length > 0) {\n segments.push(current);\n }\n\n if (segments.length === 0) {\n return [];\n }\n return [{ zoneId: 0, segments }];\n}\n\n/** Parse one reassembled `MAP.*` part (a JSON array of map-json-strings). */\nfunction parseMapPart(part: string): MowerMap[] {\n const trimmed = part.trim();\n if (trimmed.length === 0) {\n return [];\n }\n let arr: unknown;\n try {\n arr = JSON.parse(trimmed);\n } catch {\n return [];\n }\n if (!Array.isArray(arr)) {\n return [];\n }\n const out: MowerMap[] = [];\n for (const item of arr) {\n if (typeof item !== 'string') {\n continue;\n }\n try {\n out.push(parseMowerMap(item));\n } catch {\n // Skip malformed map entries (donor warn-and-tolerate).\n }\n }\n return out;\n}\n\n/**\n * Parse a full batch device-data response into the primary `MowerMap`.\n *\n * PORT of `parse_batch_map_data`. Reassembles `MAP.*`, splits on the\n * `MAP.info` character length, JSON-parses each part as an array of\n * map-json-strings, parses each (skipping throwers), picks the `mapIndex === 0`\n * primary (else the first), builds `availableMaps`, attaches `mowPaths`, and\n * returns the primary. Returns `null` on an empty batch or no valid arrays.\n *\n * The donor's recursive `maps: dict` cache is intentionally NOT ported — it is\n * internal HA active-map bookkeeping; we surface `availableMaps` instead.\n */\nexport function parseBatchMapData(batchData: Record<string, unknown>): MowerMap | null {\n if (Object.keys(batchData).length === 0) {\n return null;\n }\n\n const rawMap = reassembleMapChunks(batchData, 'MAP');\n if (!rawMap) {\n return null;\n }\n\n const splitPos = parseSplitPos(batchData['MAP.info']);\n const parts =\n splitPos > 0 && splitPos < rawMap.length\n ? [rawMap.slice(0, splitPos), rawMap.slice(splitPos)]\n : [rawMap];\n\n const parsedMaps: MowerMap[] = [];\n for (const part of parts) {\n parsedMaps.push(...parseMapPart(part));\n }\n\n if (parsedMaps.length === 0) {\n return null;\n }\n\n const primary = parsedMaps.find((m) => m.mapIndex === 0) ?? parsedMaps[0];\n if (!primary) {\n return null;\n }\n\n const availableMaps: MowerAvailableMap[] = [...parsedMaps]\n .sort((a, b) => a.mapId - b.mapId)\n .map((m) => ({\n mapId: m.mapId,\n mapIndex: m.mapIndex,\n name: m.name,\n totalArea: m.totalArea,\n }));\n\n const mowPaths = parseMowPaths(batchData);\n\n return {\n ...primary,\n availableMaps,\n mowPaths,\n currentMapId: primary.mapId,\n };\n}\n","/**\n * Deterministic SVG renderer for a structured `MowerMap`.\n *\n * ADAPT of antondaubert/dreame-mower's `svg_map_generator.py` — only the\n * geometry path is kept. We port `calculate_bounds`, `coord_to_pixel`\n * (aspect-preserving, Y-flipped, centred), `svg_polygon` (≥3 points),\n * `svg_path_from_segments` (M/L with consecutive-pixel dedupe), and the\n * `create_svg_document`/`finish_svg_document` framing.\n *\n * The HA-coordinator-coupled chrome (legend, status overlay, timestamp, title,\n * historical-file handling, rotation-from-coordinator, live-tracking overlay,\n * mower-position circle) is DROPPED — it depends on a `coordinator`, a file\n * path and live device state we don't carry. The renderer takes only a\n * `MowerMap` + optional `{ width, height, padding }`.\n *\n * Zone `name`s are NOT rendered as text in v1 (keeps the output injection-safe\n * and minimal — no untrusted strings reach the SVG). Pure string building, no\n * deps, cast-free.\n */\nimport type { MowerMap, MowerPoint } from './types.js';\n\nexport interface RenderMowerSvgOptions {\n /** Canvas width in pixels. Default 1200. */\n width?: number;\n /** Canvas height in pixels. Default 1200. */\n height?: number;\n /** Padding around the map content in pixels. Default 50. */\n padding?: number;\n}\n\nconst DEFAULT_WIDTH = 1200;\nconst DEFAULT_HEIGHT = 1200;\nconst DEFAULT_PADDING = 50;\n\nconst BACKGROUND = '#f5f5f0';\nconst MAP_BOUNDARY = '#006400';\nconst MOWING_PATH = '#ffa500';\nconst NAV_PATH = '#b4b4b4';\nconst OBSTACLE_STROKE = '#ff4d00';\nconst OBSTACLE_FILL = '#ff4d0065';\nconst TEXT_COLOR = '#000000';\n\n/** Zone fill/outline palette — soft pastels matching the Dreame app. */\nconst ZONE_COLORS: ReadonlyArray<readonly [string, string]> = [\n ['#a4d291c8', '#86be73'],\n ['#a0c8dcc8', '#82aac8'],\n ['#f0c8aac8', '#dcaf8c'],\n ['#f0b4b4c8', '#dc9696'],\n ['#e6dca0c8', '#d2c882'],\n ['#beaadcc8', '#aa91c8'],\n ['#aad7d2c8', '#8cc3be'],\n ['#dcbea0c8', '#c8a582'],\n];\n\ninterface Bounds {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\n/** Sentinel coordinate value used as a path-segment break in the donor. */\nconst SENTINEL = 2147483647;\n\n/** Compute the bounding box for all points, ignoring sentinels. */\nfunction calculateBounds(allPoints: readonly MowerPoint[]): Bounds {\n const valid = allPoints.filter((p) => p.x !== SENTINEL && p.y !== SENTINEL);\n if (valid.length === 0) {\n return { minX: 0, minY: 0, maxX: 100, maxY: 100 };\n }\n let minX = valid[0]?.x ?? 0;\n let maxX = minX;\n let minY = valid[0]?.y ?? 0;\n let maxY = minY;\n for (const p of valid) {\n if (p.x < minX) minX = p.x;\n if (p.x > maxX) maxX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.y > maxY) maxY = p.y;\n }\n return { minX, minY, maxX, maxY };\n}\n\ninterface Pixel {\n px: number;\n py: number;\n}\n\n/**\n * Convert mower coordinates to image pixel coordinates: aspect-preserving\n * (smaller scale wins), centred in the available space, Y-flipped so the map\n * origin sits at the bottom-left.\n */\nfunction coordToPixel(\n point: MowerPoint,\n bounds: Bounds,\n width: number,\n height: number,\n padding: number,\n): Pixel {\n let { maxX, maxY } = bounds;\n const { minX, minY } = bounds;\n if (maxX === minX) {\n maxX = minX + 100;\n }\n if (maxY === minY) {\n maxY = minY + 100;\n }\n const coordWidth = maxX - minX;\n const coordHeight = maxY - minY;\n const availableWidth = width - 2 * padding;\n const availableHeight = height - 2 * padding;\n const scale = Math.min(availableWidth / coordWidth, availableHeight / coordHeight);\n const renderedWidth = coordWidth * scale;\n const renderedHeight = coordHeight * scale;\n const offsetX = padding + (availableWidth - renderedWidth) / 2;\n const offsetY = padding + (availableHeight - renderedHeight) / 2;\n const px = Math.trunc(offsetX + (point.x - minX) * scale);\n const py = Math.trunc(offsetY + (maxY - point.y) * scale);\n return { px, py };\n}\n\n/** SVG `<polygon>` for ≥3 points; `''` otherwise. */\nfunction svgPolygon(\n points: readonly MowerPoint[],\n bounds: Bounds,\n width: number,\n height: number,\n padding: number,\n fill: string,\n stroke: string,\n): string {\n if (points.length < 3) {\n return '';\n }\n const pixels = points\n .map((p) => coordToPixel(p, bounds, width, height, padding))\n .map((p) => `${p.px},${p.py}`)\n .join(' ');\n return `<polygon points=\"${pixels}\" fill=\"${fill}\" stroke=\"${stroke}\"/>`;\n}\n\n/**\n * SVG `<path>` (M/L) built from one or more segments, skipping consecutive\n * duplicate pixels and segments shorter than two points. `''` when nothing\n * renders.\n */\nfunction svgPathFromSegments(\n segments: readonly (readonly MowerPoint[])[],\n bounds: Bounds,\n width: number,\n height: number,\n padding: number,\n stroke: string,\n strokeWidth: number,\n dashed: boolean,\n): string {\n const parts: string[] = [];\n for (const segment of segments) {\n if (segment.length < 2) {\n continue;\n }\n const first = segment[0];\n if (!first) {\n continue;\n }\n let prev = coordToPixel(first, bounds, width, height, padding);\n parts.push(`M ${prev.px} ${prev.py}`);\n for (let i = 1; i < segment.length; i += 1) {\n const point = segment[i];\n if (!point) {\n continue;\n }\n const pixel = coordToPixel(point, bounds, width, height, padding);\n if (pixel.px !== prev.px || pixel.py !== prev.py) {\n parts.push(`L ${pixel.px} ${pixel.py}`);\n prev = pixel;\n }\n }\n }\n if (parts.length === 0) {\n return '';\n }\n const dash = dashed ? ' stroke-dasharray=\"10,5\"' : '';\n return `<path d=\"${parts.join(' ')}\" stroke=\"${stroke}\" stroke-width=\"${strokeWidth}\"${dash} fill=\"none\"/>`;\n}\n\n/**\n * Render a `MowerMap` into a deterministic SVG document string.\n *\n * Draw order (back to front): background, nav paths (dashed grey), zone fills\n * (per-zone pastel), zone outlines, mow-path tracks (orange), forbidden-area\n * polygons (red). An empty map renders a centred \"No map data available\"\n * fallback inside a closed `<svg>`.\n */\nexport function renderMowerSvg(map: MowerMap, opts: RenderMowerSvgOptions = {}): string {\n const width = opts.width ?? DEFAULT_WIDTH;\n const height = opts.height ?? DEFAULT_HEIGHT;\n const padding = opts.padding ?? DEFAULT_PADDING;\n\n const lines: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<svg width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\" xmlns=\"http://www.w3.org/2000/svg\">`,\n `<rect width=\"100%\" height=\"100%\" fill=\"${BACKGROUND}\"/>`,\n ];\n\n const allPoints: MowerPoint[] = [];\n for (const zone of map.zones) {\n allPoints.push(...zone.path);\n }\n for (const area of map.forbiddenAreas) {\n allPoints.push(...area.path);\n }\n for (const path of map.paths) {\n allPoints.push(...path.path);\n }\n for (const contour of map.contours) {\n allPoints.push(...contour.path);\n }\n for (const mp of map.mowPaths) {\n for (const seg of mp.segments) {\n allPoints.push(...seg);\n }\n }\n\n if (allPoints.length === 0) {\n lines.push(\n `<text x=\"${Math.trunc(width / 2)}\" y=\"${Math.trunc(height / 2)}\" ` +\n `font-family=\"Arial, sans-serif\" font-size=\"16\" fill=\"${TEXT_COLOR}\" ` +\n `text-anchor=\"middle\">No map data available</text>`,\n );\n lines.push('</svg>');\n return lines.join('\\n');\n }\n\n const bounds = calculateBounds(allPoints);\n const multiZone = map.zones.length > 1;\n\n // 0. Inter-zone navigation paths — dashed grey, behind everything.\n for (const navPath of map.paths) {\n const dashed = svgPathFromSegments(\n [navPath.path],\n bounds,\n width,\n height,\n padding,\n NAV_PATH,\n 3,\n true,\n );\n if (dashed) {\n lines.push(dashed);\n }\n }\n\n // 1. Zone fills (only when more than one zone, matching the donor).\n map.zones.forEach((zone, i) => {\n if (!multiZone) {\n return;\n }\n const palette = ZONE_COLORS[i % ZONE_COLORS.length];\n const [fill, outline] = palette ?? ZONE_COLORS[0] ?? ['#cccccc', '#888888'];\n const poly = svgPolygon(zone.path, bounds, width, height, padding, fill, outline);\n if (poly) {\n lines.push(poly);\n }\n });\n\n // 2. Zone boundary outlines.\n map.zones.forEach((zone, i) => {\n const outline = multiZone\n ? (ZONE_COLORS[i % ZONE_COLORS.length]?.[1] ?? MAP_BOUNDARY)\n : MAP_BOUNDARY;\n const boundary = svgPathFromSegments(\n [zone.path],\n bounds,\n width,\n height,\n padding,\n outline,\n 2,\n false,\n );\n if (boundary) {\n lines.push(boundary);\n }\n });\n\n // 3. Mow-path tracks — orange.\n for (const mp of map.mowPaths) {\n const track = svgPathFromSegments(\n mp.segments,\n bounds,\n width,\n height,\n padding,\n MOWING_PATH,\n 2,\n false,\n );\n if (track) {\n lines.push(track);\n }\n }\n\n // 4. Forbidden-area polygons — red-ish fill.\n for (const area of map.forbiddenAreas) {\n const poly = svgPolygon(\n area.path,\n bounds,\n width,\n height,\n padding,\n OBSTACLE_FILL,\n OBSTACLE_STROKE,\n );\n if (poly) {\n lines.push(poly);\n }\n }\n\n lines.push('</svg>');\n return lines.join('\\n');\n}\n","import { BaseDevice, type BaseDeviceInput } from '../../device/base-device.js';\nimport {\n MOWER_ACTION,\n MOWER_PROP,\n buildAllAreaPayload,\n buildEdgePayload,\n buildResumePayload,\n buildSpotPayload,\n buildZonePayload,\n} from './properties.js';\nimport {\n MowerChargingStatus,\n MowerControlAction,\n MowerFault,\n MowerStatus,\n MowerTaskStatus,\n} from './enums.js';\nimport {\n asNum,\n enumLookup,\n parseControlStatus,\n parseTaskDescriptor,\n type MowerControlState,\n type MowerTaskDescriptor,\n} from './decode.js';\nimport {\n MowerCapabilityResolver,\n getMowerCapabilities,\n type MowerCapabilities,\n} from './capabilities.js';\nimport { DreameError } from '../../transport/errors.js';\nimport {\n parseBatchMapData,\n renderMowerSvg,\n type MowerMap,\n type RenderMowerSvgOptions,\n} from './map/index.js';\n\n/**\n * Injectable batch-fetch seam. Given a device id + the batch property groups\n * (e.g. `['MAP','M_PATH']`), resolves the raw `{ 'MAP.0': …, 'MAP.info': … }`\n * chunk dict the firmware returns. The default wires `getBatchDeviceDatas` from\n * the cloud layer — but its live endpoint path is NOT yet recovered (see\n * `cloud/commands.ts`), so production callers MUST inject a working fetcher.\n * Tests inject a fake with no cast.\n */\nexport type BatchDeviceDataFetcher = (\n did: string,\n props: string[],\n) => Promise<Record<string, unknown>>;\n\n/** Default batch property groups the firmware exposes the vector map under. */\nexport const DEFAULT_MAP_PROPS: readonly string[] = ['MAP', 'M_PATH'];\n\n/** Mower-specific construction input: adds the injectable batch-fetch seam. */\nexport interface MowerDeviceInput extends BaseDeviceInput {\n /** Inject the batched device-data fetcher (maps). Tests pass a fake. */\n getBatchDeviceDatas?: BatchDeviceDataFetcher;\n}\n\nconst STATUS = enumLookup<MowerStatus>(\n Object.values(MowerStatus).filter((v): v is MowerStatus => typeof v === 'number'),\n);\nconst CHARGING = enumLookup<MowerChargingStatus>(\n Object.values(MowerChargingStatus).filter((v): v is MowerChargingStatus => typeof v === 'number'),\n);\nconst TASK_STATUS = enumLookup<MowerTaskStatus>(\n Object.values(MowerTaskStatus).filter((v): v is MowerTaskStatus => typeof v === 'number'),\n);\nconst FAULT = enumLookup<MowerFault>(\n Object.values(MowerFault).filter((v): v is MowerFault => typeof v === 'number'),\n);\n\n/** A typed Dreame-mower handle (state + capability-gated commands). */\nexport class MowerDevice extends BaseDevice {\n readonly #caps: MowerCapabilities;\n readonly #fetchBatch: BatchDeviceDataFetcher | null;\n #lastMap: MowerMap | null = null;\n\n constructor(input: MowerDeviceInput) {\n super({\n ...input,\n // Inject the mower resolver so the inherited generic `capabilities` getter\n // carries the mower token set (no banned cast).\n capabilities: input.capabilities ?? new MowerCapabilityResolver().resolve(input.device.model),\n });\n this.#caps = getMowerCapabilities(input.device.model);\n this.#fetchBatch = input.getBatchDeviceDatas ?? null;\n }\n\n /** Rich, mower-specific capability record. */\n get mowerCapabilities(): MowerCapabilities {\n return this.#caps;\n }\n\n #num(siid: number, piid: number): number | null {\n return asNum(this.getProperty(siid, piid)?.value);\n }\n\n // -- typed state --------------------------------------------------------\n get statusRaw(): number | null {\n return this.#num(MOWER_PROP.STATUS.siid, MOWER_PROP.STATUS.piid);\n }\n get status(): MowerStatus | null {\n return STATUS(this.statusRaw);\n }\n get battery(): number | null {\n return this.#num(MOWER_PROP.BATTERY.siid, MOWER_PROP.BATTERY.piid);\n }\n get chargingRaw(): number | null {\n return this.#num(MOWER_PROP.CHARGING_STATUS.siid, MOWER_PROP.CHARGING_STATUS.piid);\n }\n get charging(): MowerChargingStatus | null {\n return CHARGING(this.chargingRaw);\n }\n /** Docked => on the dock (Charging / ChargingComplete state). */\n get isDocked(): boolean {\n const s = this.status;\n return s === MowerStatus.Charging || s === MowerStatus.ChargingComplete;\n }\n get isMowing(): boolean {\n return this.status === MowerStatus.Mowing;\n }\n get taskStatusRaw(): number | null {\n return this.#num(MOWER_PROP.TASK_STATUS.siid, MOWER_PROP.TASK_STATUS.piid);\n }\n /**\n * TASK_STATUS (5:104) decoded to {@link MowerTaskStatus}, or null. The donor\n * names only code 7 (SpotIncomplete); codes 2/3/10/13 are documented \"Unknown\n * task status\" and intentionally surface as raw via {@link taskStatusRaw}.\n */\n get taskStatus(): MowerTaskStatus | null {\n return TASK_STATUS(this.taskStatusRaw);\n }\n get faultRaw(): number | null {\n return this.#num(MOWER_PROP.DEVICE_CODE.siid, MOWER_PROP.DEVICE_CODE.piid);\n }\n /**\n * DEVICE_CODE (2:2) decoded to a {@link MowerFault} (the donor's\n * BASE_DEVICE_CODES registry, 0..73), or null for a model-specific/undocumented\n * code — which still surfaces as the raw number via {@link faultRaw}.\n */\n get fault(): MowerFault | null {\n return FAULT(this.faultRaw);\n }\n /** Parsed scheduling task descriptor (2:50), or null. */\n get task(): MowerTaskDescriptor | null {\n return parseTaskDescriptor(\n this.getProperty(MOWER_PROP.SCHEDULING_TASK.siid, MOWER_PROP.SCHEDULING_TASK.piid)?.value,\n );\n }\n /**\n * Mowing coverage target / progress signal from the task descriptor (`d.o`).\n * This is the P4 progress surface; the byte-accurate pose-track % is P5.\n */\n get coverageTargetPct(): number | null {\n return this.task?.coverageTarget ?? null;\n }\n /** Parsed per-zone control status (2:56), or null. */\n get controlStatus(): MowerControlState | null {\n return parseControlStatus(\n this.getProperty(MOWER_PROP.MOWER_CONTROL_STATUS.siid, MOWER_PROP.MOWER_CONTROL_STATUS.piid)\n ?.value,\n );\n }\n get controlAction(): MowerControlAction | null {\n return this.controlStatus?.action ?? null;\n }\n\n // -- command helpers ----------------------------------------------------\n #requireCap(flag: boolean, op: string, feature: string): void {\n if (!flag) {\n throw new DreameError(`${op}: model ${this.model} does not support ${feature}`);\n }\n }\n\n /** Send a scheduling-task action (2:50) carrying a single opcode object. */\n #sendTask(payload: Record<string, unknown>): Promise<unknown> {\n return this.callAction(MOWER_PROP.SCHEDULING_TASK.siid, MOWER_PROP.SCHEDULING_TASK.piid, [\n payload,\n ]);\n }\n\n // -- no-arg commands ----------------------------------------------------\n /**\n * Start the generic mowing action (siid 5 aiid 1). Named `startMowing` — NOT\n * `start` — because `BaseDevice.start()` is the MQTT lifecycle method the\n * facade relies on; overriding it would break handle startup.\n */\n startMowing(): Promise<unknown> {\n return this.callAction(MOWER_ACTION.START_MOWING.siid, MOWER_ACTION.START_MOWING.aiid, []);\n }\n pause(): Promise<unknown> {\n return this.callAction(MOWER_ACTION.PAUSE.siid, MOWER_ACTION.PAUSE.aiid, []);\n }\n stop(): Promise<unknown> {\n return this.callAction(MOWER_ACTION.STOP.siid, MOWER_ACTION.STOP.aiid, []);\n }\n /** Send the mower to its dock (siid 5 aiid 3). */\n dock(): Promise<unknown> {\n return this.callAction(MOWER_ACTION.DOCK.siid, MOWER_ACTION.DOCK.aiid, []);\n }\n /**\n * Resume mowing after a pause. Encoded as the continueControl opcode\n * `{m:'a', p:0, o:5}` sent as the single in-param of the SCHEDULING_TASK\n * (2:50) action — NOT a siid-5 action. Mirrors the donor TASK_PAYLOAD_RESUME.\n */\n async resume(): Promise<unknown> {\n this.#requireCap(this.#caps.canResume, 'resume', 'resume');\n return this.#sendTask(buildResumePayload());\n }\n\n // -- targeted mowing ----------------------------------------------------\n /** All-area, map-targeted mowing (2:50 o:100). */\n async startMowingAllArea(mapId: number): Promise<unknown> {\n this.#requireCap(this.#caps.canMowAllArea, 'startMowingAllArea', 'all-area mowing');\n return this.#sendTask(buildAllAreaPayload(Math.trunc(mapId)));\n }\n /** Zone-selective mowing (2:50 o:102). */\n async startMowingZones(zoneIds: number[]): Promise<unknown> {\n this.#requireCap(this.#caps.canMowZones, 'startMowingZones', 'zone mowing');\n if (zoneIds.length === 0) {\n throw new RangeError('startMowingZones: zoneIds must not be empty');\n }\n return this.#sendTask(buildZonePayload(zoneIds.map((z) => Math.trunc(z))));\n }\n /** Edge / contour mowing (2:50 o:101). Contour ids are two-int pairs [[1,0]]. */\n async startMowingEdges(contourIds: number[][]): Promise<unknown> {\n this.#requireCap(this.#caps.canMowEdges, 'startMowingEdges', 'edge mowing');\n if (contourIds.length === 0) {\n throw new RangeError('startMowingEdges: contourIds must not be empty');\n }\n return this.#sendTask(buildEdgePayload(contourIds));\n }\n /** Spot mowing (2:50 o:103). */\n async startMowingSpots(spotAreaIds: number[]): Promise<unknown> {\n this.#requireCap(this.#caps.canMowSpots, 'startMowingSpots', 'spot mowing');\n if (spotAreaIds.length === 0) {\n throw new RangeError('startMowingSpots: spotAreaIds must not be empty');\n }\n return this.#sendTask(buildSpotPayload(spotAreaIds.map((s) => Math.trunc(s))));\n }\n\n /**\n * Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the\n * mower — reads {@link MowerDevice.DEFAULT_PROPS} from the cloud-cached\n * endpoint. After it resolves, every typed getter (status/battery/charging/\n * coverage/task/controlAction…) reflects the cached values, so a docked/\n * standby mower reports its state exactly as the Dreamehome app does.\n */\n async refreshFromCache(): Promise<void> {\n await this.refreshCachedProperties([...MowerDevice.DEFAULT_PROPS]);\n }\n\n // -- maps ---------------------------------------------------------------\n /** The most-recently-parsed map, or `null` until {@link getMap} succeeds. */\n get lastMap(): MowerMap | null {\n return this.#lastMap;\n }\n\n /**\n * Fetch the batched vector-map data, parse it into a {@link MowerMap}, cache\n * it as {@link lastMap}, and return it. Capability-gated on `canMap`.\n *\n * The batch fetcher is injected at construction (the live cloud endpoint path\n * is not yet recovered — see `cloud/commands.ts`). Rejects with\n * {@link DreameError} if no fetcher was injected or the batch yields no\n * parseable map (asleep mower / empty data).\n */\n async getMap(props: readonly string[] = DEFAULT_MAP_PROPS): Promise<MowerMap> {\n this.#requireCap(this.#caps.canMap, 'getMap', 'map parsing');\n if (!this.#fetchBatch) {\n throw new DreameError(\n 'getMap: no batch device-data fetcher injected (live endpoint unrecovered)',\n );\n }\n const batch = await this.#fetchBatch(this.deviceId, [...props]);\n const map = parseBatchMapData(batch);\n if (!map) {\n throw new DreameError('getMap: batch device-data contained no parseable map');\n }\n this.#lastMap = map;\n return map;\n }\n\n /**\n * Render the current map to a deterministic SVG string. Uses {@link lastMap}\n * when present, otherwise fetches first via {@link getMap}.\n */\n async mapSvg(opts?: RenderMowerSvgOptions): Promise<string> {\n const map = this.#lastMap ?? (await this.getMap());\n return renderMowerSvg(map, opts ?? {});\n }\n\n /** Props worth seeding on start() / polling — exported for the facade. */\n static readonly DEFAULT_PROPS = [\n MOWER_PROP.STATUS,\n MOWER_PROP.DEVICE_CODE,\n MOWER_PROP.BATTERY,\n MOWER_PROP.CHARGING_STATUS,\n MOWER_PROP.TASK_STATUS,\n MOWER_PROP.SCHEDULING_TASK,\n MOWER_PROP.MOWER_CONTROL_STATUS,\n ] as const;\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type { DreameDevice, DreameSession } from '../cloud/types.js';\nimport { login as defaultLogin, refresh as defaultRefresh } from '../auth/dreame-account.js';\nimport { listDevices as defaultListDevices } from '../cloud/devices.js';\nimport { BaseDevice } from '../device/base-device.js';\nimport { VacuumDevice } from '../models/vacuum/vacuum-device.js';\nimport { MowerDevice } from '../models/mower/mower-device.js';\nimport { TypedEmitter } from '../transport/typed-emitter.js';\nimport { DreameAuthError } from '../transport/errors.js';\nimport type { FetchImpl } from '../transport/http.js';\nimport type { NodreameOptions, DeviceEvent, StateChangedEvent } from './types.js';\n\nconst DEFAULT_REFRESH_LEEWAY_SECS = 100;\n\n/**\n * Device-type factory: pick the handle class by model prefix. Returns a\n * `BaseDevice` constructor (`VacuumDevice` / `MowerDevice` are subtypes, so the\n * return is covariant and needs no cast).\n */\nexport function deviceClassFor(model: string): typeof BaseDevice {\n if (model.startsWith('dreame.vacuum.')) {\n return VacuumDevice;\n }\n if (model.startsWith('dreame.mower.')) {\n return MowerDevice;\n }\n return BaseDevice;\n}\n\n/** Args the facade passes to `createDevice` (a seam for tests). */\nexport interface CreateDeviceArgs {\n device: DreameDevice;\n region: DreameRegion;\n sessionRef: () => DreameSession;\n}\n\n/** Injectable collaborators — defaults wire the real P1 modules. */\nexport interface NodreameDeps {\n login(input: {\n email: string;\n password: string;\n region: DreameRegion;\n country?: string;\n lang?: string;\n fetchImpl?: FetchImpl;\n }): Promise<DreameSession>;\n refresh(input: {\n refreshToken: string;\n region: DreameRegion;\n country?: string;\n lang?: string;\n fetchImpl?: FetchImpl;\n }): Promise<DreameSession>;\n listDevices(input: {\n session: DreameSession;\n region: DreameRegion;\n fetchImpl?: FetchImpl;\n }): Promise<DreameDevice[]>;\n createDevice(args: CreateDeviceArgs): BaseDevice;\n}\n\nfunction defaultDeps(opts: NodreameOptions): NodreameDeps {\n return {\n login: (input) => defaultLogin(input),\n refresh: (input) => defaultRefresh(input),\n listDevices: (input) => defaultListDevices(input),\n createDevice: (args) => {\n const DeviceClass = deviceClassFor(args.device.model);\n return new DeviceClass({\n device: args.device,\n region: args.region,\n sessionRef: args.sessionRef,\n ...(opts.fetchInitialValues !== undefined\n ? { fetchInitialValues: opts.fetchInitialValues }\n : {}),\n ...(opts.pollIntervalMs !== undefined ? { pollIntervalMs: opts.pollIntervalMs } : {}),\n });\n },\n };\n}\n\nexport type NodreameEvents = {\n /** Re-emitted device state change, tagged with the deviceId. */\n stateChanged: [StateChangedEvent];\n /** Re-emitted device event, tagged with the deviceId. */\n event: [DeviceEvent];\n error: [Error];\n};\n\n/** Public facade for the Dreamehome cloud. */\nexport class Nodreame extends TypedEmitter<NodreameEvents> {\n readonly #opts: NodreameOptions;\n readonly #deps: NodreameDeps;\n readonly #leewayMs: number;\n #session: DreameSession | null = null;\n #devices: BaseDevice[] = [];\n #closed = false;\n\n constructor(opts: NodreameOptions, deps?: NodreameDeps) {\n super();\n if (!opts.username || !opts.password) {\n throw new DreameAuthError('username and password are required');\n }\n this.#opts = opts;\n this.#deps = deps ?? defaultDeps(opts);\n this.#leewayMs = (opts.refreshLeewaySecs ?? DEFAULT_REFRESH_LEEWAY_SECS) * 1000;\n }\n\n get region(): DreameRegion {\n return this.#opts.region;\n }\n\n get session(): DreameSession | null {\n return this.#session;\n }\n\n get devices(): readonly BaseDevice[] {\n return this.#devices;\n }\n\n /** Authenticate and stash the single shared session. */\n async login(): Promise<DreameSession> {\n this.#session = await this.#deps.login({\n email: this.#opts.username,\n password: this.#opts.password,\n region: this.#opts.region,\n ...(this.#opts.country !== undefined ? { country: this.#opts.country } : {}),\n ...(this.#opts.lang !== undefined ? { lang: this.#opts.lang } : {}),\n ...(this.#opts.fetchImpl !== undefined ? { fetchImpl: this.#opts.fetchImpl } : {}),\n });\n return this.#session;\n }\n\n /** Return a valid session, refreshing proactively within the leeway window. */\n async ensureSession(): Promise<DreameSession> {\n const current = this.#session;\n if (!current) {\n return this.login();\n }\n if (Date.now() < current.expiresAt - this.#leewayMs) {\n return current;\n }\n if (current.refreshToken) {\n try {\n const next = await this.#deps.refresh({\n refreshToken: current.refreshToken,\n region: this.#opts.region,\n ...(this.#opts.country !== undefined ? { country: this.#opts.country } : {}),\n ...(this.#opts.lang !== undefined ? { lang: this.#opts.lang } : {}),\n ...(this.#opts.fetchImpl !== undefined ? { fetchImpl: this.#opts.fetchImpl } : {}),\n });\n await this.#adoptSession(next);\n return next;\n } catch {\n // fall through to a full re-login\n }\n }\n const fresh = await this.login();\n await this.#propagateSession(fresh);\n return fresh;\n }\n\n /** Discover devices and build a live handle per device. */\n async discoverDevices(): Promise<readonly BaseDevice[]> {\n if (this.#closed) {\n throw new DreameAuthError('client is closed');\n }\n const session = await this.ensureSession();\n const records = await this.#deps.listDevices({\n session,\n region: this.#opts.region,\n ...(this.#opts.fetchImpl !== undefined ? { fetchImpl: this.#opts.fetchImpl } : {}),\n });\n const handles = records.map((device) =>\n this.#deps.createDevice({\n device,\n region: this.#opts.region,\n sessionRef: () => this.#requireSession(),\n }),\n );\n for (const h of handles) {\n h.on('stateChanged', (e) => this.emit('stateChanged', e));\n h.on('event', (e) => this.emit('event', e));\n h.on('error', (err) => this.emit('error', err));\n await h.start();\n }\n // Close any handles from a previous discovery before adopting the new set —\n // each holds an open MQTT push + a poll timer that would otherwise leak.\n const previous = this.#devices;\n this.#devices = [...handles];\n await Promise.all(previous.map((d) => d.close()));\n return this.#devices;\n }\n\n /** Tear everything down: close every device push and clear timers. */\n async close(): Promise<void> {\n this.#closed = true;\n const devices = this.#devices;\n this.#devices = [];\n await Promise.all(devices.map((d) => d.close()));\n this.removeAllListeners();\n }\n\n #requireSession(): DreameSession {\n if (!this.#session) {\n throw new DreameAuthError('no active session — call login() first');\n }\n return this.#session;\n }\n\n async #adoptSession(session: DreameSession): Promise<void> {\n this.#session = session;\n await this.#propagateSession(session);\n }\n\n /** Push the refreshed token to every live device push. */\n async #propagateSession(session: DreameSession): Promise<void> {\n if (this.#closed) {\n return;\n }\n await Promise.all(this.#devices.map((d) => d.applySession(session)));\n }\n}\n"],"mappings":"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","names":["MODEL_CAPABILITIES","undiciFetch","import_zod","mqtt","key","MiotState","ChargingStatus","SuctionLevel","WaterVolume","CleaningMode","MiotError","TaskStatus","import_node_crypto","key","key","z","MowerStatus","MowerChargingStatus","MowerControlAction","MowerTaskStatus","MowerFault","FALLBACK","deepFreeze","MODEL_CAPABILITIES","tokensFor","isRecord","key","CHARGING","z"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/support/version.ts","../src/transport/errors.ts","../src/transport/crypto.ts","../src/auth/config.ts","../src/transport/headers.ts","../src/transport/fetch.ts","../src/transport/http.ts","../src/transport/schemas.ts","../src/auth/dreame-account.ts","../src/cloud/devices.ts","../src/cloud/commands.ts","../src/transport/mqtt-push.ts","../src/transport/typed-emitter.ts","../src/device/capability.ts","../src/device/base-device.ts","../src/models/vacuum/properties.ts","../src/models/vacuum/enums.ts","../src/models/_shared/decode.ts","../src/models/vacuum/decode.ts","../src/models/vacuum/capabilities.ts","../src/models/vacuum/map/envelope.ts","../src/models/vacuum/map/header.ts","../src/models/vacuum/map/tail.ts","../src/models/vacuum/map/pixel-grid.ts","../src/models/vacuum/map/path.ts","../src/models/vacuum/map/field-utils.ts","../src/models/vacuum/map/obstacles.ts","../src/models/vacuum/map/geometry.ts","../src/models/vacuum/map/cleaned-area.ts","../src/models/vacuum/map/decode.ts","../src/models/vacuum/map/render.ts","../src/models/vacuum/map/oss-fetch.ts","../src/models/vacuum/vacuum-device.ts","../src/models/mower/properties.ts","../src/models/mower/enums.ts","../src/models/mower/decode.ts","../src/models/mower/capabilities.ts","../src/models/mower/map/parser.ts","../src/models/mower/map/render.ts","../src/models/mower/mower-device.ts","../src/api/nodreame.ts","../src/diagnostics/redact.ts","../src/diagnostics/dump-format.ts","../src/diagnostics/dumper.ts"],"sourcesContent":["// Public API surface of nodedreame.\n// Phase 1 exports only the error classes and core domain types; transport,\n// cloud and model internals stay private to keep the published surface small\n// and stable. The high-level facade is added in Phase 2.\n\nexport { LIBRARY_NAME } from './support/version.js';\n\n// Stable public surface for Phase 1: error classes + core types. The full\n// facade (login/discoverDevices) is added in Phase 2 — we intentionally do\n// not export transport internals here to keep the published API small.\n\nexport {\n DreameError,\n DreameAuthError,\n DreameApiError,\n DreameDeviceOfflineError,\n DreameTransportError,\n} from './transport/errors.js';\n\nexport type { DreameRegion } from './auth/config.js';\nexport type {\n DreameSession,\n DreameDevice,\n DreameCloudState,\n MiotProp,\n MiotAction,\n PropertyWrite,\n PropertyResult,\n} from './cloud/types.js';\n\n// --- Phase 2: high-level facade + generic device handle ------------------\nexport { Nodreame } from './api/nodreame.js';\nexport type { NodreameDeps, CreateDeviceArgs, NodreameEvents } from './api/nodreame.js';\nexport { BaseDevice } from './device/base-device.js';\nexport type { BaseDeviceEvents } from './device/base-device.js';\nexport { DefaultCapabilityResolver, resolveCapabilities } from './device/capability.js';\nexport type { CapabilityResolver, DeviceCapabilities } from './device/capability.js';\nexport type {\n NodreameOptions,\n PropertyState,\n PropertyChangedEvent,\n StateChangedEvent,\n DeviceEvent,\n} from './api/types.js';\n\n// --- Phase 3: vacuum model ------------------------------------------------\n// Public vacuum surface only: the typed VacuumDevice handle, its value enums,\n// and the capability records/resolver. Internal siid/piid/aiid property maps,\n// the model factory's property tables, and the decode helpers stay private.\nexport { VacuumDevice } from './models/vacuum/vacuum-device.js';\nexport type { CleanOpts, VacuumGetMapInput } from './models/vacuum/vacuum-device.js';\n// Fetcher-injection seam for VacuumDevice.getMap: the interface a custom\n// signed-blob fetcher implements, plus its input shape. The concrete\n// OssFetcher class and the decode internals stay private.\nexport type { OssFetcherLike, OssFetchInput } from './models/vacuum/map/index.js';\nexport {\n MiotState,\n ChargingStatus,\n SuctionLevel,\n WaterVolume,\n CleaningMode,\n MiotError,\n TaskStatus,\n} from './models/vacuum/enums.js';\nexport {\n getVacuumCapabilities,\n VacuumCapabilityResolver,\n MODEL_CAPABILITIES as VACUUM_MODEL_CAPABILITIES,\n} from './models/vacuum/capabilities.js';\nexport type { VacuumCapabilities } from './models/vacuum/capabilities.js';\n\n// --- Phase 4: mower model -------------------------------------------------\n// Public mower surface only: the typed MowerDevice handle, its value enums,\n// the capability records/resolver, and the parsed task/control types. Internal\n// siid/piid/aiid maps, opcode payload builders, decode helpers, and the\n// deviceClassFor factory stay private.\nexport { MowerDevice } from './models/mower/mower-device.js';\n// Batch-fetch injection seam for MowerDevice.getMap: the fetcher signature and\n// the construction input that carries it. The opcode/decode internals and the\n// device-class factory stay private.\nexport type { BatchDeviceDataFetcher, MowerDeviceInput } from './models/mower/mower-device.js';\nexport {\n MowerStatus,\n MowerChargingStatus,\n MowerControlAction,\n MowerTaskStatus,\n MowerFault,\n} from './models/mower/enums.js';\nexport {\n getMowerCapabilities,\n MowerCapabilityResolver,\n MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES,\n} from './models/mower/capabilities.js';\nexport type { MowerCapabilities } from './models/mower/capabilities.js';\nexport type { MowerTaskDescriptor, MowerControlState } from './models/mower/decode.js';\n\n// --- Phase 5: maps --------------------------------------------------------\n// Public map surface: the structured model types + the two renderers. The\n// binary/JSON decoders, the OSS signed-blob fetcher, and every intermediate\n// decode step (envelope/header/pixel-grid/path/obstacles/geometry/cleaned-area/\n// merge, mower chunk reassembly + parser) stay PRIVATE — consumers obtain maps\n// via VacuumDevice.getMap()/currentSegmentId/lastMap and MowerDevice.getMap()/\n// mapSvg(), all reachable through the already-exported device handles.\nexport { renderVacuumPng } from './models/vacuum/map/render.js';\nexport type { RenderVacuumPngOptions } from './models/vacuum/map/render.js';\nexport type {\n VacuumMap,\n MapDimensions,\n MapBoundingBox,\n MapPoint,\n MapPose,\n MapRun,\n MapLayer,\n MapLayerType,\n MapSegment,\n MapPath,\n MapPathType,\n MapObstacle,\n MapFrameType,\n MapVirtualWall,\n MapRestrictedArea,\n MapLowLyingArea,\n MapWallsInfo,\n MapStorey,\n MapRoom,\n MapRoomWall,\n MapCleanedAreaOverlay,\n} from './models/vacuum/map/types.js';\n\nexport { renderMowerSvg } from './models/mower/map/render.js';\nexport type { RenderMowerSvgOptions } from './models/mower/map/render.js';\nexport type {\n MowerMap,\n MowerPoint,\n MowerZone,\n MowerSpotArea,\n MowerPathEntry,\n MowerContour,\n MowerMapBoundary,\n MowerMowPath,\n MowerAvailableMap,\n} from './models/mower/map/types.js';\n\n// --- Diagnostics: read-only device dump ----------------------------------\n// Public surface: the createDumper/createClientDumper factories + the shared\n// DeviceDump output type + the options type. The Dumper class, redact(), the\n// DeviceDumpSchema, the accumulator, and the per-model decoder/catalog helpers\n// stay PRIVATE — consumers build a dump only via the factory.\nexport { createDumper, createClientDumper } from './diagnostics/dumper.js';\nexport type { DumperOptions } from './diagnostics/dumper.js';\nexport type { DeviceDump } from './diagnostics/dump-format.js';\n","/** Public name of this library. */\nexport const LIBRARY_NAME = 'nodedreame';\n\n/**\n * Public version of this library, embedded in a {@link DeviceDump}'s\n * `libraryVersion`. Kept in lock-step with `package.json` (a version-match test\n * guards drift) — a string constant avoids an ESM JSON-import assertion wrinkle\n * in the published bundle.\n */\nexport const LIBRARY_VERSION = '1.3.0';\n","export class DreameError extends Error {\n constructor(\n message: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = 'DreameError';\n }\n}\n\nexport class DreameAuthError extends DreameError {\n constructor(\n message: string,\n public readonly status?: number,\n cause?: unknown,\n ) {\n super(message, cause);\n this.name = 'DreameAuthError';\n }\n}\n\nexport class DreameApiError extends DreameError {\n constructor(\n message: string,\n public readonly status: number,\n public readonly body?: unknown,\n cause?: unknown,\n ) {\n super(message, cause);\n this.name = 'DreameApiError';\n }\n}\n\n/**\n * Thrown when the cloud returns code 80001. The literal `msg` claims the\n * device is offline — that interpretation is FREQUENTLY WRONG. 80001 means\n * the cloud's HTTP-side waiter for the device's MQTT ACK gave up after ~8s.\n * It does NOT prove the action failed to reach the device. The MQTT\n * subscription is the source of truth for reachability. The only true\n * positive is a genuinely unreachable device (powered off / network lost /\n * rebooting) — in which case no MQTT echo arrives either.\n */\nexport class DreameDeviceOfflineError extends DreameApiError {\n constructor(message: string, status: number, body?: unknown) {\n super(message, status, body);\n this.name = 'DreameDeviceOfflineError';\n }\n}\n\nexport class DreameTransportError extends DreameError {\n constructor(message: string, cause?: unknown) {\n super(message, cause);\n this.name = 'DreameTransportError';\n }\n}\n","import { createCipheriv, createHash, randomBytes } from 'node:crypto';\n\n/**\n * App-global secrets extracted from the Dreamehome Flutter binary. These\n * rotate with new app versions — if login starts failing with no obvious\n * cause, re-extract from the latest APK. Last verified against\n * `dreame-meta: cv=i_829`.\n */\nconst PASSWORD_SALT = 'RAylYC%fmSKp7%Tq';\nconst RLC_AES_KEY = 'EETjszu*XI5znHsI';\n\n/**\n * Salted MD5 of the plaintext password, lower-case hex. Sent on the wire\n * instead of the cleartext password.\n */\nexport function hashPassword(plaintext: string): string {\n return createHash('md5')\n .update(plaintext + PASSWORD_SALT)\n .digest('hex');\n}\n\n/**\n * Compute the value of the `dreame-rlc` request header: AES-128-ECB (PKCS7\n * padding) of `<region>|<lang>|<country>` with the static app key, output as\n * lowercase hex.\n */\nexport function buildRlcHeader(region: string, lang: string, country: string): string {\n const plaintext = `${region}|${lang}|${country}`;\n const cipher = createCipheriv('aes-128-ecb', Buffer.from(RLC_AES_KEY, 'utf8'), null);\n const out = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n return out.toString('hex');\n}\n\n/** Random MQTT client id matching the format used by the Dreamehome app. */\nexport function randomMqttClientId(): string {\n return 'p_' + randomBytes(8).toString('hex');\n}\n\n/**\n * Random positive 31-bit integer for the `id` field of a sendCommand\n * envelope. Wide enough that concurrent fan-out won't collide.\n */\nexport function randomRequestId(): number {\n return Math.floor(Math.random() * 0x7fffffff) + 1;\n}\n","/** Supported Dreame cloud regions. */\nexport type DreameRegion = 'eu' | 'us' | 'cn' | 'ru' | 'sg' | 'in' | 'de' | 'tw';\n\n/** All regions, in canonical order. Used for table-completeness checks. */\nexport const ALL_REGIONS: readonly DreameRegion[] = [\n 'eu',\n 'us',\n 'cn',\n 'ru',\n 'sg',\n 'in',\n 'de',\n 'tw',\n];\n\n/** Hostname (with non-standard port) of the Dreame auth + API server, per region. */\nexport const REGION_HOSTS: Record<DreameRegion, string> = {\n eu: 'eu.iot.dreame.tech:13267',\n us: 'us.iot.dreame.tech:13267',\n cn: 'cn.iot.dreame.tech:13267',\n ru: 'ru.iot.dreame.tech:13267',\n sg: 'sg.iot.dreame.tech:13267',\n in: 'in.iot.dreame.tech:13267',\n de: 'eu.iot.dreame.tech:13267',\n tw: 'cn.iot.dreame.tech:13267',\n};\n\n/** Default `country` form-field per region (ISO-3166 alpha-2). */\nexport const REGION_DEFAULT_COUNTRY: Record<DreameRegion, string> = {\n eu: 'GB',\n us: 'US',\n cn: 'CN',\n ru: 'RU',\n sg: 'SG',\n in: 'IN',\n de: 'DE',\n tw: 'TW',\n};\n\n/** Default UI language per region (ISO-639-1). */\nexport const REGION_DEFAULT_LANG: Record<DreameRegion, string> = {\n eu: 'en',\n us: 'en',\n cn: 'zh',\n ru: 'ru',\n sg: 'en',\n in: 'en',\n de: 'de',\n tw: 'zh',\n};\n\n/**\n * App-level OAuth2 CLIENT credentials (a Basic header) baked into the public\n * Dreamehome app binary — NOT a user secret, password, or per-account token.\n * These identify the app to the OAuth endpoint and are identical for every\n * install; they rotate only when Dreame ships a new app release (last verified\n * against cv=i_829). Not sensitive — safe to commit. (SAST note: this is a\n * shipped public client id/secret, not a leaked credential.)\n */\nexport const OAUTH_BASIC_AUTH = 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=';\n\n/** App-version fingerprint. Update if a new app version starts requiring it. */\nexport const APP_META = 'cv=i_829';\n\n/** User-Agent the Flutter Dreamehome app sends. */\nexport const APP_USER_AGENT = 'Dart/3.2 (dart:io)';\n\n/** Tenant id — Dreame brand. */\nexport const TENANT_DREAME = '000000';\n/** Tenant id — Mova brand. */\nexport const TENANT_MOVA = '000002';\n\n/**\n * Literal value placed in the `from` field of every command envelope. Dreame's\n * cloud ignores it — the original Flutter app sends \"XXXXXX\" verbatim.\n */\nexport const COMMAND_FROM_FIELD = 'XXXXXX';\n\n/** `iotComPrefix` path component (/dreame-iot-com-<n>/) for Dreame. */\nexport const IOT_COM_PREFIX_DREAME = 10000;\n/** `iotComPrefix` for Mova brand requests (untested). */\nexport const IOT_COM_PREFIX_MOVA = 20000;\n\n/** Standard HTTP content types used by the Dreame backend. */\nexport const CONTENT_TYPE_JSON = 'application/json';\nexport const CONTENT_TYPE_FORM = 'application/x-www-form-urlencoded';\n","import type { DreameRegion } from '../auth/config.js';\nimport {\n APP_META,\n APP_USER_AGENT,\n CONTENT_TYPE_FORM,\n OAUTH_BASIC_AUTH,\n TENANT_DREAME,\n} from '../auth/config.js';\nimport { buildRlcHeader } from './crypto.js';\n\n/**\n * Compose the static request headers the Dreame backend expects on every\n * call. Pass `accessToken` after login; omit (or pass `null`) for the\n * unauthenticated login request itself.\n */\nexport function buildHeaders(opts: {\n region: DreameRegion;\n country: string;\n lang: string;\n accessToken?: string | null;\n contentType?: string;\n}): Record<string, string> {\n const auth = opts.accessToken ? `bearer ${opts.accessToken}` : 'bearer';\n return {\n 'user-agent': APP_USER_AGENT,\n authorization: OAUTH_BASIC_AUTH,\n 'content-type': opts.contentType ?? CONTENT_TYPE_FORM,\n 'dreame-auth': auth,\n 'dreame-meta': APP_META,\n 'dreame-rlc': buildRlcHeader(opts.region, opts.lang, opts.country),\n 'tenant-id': TENANT_DREAME,\n };\n}\n","import { fetch as undiciFetch } from 'undici';\n\n/**\n * Narrow fetch-shaped function type backed by undici. Defined here so all\n * callers share one declaration — no DOM lib globals, no casts.\n *\n * We re-export `FetchImpl` from this module as the canonical definition;\n * `transport/http.ts` re-exports it from here so downstream modules have a\n * single import path.\n */\nexport type FetchImpl = typeof undiciFetch;\n\n/**\n * Default HTTP fetch implementation. We use undici explicitly (rather than the\n * Node global `fetch`) so the library works identically on Node ≥20 and so the\n * exact client behavior is pinned to a known version.\n */\nexport const defaultFetch: FetchImpl = undiciFetch;\n","import type { DreameRegion } from '../auth/config.js';\nimport {\n CONTENT_TYPE_JSON,\n REGION_DEFAULT_COUNTRY,\n REGION_DEFAULT_LANG,\n REGION_HOSTS,\n} from '../auth/config.js';\nimport { buildHeaders } from './headers.js';\nimport { defaultFetch, type FetchImpl } from './fetch.js';\nimport {\n DreameApiError,\n DreameAuthError,\n DreameDeviceOfflineError,\n DreameTransportError,\n} from './errors.js';\n\n/** Re-export so other modules can import FetchImpl from a single location. */\nexport type { FetchImpl };\n\n/** Cloud response code meaning \"device didn't ACK; may be offline\" (often a false negative). */\nconst CODE_DEVICE_OFFLINE = 80001;\n\n/** Default per-request timeout — fetch() is otherwise unbounded. */\nconst DEFAULT_TIMEOUT_MS = 30000;\n\nexport interface RequestContextOpts {\n region: DreameRegion;\n country?: string;\n lang?: string;\n host?: string;\n fetchImpl?: FetchImpl;\n}\n\nexport interface RequestContextInput {\n region: DreameRegion;\n country?: string | undefined;\n lang?: string | undefined;\n /** Override host. Some callers spell this `authHost`/`apiHost` — pass that here. */\n host?: string | undefined;\n fetchImpl?: FetchImpl | undefined;\n}\n\nexport class RequestContext {\n readonly region: DreameRegion;\n readonly country: string;\n readonly lang: string;\n readonly host: string;\n readonly fetchImpl: FetchImpl;\n\n constructor(opts: RequestContextOpts) {\n this.region = opts.region;\n this.country = opts.country ?? REGION_DEFAULT_COUNTRY[opts.region];\n this.lang = opts.lang ?? REGION_DEFAULT_LANG[opts.region];\n this.host = opts.host ?? REGION_HOSTS[opts.region];\n this.fetchImpl = opts.fetchImpl ?? defaultFetch;\n }\n\n static from(input: RequestContextInput): RequestContext {\n return new RequestContext({\n region: input.region,\n ...(input.country !== undefined ? { country: input.country } : {}),\n ...(input.lang !== undefined ? { lang: input.lang } : {}),\n ...(input.host !== undefined ? { host: input.host } : {}),\n ...(input.fetchImpl !== undefined ? { fetchImpl: input.fetchImpl } : {}),\n });\n }\n\n /** `https://<host><path>` — pass a path with a leading slash. */\n url(path: string): string {\n return `https://${this.host}${path}`;\n }\n\n /** Build the static Dreame headers, optionally with a bearer token + content-type. */\n headers(\n opts: { accessToken?: string | null; contentType?: string } = {},\n ): Record<string, string> {\n return buildHeaders({\n region: this.region,\n country: this.country,\n lang: this.lang,\n ...(opts.accessToken !== undefined ? { accessToken: opts.accessToken } : {}),\n ...(opts.contentType !== undefined ? { contentType: opts.contentType } : {}),\n });\n }\n}\n\n/** Minimal common shape every Dreame JSON response carries. */\nexport interface BaseResponse {\n code?: number;\n msg?: string;\n}\n\nexport async function httpPostJson<T extends BaseResponse>(input: {\n ctx: RequestContext;\n url: string;\n headers: Record<string, string>;\n body: string | URLSearchParams;\n context: string;\n errorClass?: typeof DreameApiError | typeof DreameAuthError;\n skipCodeCheck?: boolean;\n timeoutMs?: number;\n signal?: AbortSignal;\n}): Promise<T> {\n const Err = input.errorClass ?? DreameApiError;\n const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const signal = composeSignals(input.signal, timeoutMs);\n\n let res: Response;\n try {\n res = await input.ctx.fetchImpl(input.url, {\n method: 'POST',\n headers: input.headers,\n body: input.body,\n ...(signal !== undefined ? { signal } : {}),\n });\n } catch (err) {\n if (isAbortError(err)) {\n throw new DreameTransportError(\n `request to ${input.url} aborted after ${timeoutMs}ms or by caller`,\n err,\n );\n }\n throw new DreameTransportError(`network error contacting ${input.url}`, err);\n }\n\n const text = await res.text();\n let parsed: T | null = null;\n if (text) {\n try {\n parsed = JSON.parse(text) as T;\n } catch {\n // leave parsed null\n }\n }\n\n if (!res.ok) {\n throw new Err(\n `${input.context} failed: ${res.status} ${text.slice(0, 200)}`,\n res.status,\n parsed,\n );\n }\n if (!parsed) {\n throw new Err(`${input.context} response was not JSON (status ${res.status})`, res.status);\n }\n\n if (!input.skipCodeCheck && parsed.code !== undefined && parsed.code !== 0) {\n if (parsed.code === CODE_DEVICE_OFFLINE) {\n throw new DreameDeviceOfflineError(\n `device offline: ${parsed.msg ?? 'timeout'}`,\n res.status,\n parsed,\n );\n }\n throw new Err(\n `${input.context} rejected: code=${parsed.code} msg=${parsed.msg ?? '?'}`,\n res.status,\n parsed,\n );\n }\n\n return parsed;\n}\n\nexport async function httpPostJsonBody<T extends BaseResponse>(input: {\n ctx: RequestContext;\n path: string;\n accessToken?: string;\n body: unknown;\n context: string;\n timeoutMs?: number;\n signal?: AbortSignal;\n}): Promise<T> {\n return httpPostJson<T>({\n ctx: input.ctx,\n url: input.ctx.url(input.path),\n headers: input.ctx.headers({\n ...(input.accessToken !== undefined ? { accessToken: input.accessToken } : {}),\n contentType: CONTENT_TYPE_JSON,\n }),\n body: JSON.stringify(input.body),\n context: input.context,\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n });\n}\n\nfunction composeSignals(\n callerSignal: AbortSignal | undefined,\n timeoutMs: number,\n): AbortSignal | undefined {\n if (timeoutMs <= 0) {\n return callerSignal;\n }\n const timeoutSignal = AbortSignal.timeout(timeoutMs);\n if (!callerSignal) {\n return timeoutSignal;\n }\n return AbortSignal.any([callerSignal, timeoutSignal]);\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof Error && (err.name === 'AbortError' || err.name === 'TimeoutError');\n}\n","import { z } from 'zod';\n\n/** OAuth token endpoint response. Permissive: Dreame may add fields. */\nexport const OAuthTokenResponseSchema = z\n .object({\n access_token: z.string().optional(),\n refresh_token: z.string().optional(),\n expires_in: z.number().optional(),\n token_type: z.string().optional(),\n uid: z.union([z.string(), z.number()]).optional(),\n region: z.string().optional(),\n country: z.string().optional(),\n lang: z.string().optional(),\n tenant_id: z.string().optional(),\n error: z.string().optional(),\n error_description: z.string().optional(),\n code: z.number().optional(),\n msg: z.string().optional(),\n })\n .passthrough();\nexport type OAuthTokenResponse = z.infer<typeof OAuthTokenResponseSchema>;\n\n/** Single raw device record from the device-list endpoint. */\nexport const RawDeviceSchema = z\n .object({\n did: z.union([z.string(), z.number()]).optional(),\n model: z.string().optional(),\n customName: z.string().optional(),\n deviceName: z.string().optional(),\n mac: z.string().optional(),\n online: z.boolean().optional(),\n lwt: z.number().optional(),\n bindDomain: z.string().optional(),\n master: z.boolean().optional(),\n })\n .passthrough();\nexport type RawDevice = z.infer<typeof RawDeviceSchema>;\n\nexport const DeviceListResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().optional(),\n data: z\n .object({\n page: z\n .object({ records: z.array(RawDeviceSchema).optional() })\n .partial()\n .optional(),\n records: z.array(RawDeviceSchema).optional(),\n })\n .passthrough()\n .optional(),\n records: z.array(RawDeviceSchema).optional(),\n })\n .passthrough();\nexport type DeviceListResponse = z.infer<typeof DeviceListResponseSchema>;\n\n/**\n * Per-property result. Lenient by design: every known field is OPTIONAL and\n * extra keys pass through. This validates the STRUCTURE (each element must be\n * an object) without rejecting real cloud responses whose exact shape we have\n * not fully observed — the command path is not exercised by the live e2e.\n */\nexport const PropertyResultSchema = z\n .object({\n siid: z.number().optional(),\n piid: z.number().optional(),\n value: z.unknown().optional(),\n code: z.number().optional(),\n })\n .passthrough();\n\n/**\n * Single cloud-shadow entry from `dreame-user-iot/iotstatus/props`. The cloud\n * returns the last-known value as a STRING (e.g. `\"100\"`, `\"true\"`); the value\n * is widened to `unknown` and coerced by the command layer. `updateDate` is the\n * epoch-ms the cloud last saw that value (used for cache-age reporting).\n */\nexport const CachedPropEntrySchema = z\n .object({\n key: z.string(),\n value: z.unknown().optional(),\n updateDate: z.number().optional(),\n })\n .passthrough();\nexport type CachedPropEntry = z.infer<typeof CachedPropEntrySchema>;\n\n/**\n * Cloud-shadow read response (`iotstatus/props`). `data` is an ARRAY of\n * per-property entries (NOT the nested `data.result` shape `sendCommand`\n * returns). Permissive: missing props are simply absent; on an error code\n * `data` may be absent entirely.\n */\nexport const CachedPropsResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().optional(),\n data: z.array(CachedPropEntrySchema).optional(),\n })\n .passthrough();\nexport type CachedPropsResponse = z.infer<typeof CachedPropsResponseSchema>;\n\nexport const SendCommandResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().optional(),\n data: z.object({ result: z.unknown().optional() }).passthrough().optional(),\n result: z.unknown().optional(),\n })\n .passthrough();\nexport type SendCommandResponse = z.infer<typeof SendCommandResponseSchema>;\n\n/** Raw MQTT envelope as received from the broker, before flattening. */\nexport const RawMqttEventSchema = z\n .object({\n id: z.number().optional(),\n did: z.union([z.string(), z.number()]).optional(),\n data: z\n .object({\n id: z.number().optional(),\n method: z.string().optional(),\n params: z.unknown().optional(),\n })\n .passthrough()\n .optional(),\n })\n .passthrough();\nexport type RawMqttEvent = z.infer<typeof RawMqttEventSchema>;\n","import type { DreameRegion } from './config.js';\nimport type { DreameSession } from '../cloud/types.js';\nimport { DreameAuthError } from '../transport/errors.js';\nimport { hashPassword } from '../transport/crypto.js';\nimport { httpPostJson, RequestContext, type BaseResponse } from '../transport/http.js';\nimport { OAuthTokenResponseSchema } from '../transport/schemas.js';\nimport type { FetchImpl } from '../transport/http.js';\n\nexport interface LoginInput {\n email: string;\n password: string;\n region: DreameRegion;\n /** ISO-3166 alpha-2. Defaults from region. */\n country?: string;\n /** ISO-639-1. Defaults from region. */\n lang?: string;\n /** Override host (advanced — for testing). */\n authHost?: string;\n /** Inject a fetch impl for testing. */\n fetchImpl?: FetchImpl;\n}\n\nexport interface RefreshInput {\n refreshToken: string;\n region: DreameRegion;\n country?: string;\n lang?: string;\n authHost?: string;\n fetchImpl?: FetchImpl;\n}\n\nfunction ctxFromInput(input: {\n region: DreameRegion;\n country?: string;\n lang?: string;\n authHost?: string;\n fetchImpl?: FetchImpl;\n}): RequestContext {\n return RequestContext.from({ ...input, host: input.authHost });\n}\n\n/** Authenticate against the Dreame native cloud (OAuth2 password grant). */\nexport async function login(input: LoginInput): Promise<DreameSession> {\n const ctx = ctxFromInput(input);\n const body = new URLSearchParams({\n grant_type: 'password',\n scope: 'all',\n platform: 'IOS',\n type: 'account',\n username: input.email,\n password: hashPassword(input.password),\n country: ctx.country,\n lang: ctx.lang,\n });\n return postForToken(ctx, body);\n}\n\n/** Exchange a refresh token for a fresh access token. Refresh ~100s before expiry. */\nexport async function refresh(input: RefreshInput): Promise<DreameSession> {\n const ctx = ctxFromInput(input);\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: input.refreshToken,\n });\n return postForToken(ctx, body);\n}\n\nasync function postForToken(ctx: RequestContext, body: URLSearchParams): Promise<DreameSession> {\n // The OAuth response is validated/narrowed by `OAuthTokenResponseSchema.parse`\n // below; the HTTP layer only needs the minimal `BaseResponse` shape here. The\n // zod-inferred `OAuthTokenResponse` cannot be the generic directly because its\n // passthrough widening makes `code` `number | undefined`, which is\n // incompatible with `BaseResponse.code?: number` under exactOptionalPropertyTypes.\n const raw = await httpPostJson<BaseResponse>({\n ctx,\n url: ctx.url('/dreame-auth/oauth/token'),\n headers: ctx.headers(),\n body,\n context: 'auth',\n errorClass: DreameAuthError,\n skipCodeCheck: true, // OAuth uses HTTP status + top-level error fields, not parsed.code\n });\n\n const data = OAuthTokenResponseSchema.parse(raw);\n\n if (data.error || data.error_description) {\n throw new DreameAuthError(\n `auth failed: ${data.error ?? '?'} — ${data.error_description ?? 'no description'}`,\n );\n }\n if (!data.access_token) {\n // Redacted: never stringify the full payload — it carries refresh_token and\n // other secrets. Surface only non-secret diagnostic fields.\n throw new DreameAuthError(\n `auth response missing access_token: uid=${data.uid ?? '?'} error=${data.error ?? '?'} code=${data.code ?? '?'}`,\n );\n }\n\n const expiresIn = typeof data.expires_in === 'number' ? data.expires_in : 7200;\n const uid = data.uid !== undefined ? String(data.uid) : '';\n if (!uid) {\n throw new DreameAuthError('auth response missing uid');\n }\n\n const session: DreameSession = {\n accessToken: data.access_token,\n uid,\n expiresAt: Date.now() + expiresIn * 1000,\n region: ctx.region,\n };\n if (typeof data.refresh_token === 'string') {\n session.refreshToken = data.refresh_token;\n }\n return session;\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type { DreameCloudState, DreameDevice, DreameSession } from './types.js';\nimport {\n httpPostJsonBody,\n RequestContext,\n type BaseResponse,\n type FetchImpl,\n} from '../transport/http.js';\nimport { DeviceListResponseSchema, type RawDevice } from '../transport/schemas.js';\n\nexport interface ListDevicesInput {\n session: DreameSession;\n region: DreameRegion;\n ctx?: RequestContext;\n country?: string;\n lang?: string;\n apiHost?: string;\n fetchImpl?: FetchImpl;\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\n/** Enumerate the devices visible to the authenticated account (incl. shared). */\nexport async function listDevices(input: ListDevicesInput): Promise<DreameDevice[]> {\n const ctx = input.ctx ?? RequestContext.from({ ...input, host: input.apiHost });\n\n // The response is validated/narrowed by `DeviceListResponseSchema.parse`\n // below; the HTTP layer only needs the minimal `BaseResponse` shape. The\n // zod-inferred `DeviceListResponse` cannot be the generic directly because\n // its passthrough widening makes `code` `number | undefined`, which is\n // incompatible with `BaseResponse.code?: number` under exactOptionalPropertyTypes.\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: '/dreame-user-iot/iotuserbind/device/listV2',\n accessToken: input.session.accessToken,\n body: {\n sharedStatus: 1,\n current: 1,\n size: 100,\n lang: ctx.lang,\n timestamp: Date.now(),\n },\n context: 'device list',\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n });\n\n const parsed = DeviceListResponseSchema.parse(raw);\n const records = parsed.data?.page?.records ?? parsed.data?.records ?? parsed.records ?? [];\n return records.map(toDevice);\n}\n\nfunction toDevice(rawIn: RawDevice): DreameDevice {\n const raw = rawIn as Record<string, unknown>;\n const did = String(rawIn.did ?? '');\n const model = String(rawIn.model ?? '');\n const name = String(rawIn.customName || rawIn.deviceName || model || did);\n\n let lwtFlag = 0;\n if (typeof raw['property'] === 'string') {\n try {\n const p = JSON.parse(raw['property']) as { lwt?: number };\n if (p && typeof p.lwt === 'number') {\n lwtFlag = p.lwt;\n }\n } catch {\n // ignore malformed JSON\n }\n }\n const online = rawIn.online === true || rawIn.lwt === 1 || lwtFlag === 1;\n\n const device: DreameDevice = {\n did,\n model,\n name,\n online,\n raw,\n cloudState: parseCloudState(raw),\n };\n if (rawIn.mac) {\n device.mac = String(rawIn.mac);\n }\n if (typeof raw['ver'] === 'string') {\n device.firmwareVersion = raw['ver'];\n }\n if (typeof raw['sn'] === 'string') {\n device.serialNumber = raw['sn'];\n }\n return device;\n}\n\nfunction parseCloudState(raw: Record<string, unknown>): DreameCloudState {\n const latestStatus = typeof raw['latestStatus'] === 'number' ? raw['latestStatus'] : null;\n const battery = typeof raw['battery'] === 'number' ? raw['battery'] : null;\n const featureCode2 = typeof raw['featureCode2'] === 'number' ? raw['featureCode2'] : null;\n let videoActive: boolean | null = null;\n if (typeof raw['videoStatus'] === 'string') {\n try {\n const v = JSON.parse(raw['videoStatus']) as { operType?: string; status?: number };\n if (v?.operType === 'end' || v?.status === 0) {\n videoActive = false;\n } else if (v?.operType || v?.status) {\n videoActive = true;\n }\n } catch {\n // ignore malformed JSON\n }\n }\n return { latestStatus, battery, videoActive, featureCode2 };\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type {\n DreameSession,\n MiotProp,\n MiotAction,\n PropertyWrite,\n PropertyResult,\n} from './types.js';\nimport { COMMAND_FROM_FIELD, IOT_COM_PREFIX_DREAME } from '../auth/config.js';\nimport { randomRequestId } from '../transport/crypto.js';\nimport { DreameApiError } from '../transport/errors.js';\nimport {\n httpPostJsonBody,\n RequestContext,\n type BaseResponse,\n type FetchImpl,\n} from '../transport/http.js';\nimport { z } from 'zod';\nimport {\n SendCommandResponseSchema,\n CachedPropsResponseSchema,\n PropertyResultSchema,\n type SendCommandResponse,\n} from '../transport/schemas.js';\n\ninterface SendCommandInput {\n session: DreameSession;\n region: DreameRegion;\n did: string;\n /** MIoT method: `get_properties`, `set_properties`, `action`. */\n method: string;\n /**\n * For `get_properties`/`set_properties`: an ARRAY of property descriptors.\n * For `action`: a single OBJECT (NOT an array — Dreame surfaces the wrong\n * shape as a misleading code 80001 \"device offline\" error).\n */\n params: unknown;\n ctx?: RequestContext;\n country?: string;\n lang?: string;\n apiHost?: string;\n iotComPrefix?: number;\n fetchImpl?: FetchImpl;\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\n/** Low-level dispatch to `/device/sendCommand`. Caller owns the `params` shape. */\nexport async function sendCommand(input: SendCommandInput): Promise<SendCommandResponse> {\n const ctx = input.ctx ?? RequestContext.from({ ...input, host: input.apiHost });\n const prefix = input.iotComPrefix ?? IOT_COM_PREFIX_DREAME;\n const id = randomRequestId();\n\n // The response is validated/narrowed by `SendCommandResponseSchema.parse`\n // below; the HTTP layer only needs the minimal `BaseResponse` shape. The\n // zod-inferred `SendCommandResponse` cannot be the generic directly because\n // its passthrough widening makes `code` `number | undefined`, which is\n // incompatible with `BaseResponse.code?: number` under exactOptionalPropertyTypes.\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `/dreame-iot-com-${prefix}/device/sendCommand`,\n accessToken: input.session.accessToken,\n body: {\n did: input.did,\n id,\n data: {\n did: input.did,\n id,\n method: input.method,\n params: input.params,\n from: COMMAND_FROM_FIELD,\n },\n },\n context: input.method,\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n });\n return SendCommandResponseSchema.parse(raw);\n}\n\nexport interface CommonInput {\n session: DreameSession;\n region: DreameRegion;\n did: string;\n ctx?: RequestContext;\n country?: string;\n lang?: string;\n apiHost?: string;\n /**\n * Inject a fetch implementation. Placed here (on `CommonInput`) so tests can\n * pass a mock via the first argument without any casts. Do NOT put this on\n * `CallOptions` — it belongs on the base/donor argument only.\n */\n fetchImpl?: FetchImpl;\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\nexport interface CallOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n // fetchImpl intentionally NOT here — inject it via CommonInput instead.\n}\n\n/** Read one or more MIoT properties from a device. */\nexport async function getProperties(\n base: CommonInput,\n props: MiotProp[],\n opts: CallOptions = {},\n): Promise<PropertyResult[]> {\n const params = props.map((p) => ({ did: base.did, siid: p.siid, piid: p.piid }));\n const res = await sendCommand({ ...base, ...opts, method: 'get_properties', params });\n return extractResultArray(res, 'get_properties');\n}\n\n/**\n * Read the CLOUD-CACHED (shadow) values of one or more MIoT properties WITHOUT\n * waking the device. Hits the `dreame-user-iot/iotstatus/props` endpoint (the\n * account-auth path, NOT the `device/sendCommand` envelope), which returns the\n * cloud's last-known values for a standby/offline robot and never 80001s for an\n * idle device — exactly what the Dreamehome app shows for a sleeping robot.\n *\n * `keys` MUST be the comma-joined `\"siid.piid\"` STRING (an array yields code\n * 10001; an empty value yields 10007). Each `data[]` entry's `value` arrives as\n * a STRING; it is coerced back to number/boolean (else kept as a string) so the\n * existing typed getters (which expect numbers) decode it unchanged.\n */\nexport async function getCachedProperties(\n base: CommonInput,\n props: MiotProp[],\n opts: CallOptions = {},\n): Promise<PropertyResult[]> {\n const ctx = base.ctx ?? RequestContext.from({ ...base, host: base.apiHost });\n const keys = props.map((p) => `${p.siid}.${p.piid}`).join(',');\n const signal = opts.signal ?? base.signal;\n const timeoutMs = opts.timeoutMs ?? base.timeoutMs;\n\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: '/dreame-user-iot/iotstatus/props',\n accessToken: base.session.accessToken,\n body: { did: base.did, keys },\n context: 'cached properties',\n ...(signal !== undefined ? { signal } : {}),\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n\n const parsed = CachedPropsResponseSchema.parse(raw);\n // The HTTP layer already rejects code !== 0; this is a defensive backstop so\n // the contract (code === 0 ⇒ success) holds even if that check is bypassed.\n if (parsed.code !== undefined && parsed.code !== 0) {\n throw new DreameApiError(\n `cached properties rejected: code=${parsed.code} msg=${parsed.msg ?? '?'}`,\n 200,\n parsed,\n );\n }\n return (parsed.data ?? []).flatMap((entry) => {\n const [siidStr, piidStr] = entry.key.split('.');\n const siid = Number(siidStr);\n const piid = Number(piidStr);\n if (!Number.isFinite(siid) || !Number.isFinite(piid)) {\n return [];\n }\n const result: PropertyResult = { siid, piid, value: coerceShadowValue(entry.value) };\n if (entry.updateDate !== undefined) {\n result.updateDate = entry.updateDate;\n }\n return [result];\n });\n}\n\n/**\n * Coerce a cloud-shadow value (delivered as a string) back to the type the\n * typed getters expect: a numeric string → number, `\"true\"`/`\"false\"` →\n * boolean, anything else (incl. already-typed values) is returned unchanged.\n */\nfunction coerceShadowValue(value: unknown): unknown {\n if (typeof value !== 'string') {\n return value;\n }\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n // Number('') === 0 and Number('1,2') === NaN — only coerce a clean numeric\n // token, leaving fault-list strings like \"18,107\" intact.\n if (value.trim() !== '' && Number.isFinite(Number(value))) {\n return Number(value);\n }\n return value;\n}\n\n/** Write one or more MIoT properties to a device. */\nexport async function setProperties(\n base: CommonInput,\n writes: PropertyWrite[],\n opts: CallOptions = {},\n): Promise<PropertyResult[]> {\n const params = writes.map((p) => ({\n did: base.did,\n siid: p.siid,\n piid: p.piid,\n value: p.value,\n }));\n const res = await sendCommand({ ...base, ...opts, method: 'set_properties', params });\n return extractResultArray(res, 'set_properties');\n}\n\n/**\n * Invoke a single MIoT action. Unlike property calls, `params` is a single\n * OBJECT, not an array — an array here surfaces as a misleading code 80001.\n */\nexport async function callAction(\n base: CommonInput,\n action: MiotAction,\n opts: CallOptions = {},\n): Promise<unknown> {\n const params = {\n did: base.did,\n siid: action.siid,\n aiid: action.aiid,\n in: action.in ?? [],\n };\n const res = await sendCommand({ ...base, ...opts, method: 'action', params });\n return res.data?.result ?? res.result ?? res;\n}\n\n/**\n * Fetch batched device data (the mower's vector map: `MAP.*` / `M_PATH.*`\n * chunk keys) for a set of property groups.\n *\n * ⚠️ ENDPOINT GAP — LIVE PATH UNRECOVERED. The concrete batch-fetch endpoint\n * path is OBFUSCATED in the donor (`antondaubert/dreame-mower` builds it from\n * an encoded `_api_strings` table — entries `[23]/[26]/[44]` — that we have not\n * decoded). The donor's `cloud_base` resolves to a `/dreame-iot-com-<prefix>/…`\n * route we cannot reproduce byte-exact without that table, and we will NOT\n * invent a wrong URL. Recovering the real path is a documented follow-up; until\n * then this function will THROW if invoked against the live cloud.\n *\n * The map PARSER (`parseBatchMapData`) is the shipped deliverable and is fully\n * unit-tested with synthetic batch fixtures. `MowerDevice.getMap` consumes an\n * INJECTED {@link BatchDeviceDataFetcher} seam, so tests (and any caller that\n * already knows the path) drive it with no dependency on this stub.\n *\n * @throws DreameApiError always — the endpoint path is not yet recovered.\n */\nexport async function getBatchDeviceDatas(\n base: CommonInput,\n props: string[],\n): Promise<Record<string, unknown>> {\n // Deliberately unreachable in production until the endpoint is recovered. The\n // request is described in the error so the gap is self-documenting at runtime.\n return Promise.reject(\n new DreameApiError(\n `getBatchDeviceDatas(did=${base.did}, props=${JSON.stringify(props)}): live ` +\n 'batch-fetch endpoint path is not yet recovered (obfuscated in the donor). ' +\n 'Inject a BatchDeviceDataFetcher into MowerDevice instead, or supply the ' +\n 'resolved path once known.',\n 0,\n ),\n );\n}\n\nfunction extractResultArray(res: SendCommandResponse, context: string): PropertyResult[] {\n const raw = Array.isArray(res.data?.result)\n ? res.data.result\n : Array.isArray(res.result)\n ? res.result\n : null;\n if (raw === null) {\n throw new DreameApiError(\n `${context}: response did not contain a result array — ${JSON.stringify(res).slice(0, 200)}`,\n 200,\n res,\n );\n }\n // Validate STRUCTURE leniently (array of objects, known fields optional)\n // rather than casting unknown[] straight to PropertyResult[].\n const parsed = z.array(PropertyResultSchema).safeParse(raw);\n if (!parsed.success) {\n throw new DreameApiError(\n `${context}: result array contained a non-object element — ${JSON.stringify(raw).slice(0, 200)}`,\n 200,\n res,\n );\n }\n return parsed.data;\n}\n","import mqtt from 'mqtt';\nimport type { DreameRegion } from '../auth/config.js';\nimport type { DreameDevice, DreameSession } from '../cloud/types.js';\nimport { DreameTransportError } from './errors.js';\nimport { randomMqttClientId } from './crypto.js';\nimport { TypedEmitter } from './typed-emitter.js';\nimport { RawMqttEventSchema } from './schemas.js';\n\n/** A single property update pushed by the device. */\nexport interface PropertyChange {\n did: string;\n siid: number;\n piid: number;\n value: unknown;\n}\n\n/** MIoT event push (`method: \"event_occured\"` — yes, the typo is on the wire). */\nexport interface EventOccuredPush {\n did: string;\n siid: number;\n eiid: number;\n arguments: unknown[];\n}\n\n/** Untyped k/v push on `method: \"props\"` (OTA progress/state, etc.). */\nexport interface PropsPush {\n did: string;\n params: Record<string, unknown>;\n}\n\n/** Saved-map catalogue push on `_sync.update_vacuum_mapinfo`. */\nexport interface MapInfoPush {\n did: string;\n maps: Map<number, readonly number[]>;\n activeMapId: number | null;\n savedMapIds: readonly number[];\n}\n\n/**\n * The subset of `mqtt.MqttClient` this module uses. Declaring it lets tests\n * inject a fake broker without depending on a live TLS socket.\n */\nexport interface MqttLikeClient {\n on(event: 'connect', cb: () => void): this;\n on(event: 'close', cb: () => void): this;\n on(event: 'error', cb: (err: Error) => void): this;\n on(event: 'message', cb: (topic: string, payload: Buffer) => void): this;\n once(event: 'connect', cb: () => void): this;\n once(event: 'error', cb: (err: Error) => void): this;\n removeListener(event: string, cb: (...args: never[]) => void): this;\n subscribe(topic: string, opts: { qos: 0 | 1 | 2 }, cb: (err?: Error) => void): void;\n end(force: boolean, opts: Record<string, never>, cb: () => void): void;\n}\n\nexport interface ConnectOptions {\n username: string;\n password: string;\n clientId: string;\n protocolVersion: 4;\n reconnectPeriod: number;\n connectTimeout: number;\n rejectUnauthorized: boolean;\n clean: boolean;\n}\n\n/** Factory matching `mqtt.connect`'s shape we rely on (injectable for tests). */\nexport type MqttConnectFn = (url: string, opts: ConnectOptions) => MqttLikeClient;\n\nfunction defaultConnect(url: string, opts: ConnectOptions): MqttLikeClient {\n // mqtt.connect returns a MqttClient which structurally satisfies MqttLikeClient.\n // We verify this with a local type assertion through the interface rather than\n // a raw cast, keeping the boundary explicit.\n const client = mqtt.connect(url, opts);\n // MqttClient implements the full MqttLikeClient contract; assign through the\n // interface to surface any future structural mismatch as a compile error.\n const typed: MqttLikeClient = client;\n return typed;\n}\n\nexport type DreamePushEvents = {\n properties: [PropertyChange[]];\n event: [EventOccuredPush];\n props: [PropsPush];\n mapInfo: [MapInfoPush];\n connect: [];\n close: [];\n error: [Error];\n};\n\nexport interface DreamePushInput {\n device: DreameDevice;\n session: DreameSession;\n region: DreameRegion;\n /** Injectable mqtt.connect (defaults to the real one). */\n connect?: MqttConnectFn;\n /** Backoff before a reconnect attempt after an unexpected drop. Default 5000. */\n reconnectBackoffMs?: number;\n /**\n * Whether the TLS layer must reject a server certificate that does not chain\n * to a trusted public CA. Defaults to `false`: Dreame brokers present\n * device-specific certificates that are NOT chained to a public CA, so strict\n * verification fails against the real fleet. Override to `true` only when\n * pointing at a broker with a properly chained certificate (e.g. a test rig).\n */\n rejectUnauthorized?: boolean;\n}\n\n/**\n * Durable per-device MQTT subscription. Unlike the donor, this:\n * - reconnects with backoff after an unexpected drop, then resubscribes;\n * - on `refreshSession(newSession)` closes the old client and reopens with\n * the new access token (the broker validates the token at CONNECT only and\n * cannot be re-authed on a live client);\n * - never auto-reconnects after an explicit `close()`.\n */\nexport class DreamePush extends TypedEmitter<DreamePushEvents> {\n readonly #device: DreameDevice;\n #session: DreameSession;\n readonly #region: DreameRegion;\n readonly #connect: MqttConnectFn;\n readonly #backoffMs: number;\n readonly #rejectUnauthorized: boolean;\n readonly #topic: string;\n #client: MqttLikeClient | null = null;\n #closed = false;\n #tearingDown = false;\n #reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Per-instance bound close handler. Stored so we can pass the EXACT same\n * function reference to both `client.on('close', …)` and\n * `client.removeListener('close', …)` during teardown — a throwaway arrow\n * would be a different reference and would NOT be removed.\n */\n readonly #onClose: () => void;\n\n constructor(input: DreamePushInput) {\n super();\n this.#device = input.device;\n this.#session = input.session;\n this.#region = input.region;\n this.#connect = input.connect ?? defaultConnect;\n this.#backoffMs = input.reconnectBackoffMs ?? 5000;\n this.#rejectUnauthorized = input.rejectUnauthorized ?? false;\n this.#topic = buildStatusTopic(this.#device, this.#session.uid, this.#region);\n // Bind once so the reference is stable across register/remove calls.\n this.#onClose = (): void => {\n if (this.#tearingDown) {\n // We caused this close (teardown or refresh). Do NOT reconnect.\n return;\n }\n this.emit('close');\n this.#scheduleReconnect();\n };\n }\n\n get topic(): string {\n return this.#topic;\n }\n\n /** Open the connection and resolve when subscribed. */\n async open(): Promise<void> {\n if (this.#closed) {\n throw new DreameTransportError('subscription is closed');\n }\n if (this.#client) {\n return;\n }\n await this.#connectAndSubscribe();\n }\n\n /**\n * Swap to a freshly-refreshed session: close the current client and reopen\n * with the new access token. Safe to call while connected or disconnected.\n */\n async refreshSession(session: DreameSession): Promise<void> {\n this.#session = session;\n if (this.#closed) {\n return;\n }\n // Cancel any reconnect armed by a prior drop. Without this, a timer armed\n // before refresh fires AFTER refresh reconnects, spawning a second live\n // client and orphaning the first (mirror what close() does).\n if (this.#reconnectTimer) {\n clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = null;\n }\n await this.#teardownClient();\n await this.#connectAndSubscribe();\n }\n\n /** Tear down permanently. Closed subscriptions cannot be reopened. */\n async close(): Promise<void> {\n this.#closed = true;\n if (this.#reconnectTimer) {\n clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = null;\n }\n await this.#teardownClient();\n }\n\n async #connectAndSubscribe(): Promise<void> {\n const client = this.#connect(brokerUrl(this.#device), {\n username: this.#session.uid,\n password: this.#session.accessToken,\n clientId: randomMqttClientId(),\n protocolVersion: 4,\n reconnectPeriod: 0, // we drive reconnect ourselves\n connectTimeout: 15000,\n rejectUnauthorized: this.#rejectUnauthorized,\n clean: true,\n });\n this.#client = client;\n\n client.on('message', (_topic, payload) => this.#handleMessage(payload));\n client.on('error', (err) => this.emit('error', err));\n // Use the stored bound reference so teardown can reliably remove it.\n client.on('close', this.#onClose);\n\n await new Promise<void>((resolve, reject) => {\n const onConnect = (): void => {\n client.removeListener('error', onError);\n client.subscribe(this.#topic, { qos: 0 }, (err) => {\n if (err) {\n reject(new DreameTransportError(`mqtt subscribe failed: ${err.message}`, err));\n return;\n }\n this.emit('connect');\n resolve();\n });\n };\n const onError = (err: Error): void => {\n client.removeListener('connect', onConnect);\n reject(new DreameTransportError(`mqtt connect failed: ${err.message}`, err));\n };\n client.once('connect', onConnect);\n client.once('error', onError);\n });\n }\n\n #scheduleReconnect(): void {\n if (this.#closed || this.#reconnectTimer) {\n return;\n }\n this.#client = null;\n this.#reconnectTimer = setTimeout(() => {\n this.#reconnectTimer = null;\n // Skip if we were closed or a client was already (re)established in the\n // meantime (e.g. by refreshSession) — never spawn a duplicate connection.\n if (this.#closed || this.#client) {\n return;\n }\n void this.#connectAndSubscribe().catch((err: unknown) => {\n this.emit('error', err instanceof Error ? err : new DreameTransportError(String(err)));\n this.#scheduleReconnect();\n });\n }, this.#backoffMs);\n }\n\n async #teardownClient(): Promise<void> {\n const client = this.#client;\n this.#client = null;\n if (!client) {\n return;\n }\n // Signal to #onClose that this close event is intentional — do not reconnect.\n this.#tearingDown = true;\n // Remove the stored bound reference. This is the exact function object\n // registered in #connectAndSubscribe, so removeListener works correctly.\n client.removeListener('close', this.#onClose);\n await new Promise<void>((resolve) => {\n client.end(false, {}, () => resolve());\n });\n this.#tearingDown = false;\n }\n\n #handleMessage(payload: Buffer): void {\n let raw: unknown;\n try {\n raw = JSON.parse(payload.toString('utf8'));\n } catch (err) {\n this.emit('error', new DreameTransportError('invalid mqtt payload', err));\n return;\n }\n const result = RawMqttEventSchema.safeParse(raw);\n if (!result.success) {\n this.emit('error', new DreameTransportError('unexpected mqtt envelope'));\n return;\n }\n const envelope = result.data;\n const did = String(envelope.did ?? this.#device.did);\n const method = envelope.data?.method;\n const params = envelope.data?.params;\n\n if (method === 'properties_changed' && Array.isArray(params)) {\n const changes = parsePropertyChanges(did, params);\n if (changes.length > 0) {\n this.emit('properties', changes);\n }\n return;\n }\n if (method === 'event_occured' && isObject(params)) {\n const ev = parseEventOccured(did, params);\n if (ev) {\n this.emit('event', ev);\n }\n return;\n }\n if (method === 'props' && isObject(params)) {\n this.emit('props', { did, params });\n return;\n }\n if (method === '_sync.update_vacuum_mapinfo' && isObject(params)) {\n const mi = parseMapInfo(did, params);\n if (mi) {\n this.emit('mapInfo', mi);\n }\n return;\n }\n }\n}\n\n// --- Pure helpers (exported for unit tests) ------------------------------\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nexport function brokerUrl(device: DreameDevice): string {\n const bindDomain = (device.raw['bindDomain'] as string | undefined) ?? '';\n if (!bindDomain) {\n throw new DreameTransportError(`device ${device.did} has no bindDomain — cannot connect MQTT`);\n }\n return `mqtts://${bindDomain}`;\n}\n\nexport function buildStatusTopic(device: DreameDevice, uid: string, region: DreameRegion): string {\n // Trailing slash is mandatory — the broker matches it literally.\n return `/status/${device.did}/${uid}/${device.model}/${region}/`;\n}\n\nexport function parsePropertyChanges(fallbackDid: string, params: unknown[]): PropertyChange[] {\n const changes: PropertyChange[] = [];\n for (const p of params) {\n if (!isObject(p)) {\n continue;\n }\n const siid = p['siid'];\n const piid = p['piid'];\n if (typeof siid === 'number' && typeof piid === 'number') {\n const did = p['did'];\n changes.push({\n did: typeof did === 'string' || typeof did === 'number' ? String(did) : fallbackDid,\n siid,\n piid,\n value: p['value'],\n });\n }\n }\n return changes;\n}\n\nexport function parseEventOccured(\n fallbackDid: string,\n params: Record<string, unknown>,\n): EventOccuredPush | null {\n const siid = params['siid'];\n const eiid = params['eiid'];\n if (typeof siid !== 'number' || typeof eiid !== 'number') {\n return null;\n }\n const did = params['did'];\n const args = params['arguments'];\n return {\n did: typeof did === 'string' || typeof did === 'number' ? String(did) : fallbackDid,\n siid,\n eiid,\n arguments: Array.isArray(args) ? args : [],\n };\n}\n\nexport function parseMapInfo(\n fallbackDid: string,\n params: Record<string, unknown>,\n): MapInfoPush | null {\n const inner = params['map_info'];\n if (typeof inner !== 'string') {\n return null;\n }\n let decoded: unknown;\n try {\n decoded = JSON.parse(inner);\n } catch {\n return null;\n }\n if (!isObject(decoded)) {\n return null;\n }\n const maps = new Map<number, readonly number[]>();\n for (const [key, value] of Object.entries(decoded)) {\n const id = Number(key);\n if (!Number.isFinite(id) || !Array.isArray(value)) {\n continue;\n }\n maps.set(\n id,\n value.filter((v): v is number => typeof v === 'number'),\n );\n }\n let activeMapId: number | null = null;\n for (const [id, tok] of [...maps].sort((a, b) => a[0] - b[0])) {\n if (tok.length > 1 || (tok.length === 1 && tok[0] !== 0)) {\n activeMapId = id;\n break;\n }\n }\n const savedMapIds = Object.freeze([...maps.keys()].sort((a, b) => a - b));\n const did = params['did'];\n return {\n did: typeof did === 'string' || typeof did === 'number' ? String(did) : fallbackDid,\n maps,\n activeMapId,\n savedMapIds,\n };\n}\n","import { EventEmitter } from 'node:events';\n\n/** A strongly-typed wrapper over Node's EventEmitter. */\nexport class TypedEmitter<Events extends Record<string, unknown[]>> {\n readonly #emitter = new EventEmitter();\n\n on<K extends keyof Events & string>(event: K, listener: (...args: Events[K]) => void): this {\n this.#emitter.on(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n once<K extends keyof Events & string>(event: K, listener: (...args: Events[K]) => void): this {\n this.#emitter.once(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n off<K extends keyof Events & string>(event: K, listener: (...args: Events[K]) => void): this {\n this.#emitter.off(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n emit<K extends keyof Events & string>(event: K, ...args: Events[K]): boolean {\n return this.#emitter.emit(event, ...args);\n }\n\n removeAllListeners(): this {\n this.#emitter.removeAllListeners();\n return this;\n }\n}\n","/**\n * A read-only set of capability tokens a device model supports. Phase 2 ships\n * only the shape + a no-op default; P3/P4 populate per-model tables behind the\n * same {@link CapabilityResolver} interface.\n */\nexport interface DeviceCapabilities {\n readonly model: string;\n /** Whether the model is known to support a capability token. */\n has(token: string): boolean;\n /** All known capability tokens for the model. */\n list(): readonly string[];\n}\n\n/** Resolves the capability set for a device model string. */\nexport interface CapabilityResolver {\n resolve(model: string): DeviceCapabilities;\n}\n\n/** Immutable capability set backed by a token set. */\nclass CapabilitySet implements DeviceCapabilities {\n readonly model: string;\n readonly #tokens: ReadonlySet<string>;\n\n constructor(model: string, tokens: ReadonlySet<string>) {\n this.model = model;\n this.#tokens = tokens;\n }\n\n has(token: string): boolean {\n return this.#tokens.has(token);\n }\n\n list(): readonly string[] {\n return [...this.#tokens];\n }\n}\n\n/**\n * Default resolver: every model resolves to an EMPTY capability set. This is a\n * deliberate no-op scaffold — device-family resolvers (vacuum/mower) override\n * `resolve` in later phases while keeping this interface stable.\n */\nexport class DefaultCapabilityResolver implements CapabilityResolver {\n resolve(model: string): DeviceCapabilities {\n return new CapabilitySet(model, new Set<string>());\n }\n}\n\nconst sharedDefault = new DefaultCapabilityResolver();\n\n/** Convenience: resolve capabilities via the shared default resolver. */\nexport function resolveCapabilities(model: string): DeviceCapabilities {\n return sharedDefault.resolve(model);\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type {\n DreameDevice,\n DreameSession,\n MiotProp,\n PropertyResult,\n PropertyWrite,\n} from '../cloud/types.js';\nimport {\n callAction as defaultCallAction,\n getProperties as defaultGetProperties,\n getCachedProperties as defaultGetCachedProperties,\n setProperties as defaultSetProperties,\n type CommonInput,\n} from '../cloud/commands.js';\nimport { DreamePush, type PropertyChange } from '../transport/mqtt-push.js';\nimport { DreameTransportError } from '../transport/errors.js';\nimport { TypedEmitter } from '../transport/typed-emitter.js';\nimport { DefaultCapabilityResolver, type DeviceCapabilities } from './capability.js';\nimport type {\n DeviceEvent,\n PropertyChangedEvent,\n PropertyState,\n StateChangedEvent,\n} from '../api/types.js';\n\n/**\n * The slice of {@link DreamePush} that {@link BaseDevice} depends on. Declaring\n * it as an interface lets tests inject a fake emitter with no casts; the real\n * `DreamePush` structurally satisfies it.\n */\nexport interface PushLike {\n on(event: 'properties', cb: (changes: PropertyChange[]) => void): this;\n on(\n event: 'event',\n cb: (ev: { did: string; siid: number; eiid: number; arguments: unknown[] }) => void,\n ): this;\n on(event: 'connect', cb: () => void): this;\n on(event: 'close', cb: () => void): this;\n on(event: 'error', cb: (err: Error) => void): this;\n open(): Promise<void>;\n close(): Promise<void>;\n refreshSession(session: DreameSession): Promise<void>;\n}\n\n/** Injectable collaborators — defaults wire the real transport + command layer. */\nexport interface BaseDeviceDeps {\n createPush(device: DreameDevice, session: DreameSession, region: DreameRegion): PushLike;\n getProperties(base: CommonInput, props: MiotProp[]): Promise<PropertyResult[]>;\n /** Read the cloud-cached (shadow) values WITHOUT waking the device. */\n getCachedProperties(base: CommonInput, props: MiotProp[]): Promise<PropertyResult[]>;\n setProperties(base: CommonInput, writes: PropertyWrite[]): Promise<PropertyResult[]>;\n callAction(\n base: CommonInput,\n action: { siid: number; aiid: number; in?: unknown[] },\n ): Promise<unknown>;\n}\n\n/** Default deps using the real `DreamePush` and `src/cloud/commands.ts`. */\nexport function defaultBaseDeviceDeps(): BaseDeviceDeps {\n return {\n createPush: (device, session, region) => new DreamePush({ device, session, region }),\n getProperties: (base, props) => defaultGetProperties(base, props),\n getCachedProperties: (base, props) => defaultGetCachedProperties(base, props),\n setProperties: (base, writes) => defaultSetProperties(base, writes),\n callAction: (base, action) => defaultCallAction(base, action),\n };\n}\n\nexport type BaseDeviceEvents = {\n propertyChanged: [PropertyChangedEvent];\n stateChanged: [StateChangedEvent];\n event: [DeviceEvent];\n error: [Error];\n};\n\nexport interface BaseDeviceInput {\n device: DreameDevice;\n region: DreameRegion;\n /** Always reads the LATEST session — the facade owns the variable. */\n sessionRef: () => DreameSession;\n deps?: BaseDeviceDeps;\n /** Eager-seed the cache on `start()`. Default true. */\n fetchInitialValues?: boolean;\n /** Properties to seed when `fetchInitialValues` is true. Default `[]`. */\n initialProps?: MiotProp[];\n /** Poll interval (ms) while MQTT is down. Default 30000; 0 disables. */\n pollIntervalMs?: number;\n /** Capability set (defaults to the no-op resolver). */\n capabilities?: DeviceCapabilities;\n}\n\nconst DEFAULT_POLL_INTERVAL_MS = 30000;\n\n/**\n * A device-type-agnostic live handle for one Dreame device.\n *\n * Generic over its event map so subclasses can WIDEN the typed emitter with\n * their own events (e.g. the vacuum's `'map'` event) without a banned cast.\n * `Events` is constrained to extend {@link BaseDeviceEvents} so every internal\n * `emit(...)` call stays type-safe, and it defaults to {@link BaseDeviceEvents}\n * so a bare `BaseDevice` reference behaves exactly as before.\n */\nexport class BaseDevice<\n Events extends BaseDeviceEvents = BaseDeviceEvents,\n> extends TypedEmitter<Events> {\n readonly #device: DreameDevice;\n readonly #region: DreameRegion;\n readonly #sessionRef: () => DreameSession;\n readonly #deps: BaseDeviceDeps;\n readonly #fetchInitial: boolean;\n readonly #initialProps: MiotProp[];\n readonly #pollIntervalMs: number;\n readonly #capabilities: DeviceCapabilities;\n readonly #cache = new Map<string, PropertyState>();\n #push: PushLike | null = null;\n #mqttHealthy = false;\n #pollTimer: ReturnType<typeof setInterval> | null = null;\n #closed = false;\n\n constructor(input: BaseDeviceInput) {\n super();\n this.#device = input.device;\n this.#region = input.region;\n this.#sessionRef = input.sessionRef;\n this.#deps = input.deps ?? defaultBaseDeviceDeps();\n this.#fetchInitial = input.fetchInitialValues ?? true;\n this.#initialProps = input.initialProps ?? [];\n this.#pollIntervalMs = input.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.#capabilities =\n input.capabilities ?? new DefaultCapabilityResolver().resolve(input.device.model);\n }\n\n get deviceId(): string {\n return this.#device.did;\n }\n\n get model(): string {\n return this.#device.model;\n }\n\n get name(): string {\n return this.#device.name;\n }\n\n get capabilities(): DeviceCapabilities {\n return this.#capabilities;\n }\n\n /** Snapshot of all cached property states. */\n get properties(): readonly PropertyState[] {\n return [...this.#cache.values()];\n }\n\n /** Read a cached property, or `undefined` if never observed. */\n getProperty(siid: number, piid: number): PropertyState | undefined {\n return this.#cache.get(key(siid, piid));\n }\n\n #base(): CommonInput {\n return { session: this.#sessionRef(), region: this.#region, did: this.#device.did };\n }\n\n /** The region this handle is bound to. Subclasses use it to build map fetches. */\n protected get region(): DreameRegion {\n return this.#region;\n }\n\n /** Latest session snapshot. Subclasses use it for out-of-band fetches (maps). */\n protected currentSession(): DreameSession {\n return this.#sessionRef();\n }\n\n /** Open the push, wire events, optionally seed the cache. */\n async start(): Promise<void> {\n const push = this.#deps.createPush(this.#device, this.#sessionRef(), this.#region);\n this.#push = push;\n push.on('properties', (changes) => this.#onProperties(changes));\n push.on('event', (ev) =>\n this.emit('event', {\n deviceId: ev.did || this.#device.did,\n siid: ev.siid,\n eiid: ev.eiid,\n arguments: ev.arguments,\n }),\n );\n push.on('error', (err) => this.emit('error', err));\n push.on('connect', () => this.#onMqttUp());\n push.on('close', () => this.#onMqttDown());\n await push.open();\n this.#onMqttUp();\n if (this.#fetchInitial && this.#initialProps.length > 0) {\n await this.refreshProperties(this.#initialProps);\n }\n }\n\n /** Live-read properties, update the cache, return the raw results. */\n async refreshProperties(props: MiotProp[]): Promise<PropertyResult[]> {\n this.#assertOpen();\n const results = await this.#deps.getProperties(this.#base(), props);\n this.#seedFromResults(results);\n return results;\n }\n\n /**\n * Read the CLOUD-CACHED (shadow) values of `props` WITHOUT waking the device,\n * update the cache, and emit `propertyChanged`/`stateChanged` just like\n * {@link refreshProperties} — but sourced from the cloud shadow endpoint, so\n * it works for standby/offline robots (and never surfaces a false 80001).\n */\n async refreshCachedProperties(props: MiotProp[]): Promise<PropertyResult[]> {\n this.#assertOpen();\n const results = await this.#deps.getCachedProperties(this.#base(), props);\n this.#seedFromResults(results);\n return results;\n }\n\n /** Mirror a PropertyResult[] into the cache + emit the change events. */\n #seedFromResults(results: PropertyResult[]): void {\n const changes: PropertyChange[] = [];\n for (const r of results) {\n if (typeof r.siid === 'number' && typeof r.piid === 'number') {\n changes.push({ did: this.#device.did, siid: r.siid, piid: r.piid, value: r.value });\n }\n }\n if (changes.length > 0) {\n this.#onProperties(changes);\n }\n }\n\n /** Write a property to the device. */\n async setProperty(write: PropertyWrite): Promise<PropertyResult[]> {\n this.#assertOpen();\n return this.#deps.setProperties(this.#base(), [write]);\n }\n\n /** Invoke a MIoT action on the device. */\n async callAction(siid: number, aiid: number, input: unknown[] = []): Promise<unknown> {\n this.#assertOpen();\n return this.#deps.callAction(this.#base(), { siid, aiid, in: input });\n }\n\n /** Guard against use after {@link close}. */\n #assertOpen(): void {\n if (this.#closed) {\n throw new DreameTransportError('device handle is closed');\n }\n }\n\n /** Propagate a refreshed session to the underlying push. */\n async applySession(session: DreameSession): Promise<void> {\n if (this.#push) {\n await this.#push.refreshSession(session);\n }\n }\n\n /** Tear down: stop polling, close the push. */\n async close(): Promise<void> {\n this.#closed = true;\n this.#stopPolling();\n const push = this.#push;\n this.#push = null;\n if (push) {\n await push.close();\n }\n this.removeAllListeners();\n }\n\n #onProperties(changes: PropertyChange[]): void {\n const emitted: PropertyChangedEvent[] = [];\n const now = Date.now();\n for (const c of changes) {\n const k = key(c.siid, c.piid);\n const previous = this.#cache.get(k);\n this.#cache.set(k, { siid: c.siid, piid: c.piid, value: c.value, updatedAt: now });\n const ev: PropertyChangedEvent = {\n deviceId: this.#device.did,\n siid: c.siid,\n piid: c.piid,\n value: c.value,\n previousValue: previous ? previous.value : null,\n };\n emitted.push(ev);\n this.emit('propertyChanged', ev);\n }\n if (emitted.length > 0) {\n this.emit('stateChanged', { deviceId: this.#device.did, changes: emitted });\n }\n }\n\n #onMqttUp(): void {\n this.#mqttHealthy = true;\n this.#stopPolling();\n }\n\n #onMqttDown(): void {\n this.#mqttHealthy = false;\n this.#startPolling();\n }\n\n #startPolling(): void {\n if (this.#closed || this.#pollTimer || this.#pollIntervalMs <= 0) {\n return;\n }\n if (this.#initialProps.length === 0) {\n return;\n }\n this.#pollTimer = setInterval(() => {\n if (this.#mqttHealthy) {\n this.#stopPolling();\n return;\n }\n void this.refreshProperties(this.#initialProps).catch((err: unknown) => {\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n });\n }, this.#pollIntervalMs);\n }\n\n #stopPolling(): void {\n if (this.#pollTimer) {\n clearInterval(this.#pollTimer);\n this.#pollTimer = null;\n }\n }\n}\n\nfunction key(siid: number, piid: number): string {\n return `${siid}.${piid}`;\n}\n","/**\n * Vacuum MIoT property + action maps (ported from malard/node-dreame\n * src/spec/{vacuum,battery,settings,consumable}-props.ts, MIT — attribution\n * retained). P3 subset only (state/commands/consumables); map/dock/schedule\n * tables land in later phases. VERIFIED/ASSUMED annotations preserved.\n */\n\n/** Vacuum service properties (siid 2 = state machine, siid 4 = task cluster). */\nexport const VACUUM_PROP = {\n /** VERIFIED r2532a — MiotState enum. */\n STATE: { siid: 2, piid: 1 } as const,\n /** VERIFIED r2532a — single-value error/fault code (see MiotError). */\n ERROR: { siid: 2, piid: 2 } as const,\n /** VERIFIED r2449a — writable CleaningMode (plain 0..3). SAFE write path. */\n CLEAN_MODE_SETTING: { siid: 2, piid: 6 } as const,\n /** VERIFIED r2532a — multi-value comma-separated fault-list mirror. */\n FAULTS_STR: { siid: 4, piid: 18 } as const,\n /** VERIFIED r2532a — Dreame \"task status\" enum (TaskStatus). */\n TASK_STATUS: { siid: 4, piid: 1 } as const,\n /** ASSUMED Tasshack types.py:573 — job runtime (minutes). */\n CLEANING_TIME: { siid: 4, piid: 2 } as const,\n /** ASSUMED Tasshack types.py:574 — area cleaned (m²). */\n CLEANED_AREA: { siid: 4, piid: 3 } as const,\n /** ASSUMED Tasshack types.py:575 — suction enum (SuctionLevel). */\n SUCTION_LEVEL: { siid: 4, piid: 4 } as const,\n /** ASSUMED Tasshack types.py:576 — water flow during cleaning (WaterVolume). */\n WATER_VOLUME: { siid: 4, piid: 5 } as const,\n /** ASSUMED Tasshack types.py:1479 — relocation state machine (0/1/10/11). */\n RELOCATION_STATUS: { siid: 4, piid: 20 } as const,\n /**\n * VERIFIED r2449a — packed (0x1400 capability mask | CleaningMode low-2-bits).\n * READ-ONLY here: do NOT write directly (drops mask → bricks next clean).\n * Use CLEAN_MODE_SETTING (siid 2 piid 6) to change clean mode.\n */\n CLEANING_MODE: { siid: 4, piid: 23 } as const,\n /** VERIFIED r2532a — Child Lock boolean. */\n CHILD_LOCK: { siid: 4, piid: 27 } as const,\n /** VERIFIED r2532a 2026-05-03 — task progress percentage 0..100. */\n TASK_PROGRESS_PCT: { siid: 4, piid: 63 } as const,\n /** VERIFIED r2532a — mop-drying progress (minutes ticking during MopDrying). */\n DRYING_PROGRESS: { siid: 4, piid: 64 } as const,\n} as const;\n\n/**\n * Vacuum-service actions. `callAction` builds the single-OBJECT params shape\n * (an array surfaces a misleading code 80001). START_CUSTOM carries the\n * segment/zone/spot payload as in-params (piid 1 = mode, piid 10 = JSON).\n */\nexport const VACUUM_ACTION = {\n /** ASSUMED Tasshack — start cleaning. */\n START: { siid: 2, aiid: 1 } as const,\n /** VERIFIED r2532a — pause (code 0 while idle). */\n PAUSE: { siid: 2, aiid: 2 } as const,\n /** ASSUMED Tasshack — return to dock / charge. */\n CHARGE: { siid: 3, aiid: 1 } as const,\n /** ASSUMED Tasshack — START_CUSTOM (segment/zone/spot; needs in payload). */\n START_CUSTOM: { siid: 4, aiid: 1 } as const,\n /** VERIFIED r2532a — stop (code 0 while idle). */\n STOP: { siid: 4, aiid: 2 } as const,\n /** VERIFIED r2532a 2026-05-02 — clear warning (code 0). */\n CLEAR_WARNING: { siid: 4, aiid: 3 } as const,\n /** ASSUMED Tasshack — start dock mop wash. */\n START_WASHING: { siid: 4, aiid: 4 } as const,\n /** VERIFIED r2532a 2026-05-02 — locate (robot beeps, code 0). */\n LOCATE: { siid: 7, aiid: 1 } as const,\n /** ASSUMED Tasshack — manual auto-empty trigger. */\n START_AUTO_EMPTY: { siid: 15, aiid: 1 } as const,\n} as const;\n\n/** Battery service (siid 3). */\nexport const BATTERY_PROP = {\n /** VERIFIED r2532a — battery percentage. */\n LEVEL: { siid: 3, piid: 1 } as const,\n /** VERIFIED r2532a — charging status (ChargingStatus). */\n CHARGING_STATUS: { siid: 3, piid: 2 } as const,\n} as const;\n\n/** Settings service — P3 needs voice volume only. */\nexport const SETTINGS_PROP = {\n /** ASSUMED Tasshack types.py:642 — voice volume 0-100. */\n VOLUME: { siid: 7, piid: 1 } as const,\n} as const;\n\n/** Consumables — % remaining (VERIFIED r2532a; note filter % is on piid 1). */\nexport const CONSUMABLE_PROP = {\n MAIN_BRUSH_LEFT: { siid: 9, piid: 2 } as const,\n SIDE_BRUSH_LEFT: { siid: 10, piid: 2 } as const,\n FILTER_LEFT: { siid: 11, piid: 1 } as const,\n} as const;\n\n/** START_CUSTOM mode ints (siid 4 piid 1 in-param). Tasshack device.py. */\nexport const CUSTOM_CLEAN_MODE = {\n SEGMENT: 18,\n ZONE: 19,\n SPOT: 20,\n} as const;\n","/**\n * Vacuum MIoT enum definitions (ported from malard/node-dreame\n * src/spec/enums.ts, MIT — attribution retained). Only the state/command\n * enums P3 needs are ported here; dock/schedule enums land in later phases.\n * Annotations (VERIFIED <date> / ASSUMED from Tasshack) are preserved.\n */\n\n/**\n * VERIFIED on r2532a 2026-05-02 — MIoT STATE (siid 2 piid 1). Sourced from\n * the device's keyDefine v8 JSON. 19 values verified live across a full\n * cleaning task; sub-mode: value 1 = vacuum-only, value 12 = vacuum+mop.\n */\nexport enum MiotState {\n Cleaning = 1,\n Standby = 2,\n Paused = 3,\n PausedAlt = 4,\n ReturningToCharge = 5,\n Charging = 6,\n Mopping = 7,\n MopDrying = 8,\n MopCleaning = 9,\n ReturningToWash = 10,\n Mapping = 11,\n CleaningAlt = 12,\n ChargingComplete = 13,\n Updating = 14,\n CallToClean = 15,\n AutoRepairBase = 16,\n ReturnInstallMop = 17,\n ReturnRemoveMop = 18,\n WaterSupplyDrainTest = 19,\n CleanMopRefillWater = 20,\n PausedCleaning = 21,\n AutoEmptying = 22,\n RemoteCleaning = 23,\n IntelligentCharging = 24,\n SecondCleaning = 25,\n Following = 26,\n PartialCleaning = 27,\n ReturnToEmpty = 28,\n WaitingForTask = 29,\n CleanWashboardBase = 30,\n // ASSUMED from Tasshack (DreameVacuumState) — codes 31-38 not yet live-verified.\n ReturningToDrain = 31,\n Draining = 32,\n AutoWaterDraining = 33,\n Emptying = 34,\n DustBagDrying = 35,\n DustBagDryingPaused = 36,\n HeadingToExtraCleaning = 37,\n ExtraCleaning = 38,\n FindingPetPaused = 95,\n FindingPet = 96,\n ShortcutRunning = 97,\n CameraMonitoring = 98,\n CameraMonitoringPaused = 99,\n InitialDeepClean = 101,\n // ASSUMED from Tasshack (DreameVacuumState) — codes 102-108.\n InitialDeepCleanPaused = 102,\n Sanitizing = 103,\n SanitizingWithDry = 104,\n ChangingMop = 105,\n ChangingMopPaused = 106,\n FloorMaintaining = 107,\n FloorMaintainingPaused = 108,\n}\n\n/** VERIFIED on r2532a 2026-05-02 — ChargingStatus (siid 3 piid 2). */\nexport enum ChargingStatus {\n Charging = 1,\n Discharging = 2,\n Returning = 5,\n}\n\n/** VERIFIED on r2532a 2026-05-02 — SuctionLevel (siid 4 piid 4). X50 labels. */\nexport enum SuctionLevel {\n Quiet = 0,\n Standard = 1,\n Intense = 2,\n Max = 3,\n}\n\n/** ASSUMED from Tasshack — WaterVolume (siid 4 piid 5). NOT YET verified on r2532a. */\nexport enum WaterVolume {\n Low = 1,\n Medium = 2,\n High = 3,\n}\n\n/**\n * VERIFIED on r2449a 2026-05-21 — plain 0..3 value space written via\n * CLEAN_MODE_SETTING (siid 2 piid 6). The raw CLEANING_MODE field (siid 4\n * piid 23) packs this in its low 2 bits OR'd with a 0x1400 capability mask;\n * prefer CLEAN_MODE_SETTING to avoid the bitfield trap.\n */\nexport enum CleaningMode {\n Sweeping = 0,\n Mopping = 1,\n SweepAndMop = 2,\n MopAfterSweep = 3,\n}\n\n/**\n * Error / fault value space for ERROR (siid 2 piid 2) and FAULTS_STR\n * (siid 4 piid 18). This is the FULL Tasshack `DreameVacuumErrorCode` table\n * (types.py) — every documented integer is mapped. Members whose names were\n * VERIFIED live on r2532a keep their verified labels even where the donor uses\n * a different label for the same integer (annotated inline); the rest carry the\n * donor's documented name. UNKNOWN (-1) is intentionally omitted — an unknown\n * code falls through the cast-free lookup and surfaces as the raw number.\n */\nexport enum MiotError {\n Clear = 0, // donor NO_ERROR\n WheelRotationAnomaly = 1, // donor DROP (VERIFIED label kept)\n Cliff = 2,\n Bumper = 3,\n Gesture = 4,\n BumperRepeat = 5,\n DropRepeat = 6,\n OpticalFlow = 7,\n Box = 8,\n TankBox = 9,\n WaterBoxEmpty = 10,\n BoxFull = 11,\n Brush = 12,\n SideBrush = 13,\n Fan = 14,\n LeftWheelMotor = 15,\n RightWheelMotor = 16,\n TurnSuffocate = 17,\n RobotLifted = 18, // donor FORWARD_SUFFOCATE (VERIFIED label kept)\n ChargerGet = 19,\n BatteryLow = 20,\n ChargeFault = 21,\n BatteryPercentageAnomaly = 22, // donor BATTERY_PERCENTAGE\n Heart = 23,\n CameraOcclusion = 24,\n Move = 25,\n FlowShielding = 26,\n InfraredShielding = 27,\n ChargeNoElectric = 28,\n BatteryFault = 29,\n FanSpeedError = 30,\n LeftWheelSpeed = 31,\n RightWheelSpeed = 32,\n Bmi055Acce = 33,\n Bmi055Gyro = 34,\n Xv7001 = 35,\n LeftMagnet = 36,\n RightMagnet = 37,\n FlowError = 38,\n InfraredFault = 39,\n CameraFault = 40,\n StrongMagnet = 41,\n WaterPump = 42,\n Rtc = 43,\n AutoKeyTrig = 44,\n P3v3 = 45,\n CameraIdle = 46,\n Blocked = 47,\n LdsError = 48,\n LdsBumper = 49,\n WaterPump2 = 50,\n FilterBlocked = 51,\n Edge = 54,\n Carpet = 55,\n Laser = 56,\n Edge2 = 57,\n Ultrasonic = 58,\n NoGoZone = 59,\n Route = 61,\n Route2 = 62,\n Blocked2 = 63,\n Blocked3 = 64,\n Restricted = 65,\n Restricted2 = 66,\n Restricted3 = 67,\n TaskComplete = 68, // donor REMOVE_MOP (VERIFIED label kept)\n MopRemoved = 69,\n MopRemoved2 = 70,\n MopPadStopRotate = 71,\n MopPadStopRotate2 = 72,\n ManualMopInstallRequired = 74, // donor MOP_INSTALL_FAILED (VERIFIED label kept)\n LowBatteryTurnOff = 75,\n DirtyTankNotInstalled = 76,\n RobotInHiddenRoom = 78,\n LdsFailedToLift = 79,\n RobotStuck = 80,\n RobotStuckRepeat = 81,\n SlipperyFloor = 82,\n UnknownError = 84,\n CheckMopInstall = 85,\n DirtyWaterTankFull = 86,\n RetractableLegStuck = 88,\n InternalError = 89,\n RobotStuck2 = 90,\n RobotStuckOnTables = 91,\n RobotStuckOnPassage = 92,\n RobotStuckOnThreshold = 93,\n RobotStuckOnLowLyingArea = 94,\n RobotStuckOnRamp = 95,\n RobotStuckOnObstacle = 96,\n RobotStuckOnPet = 97,\n RobotStuckOnSlipperySurface = 98,\n RobotStuckOnCarpet = 99,\n BinFull = 101,\n BinOpen = 102,\n BinOpen2 = 103,\n BinFull2 = 104,\n WastewaterTankFull = 105, // donor WATER_TANK (VERIFIED label kept)\n DirtyWaterTank = 106,\n CleanWaterTankEmpty = 107, // donor WATER_TANK_DRY (VERIFIED label kept)\n DirtyWaterTank2 = 108,\n DirtyWaterTankBlocked = 109,\n DirtyWaterTankPump = 110,\n MopPad = 111,\n WetMopPad = 112,\n WashboardFilterNeedsCleaning = 114, // donor CLEAN_MOP_PAD (VERIFIED label kept)\n CleanTankLevel = 116,\n StationDisconnected = 117,\n DirtyTankLevel = 118,\n WashboardLevel = 119,\n MopPadsMissing = 120, // donor NO_MOP_IN_STATION (VERIFIED label kept)\n DustBagFull = 121,\n UnknownWarning = 122,\n SelfTestFailed = 123,\n WashboardNotWorking = 124,\n DrainageFailed = 125,\n MopNotDetected = 126,\n MopHolderError = 127,\n DockError = 128,\n WashFailed = 129,\n RobotStuckOnCurtain = 200,\n EdgeMopStopRotate = 201,\n EdgeMopDetached = 202,\n ChassisLiftMalfunction = 203,\n InternalError2 = 207,\n MopCoverError = 209,\n RollerMopError = 210,\n OnboardWaterTankEmpty = 213,\n OnboardDirtyWaterTankFull = 214,\n MopNotInstalled = 215,\n RollerMopError2 = 218,\n FluffingRollerError = 222,\n MopCoverError2 = 223,\n BlockedByObstacle = 226,\n ReturnToChargeFailed = 1000,\n}\n\n/**\n * TASK_STATUS (siid 4 piid 1). Codes 1/2/3/6/12/14 carry the labels VERIFIED\n * live on r2532a 2026-05-02 — these are KEPT verbatim because they were\n * observed against real task transitions and differ from the donor's labels for\n * the same integers (donor: 1=AUTO_CLEANING, 2=ZONE_CLEANING, 3=SEGMENT_CLEANING,\n * 6=AUTO_CLEANING_PAUSED, 12=MOPPING_PAUSED, 14=ZONE_MOPPING_PAUSED). The\n * remaining members are ASSUMED from Tasshack `DreameVacuumTaskStatus`\n * (types.py) — added so documented codes resolve to a name. Unknown codes\n * surface as raw via {@link VacuumDevice.taskStatusRaw}.\n */\nexport enum TaskStatus {\n // ASSUMED from Tasshack\n Completed = 0,\n // VERIFIED live\n InterruptedOrPaused = 1,\n Active = 2,\n Transitioning = 3,\n // ASSUMED from Tasshack\n SpotCleaning = 4,\n FastMapping = 5,\n // VERIFIED live\n OnDockIdle = 6,\n // ASSUMED from Tasshack\n ZoneCleaningPaused = 7,\n SegmentCleaningPaused = 8,\n SpotCleaningPaused = 9,\n MapCleaningPaused = 10,\n DockingPaused = 11,\n // VERIFIED live\n TransientPauseEdge = 12,\n // ASSUMED from Tasshack\n SegmentMoppingPaused = 13,\n // VERIFIED live\n NeedsIntervention = 14,\n // ASSUMED from Tasshack\n AutoMoppingPaused = 15,\n AutoDockingPaused = 16,\n SegmentDockingPaused = 17,\n ZoneDockingPaused = 18,\n CruisingPath = 20,\n CruisingPathPaused = 21,\n CruisingPoint = 22,\n CruisingPointPaused = 23,\n SummonCleanPaused = 24,\n ReturningInstallMop = 25,\n ReturningRemoveMop = 26,\n StationCleaning = 27,\n PetFinding = 30,\n AutoCleaningWashingPaused = 31,\n AreaCleaningWashingPaused = 32,\n CustomCleaningWashingPaused = 33,\n}\n","/**\n * Cast-free decode primitives shared by the vacuum and mower model layers.\n * `enumLookup` was a private helper in vacuum-device.ts (P3); promoted here so\n * the mower reuses it verbatim with no banned cast. The narrower relies on\n * `members.find(...)` being `E | undefined`, so no `as` is needed to turn a raw\n * number back into an enum literal.\n */\n\n/** Coerce a raw MIoT value to a number, or null. */\nexport function asNum(value: unknown): number | null {\n return typeof value === 'number' ? value : null;\n}\n\n/**\n * Returns a narrower: a raw number (or null) maps to the matching ENUM-typed\n * member, or null when not a member. Cast-free.\n */\nexport function enumLookup<E extends number>(\n members: readonly E[],\n): (n: number | null) => E | null {\n const set = new Set<number>(members);\n return (n) => (n !== null && set.has(n) ? (members.find((m) => m === n) ?? null) : null);\n}\n","/**\n * Cast-free decode helpers for vacuum state (ported from node-dreame\n * src/vacuum/state.ts, MIT). The donor's `asEnum` used `num as T`; this\n * port replaces it with a numeric-membership check so callers can narrow\n * with no banned cast.\n */\n\n// asNum now lives in the shared module so vacuum + mower share one cast-free\n// implementation. parseFaultList / enumMembers stay vacuum-specific below.\nexport { asNum } from '../_shared/decode.js';\n\n/** Numeric members of a TS numeric enum object (ascending insertion order). */\nexport function enumMembers(enumObj: Record<string, unknown>): number[] {\n return Object.values(enumObj).filter((v): v is number => typeof v === 'number');\n}\n\n/**\n * Parse the multi-value FAULTS_STR (siid 4 piid 18) into an array of fault\n * ints. Comma-separated when multiple conditions are latched; the single\n * case is the int as a string. Empty / \"0\" / whitespace → []. A bare\n * non-zero number is accepted as a one-element list.\n */\nexport function parseFaultList(value: unknown): number[] {\n if (typeof value !== 'string') {\n if (typeof value === 'number' && value !== 0) {\n return [value];\n }\n return [];\n }\n const trimmed = value.trim();\n if (trimmed === '' || trimmed === '0') {\n return [];\n }\n const codes: number[] = [];\n for (const part of trimmed.split(',')) {\n const n = Number.parseInt(part.trim(), 10);\n if (Number.isFinite(n) && n !== 0) {\n codes.push(n);\n }\n }\n return codes;\n}\n","/**\n * Per-model vacuum capability records (ported from malard/node-dreame\n * src/capabilities.ts, MIT — attribution retained) plus a resolver that\n * adapts the rich record into the generic CapabilityResolver the BaseDevice\n * expects. r2538z is added mirroring r2532a (X50 sibling), marked unverified\n * until live-confirmed.\n */\nimport { SuctionLevel, WaterVolume } from './enums.js';\nimport type { CapabilityResolver, DeviceCapabilities } from '../../device/capability.js';\n\n/** Rich vacuum capability record. `canX` = behaviour, `hasX` = hardware. */\nexport interface VacuumCapabilities {\n model: string;\n /** true when the record came from the curated table (false = fallback / assumed). */\n verified: boolean;\n canMop: boolean;\n canAutoInstallMop: boolean;\n hasSideBrush: boolean;\n hasCamera: boolean;\n hasCarpetSensor: boolean;\n hasAiObstacleDetection: boolean;\n canAutoEmpty: boolean;\n canMopWash: boolean;\n canMopDry: boolean;\n canHeatMopWater: boolean;\n hasDetergentReservoir: boolean;\n canCleanPerRoom: boolean;\n supportsVirtualWalls: boolean;\n supportsNoGoZones: boolean;\n hasChildLock: boolean;\n supportsMultiFloor: boolean;\n /** Decodes/fetches the binary live/saved map (envelope → pixel grid → segments). */\n canMap: boolean;\n supportedSuctionLevels: readonly SuctionLevel[];\n supportedWaterVolumes: readonly WaterVolume[];\n}\n\nconst FALLBACK: Omit<VacuumCapabilities, 'model'> = {\n verified: false,\n canMop: false,\n canAutoInstallMop: false,\n hasSideBrush: true,\n hasCamera: false,\n hasCarpetSensor: false,\n hasAiObstacleDetection: false,\n canAutoEmpty: false,\n canMopWash: false,\n canMopDry: false,\n canHeatMopWater: false,\n hasDetergentReservoir: false,\n canCleanPerRoom: false,\n supportsVirtualWalls: false,\n supportsNoGoZones: false,\n hasChildLock: false,\n supportsMultiFloor: false,\n // Map decoding is model-agnostic (binary frame format is identical across\n // the lineup); the fallback record leaves it false so an unknown model does\n // not advertise a feature we have not verified its firmware exposes.\n canMap: false,\n supportedSuctionLevels: [\n SuctionLevel.Quiet,\n SuctionLevel.Standard,\n SuctionLevel.Intense,\n SuctionLevel.Max,\n ],\n supportedWaterVolumes: [WaterVolume.Low, WaterVolume.Medium, WaterVolume.High],\n};\n\n/** Full-feature X50-family record body (r2532a verified; r2538z assumed sibling). */\nconst X50_FAMILY: Omit<VacuumCapabilities, 'model' | 'verified'> = {\n canMop: true,\n canAutoInstallMop: true,\n hasSideBrush: true,\n hasCamera: true,\n hasCarpetSensor: true,\n hasAiObstacleDetection: true,\n canAutoEmpty: true,\n canMopWash: true,\n canMopDry: true,\n canHeatMopWater: true,\n hasDetergentReservoir: true,\n canCleanPerRoom: true,\n supportsVirtualWalls: true,\n supportsNoGoZones: true,\n hasChildLock: true,\n supportsMultiFloor: true,\n canMap: true,\n supportedSuctionLevels: [\n SuctionLevel.Quiet,\n SuctionLevel.Standard,\n SuctionLevel.Intense,\n SuctionLevel.Max,\n ],\n supportedWaterVolumes: [WaterVolume.Low, WaterVolume.Medium, WaterVolume.High],\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {\n Object.freeze(obj);\n for (const v of Object.values(obj)) {\n deepFreeze(v);\n }\n }\n return obj;\n}\n\nexport const MODEL_CAPABILITIES: Readonly<Record<string, VacuumCapabilities>> = {\n /** Dreame X40 Ultra Complete — VERIFIED r2449a (2026-05-21). */\n 'dreame.vacuum.r2449a': { model: 'dreame.vacuum.r2449a', verified: true, ...X50_FAMILY },\n /** Dreame X50 Ultra Complete — VERIFIED r2532a (firmware 4.3.9_2199, 2026-05-02). */\n 'dreame.vacuum.r2532a': { model: 'dreame.vacuum.r2532a', verified: true, ...X50_FAMILY },\n /**\n * Dreame r2538z — the user's device. ASSUMED from the r2532a (X50) sibling:\n * same dock-side feature set is the working hypothesis. Marked verified:false\n * until confirmed live (e2e). Flip to true + drop this note once verified.\n */\n 'dreame.vacuum.r2538z': { model: 'dreame.vacuum.r2538z', verified: false, ...X50_FAMILY },\n};\n\nfor (const entry of Object.values(MODEL_CAPABILITIES)) {\n deepFreeze(entry);\n}\n\n/** Resolve a model to its rich vacuum capability record (frozen / fallback). */\nexport function getVacuumCapabilities(model: string): VacuumCapabilities {\n const known = MODEL_CAPABILITIES[model];\n if (known) {\n return known;\n }\n return deepFreeze({ model, ...FALLBACK });\n}\n\n/** Map the rich record's booleans → the generic capability token set. */\nfunction tokensFor(c: VacuumCapabilities): Set<string> {\n const tokens = new Set<string>();\n const add = (flag: boolean, token: string): void => {\n if (flag) {\n tokens.add(token);\n }\n };\n add(c.canMop, 'mop');\n add(c.canAutoInstallMop, 'auto-install-mop');\n add(c.hasSideBrush, 'side-brush');\n add(c.hasCamera, 'camera');\n add(c.hasCarpetSensor, 'carpet-sensor');\n add(c.hasAiObstacleDetection, 'ai-obstacle');\n add(c.canAutoEmpty, 'auto-empty');\n add(c.canMopWash, 'mop-wash');\n add(c.canMopDry, 'mop-dry');\n add(c.canHeatMopWater, 'heat-mop-water');\n add(c.hasDetergentReservoir, 'detergent');\n add(c.canCleanPerRoom, 'clean-per-room');\n add(c.supportsVirtualWalls, 'virtual-walls');\n add(c.supportsNoGoZones, 'no-go-zones');\n add(c.hasChildLock, 'child-lock');\n add(c.supportsMultiFloor, 'multi-floor');\n return tokens;\n}\n\nclass VacuumCapabilitySet implements DeviceCapabilities {\n readonly model: string;\n readonly #tokens: ReadonlySet<string>;\n constructor(model: string, tokens: ReadonlySet<string>) {\n this.model = model;\n this.#tokens = tokens;\n }\n has(token: string): boolean {\n return this.#tokens.has(token);\n }\n list(): readonly string[] {\n return [...this.#tokens];\n }\n}\n\n/** Resolver that the BaseDevice accepts via BaseDeviceInput.capabilities. */\nexport class VacuumCapabilityResolver implements CapabilityResolver {\n resolve(model: string): DeviceCapabilities {\n return new VacuumCapabilitySet(model, tokensFor(getVacuumCapabilities(model)));\n }\n}\n","/**\n * Outer-envelope handling for Dreame map blobs.\n *\n * Wire format: URL-safe base64 → optional AES-256-CBC decrypt → zlib\n * inflate → 27-byte binary header + width*height pixel grid + UTF-8\n * JSON tail. This module covers the first three steps; header / tail\n * parsing live in `header.ts` / `tail.ts`.\n */\n\nimport { createDecipheriv, createHash } from 'node:crypto';\nimport * as zlib from 'node:zlib';\nimport type { VacuumMapDecodeOptions } from './types.js';\n\n/** Bytes 0-26 are the fixed-layout binary header. */\nexport const HEADER_SIZE = 27;\n\n/** Sentinel value in `robot.a` / `charger.a` meaning \"absent\". */\nexport const ANGLE_ABSENT = 0x7fff;\n\n/** Frame-type byte values from the header. */\nexport const FRAME_TYPE = {\n I: 73,\n P: 80,\n W: 87,\n} as const;\n\nexport class MapDecodeError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'MapDecodeError';\n }\n}\n\n/**\n * Unwrap a raw MAP_DATA envelope value: URL-safe base64 → optional\n * AES-256-CBC decrypt → zlib inflate. The returned buffer starts with\n * the 27-byte header.\n *\n * If the value contains a comma, everything after the first comma is\n * treated as the per-blob AES key and AES is forced on (matching the\n * outer-envelope convention from Tasshack `map.py:3759-3792`). If `opts.key`\n * and `opts.iv` are also supplied, the embedded key wins.\n */\nexport function unwrapEnvelope(value: string, opts: VacuumMapDecodeOptions = {}): Buffer {\n const commaIdx = value.indexOf(',');\n const b64 = commaIdx >= 0 ? value.slice(0, commaIdx) : value;\n const embeddedKey = commaIdx >= 0 ? value.slice(commaIdx + 1) : null;\n\n const standard = b64.replace(/-/g, '+').replace(/_/g, '/');\n let bytes: Buffer;\n try {\n bytes = Buffer.from(standard, 'base64');\n } catch (err) {\n throw new MapDecodeError('envelope: base64 decode failed', { cause: err });\n }\n if (bytes.length === 0) {\n throw new MapDecodeError('envelope: empty payload after base64 decode');\n }\n\n const key = embeddedKey ?? opts.key ?? null;\n if (key) {\n if (!opts.iv) {\n throw new MapDecodeError(\n 'envelope: AES key supplied but no IV — pass `opts.iv` (16-byte ASCII)',\n );\n }\n bytes = aesCbcDecrypt(bytes, key, opts.iv);\n }\n\n try {\n return zlib.inflateSync(bytes);\n } catch (err) {\n throw new MapDecodeError('envelope: zlib inflate failed', { cause: err });\n }\n}\n\nfunction aesCbcDecrypt(cipher: Buffer, rawKey: string, iv: string): Buffer {\n const keyBytes = Buffer.from(\n createHash('sha256').update(rawKey).digest('hex').slice(0, 32),\n 'utf8',\n );\n const ivBytes = Buffer.from(iv, 'utf8');\n if (ivBytes.length !== 16) {\n throw new MapDecodeError(`envelope: AES IV must be 16 ASCII bytes, got ${ivBytes.length}`);\n }\n try {\n const decipher = createDecipheriv('aes-256-cbc', keyBytes, ivBytes);\n decipher.setAutoPadding(false);\n return Buffer.concat([decipher.update(cipher), decipher.final()]);\n } catch (err) {\n throw new MapDecodeError('envelope: AES decrypt failed', { cause: err });\n }\n}\n","/**\n * 27-byte binary header at the start of every inflated map frame.\n * Layout per `docs/live-map-format.md` §\"27-byte header\".\n */\n\nimport type { MapFrameType } from './types.js';\nimport { FRAME_TYPE, HEADER_SIZE, MapDecodeError } from './envelope.js';\n\nexport interface MapHeader {\n mapId: number;\n frameId: number;\n frameType: MapFrameType;\n robotX: number;\n robotY: number;\n robotA: number;\n chargerX: number;\n chargerY: number;\n chargerA: number;\n gridSize: number;\n width: number;\n height: number;\n left: number;\n top: number;\n}\n\nexport function parseMapHeader(buf: Buffer): MapHeader {\n if (buf.length < HEADER_SIZE) {\n throw new MapDecodeError(`header: need ${HEADER_SIZE} bytes, got ${buf.length}`);\n }\n return {\n mapId: buf.readInt16LE(0),\n frameId: buf.readInt16LE(2),\n frameType: frameTypeFromByte(buf[4]!),\n robotX: buf.readInt16LE(5),\n robotY: buf.readInt16LE(7),\n robotA: buf.readInt16LE(9),\n chargerX: buf.readInt16LE(11),\n chargerY: buf.readInt16LE(13),\n chargerA: buf.readInt16LE(15),\n gridSize: buf.readInt16LE(17),\n width: buf.readInt16LE(19),\n height: buf.readInt16LE(21),\n left: buf.readInt16LE(23),\n top: buf.readInt16LE(25),\n };\n}\n\nfunction frameTypeFromByte(b: number): MapFrameType {\n switch (b) {\n case FRAME_TYPE.I:\n return 'I';\n case FRAME_TYPE.P:\n return 'P';\n case FRAME_TYPE.W:\n return 'W';\n default:\n throw new MapDecodeError(`header: unknown frame_type byte ${b}`);\n }\n}\n","/**\n * JSON tail extraction from an inflated map frame buffer.\n *\n * The tail is UTF-8 JSON immediately after the header + pixel grid;\n * `MapTail` (`./types.ts`) describes the subset of keys we consume.\n *\n * `parseFrame` is the composed seam used by both `decodeVacuumMap`\n * and the rism-recurse path inside it — keeping the\n * \"header → tail\" sequence in one place ensures the two call sites\n * can't drift independently.\n */\n\nimport { HEADER_SIZE, MapDecodeError } from './envelope.js';\nimport { parseMapHeader, type MapHeader } from './header.js';\nimport type { MapTail } from './types.js';\n\nexport function sliceTailText(inflated: Buffer, header: MapHeader): string {\n const start = HEADER_SIZE + header.width * header.height;\n if (inflated.length < start) {\n throw new MapDecodeError(\n `tail: inflated payload shorter than header+pixels (${inflated.length} < ${start})`,\n );\n }\n return inflated.subarray(start).toString('utf8');\n}\n\n/**\n * Narrow a parsed JSON value to the loose `MapTail` shape without a\n * cast: `MapTail` is an all-optional interface with a\n * `[key: string]: unknown` index signature, so any plain non-null\n * object satisfies it once we've confirmed it is a record. A non-object\n * JSON top-level (number, string, array, null) is rejected.\n */\nfunction asMapTail(value: unknown): MapTail {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new MapDecodeError('tail: JSON top-level is not an object');\n }\n const record: Record<string, unknown> = { ...value };\n return record;\n}\n\nexport function parseMapJsonTail(text: string): MapTail {\n if (!text) {\n return {};\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n throw new MapDecodeError('tail: JSON parse failed', { cause: err });\n }\n return asMapTail(parsed);\n}\n\n/**\n * Parse the header + JSON tail from an already-inflated frame buffer.\n *\n * Combines `parseMapHeader` + `sliceTailText` + `parseMapJsonTail`,\n * which the decoder, the rism-recurse path, and (deliberately not)\n * `merge.ts` invoke as a unit. Centralising the sequence keeps the\n * call sites in lockstep — the alternative is to drift independently\n * if one is updated without the others.\n *\n * Pure; doesn't unwrap base64 — call `unwrapEnvelope` first.\n */\nexport function parseFrame(inflated: Buffer): { header: MapHeader; tail: MapTail } {\n const header = parseMapHeader(inflated);\n const tail = parseMapJsonTail(sliceTailText(inflated, header));\n return { header, tail };\n}\n","/**\n * Pixel-grid decoder for fsm:1 (path B / frame-map mode) — the layout\n * r2532a uses. Walks the grid row-by-row and emits run-length\n * `MapLayer` entries the renderer can blit straight onto canvas\n * without tracking individual pixels.\n *\n * The fsm:0 (variant A) layout is documented in the roadmap but isn't\n * exercised by any captured fixture; not implemented.\n */\n\nimport type { MapDimensions, MapLayer, MapRun, MapSegment, MapTail } from './types.js';\n\n/**\n * Pixel-byte classifications for path B (`fsm: 1`).\n *\n * Layout (per roadmap):\n * bits 7-2 = segment_id (top 6 bits, range 0..63)\n * bits 1-0 = meta (low 2 bits)\n *\n * Special segment ids: 63 = WALL, 62 = FLOOR, 61 = UNKNOWN.\n * When the top 6 bits are zero, the low 2 bits encode markers\n * (01 = NEW_SEGMENT, 10 = WALL).\n * The `11` low-bit meta is a CARPET overlay independent of segment id.\n *\n * v1 layer types collapse this down to `wall | floor | segment`.\n * Carpet overlay is dropped for now — extending the v1 contract is a\n * separate decision (see types.ts MapLayerType).\n */\nexport type PixelClass = 'wall' | 'floor' | 'segment' | 'outside';\n\n/**\n * Classify a single pixel byte under fsm:1 (path B) layout.\n *\n * `kind` is the primary mutually-exclusive class. `carpet` is an\n * independent overlay flag (low bits == 11): it can co-occur with any\n * primary class — a carpet floor, a carpet pixel inside a segment, etc.\n * The wall marker (low bits == 10 with high bits zero) and the carpet\n * marker share the low-bits field, so a pixel cannot be both \"wall\n * marker\" and \"carpet\" at the same time.\n */\nexport function classifyPixelFsm1(byte: number): {\n kind: PixelClass;\n segmentId?: number;\n carpet?: true;\n} {\n if (byte === 0) {\n return { kind: 'outside' };\n }\n const seg = byte >> 2;\n const meta = byte & 3;\n const isCarpet = meta === 3;\n\n if (seg === 63) {\n return isCarpet ? { kind: 'wall', carpet: true } : { kind: 'wall' };\n }\n if (seg === 62) {\n return isCarpet ? { kind: 'floor', carpet: true } : { kind: 'floor' };\n }\n if (seg === 61) {\n return { kind: 'outside' };\n }\n if (seg === 0) {\n if (meta === 2) {\n return { kind: 'wall' };\n }\n if (isCarpet) {\n return { kind: 'outside', carpet: true };\n }\n return { kind: 'outside' };\n }\n return isCarpet\n ? { kind: 'segment', segmentId: seg, carpet: true }\n : { kind: 'segment', segmentId: seg };\n}\n\n/**\n * Walk the pixel grid row-by-row and emit run-length layers. Each row\n * resets the run state; runs never cross row boundaries (so the renderer\n * doesn't need to know the width to interpret them).\n *\n * Two kinds of run accumulation happen in parallel:\n * - the primary classification (wall / floor / segment) — one run per\n * contiguous same-class span\n * - the carpet overlay — one run per contiguous carpet span, regardless\n * of underlying primary class (a carpet stripe spanning the boundary\n * between segment 5 and segment 7 produces one carpet run)\n */\nexport function decodePixelGridFsm1(pixels: Buffer, width: number, height: number): MapLayer[] {\n const wallRuns: MapRun[] = [];\n const floorRuns: MapRun[] = [];\n const segmentRuns = new Map<number, MapRun[]>();\n const carpetRuns: MapRun[] = [];\n\n for (let y = 0; y < height; y++) {\n const rowBase = y * width;\n let runStart = -1;\n let runKind: PixelClass = 'outside';\n let runSegmentId = 0;\n let carpetStart = -1;\n for (let x = 0; x <= width; x++) {\n const byte = x < width ? pixels[rowBase + x]! : 0;\n const c =\n x < width ? classifyPixelFsm1(byte) : { kind: 'outside' as const, carpet: undefined };\n const sameAsRun =\n runStart >= 0 &&\n c.kind === runKind &&\n (c.kind !== 'segment' || c.segmentId === runSegmentId);\n if (!sameAsRun) {\n // Emit the previous primary run (if any).\n if (runStart >= 0 && runKind !== 'outside') {\n const length = x - runStart;\n const run: MapRun = [runStart, y, length];\n if (runKind === 'wall') {\n wallRuns.push(run);\n } else if (runKind === 'floor') {\n floorRuns.push(run);\n } else if (runKind === 'segment') {\n let bucket = segmentRuns.get(runSegmentId);\n if (!bucket) {\n bucket = [];\n segmentRuns.set(runSegmentId, bucket);\n }\n bucket.push(run);\n }\n }\n // Start a new primary run.\n if (x < width && c.kind !== 'outside') {\n runStart = x;\n runKind = c.kind;\n runSegmentId = c.kind === 'segment' ? c.segmentId! : 0;\n } else {\n runStart = -1;\n }\n }\n\n // Carpet overlay accumulates independently of primary classification.\n const isCarpet = c.carpet === true;\n if (isCarpet && carpetStart < 0) {\n carpetStart = x;\n } else if (!isCarpet && carpetStart >= 0) {\n carpetRuns.push([carpetStart, y, x - carpetStart]);\n carpetStart = -1;\n }\n }\n }\n\n const layers: MapLayer[] = [];\n if (wallRuns.length > 0) {\n layers.push({ type: 'wall', runs: wallRuns });\n }\n if (floorRuns.length > 0) {\n layers.push({ type: 'floor', runs: floorRuns });\n }\n for (const [id, runs] of [...segmentRuns.entries()].sort(([a], [b]) => a - b)) {\n layers.push({ type: 'segment', segmentId: id, runs });\n }\n if (carpetRuns.length > 0) {\n layers.push({ type: 'carpet', runs: carpetRuns });\n }\n return layers;\n}\n\n/**\n * Project decoded pixel-grid layers into per-segment metadata: bbox\n * and centroid in mm world-frame, plus the optional `seg_inf.<id>`\n * fields (name, neighbours, material, direction). `tail.sa` flags\n * which segments are in the device's current cleaning set.\n */\nexport function collectSegments(\n layers: MapLayer[],\n dim: MapDimensions,\n tail: MapTail,\n): MapSegment[] {\n const activeIds = new Set<number>();\n for (const entry of tail.sa ?? []) {\n if (Array.isArray(entry) && typeof entry[0] === 'number') {\n activeIds.add(entry[0]);\n }\n }\n\n const segs: MapSegment[] = [];\n for (const layer of layers) {\n if (layer.type !== 'segment' || layer.segmentId === undefined) {\n continue;\n }\n const id = layer.segmentId;\n let xMinPx = Infinity;\n let yMinPx = Infinity;\n let xMaxPx = -Infinity;\n let yMaxPx = -Infinity;\n let sumXPx = 0;\n let sumYPx = 0;\n let count = 0;\n for (const [x, y, len] of layer.runs) {\n if (x < xMinPx) {\n xMinPx = x;\n }\n if (y < yMinPx) {\n yMinPx = y;\n }\n if (x + len - 1 > xMaxPx) {\n xMaxPx = x + len - 1;\n }\n if (y > yMaxPx) {\n yMaxPx = y;\n }\n // centroid sum: for a run of `len` pixels starting at `x`, the\n // x-sum is len*x + (0+1+...+len-1) = len*x + len*(len-1)/2\n sumXPx += len * x + (len * (len - 1)) / 2;\n sumYPx += len * y;\n count += len;\n }\n if (count === 0) {\n continue;\n }\n\n const meta = tail.seg_inf?.[String(id)] ?? null;\n const bbox = {\n xMin: dim.left + xMinPx * dim.gridSize,\n yMin: dim.top + yMinPx * dim.gridSize,\n xMax: dim.left + (xMaxPx + 1) * dim.gridSize,\n yMax: dim.top + (yMaxPx + 1) * dim.gridSize,\n };\n const centroid = {\n x: dim.left + (sumXPx / count) * dim.gridSize,\n y: dim.top + (sumYPx / count) * dim.gridSize,\n };\n\n segs.push({\n id,\n name: meta?.name ? safeBase64ToUtf8(meta.name) : null,\n bbox,\n centroid,\n neighbours: meta?.nei_id ?? [],\n floorMaterial: meta?.material ?? null,\n floorDirection: meta?.direction ?? null,\n active: activeIds.has(id),\n });\n }\n return segs;\n}\n\nfunction safeBase64ToUtf8(s: string): string | null {\n try {\n return Buffer.from(s, 'base64').toString('utf8');\n } catch {\n return null;\n }\n}\n","/**\n * Cleaning-path string parser. Wire format: a flat sequence of\n * `<op-letter><x>,<y>` ops in the JSON tail's `tr` field. Op letters\n * are M (mop), W (sweep+mop), S (sweep), L (line moveTo); lowercase\n * `l` is a P-frame line continuation, treated as L after the merge\n * layer concatenates prev+P trs.\n */\n\nimport type { MapPath, MapPathType } from './types.js';\n\nconst PATH_TYPE_FROM_OP: Record<string, MapPathType> = {\n M: 'mop',\n W: 'sweep-and-mop',\n S: 'sweep',\n L: 'line',\n};\n\nconst PATH_OP_REGEX = /([MWSLl])(-?\\d+),(-?\\d+)/g;\n\n/**\n * Parse Dreame's compact path string into typed segments.\n *\n * Each op letter starts a new segment:\n * M = mop, W = sweep+mop, S = sweep, L = line moveTo,\n * l = P-frame line continuation (treated as L per Tasshack map.py:3987).\n *\n * Coordinates are millimetres world-frame for `mop` / `sweep` /\n * `sweep-and-mop` waypoints. **Line ops are RELATIVE deltas** to the\n * preceding absolute waypoint (or the previous point within the same\n * line). The accumulator unwinds them so every surfaced point is\n * absolute world-frame mm — verified live 2026-05-07 against r2532a\n * (without unwinding, 1000s of `line` deltas all cluster around the\n * world origin and render as a tight artifact).\n *\n * When a `line` op appears with no preceding absolute waypoint\n * (e.g. when `tr` starts with `L`/`l` and there's no anchor to\n * accumulate against — rare but possible at the very start of a\n * fresh subscription), the points are emitted literally. Callers\n * downstream of the merge layer don't see this path because P-frame\n * `tr` concatenation always prepends the prior absolute waypoint.\n */\nexport function parsePathTr(tr: string): MapPath[] {\n if (!tr) {\n return [];\n }\n // Locally mutable so we can push as we walk the regex matches; widened\n // to the public `readonly`-decorated MapPath shape on return.\n type MutablePath = { type: MapPathType; points: { x: number; y: number }[] };\n const out: MutablePath[] = [];\n let current: MutablePath | null = null;\n let anchor: { x: number; y: number } | null = null;\n for (const m of tr.matchAll(PATH_OP_REGEX)) {\n const opRaw = m[1]!;\n const xRaw = Number(m[2]);\n const yRaw = Number(m[3]);\n const op = opRaw === 'l' ? 'L' : opRaw;\n const type = PATH_TYPE_FROM_OP[op];\n if (!type) {\n continue;\n }\n if (type === 'line' && anchor !== null) {\n // Accumulate the relative delta against the running anchor.\n const abs: { x: number; y: number } = {\n x: anchor.x + xRaw,\n y: anchor.y + yRaw,\n };\n if (!current || current.type !== type) {\n // New line segment: seed with the anchor itself so the line\n // draws a continuous trace from the preceding waypoint.\n current = { type, points: [{ x: anchor.x, y: anchor.y }, abs] };\n out.push(current);\n } else {\n current.points.push(abs);\n }\n anchor = abs;\n } else {\n // Either an absolute waypoint (S / W / M) or a `line` with no\n // anchor yet — emit literally.\n const pt = { x: xRaw, y: yRaw };\n if (!current || current.type !== type) {\n current = { type, points: [pt] };\n out.push(current);\n } else {\n current.points.push(pt);\n }\n if (type !== 'line') {\n // Absolute waypoint — update the anchor for any following\n // `line` op to accumulate against.\n anchor = pt;\n }\n }\n }\n return out;\n}\n","/**\n * Wire-value coercion helpers used by the map decoder modules.\n *\n * Dreame's tail JSON ships some numeric fields as strings (notably\n * `ai_obstacle` records' position fields and the rect/line corner\n * arrays) and some as numbers. Both forms decode to the same world-\n * frame mm value; these helpers normalise.\n *\n * Both return `null` on non-numeric / non-finite input — callers\n * decide whether to skip the entry or substitute a default.\n */\n\nexport function parseFloatField(v: unknown): number | null {\n if (typeof v === 'number') {\n return Number.isFinite(v) ? v : null;\n }\n if (typeof v === 'string') {\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n\nexport function parseIntField(v: unknown): number | null {\n const n = parseFloatField(v);\n return n === null ? null : Math.trunc(n);\n}\n","/**\n * AI-detected obstacles parser. Wire format: the JSON tail's\n * `ai_obstacle` field is an array of positional records (~14 fields\n * each on r2532a). Field layout per `docs/live-map-format.md`.\n */\n\nimport type { MapObstacle } from './types.js';\nimport { parseFloatField, parseIntField } from './field-utils.js';\n\n/**\n * Decode one `ai_obstacle` entry. Field layout observed on r2532a\n * (2026-05-02 fixtures), 14 positional string fields:\n * [0] x mm [1] y mm [2] type id\n * [3] confidence 0-1 [4] timestamp.usec [5] photo file path\n * [6] photo id [7..10] bbox-like [11..13] unknown small ints\n *\n * We use field 6 as `id` (numeric, fits comfortably in a JS number) and\n * field 4 (timestamp) is preserved via the schema's existing fields.\n * `photoKey` is left null — no AES key has been observed at this layer\n * on the live channel; it likely arrives via a separate cloud lookup.\n */\nexport function parseObstacles(raw: unknown[]): MapObstacle[] {\n const out: MapObstacle[] = [];\n for (const entry of raw) {\n if (!Array.isArray(entry) || entry.length < 6) {\n continue;\n }\n const x = parseFloatField(entry[0]);\n const y = parseFloatField(entry[1]);\n const type = parseIntField(entry[2]);\n const confRaw = parseFloatField(entry[3]);\n if (x === null || y === null || type === null) {\n continue;\n }\n const photoFileName = typeof entry[5] === 'string' ? entry[5] : null;\n const idCandidate = parseIntField(entry[6]);\n const id = idCandidate ?? Math.round((parseFloatField(entry[4]) ?? 0) * 1e6);\n out.push({\n id,\n x,\n y,\n type,\n confidence: confRaw === null ? 0 : Math.round(confRaw * 100),\n photoFileName,\n photoKey: null,\n });\n }\n return out;\n}\n","/**\n * User-defined map geometry — the `vw`, `vws`, `walls_info`,\n * `sneak_areas` / `sneak_areas_end` blocks of the JSON tail.\n *\n * On r2532a fw 4.3.9_2199 (verified 2026-05-07) the live I-frame's\n * top-level tail does NOT carry these blocks; they live inside the\n * embedded saved-map blob (`tail.rism`). `decodeVacuumMap` recurses\n * into rism and merges the inner geometry onto the outer `VacuumMap`,\n * so consumers see \"all geometry for this floor\" regardless of which\n * tail physically carried it.\n */\n\nimport type {\n MapLowLyingArea,\n MapRestrictedArea,\n MapRoom,\n MapRoomWall,\n MapStorey,\n MapTail,\n MapVirtualWall,\n MapWallsInfo,\n} from './types.js';\nimport { parseFloatField } from './field-utils.js';\n\n/**\n * Parse the `vw` (and optional `vws`) blocks — Dreame's user-defined\n * geometry. Tasshack `dev` `map.py:4597-4702` is the canonical\n * reference; we mirror its shape interpretations:\n *\n * `vw` (classic block):\n * - `vw.line`: `[x0,y0,x1,y1]` line segments — virtual walls.\n * - `vw.rect`: `[x0,y0,x1,y1, angle?]` axis-aligned no-go\n * rectangles. Corners get sorted so the bbox is well-formed\n * regardless of wire order.\n * - `vw.mop`: same shape as `vw.rect`, no-mop zones.\n * - `vw.nocpt`: `[x0,y0,x1,y1]` \"do not cross\" no-go rectangles —\n * verified live 2026-05-07 on r2532a as additional no-go zones\n * the user marked in the app. (Note: NOT carpets despite the\n * name; Tasshack `map.py:4668` reads them as no-go rects.)\n *\n * `vws` (X50 threshold block — only present when the user has\n * configured thresholds; absent on older firmware):\n * - `vws.vwsl`: `[x0,y0,x1,y1]` lines. When `vws.npthrsd` is\n * present in the SAME `vws` object, these are *passable*\n * thresholds (`kind: \"threshold\", passable: true`). When\n * `npthrsd` is absent, they're \"virtual\" thresholds\n * (`kind: \"threshold\"` with no `passable` hint).\n * - `vws.npthrsd`: `[x0,y0,x1,y1]` lines — *impassable* thresholds\n * (`kind: \"threshold\", passable: false`). Verified live\n * 2026-05-07 on r2532a fw 4.3.9_2199.\n *\n * `vw.cliff` and `vws.cliff` (line segments) and `vws.ramp` (areas)\n * have been observed empty on r2532a; they're not surfaced here\n * until a fixture exists for the populated form. `vw.addcpt` /\n * `vws.rec_*` recommendation mirrors are also out of scope for now.\n *\n * Returns empty arrays when both blocks are absent — there's no\n * meaningful difference between \"no walls configured\" and \"this\n * frame doesn't carry the field\" at the public-API layer, and the\n * merge layer + rism recurse handle the latter via fallback.\n */\nexport function parseVirtualWalls(\n vw:\n | {\n line?: number[][];\n rect?: number[][];\n mop?: number[][];\n nocpt?: number[][];\n }\n | undefined,\n vws?: { vwsl?: number[][]; npthrsd?: number[][] },\n): { virtualWalls: MapVirtualWall[]; restrictedAreas: MapRestrictedArea[] } {\n const virtualWalls: MapVirtualWall[] = [];\n const restrictedAreas: MapRestrictedArea[] = [];\n\n if (vw) {\n for (const line of vw.line ?? []) {\n const wall = parseLine(line, 'wall');\n if (wall) {\n virtualWalls.push(wall);\n }\n }\n for (const rect of vw.rect ?? []) {\n const area = parseRestrictedArea('noGo', rect);\n if (area) {\n restrictedAreas.push(area);\n }\n }\n for (const rect of vw.mop ?? []) {\n const area = parseRestrictedArea('noMop', rect);\n if (area) {\n restrictedAreas.push(area);\n }\n }\n for (const rect of vw.nocpt ?? []) {\n const area = parseRestrictedArea('noGo', rect);\n if (area) {\n restrictedAreas.push(area);\n }\n }\n }\n\n if (vws) {\n const npthrsdPresent = Array.isArray(vws.npthrsd) && vws.npthrsd.length > 0;\n for (const line of vws.vwsl ?? []) {\n // vwsl semantics flip on the presence of npthrsd in the same block.\n const wall = parseLine(line, 'threshold');\n if (wall) {\n if (npthrsdPresent) {\n wall.passable = true;\n }\n // else: leave `passable` absent — these are \"virtual\" thresholds\n // from older firmware that doesn't split the two.\n virtualWalls.push(wall);\n }\n }\n for (const line of vws.npthrsd ?? []) {\n const wall = parseLine(line, 'threshold');\n if (wall) {\n wall.passable = false;\n virtualWalls.push(wall);\n }\n }\n }\n\n return { virtualWalls, restrictedAreas };\n}\n\nfunction parseLine(raw: unknown, kind: 'wall' | 'threshold'): MapVirtualWall | null {\n if (!Array.isArray(raw) || raw.length < 4) {\n return null;\n }\n const x0 = parseFloatField(raw[0]);\n const y0 = parseFloatField(raw[1]);\n const x1 = parseFloatField(raw[2]);\n const y1 = parseFloatField(raw[3]);\n if (x0 === null || y0 === null || x1 === null || y1 === null) {\n return null;\n }\n // `kind` is omitted from the emitted object when it would be the\n // default (\"wall\"), matching the historical wire-empty case so\n // identity comparisons in existing tests don't break unnecessarily.\n if (kind === 'wall') {\n return { from: { x: x0, y: y0 }, to: { x: x1, y: y1 } };\n }\n return { from: { x: x0, y: y0 }, to: { x: x1, y: y1 }, kind };\n}\n\nfunction parseRestrictedArea(kind: 'noGo' | 'noMop', raw: unknown): MapRestrictedArea | null {\n if (!Array.isArray(raw) || raw.length < 4) {\n return null;\n }\n const a = parseFloatField(raw[0]);\n const b = parseFloatField(raw[1]);\n const c = parseFloatField(raw[2]);\n const d = parseFloatField(raw[3]);\n if (a === null || b === null || c === null || d === null) {\n return null;\n }\n const xMin = Math.min(a, c);\n const xMax = Math.max(a, c);\n const yMin = Math.min(b, d);\n const yMax = Math.max(b, d);\n const area: MapRestrictedArea = {\n kind,\n bbox: { xMin, yMin, xMax, yMax },\n };\n const e = raw.length > 4 ? parseFloatField(raw[4]) : null;\n if (e !== null) {\n area.angle = e;\n }\n return area;\n}\n\n/**\n * Parse the saved-map's per-room wall geometry. Wire shape (verified\n * 2026-05-07 against r2532a fw 4.3.9_2199):\n *\n * ```\n * {\n * version_flag: 3,\n * storeys: [{\n * rooms: [{\n * room_id: 10,\n * walls: [{\n * type: 0, // 0 = solid wall, 1 = opening (observed)\n * beg_pt_x: -8225,\n * beg_pt_y: 9275,\n * end_pt_x: -9025,\n * end_pt_y: 9275,\n * normal_x: 0, // unit-vector pointing into the room\n * normal_y: -1\n * }, …]\n * }, …]\n * }, …]\n * }\n * ```\n *\n * Returns `null` if the wire object is missing or has no storeys —\n * the public field on `VacuumMap` is null in that case rather than an\n * empty `MapWallsInfo`.\n */\nexport function parseWallsInfo(\n raw: NonNullable<MapTail['walls_info']> | undefined,\n): MapWallsInfo | null {\n if (!raw || !Array.isArray(raw.storeys) || raw.storeys.length === 0) {\n return null;\n }\n const storeys: MapStorey[] = [];\n for (const s of raw.storeys) {\n if (!s || !Array.isArray(s.rooms)) {\n continue;\n }\n const rooms: MapRoom[] = [];\n for (const r of s.rooms) {\n if (!r || typeof r.room_id !== 'number' || !Array.isArray(r.walls)) {\n continue;\n }\n const walls: MapRoomWall[] = [];\n for (const w of r.walls) {\n if (\n !w ||\n typeof w.type !== 'number' ||\n typeof w.beg_pt_x !== 'number' ||\n typeof w.beg_pt_y !== 'number' ||\n typeof w.end_pt_x !== 'number' ||\n typeof w.end_pt_y !== 'number' ||\n typeof w.normal_x !== 'number' ||\n typeof w.normal_y !== 'number'\n ) {\n continue;\n }\n walls.push({\n type: w.type,\n from: { x: w.beg_pt_x, y: w.beg_pt_y },\n to: { x: w.end_pt_x, y: w.end_pt_y },\n normal: { x: w.normal_x, y: w.normal_y },\n });\n }\n rooms.push({ roomId: r.room_id, walls });\n }\n storeys.push({ rooms });\n }\n if (storeys.length === 0) {\n return null;\n }\n return {\n versionFlag: typeof raw.version_flag === 'number' ? raw.version_flag : 0,\n storeys,\n };\n}\n\ninterface SneakAreaEntry {\n id?: number;\n type?: number;\n hide?: number;\n roi?: number[];\n ms?: number;\n area?: number;\n}\n\n/**\n * Aggregate of every geometry-bearing field surfaced from the tail.\n * `decodeVacuumMap` parses one of these per tail and the rism-recurse\n * path uses `coalesceGeometry` to fill in fields the outer tail left\n * empty.\n */\nexport interface MapGeometry {\n virtualWalls: MapVirtualWall[];\n restrictedAreas: MapRestrictedArea[];\n lowLyingAreas: MapLowLyingArea[];\n wallsInfo: MapWallsInfo | null;\n}\n\n/**\n * Decode every geometry-bearing field from a tail in one shot.\n *\n * Adding a new geometry block (e.g. `vw.cliff` once we get a fixture)\n * is a one-line edit here — `decodeVacuumMap` and the rism-recurse\n * path automatically pick up the new field via `coalesceGeometry`.\n */\nexport function parseTailGeometry(tail: MapTail): MapGeometry {\n const { virtualWalls, restrictedAreas } = parseVirtualWalls(tail.vw, tail.vws);\n return {\n virtualWalls,\n restrictedAreas,\n lowLyingAreas: parseLowLyingAreas(tail.sneak_areas_end, tail.sneak_areas),\n wallsInfo: parseWallsInfo(tail.walls_info),\n };\n}\n\n/**\n * `true` when every geometry field has at least one entry. Used by\n * the rism-recurse path to skip the inner decode when the outer tail\n * already supplied everything.\n */\nexport function isGeometryComplete(g: MapGeometry): boolean {\n return (\n g.virtualWalls.length > 0 &&\n g.restrictedAreas.length > 0 &&\n g.lowLyingAreas.length > 0 &&\n g.wallsInfo !== null\n );\n}\n\n/**\n * Merge two `MapGeometry` snapshots. `primary` wins on every field\n * that's non-empty there; `fallback` fills the rest. Used by the\n * rism-recurse path: outer tail's geometry as primary, inner saved-\n * map blob's geometry as fallback.\n */\nexport function coalesceGeometry(primary: MapGeometry, fallback: MapGeometry): MapGeometry {\n return {\n virtualWalls: primary.virtualWalls.length > 0 ? primary.virtualWalls : fallback.virtualWalls,\n restrictedAreas:\n primary.restrictedAreas.length > 0 ? primary.restrictedAreas : fallback.restrictedAreas,\n lowLyingAreas:\n primary.lowLyingAreas.length > 0 ? primary.lowLyingAreas : fallback.lowLyingAreas,\n wallsInfo: primary.wallsInfo !== null ? primary.wallsInfo : fallback.wallsInfo,\n };\n}\n\n/**\n * Parse low-clearance \"sneak under furniture\" zones from a tail's\n * `sneak_areas` / `sneak_areas_end` arrays. Verified live 2026-05-07\n * on r2532a fw 4.3.9_2199 (every observed entry was a 4-corner rect,\n * 8 ints in `roi`); Tasshack `dev` `map.py:4776-4809` parses\n * arbitrary even-length polygons, so we surface points as-emitted\n * without coercing to a bounding box.\n *\n * `sneak_areas_end` is preferred when both fields are present in the\n * same tail — it carries the saved `area` field. `sneak_areas` is the\n * live-fly variant.\n */\nexport function parseLowLyingAreas(\n end: SneakAreaEntry[] | undefined,\n live: SneakAreaEntry[] | undefined,\n): MapLowLyingArea[] {\n const source = end && end.length > 0 ? end : live;\n if (!source) {\n return [];\n }\n const out: MapLowLyingArea[] = [];\n for (const entry of source) {\n if (!entry || !Array.isArray(entry.roi) || entry.roi.length < 4 || entry.roi.length % 2 !== 0) {\n continue;\n }\n const points: { x: number; y: number }[] = [];\n let badPoint = false;\n for (let i = 0; i + 1 < entry.roi.length; i += 2) {\n const x = parseFloatField(entry.roi[i]);\n const y = parseFloatField(entry.roi[i + 1]);\n if (x === null || y === null) {\n badPoint = true;\n break;\n }\n points.push({ x, y });\n }\n if (badPoint || points.length === 0) {\n continue;\n }\n const id = typeof entry.id === 'number' ? entry.id : -1;\n const area: MapLowLyingArea = { id, points };\n if (typeof entry.area === 'number') {\n area.area = entry.area;\n }\n out.push(area);\n }\n return out;\n}\n","/**\n * Cleaned-area overlay decoder for the JSON tail's `decmap` field.\n *\n * `decmap` is a recursive map envelope embedded as a base64 string —\n * the same outer-envelope shape as the parent (URL-safe base64 →\n * zlib → 27-byte header + pixel grid + UTF-8 JSON tail) but with a\n * different per-pixel encoding. Inner pixel encoding uses only the\n * low 2 bits (`& 0x03`): `1` → cleaned, `2` → dirty, others ignored.\n *\n * Tasshack reference: `dev` `map.py:5162-5233`.\n */\n\nimport { HEADER_SIZE, unwrapEnvelope } from './envelope.js';\nimport { parseMapHeader, type MapHeader } from './header.js';\nimport type { MapCleanedAreaOverlay, MapRun } from './types.js';\n\n/**\n * Decode the `decmap` recursive blob into a `MapCleanedAreaOverlay`.\n *\n * Returns `null` for any decode failure — the parent decode should\n * never abort because of a malformed inner blob.\n */\nexport function parseCleanedAreaOverlay(decmap: string): MapCleanedAreaOverlay | null {\n if (!decmap) {\n return null;\n }\n let inflated: Buffer;\n try {\n inflated = unwrapEnvelope(decmap);\n } catch {\n return null;\n }\n if (inflated.length < HEADER_SIZE) {\n return null;\n }\n let header: MapHeader;\n try {\n header = parseMapHeader(inflated);\n } catch {\n return null;\n }\n const pixelStart = HEADER_SIZE;\n const pixelEnd = pixelStart + header.width * header.height;\n if (inflated.length < pixelEnd || header.width <= 0 || header.height <= 0) {\n return null;\n }\n const pixels = inflated.subarray(pixelStart, pixelEnd);\n const { cleaned, dirty } = decodeCleanedAreaPixels(pixels, header.width, header.height);\n\n const overlay: MapCleanedAreaOverlay = {\n dimensions: {\n left: header.left,\n top: header.top,\n width: header.width,\n height: header.height,\n gridSize: header.gridSize,\n },\n cleaned,\n dirty,\n };\n\n // Pull out CleanArea from the inner JSON tail when present — opaque\n // to us, useful for downstream stats.\n if (inflated.length > pixelEnd) {\n const tailText = inflated.subarray(pixelEnd).toString('utf8');\n if (tailText) {\n try {\n const innerTail: unknown = JSON.parse(tailText);\n if (\n typeof innerTail === 'object' &&\n innerTail !== null &&\n !Array.isArray(innerTail) &&\n 'CleanArea' in innerTail\n ) {\n overlay.cleanedSegments = innerTail.CleanArea;\n }\n } catch {\n // Inner tail malformed — keep the pixel decoding, drop the stats.\n }\n }\n }\n\n return overlay;\n}\n\nfunction decodeCleanedAreaPixels(\n pixels: Buffer,\n width: number,\n height: number,\n): { cleaned: MapRun[]; dirty: MapRun[] } {\n const cleaned: MapRun[] = [];\n const dirty: MapRun[] = [];\n for (let y = 0; y < height; y++) {\n let cleanStart = -1;\n let dirtyStart = -1;\n const rowOff = y * width;\n for (let x = 0; x < width; x++) {\n const v = pixels[rowOff + x]! & 0x03;\n if (v === 1) {\n if (dirtyStart >= 0) {\n dirty.push([dirtyStart, y, x - dirtyStart]);\n dirtyStart = -1;\n }\n if (cleanStart < 0) {\n cleanStart = x;\n }\n } else if (v === 2) {\n if (cleanStart >= 0) {\n cleaned.push([cleanStart, y, x - cleanStart]);\n cleanStart = -1;\n }\n if (dirtyStart < 0) {\n dirtyStart = x;\n }\n } else {\n if (cleanStart >= 0) {\n cleaned.push([cleanStart, y, x - cleanStart]);\n cleanStart = -1;\n }\n if (dirtyStart >= 0) {\n dirty.push([dirtyStart, y, x - dirtyStart]);\n dirtyStart = -1;\n }\n }\n }\n if (cleanStart >= 0) {\n cleaned.push([cleanStart, y, width - cleanStart]);\n }\n if (dirtyStart >= 0) {\n dirty.push([dirtyStart, y, width - dirtyStart]);\n }\n }\n return { cleaned, dirty };\n}\n","/**\n * Public entry-point for decoding a single map-frame envelope into a\n * `VacuumMap`. Composes the per-concern modules:\n *\n * - `envelope.ts` — base64 / AES / zlib unwrap\n * - `header.ts` — 27-byte binary header\n * - `tail.ts` — UTF-8 JSON tail (+ `parseFrame` seam)\n * - `pixel-grid.ts` — fsm:1 pixel decode + segment collect\n * - `path.ts` — `tr` cleaning-path string parser\n * - `obstacles.ts` — AI-detected obstacle records\n * - `geometry.ts` — `vw` / `vws` / `walls_info` / sneak-zones\n * - `cleaned-area.ts` — recursive `decmap` overlay\n *\n * Public surface: the `decodeVacuumMap` / `applyVacuumPFrame` functions\n * plus `MapDecodeError` (from `envelope.ts`) and the small wire-format\n * constants. Per-concern parsers are also re-exported via the sub-tree\n * barrel for callers that want to feed raw bytes through specific stages.\n *\n * The donor's stateless `MapDecoder` class is adapted here into plain\n * functions — `decode`/`applyPFrame` carried no instance state, so a\n * function form is a clean simplification.\n */\n\nimport type {\n VacuumMap,\n VacuumMapDecodeOptions,\n MapDimensions,\n MapPose,\n MapTail,\n} from './types.js';\nimport { ANGLE_ABSENT, HEADER_SIZE, unwrapEnvelope } from './envelope.js';\nimport type { MapHeader } from './header.js';\nimport { parseFrame } from './tail.js';\nimport { collectSegments, decodePixelGridFsm1 } from './pixel-grid.js';\nimport { parsePathTr } from './path.js';\nimport { parseObstacles } from './obstacles.js';\nimport { coalesceGeometry, isGeometryComplete, parseTailGeometry } from './geometry.js';\nimport { parseCleanedAreaOverlay } from './cleaned-area.js';\nimport { mergePFrame, mergePFrameEnvelope } from './merge.js';\n\n/**\n * Decode a single frame envelope to a `VacuumMap`. Accepts the raw MQTT\n * value (URL-safe base64 string, optionally with `,<aes-key>` suffix)\n * or the inflated bytes directly (`Buffer`). Pure — no IO.\n */\nexport function decodeVacuumMap(\n input: Buffer | string,\n opts: VacuumMapDecodeOptions = {},\n): VacuumMap {\n const inflated = typeof input === 'string' ? unwrapEnvelope(input, opts) : input;\n const { header, tail } = parseFrame(inflated);\n\n const dimensions = mergeDimensions(header, tail);\n const robot = pose(header.robotX, header.robotY, header.robotA, tail.nr === true);\n const dock = pose(header.chargerX, header.chargerY, header.chargerA, tail.nc === true);\n const docked = tail.oc === true;\n\n // Pixel grid in a P-frame is byte-add deltas over a previous I-frame's\n // grid, not absolute classifications — decoding it standalone produces\n // garbage (high-byte deltas masquerade as segment ids). Only run the\n // pixel decoder on I-frames; the merge layer produces a synthetic\n // I-frame for ongoing P-frame state.\n const pixelStart = HEADER_SIZE;\n const pixelEnd = pixelStart + header.width * header.height;\n const pixelGrid = inflated.subarray(pixelStart, pixelEnd);\n const canDecodePixels =\n header.frameType === 'I' && pixelGrid.length === header.width * header.height;\n const layers = canDecodePixels ? decodePixelGridFsm1(pixelGrid, header.width, header.height) : [];\n\n const segments = canDecodePixels ? collectSegments(layers, dimensions, tail) : [];\n const paths = parsePathTr(tail.tr ?? '');\n const obstacles = parseObstacles(tail.ai_obstacle ?? []);\n let geometry = parseTailGeometry(tail);\n // The persistent saved-map blob is embedded inline as `tail.rism`\n // (URL-safe-base64 + zlib + same envelope shape). On r2532a fw\n // 4.3.9_2199 the outer tail's geometry blocks are absent and the\n // geometry lives only in the inner saved-map's tail. Recurse to\n // surface it; if the inner blob fails to decode (corrupt,\n // unexpected shape, missing AES IV, etc.) leave the outer values\n // as-is and swallow the error — geometry decode failure must\n // never break pixel/path/obstacle decode of the outer frame.\n // Recurses one level only — the inner saved-map blob does not\n // carry its own `rism`.\n if (!isGeometryComplete(geometry) && typeof tail.rism === 'string' && tail.rism.length > 0) {\n try {\n const innerInflated = unwrapEnvelope(tail.rism);\n const { tail: innerTail } = parseFrame(innerInflated);\n geometry = coalesceGeometry(geometry, parseTailGeometry(innerTail));\n } catch {\n // intentional — outer frame remains valid even if rism is unreadable\n }\n }\n const cleanedArea = typeof tail.decmap === 'string' ? parseCleanedAreaOverlay(tail.decmap) : null;\n\n return {\n mapId: header.mapId,\n frameId: header.frameId,\n frameType: header.frameType,\n timestamp: tail.timestamp_ms ?? 0,\n rotation: tail.mra ?? 0,\n dimensions,\n robot,\n dock,\n docked,\n layers,\n segments,\n paths,\n obstacles,\n ...geometry,\n cleanedArea,\n };\n}\n\n/**\n * Merge a P-frame onto a previous (I-frame or already-merged) inflated\n * frame buffer and decode the result. Returns both the merged buffer\n * (so callers can chain further merges without re-allocating) and the\n * decoded `VacuumMap`.\n *\n * Throws `OutOfOrderFrameError` when `pframe.frameId !== prev.frameId + 1`.\n *\n * Inputs are envelope strings or already-unwrapped buffers — both\n * sides are normalised internally via `unwrapEnvelope` when given a\n * string, with the matching `VacuumMapDecodeOptions` for the AES key/IV.\n */\nexport function applyVacuumPFrame(\n prev: Buffer | string,\n pframe: Buffer | string,\n opts: { prev?: VacuumMapDecodeOptions; pframe?: VacuumMapDecodeOptions } = {},\n): { buffer: Buffer; data: VacuumMap } {\n const merged =\n typeof prev === 'string' || typeof pframe === 'string'\n ? mergePFrameEnvelope(prev, pframe, opts.prev, opts.pframe)\n : mergePFrame(prev, pframe);\n return { buffer: merged, data: decodeVacuumMap(merged) };\n}\n\nfunction mergeDimensions(header: MapHeader, tail: MapTail): MapDimensions {\n const left = tail.origin?.[0] ?? header.left;\n const top = tail.origin?.[1] ?? header.top;\n return {\n left,\n top,\n width: header.width,\n height: header.height,\n gridSize: header.gridSize,\n };\n}\n\nfunction pose(x: number, y: number, angle: number, absent: boolean): MapPose | null {\n if (absent || angle === ANGLE_ABSENT) {\n return null;\n }\n return { x, y, angle };\n}\n","/**\n * Raster PNG renderer for a decoded `VacuumMap`.\n *\n * node-dreame ships only the structured model (no image); this is NEW. We paint\n * one pixel per grid cell (× an optional integer upscale `scale`) from the\n * run-length `layers`, later layers painting over earlier ones. The background\n * is transparent. Per-segment colours come from a deterministic golden-angle\n * palette so the same room id always renders the same hue.\n *\n * Pure: only `pngjs` + arithmetic. No casts.\n */\n\nimport { PNG } from 'pngjs';\nimport type { VacuumMap, MapLayer, MapRun } from './types.js';\n\nexport interface RenderVacuumPngOptions {\n /** Integer upscale factor (nearest-neighbour). Default 1. */\n scale?: number;\n}\n\ntype Rgba = readonly [number, number, number, number];\n\nconst RGBA: Record<Exclude<MapLayer['type'], 'segment'>, Rgba> = {\n wall: [60, 60, 60, 255],\n floor: [210, 222, 235, 255],\n carpet: [180, 150, 120, 200],\n};\n\n/** HSV (h in degrees, s/v in 0..1) → opaque RGBA. */\nfunction hsvToRgba(h: number, s: number, v: number): Rgba {\n const c = v * s;\n const hp = (((h % 360) + 360) % 360) / 60;\n const x = c * (1 - Math.abs((hp % 2) - 1));\n let r = 0;\n let g = 0;\n let b = 0;\n if (hp < 1) {\n r = c;\n g = x;\n } else if (hp < 2) {\n r = x;\n g = c;\n } else if (hp < 3) {\n g = c;\n b = x;\n } else if (hp < 4) {\n g = x;\n b = c;\n } else if (hp < 5) {\n r = x;\n b = c;\n } else {\n r = c;\n b = x;\n }\n const m = v - c;\n return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255), 255];\n}\n\n/** Deterministic per-segment palette (golden-angle hue spread). */\nfunction segmentColor(id: number): Rgba {\n const hue = (id * 137.508) % 360;\n return hsvToRgba(hue, 0.45, 0.95);\n}\n\nexport function renderVacuumPng(map: VacuumMap, opts: RenderVacuumPngOptions = {}): Buffer {\n const scale = Math.max(1, Math.trunc(opts.scale ?? 1));\n const w = map.dimensions.width * scale;\n const h = map.dimensions.height * scale;\n const png = new PNG({ width: Math.max(1, w), height: Math.max(1, h) });\n png.data.fill(0); // transparent background\n for (const layer of map.layers) {\n paintLayer(png, layer, scale);\n }\n return PNG.sync.write(png);\n}\n\nfunction paintLayer(png: PNG, layer: MapLayer, scale: number): void {\n const color = layer.type === 'segment' ? segmentColor(layer.segmentId ?? 0) : RGBA[layer.type];\n for (const run of layer.runs) {\n paintRun(png, run, color, scale, png.width, png.height);\n }\n}\n\n/** Fill the scale×scale block of every pixel in a `[xPx, yPx, len]` run. */\nfunction paintRun(\n png: PNG,\n run: MapRun,\n color: Rgba,\n scale: number,\n width: number,\n height: number,\n): void {\n const [xPx, yPx, len] = run;\n for (let i = 0; i < len; i += 1) {\n const baseX = (xPx + i) * scale;\n const baseY = yPx * scale;\n for (let dy = 0; dy < scale; dy += 1) {\n const py = baseY + dy;\n if (py < 0 || py >= height) {\n continue;\n }\n for (let dx = 0; dx < scale; dx += 1) {\n const px = baseX + dx;\n if (px < 0 || px >= width) {\n continue;\n }\n const off = (py * width + px) * 4;\n png.data[off] = color[0];\n png.data[off + 1] = color[1];\n png.data[off + 2] = color[2];\n png.data[off + 3] = color[3];\n }\n }\n }\n}\n","/**\n * Resolve a Dreame OSS object name (advertised via the PATH push,\n * `siid 6 piid 3`) to a signed download URL and fetch the blob.\n *\n * Endpoint: `POST /dreame-user-iot/iotfile/getDownloadUrl`\n * Body: `{ did, model, filename, region }`\n * Response: `{ code: 0, data: \"<signed-url-string>\" }` — `data` is the\n * URL string itself, not a nested `{ url }` object.\n *\n * The signed URL is good for ~1 hour (Aliyun signature). We cache it\n * for 30 min by default and on cache hit append `current=<unix_ts>` to\n * bust any intermediate caches (Tasshack convention).\n *\n * v1 only handles live blobs (the I-frame parked in OSS by the device).\n * Permanent saved-map blobs use a different filename mangling — deferred.\n *\n * ADAPTED from node-dreame's `src/map/oss-fetch.ts`: the only changes are\n * import-path rewrites onto our transport (`RequestContext`/`httpPostJson`\n * from `transport/http`, `DreameApiError`/`DreameTransportError` from\n * `transport/errors`, `DreameRegion` from `auth/config`) and typing the\n * injected fetch seam as our `FetchImpl` (undici-backed) rather than the\n * donor's global `typeof fetch` — so the mock type-checks with no cast.\n */\n\nimport type { DreameRegion } from '../../../auth/config.js';\nimport { RequestContext, httpPostJson } from '../../../transport/http.js';\nimport type { FetchImpl } from '../../../transport/fetch.js';\nimport { defaultFetch } from '../../../transport/fetch.js';\nimport { DreameApiError, DreameTransportError } from '../../../transport/errors.js';\n\nconst OSS_DOWNLOAD_PATH = '/dreame-user-iot/iotfile/getDownloadUrl';\nconst DEFAULT_TTL_MS = 30 * 60 * 1000;\n\nexport interface OssFetchInput {\n /** Resolved API host (e.g. `eu.iot.dreame.tech:13267`). */\n host: string;\n /** Bearer token from the active `DreameSession`. */\n accessToken: string;\n /** Region — used for header construction and the body's `region` field. */\n region: DreameRegion;\n /** Optional `country` override for headers (defaults from region). */\n country?: string;\n /** Optional `lang` override for headers (defaults from region). */\n lang?: string;\n /** Device id. */\n did: string;\n /** Device model. */\n model: string;\n /** OSS object name (`ali_dreame/<uid>/<did>/<n>`). */\n filename: string;\n /** Caller-supplied AbortSignal — composed with the HTTP timeout. */\n signal?: AbortSignal;\n /** Per-request timeout override in ms. Pass `0` to disable. */\n timeoutMs?: number;\n}\n\nexport interface OssFetcherOpts {\n /** Cache TTL in ms. Default 30 min. */\n ttlMs?: number;\n /** Inject a clock (for tests). Default `Date.now`. */\n now?: () => number;\n /** Inject fetch (for tests). Default the shared undici-backed `defaultFetch`. */\n fetchImpl?: FetchImpl;\n}\n\ninterface OssResolveResponse {\n code?: number;\n msg?: string;\n data?: string;\n}\n\ninterface CacheEntry {\n url: string;\n expiresAt: number;\n}\n\n/**\n * Minimal fetcher seam consumed by `VacuumDevice.getMap`. Lets callers\n * inject a custom signed-blob fetcher (e.g. a cache-backed or proxied one)\n * without depending on the concrete {@link OssFetcher} class. The real\n * {@link OssFetcher} implements this interface.\n */\nexport interface OssFetcherLike {\n fetchBlob(input: OssFetchInput): Promise<Buffer>;\n}\n\nexport class OssFetcher implements OssFetcherLike {\n private readonly ttlMs: number;\n private readonly now: () => number;\n private readonly fetchImpl: FetchImpl;\n private readonly cache = new Map<string, CacheEntry>();\n /** In-flight fetchBlob calls, keyed `did:filename`. Lets us coalesce concurrent fetches for the same blob. */\n private readonly inflight = new Map<string, Promise<Buffer>>();\n\n constructor(opts: OssFetcherOpts = {}) {\n this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;\n this.now = opts.now ?? Date.now;\n this.fetchImpl = opts.fetchImpl ?? defaultFetch;\n }\n\n /**\n * Resolve `input.filename` to a signed download URL. Uses the cache\n * when fresh; on cache hit returns the cached URL with\n * `current=<unix_ts>` appended (using `&` if the URL already has a\n * query string — Aliyun signed URLs always do).\n */\n async resolveUrl(input: OssFetchInput): Promise<string> {\n const key = `${input.did}:${input.filename}`;\n const now = this.now();\n const cached = this.cache.get(key);\n if (cached && cached.expiresAt > now) {\n const sep = cached.url.includes('?') ? '&' : '?';\n return `${cached.url}${sep}current=${Math.floor(now / 1000)}`;\n }\n\n const ctx = RequestContext.from({ ...input, fetchImpl: this.fetchImpl });\n\n const resp = await httpPostJson<OssResolveResponse>({\n ctx,\n url: ctx.url(OSS_DOWNLOAD_PATH),\n headers: ctx.headers({\n accessToken: input.accessToken,\n contentType: 'application/json',\n }),\n body: JSON.stringify({\n did: input.did,\n model: input.model,\n filename: input.filename,\n region: input.region,\n }),\n context: 'oss download url',\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n });\n\n if (typeof resp.data !== 'string' || resp.data.length === 0) {\n throw new DreameApiError(\n `oss download url response missing data: ${JSON.stringify(resp).slice(0, 200)}`,\n 200,\n resp,\n );\n }\n\n this.cache.set(key, { url: resp.data, expiresAt: now + this.ttlMs });\n return resp.data;\n }\n\n /**\n * Resolve, GET the signed URL, and return the raw bytes.\n *\n * Concurrent calls for the same `(did, filename)` pair are coalesced\n * into a single network request — the second caller awaits the first's\n * Promise instead of issuing a duplicate fetch.\n */\n async fetchBlob(input: OssFetchInput): Promise<Buffer> {\n const key = `${input.did}:${input.filename}`;\n const existing = this.inflight.get(key);\n if (existing) {\n return existing;\n }\n const promise = this.#doFetchBlob(input).finally(() => {\n this.inflight.delete(key);\n });\n this.inflight.set(key, promise);\n return promise;\n }\n\n async #doFetchBlob(input: OssFetchInput): Promise<Buffer> {\n const url = await this.resolveUrl(input);\n let res: Awaited<ReturnType<FetchImpl>>;\n try {\n res = await this.fetchImpl(url, {\n method: 'GET',\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n });\n } catch (err) {\n throw new DreameTransportError(`oss download network error`, err);\n }\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new DreameApiError(\n `oss download failed: ${res.status} ${text.slice(0, 200)}`,\n res.status,\n );\n }\n return Buffer.from(await res.arrayBuffer());\n }\n\n /** Drop all cached URLs. */\n clearCache(): void {\n this.cache.clear();\n }\n}\n","import {\n BaseDevice,\n type BaseDeviceInput,\n type BaseDeviceEvents,\n} from '../../device/base-device.js';\nimport {\n VACUUM_PROP,\n VACUUM_ACTION,\n BATTERY_PROP,\n SETTINGS_PROP,\n CONSUMABLE_PROP,\n CUSTOM_CLEAN_MODE,\n} from './properties.js';\nimport {\n ChargingStatus,\n CleaningMode,\n MiotState,\n SuctionLevel,\n TaskStatus,\n WaterVolume,\n} from './enums.js';\nimport { asNum, parseFaultList } from './decode.js';\nimport { enumLookup } from '../_shared/decode.js';\nimport {\n VacuumCapabilityResolver,\n getVacuumCapabilities,\n type VacuumCapabilities,\n} from './capabilities.js';\nimport { DreameError } from '../../transport/errors.js';\nimport {\n decodeVacuumMap,\n OssFetcher,\n type VacuumMap,\n type OssFetchInput,\n type OssFetcherLike,\n} from './map/index.js';\nimport { REGION_HOSTS } from '../../auth/config.js';\n\n/** Knobs for the segment/zone/spot helpers. Defaults pull from cached state. */\nexport interface CleanOpts {\n repeats?: number;\n fan?: number;\n water?: number;\n}\n\n/**\n * The vacuum widens the {@link BaseDeviceEvents} map with a `'map'` event,\n * emitted by {@link VacuumDevice.getMap} once a fresh map has been decoded.\n * Declaring it here lets `VacuumDevice extends BaseDevice<VacuumDeviceEvents>`\n * type the emitter with no banned cast.\n */\nexport type VacuumDeviceEvents = BaseDeviceEvents & {\n map: [VacuumMap];\n};\n\n/** Input to {@link VacuumDevice.getMap}. */\nexport interface VacuumGetMapInput {\n /** OSS object name advertised via the PATH push (siid 6 piid 3). */\n filename: string;\n /**\n * Inject the signed-blob fetcher (tests pass a fake). Defaults to a fresh\n * {@link OssFetcher}. Typed as {@link OssFetcherLike} so consumers can pass\n * any object exposing a compatible `fetchBlob`.\n */\n fetcher?: OssFetcherLike;\n /** Optional AES key for an encrypted blob (hex). */\n key?: string;\n /** Optional AES IV for an encrypted blob (hex). */\n iv?: string;\n /** Override the API host (defaults from the device region). */\n host?: string;\n /** Per-request timeout override in ms. */\n timeoutMs?: number;\n /** Caller-supplied AbortSignal. */\n signal?: AbortSignal;\n}\n\nconst SUCTION = enumLookup<SuctionLevel>([\n SuctionLevel.Quiet,\n SuctionLevel.Standard,\n SuctionLevel.Intense,\n SuctionLevel.Max,\n]);\nconst WATER = enumLookup<WaterVolume>([WaterVolume.Low, WaterVolume.Medium, WaterVolume.High]);\nconst CLEAN_MODE = enumLookup<CleaningMode>([\n CleaningMode.Sweeping,\n CleaningMode.Mopping,\n CleaningMode.SweepAndMop,\n CleaningMode.MopAfterSweep,\n]);\nconst STATE = enumLookup<MiotState>(\n Object.values(MiotState).filter((v): v is MiotState => typeof v === 'number'),\n);\nconst CHARGING = enumLookup<ChargingStatus>([\n ChargingStatus.Charging,\n ChargingStatus.Discharging,\n ChargingStatus.Returning,\n]);\nconst TASK = enumLookup<TaskStatus>([\n TaskStatus.InterruptedOrPaused,\n TaskStatus.Active,\n TaskStatus.Transitioning,\n TaskStatus.OnDockIdle,\n TaskStatus.TransientPauseEdge,\n TaskStatus.NeedsIntervention,\n]);\n\n/** A typed Dreame-vacuum handle (state + capability-gated commands). */\nexport class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {\n readonly #caps: VacuumCapabilities;\n #lastMap: VacuumMap | null = null;\n\n constructor(input: BaseDeviceInput) {\n super({\n ...input,\n // Inject the vacuum resolver so the inherited generic `capabilities`\n // getter carries the vacuum token set (no banned cast).\n capabilities:\n input.capabilities ?? new VacuumCapabilityResolver().resolve(input.device.model),\n });\n this.#caps = getVacuumCapabilities(input.device.model);\n }\n\n /** Rich, vacuum-specific capability record (booleans + supported enums). */\n get vacuumCapabilities(): VacuumCapabilities {\n return this.#caps;\n }\n\n /**\n * The suction levels this model supports, as a {@link SuctionLevel} enum array\n * (from the capability record). Lets a consumer see which fan speeds are\n * selectable without reaching into {@link vacuumCapabilities}.\n */\n get supportedSuctionLevels(): readonly SuctionLevel[] {\n return this.#caps.supportedSuctionLevels;\n }\n /** The water/mop volumes this model supports, as a {@link WaterVolume} enum array. */\n get supportedWaterVolumes(): readonly WaterVolume[] {\n return this.#caps.supportedWaterVolumes;\n }\n\n #num(siid: number, piid: number): number | null {\n return asNum(this.getProperty(siid, piid)?.value);\n }\n\n // -- typed state --------------------------------------------------------\n get statusRaw(): number | null {\n return this.#num(VACUUM_PROP.STATE.siid, VACUUM_PROP.STATE.piid);\n }\n get status(): MiotState | null {\n return STATE(this.statusRaw);\n }\n get battery(): number | null {\n return this.#num(BATTERY_PROP.LEVEL.siid, BATTERY_PROP.LEVEL.piid);\n }\n get chargingRaw(): number | null {\n return this.#num(BATTERY_PROP.CHARGING_STATUS.siid, BATTERY_PROP.CHARGING_STATUS.piid);\n }\n get charging(): ChargingStatus | null {\n return CHARGING(this.chargingRaw);\n }\n get isCharging(): boolean {\n return this.charging === ChargingStatus.Charging;\n }\n /** Docked => MiotState Charging or ChargingComplete (on the dock). */\n get isDocked(): boolean {\n const s = this.status;\n return s === MiotState.Charging || s === MiotState.ChargingComplete;\n }\n get suctionRaw(): number | null {\n return this.#num(VACUUM_PROP.SUCTION_LEVEL.siid, VACUUM_PROP.SUCTION_LEVEL.piid);\n }\n get suction(): SuctionLevel | null {\n return SUCTION(this.suctionRaw);\n }\n get waterRaw(): number | null {\n return this.#num(VACUUM_PROP.WATER_VOLUME.siid, VACUUM_PROP.WATER_VOLUME.piid);\n }\n get water(): WaterVolume | null {\n return WATER(this.waterRaw);\n }\n /** Reads the SAFE CLEAN_MODE_SETTING (siid 2 piid 6), plain 0-3. */\n get cleaningModeRaw(): number | null {\n return this.#num(VACUUM_PROP.CLEAN_MODE_SETTING.siid, VACUUM_PROP.CLEAN_MODE_SETTING.piid);\n }\n get cleaningMode(): CleaningMode | null {\n return CLEAN_MODE(this.cleaningModeRaw);\n }\n get taskStatusRaw(): number | null {\n return this.#num(VACUUM_PROP.TASK_STATUS.siid, VACUUM_PROP.TASK_STATUS.piid);\n }\n get taskStatus(): TaskStatus | null {\n return TASK(this.taskStatusRaw);\n }\n get errorCode(): number | null {\n return this.#num(VACUUM_PROP.ERROR.siid, VACUUM_PROP.ERROR.piid);\n }\n get faults(): readonly number[] {\n return parseFaultList(\n this.getProperty(VACUUM_PROP.FAULTS_STR.siid, VACUUM_PROP.FAULTS_STR.piid)?.value,\n );\n }\n get taskProgressPct(): number | null {\n return this.#num(VACUUM_PROP.TASK_PROGRESS_PCT.siid, VACUUM_PROP.TASK_PROGRESS_PCT.piid);\n }\n get mainBrushLeftPct(): number | null {\n return this.#num(CONSUMABLE_PROP.MAIN_BRUSH_LEFT.siid, CONSUMABLE_PROP.MAIN_BRUSH_LEFT.piid);\n }\n get sideBrushLeftPct(): number | null {\n return this.#num(CONSUMABLE_PROP.SIDE_BRUSH_LEFT.siid, CONSUMABLE_PROP.SIDE_BRUSH_LEFT.piid);\n }\n get filterLeftPct(): number | null {\n return this.#num(CONSUMABLE_PROP.FILTER_LEFT.siid, CONSUMABLE_PROP.FILTER_LEFT.piid);\n }\n get volume(): number | null {\n return this.#num(SETTINGS_PROP.VOLUME.siid, SETTINGS_PROP.VOLUME.piid);\n }\n\n // -- command helpers ----------------------------------------------------\n #resolveCleanOpts(opts: CleanOpts): { repeats: number; fan: number; water: number } {\n const repeats = Math.max(1, Math.trunc(opts.repeats ?? 1));\n const fan = opts.fan ?? this.suctionRaw ?? SuctionLevel.Standard;\n const water = opts.water ?? this.waterRaw ?? WaterVolume.Medium;\n return { repeats, fan, water };\n }\n\n #startCustom(mode: number, payload: Record<string, unknown>): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.START_CUSTOM.siid, VACUUM_ACTION.START_CUSTOM.aiid, [\n { piid: 1, value: mode },\n { piid: 10, value: JSON.stringify(payload) },\n ]);\n }\n\n #requireCap(flag: boolean, op: string, feature: string): void {\n if (!flag) {\n throw new DreameError(`${op}: model ${this.model} does not support ${feature}`);\n }\n }\n\n // -- no-arg commands ----------------------------------------------------\n /**\n * Start cleaning (MIoT action siid 2 aiid 1). Named `startCleaning` — NOT\n * `start` — because `BaseDevice.start()` is the lifecycle method that opens\n * the MQTT push, and the facade relies on that meaning. Donor `node-dreame`\n * names this `start()` only because its standalone `Vacuum` class owns no\n * lifecycle method to collide with.\n */\n startCleaning(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.START.siid, VACUUM_ACTION.START.aiid, []);\n }\n pause(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.PAUSE.siid, VACUUM_ACTION.PAUSE.aiid, []);\n }\n stop(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.STOP.siid, VACUUM_ACTION.STOP.aiid, []);\n }\n /** Return to the dock / charge (MIoT action CHARGE, siid 3 aiid 1). */\n dock(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.CHARGE.siid, VACUUM_ACTION.CHARGE.aiid, []);\n }\n locate(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.LOCATE.siid, VACUUM_ACTION.LOCATE.aiid, []);\n }\n clearWarning(): Promise<unknown> {\n return this.callAction(VACUUM_ACTION.CLEAR_WARNING.siid, VACUUM_ACTION.CLEAR_WARNING.aiid, []);\n }\n async startAutoEmpty(): Promise<unknown> {\n this.#requireCap(this.#caps.canAutoEmpty, 'startAutoEmpty', 'auto-empty');\n return this.callAction(\n VACUUM_ACTION.START_AUTO_EMPTY.siid,\n VACUUM_ACTION.START_AUTO_EMPTY.aiid,\n [],\n );\n }\n\n // -- settings writes ----------------------------------------------------\n /** Type-safe suction write. Validates against the model's supported levels. */\n setSuction(level: SuctionLevel): Promise<unknown> {\n return this.setSuctionRaw(level);\n }\n /**\n * Untyped suction write: validates an arbitrary number against the model's\n * supported levels and rejects with `RangeError` on an invalid input. This\n * is the raw-input entry point so callers (and tests) can exercise validation\n * with no type assertions. `async` so the guard surfaces as a rejection.\n */\n async setSuctionRaw(level: number): Promise<unknown> {\n if (!this.#caps.supportedSuctionLevels.includes(level)) {\n throw new RangeError(`setSuction: unsupported suction level ${String(level)}`);\n }\n return this.setProperty({ ...VACUUM_PROP.SUCTION_LEVEL, value: level });\n }\n /** Type-safe water-volume write. Validates against supported volumes. */\n setWater(volume: WaterVolume): Promise<unknown> {\n return this.setWaterRaw(volume);\n }\n /** Untyped water-volume write: validates an arbitrary number (zero casts). */\n async setWaterRaw(volume: number): Promise<unknown> {\n if (!this.#caps.supportedWaterVolumes.includes(volume)) {\n throw new RangeError(`setWater: unsupported water volume ${String(volume)}`);\n }\n return this.setProperty({ ...VACUUM_PROP.WATER_VOLUME, value: volume });\n }\n /** SAFE clean-mode write — uses CLEAN_MODE_SETTING (siid 2 piid 6), never the 0x1400 bitfield. */\n setCleaningMode(mode: CleaningMode): Promise<unknown> {\n return this.setProperty({ ...VACUUM_PROP.CLEAN_MODE_SETTING, value: mode });\n }\n\n // -- targeted cleaning --------------------------------------------------\n async cleanSegments(ids: number[], opts: CleanOpts = {}): Promise<unknown> {\n // Argument validation precedes capability gating (donor convention): an\n // empty array throws RangeError regardless of model capability.\n if (ids.length === 0) {\n throw new RangeError('cleanSegments: ids must not be empty');\n }\n this.#requireCap(this.#caps.canCleanPerRoom, 'cleanSegments', 'per-room cleaning');\n const { repeats, fan, water } = this.#resolveCleanOpts(opts);\n const selects = ids.map((id) => [id, repeats, fan, water, 1]);\n return this.#startCustom(CUSTOM_CLEAN_MODE.SEGMENT, { selects });\n }\n async cleanZones(\n zones: Array<{ x0: number; y0: number; x1: number; y1: number }>,\n opts: CleanOpts = {},\n ): Promise<unknown> {\n // Argument validation precedes capability gating (donor convention): an\n // empty array throws RangeError regardless of model capability.\n if (zones.length === 0) {\n throw new RangeError('cleanZones: zones must not be empty');\n }\n this.#requireCap(this.#caps.canCleanPerRoom, 'cleanZones', 'per-room cleaning');\n const { repeats, fan, water } = this.#resolveCleanOpts(opts);\n const areas = zones.map((z) => [\n Math.round(z.x0),\n Math.round(z.y0),\n Math.round(z.x1),\n Math.round(z.y1),\n repeats,\n fan,\n water,\n ]);\n return this.#startCustom(CUSTOM_CLEAN_MODE.ZONE, { areas });\n }\n /**\n * Send the robot to clean a single point (the SPOT custom-clean action,\n * mode 20). ASSUMED: Tasshack's \"go to point\" is the same SPOT action — this\n * is exposed as `cleanSpot` because it maps to the spot/custom-clean action,\n * not a distinct go-to. No separate `goTo` is exposed.\n */\n async cleanSpot(point: { x: number; y: number }, opts: CleanOpts = {}): Promise<unknown> {\n this.#requireCap(this.#caps.canCleanPerRoom, 'cleanSpot', 'per-room cleaning');\n const { repeats, fan, water } = this.#resolveCleanOpts(opts);\n const points = [[Math.round(point.x), Math.round(point.y), repeats, fan, water]];\n return this.#startCustom(CUSTOM_CLEAN_MODE.SPOT, { points });\n }\n\n /**\n * Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the\n * robot — reads {@link VacuumDevice.DEFAULT_PROPS} from the cloud-cached\n * endpoint. After it resolves, every typed getter (status/battery/suction/\n * water/cleaningMode/error/charging…) reflects the cached values, so a\n * standby/docked vacuum reports its state exactly as the Dreamehome app does.\n */\n async refreshFromCache(): Promise<void> {\n await this.refreshCachedProperties([...VacuumDevice.DEFAULT_PROPS]);\n }\n\n // -- maps ---------------------------------------------------------------\n /** The most-recently-decoded map, or `null` until {@link getMap} succeeds. */\n get lastMap(): VacuumMap | null {\n return this.#lastMap;\n }\n\n /**\n * The current room/segment id, derived from the most-recently-decoded map's\n * active-segment set (`sa`). `null` when no map has been fetched or no\n * segment is currently active. REPLACES the P3 \"intentionally not exposed\"\n * placeholder — the value now comes from the decoded map layer (P5).\n */\n get currentSegmentId(): number | null {\n return this.#lastMap?.segments.find((s) => s.active)?.id ?? null;\n }\n\n /**\n * Fetch the current saved/live-map blob (OSS), decode it, cache it as\n * {@link lastMap}, emit a `'map'` event, and return the {@link VacuumMap}.\n *\n * Capability-gated on `canMap`. The `filename` is the OSS object name the\n * caller resolves from a `mapInfo`/PATH push. The fetcher is injectable so\n * tests drive it with a synthetic blob and no live network.\n *\n * NOTE: active live-frame P-frame STREAMING (continuous merge orchestration)\n * is a documented follow-up; `getMap` resolves a single frame here. The\n * `applyVacuumPFrame` merge primitive ships separately for that work.\n */\n async getMap(input: VacuumGetMapInput): Promise<VacuumMap> {\n this.#requireCap(this.#caps.canMap, 'getMap', 'map decoding');\n const session = this.currentSession();\n const region = this.region;\n const fetcher = input.fetcher ?? new OssFetcher();\n const fetchInput: OssFetchInput = {\n host: input.host ?? REGION_HOSTS[region],\n accessToken: session.accessToken,\n region,\n did: this.deviceId,\n model: this.model,\n filename: input.filename,\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n };\n const blob = await fetcher.fetchBlob(fetchInput);\n const map = decodeVacuumMap(blob, {\n ...(input.key !== undefined ? { key: input.key } : {}),\n ...(input.iv !== undefined ? { iv: input.iv } : {}),\n });\n this.#lastMap = map;\n this.emit('map', map);\n return map;\n }\n\n /** Props worth seeding on start() / polling — exported for the facade. */\n static readonly DEFAULT_PROPS = [\n VACUUM_PROP.STATE,\n VACUUM_PROP.ERROR,\n VACUUM_PROP.FAULTS_STR,\n VACUUM_PROP.TASK_STATUS,\n VACUUM_PROP.SUCTION_LEVEL,\n VACUUM_PROP.WATER_VOLUME,\n VACUUM_PROP.CLEAN_MODE_SETTING,\n VACUUM_PROP.TASK_PROGRESS_PCT,\n BATTERY_PROP.LEVEL,\n BATTERY_PROP.CHARGING_STATUS,\n CONSUMABLE_PROP.MAIN_BRUSH_LEFT,\n CONSUMABLE_PROP.SIDE_BRUSH_LEFT,\n CONSUMABLE_PROP.FILTER_LEFT,\n SETTINGS_PROP.VOLUME,\n ] as const;\n}\n","/**\n * Mower MIoT property / action / event identifiers and the embedded\n * scheduling-task opcode payloads, reverse-engineered from\n * antondaubert/dreame-mower (const.py + device.py _build_*_task_payload).\n * Only KNOWN/useful ids are ported; the donor's unnamed SERVICE2_PROPERTY_* /\n * SERVICE5_PROPERTY_* are intentionally omitted.\n *\n * Encoding note: the four siid-5 actions (START_MOWING/STOP/DOCK/PAUSE) take NO\n * in-params. Targeted starts + resume are sent as ACTIONS on SCHEDULING_TASK\n * (siid 2 piid 50) whose in-params are `[ <opcodeObject> ]` — a one-element\n * array carrying the raw {m,p,o,d?} object (NOT the vacuum's {piid,value} form).\n */\n\n/** Mower service properties (subset; unnamed reverse-engineered props skipped). */\nexport const MOWER_PROP = {\n /** VERIFIED-by-donor — pose + coverage telemetry (binary). P4 uses progress % only. */\n POSE_COVERAGE: { siid: 1, piid: 4 } as const,\n /** VERIFIED-by-donor — MowerStatus (DeviceStatus). */\n STATUS: { siid: 2, piid: 1 } as const,\n /** VERIFIED-by-donor — device code blob (model/region). Not decoded in P4. */\n DEVICE_CODE: { siid: 2, piid: 2 } as const,\n /** VERIFIED-by-donor — mission task descriptor object {t,d:{...}}; also the action target. */\n SCHEDULING_TASK: { siid: 2, piid: 50 } as const,\n /** VERIFIED-by-donor — mission completion summary (often empty). */\n SCHEDULING_SUMMARY: { siid: 2, piid: 52 } as const,\n /** VERIFIED-by-donor — per-zone control status {status:[[zone,code],...]}. */\n MOWER_CONTROL_STATUS: { siid: 2, piid: 56 } as const,\n /** VERIFIED-by-donor — battery percentage 0-100. */\n BATTERY: { siid: 3, piid: 1 } as const,\n /** VERIFIED-by-donor — MowerChargingStatus. */\n CHARGING_STATUS: { siid: 3, piid: 2 } as const,\n /** VERIFIED-by-donor — task status code (MowerTaskStatus; mostly diagnostic). */\n TASK_STATUS: { siid: 5, piid: 104 } as const,\n} as const;\n\n/** Mower actions (siid 5). No in-params — parity with the donor execute_action. */\nexport const MOWER_ACTION = {\n START_MOWING: { siid: 5, aiid: 1 } as const,\n STOP: { siid: 5, aiid: 2 } as const,\n DOCK: { siid: 5, aiid: 3 } as const,\n PAUSE: { siid: 5, aiid: 4 } as const,\n} as const;\n\n/** Mower events. */\nexport const MOWER_EVENT = {\n /** VERIFIED-by-donor — mission completion (siid 4 eiid 1). */\n MISSION_COMPLETION: { siid: 4, eiid: 1 } as const,\n} as const;\n\n/**\n * Embedded scheduling-task opcodes carried in SCHEDULING_TASK (2:50) action\n * in-params. `m:'a'` = action, `p` = priority, `o` = opcode, `d` = data.\n * RESUME (o:5) is the donor's TASK_PAYLOAD_RESUME (continueControl).\n */\nexport const TASK_OPCODE = {\n RESUME: { m: 'a', p: 0, o: 5 } as const,\n ALL_AREA: 100,\n EDGE: 101,\n ZONE: 102,\n SPOT: 103,\n} as const;\n\n/** A fresh resume opcode object (never share the frozen literal across calls). */\nexport function buildResumePayload(): { m: string; p: number; o: number } {\n return { m: 'a', p: 0, o: 5 };\n}\n\n/** All-area start: o:100, region_id=[mapId], area_id=[]. device.py _build_all_area_task_payload. */\nexport function buildAllAreaPayload(mapId: number): {\n m: string;\n p: number;\n o: number;\n d: { region_id: number[]; area_id: number[] };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.ALL_AREA, d: { region_id: [mapId], area_id: [] } };\n}\n\n/** Zone start: o:102, region=zoneIds. device.py _build_zone_task_payload. */\nexport function buildZonePayload(zoneIds: number[]): {\n m: string;\n p: number;\n o: number;\n d: { region: number[] };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.ZONE, d: { region: [...zoneIds] } };\n}\n\n/** Edge start: o:101, edge=contourIdPairs. device.py _build_edge_task_payload. */\nexport function buildEdgePayload(contourIds: number[][]): {\n m: string;\n p: number;\n o: number;\n d: { edge: number[][] };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.EDGE, d: { edge: contourIds.map((c) => [...c]) } };\n}\n\n/** Spot start: o:103, area=spotAreaIds. device.py _build_spot_task_payload. */\nexport function buildSpotPayload(spotAreaIds: number[]): {\n m: string;\n p: number;\n o: number;\n d: { area: number[] };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.SPOT, d: { area: [...spotAreaIds] } };\n}\n","/**\n * Mower MIoT enum value spaces, reverse-engineered from antondaubert/dreame-mower\n * (const.py DeviceStatus + STATUS_MAPPING + CHARGING_STATUS_MAPPING, and\n * property/mower_control.py + service5.py). The donor is a working HA\n * integration, so these are VERIFIED-by-donor; members annotated ASSUMED are\n * not yet corroborated. The integer is always the source of truth.\n */\n\n/** VERIFIED-by-donor — STATUS_PROPERTY (siid 2 piid 1), const.py DeviceStatus. */\nexport enum MowerStatus {\n NoStatus = 0,\n Mowing = 1,\n Standby = 2,\n Paused = 3,\n PausedDueToErrors = 4,\n ReturningToCharge = 5,\n Charging = 6,\n Mapping = 11,\n ChargingComplete = 13,\n Updating = 14,\n}\n\n/** VERIFIED-by-donor — CHARGING_STATUS_PROPERTY (siid 3 piid 2), CHARGING_STATUS_MAPPING. */\nexport enum MowerChargingStatus {\n NotDocked = 0,\n Charging = 1,\n NotCharging = 2,\n ChargingCompleted = 3,\n ReturnToCharge = 5,\n /** Charging paused: battery temperature too low (donor issue #40). */\n ChargingPausedLowTemperature = 16,\n}\n\n/**\n * VERIFIED-by-donor — per-zone control code in MOWER_CONTROL_STATUS (siid 2\n * piid 56) `status` array entries `[zone_id, code]`. mower_control.py.\n */\nexport enum MowerControlAction {\n /** Zone waiting in a multi-zone session. */\n Queued = -1,\n /** Actively mowing. */\n Continue = 0,\n Completed = 2,\n Pause = 4,\n}\n\n/**\n * Observed TASK_STATUS codes (siid 5 piid 104), service5.py TASK_STATUS_MAPPING.\n * Only `SpotIncomplete` (7) has a confirmed meaning; the donor explicitly marks\n * codes 2/3/10/13 as \"Unknown task status: N\" (observed after RAIN/LOST/\n * LOW_BATTERY events). Those are NOT invented as named members — they surface as\n * raw via {@link MowerDevice.taskStatusRaw}. This is the FULL documented set:\n * the donor names exactly one code.\n */\nexport enum MowerTaskStatus {\n /** \"Task incomplete - spot mowing\". */\n SpotIncomplete = 7,\n}\n\n/**\n * Mower device-code / fault value space for DEVICE_CODE (siid 2 piid 2). This is\n * the FULL donor `BASE_DEVICE_CODES` registry (property/device_code.py), codes\n * 0..73 contiguous. Each integer is documented by the donor with a name +\n * severity (INFO / WARNING / ERROR — noted inline). 0 = NO_DEVICE_CODE (normal\n * operation). Codes not in this table (model-specific extensions) fall through\n * the cast-free lookup and surface as raw via {@link MowerDevice.faultRaw}.\n *\n * Two donor names repeat at different integers; the second occurrence is\n * suffixed (`LidarDirtyWarning` 38, `BatteryOverheatWarning` 42) to keep enum\n * member names unique while preserving the integer source of truth.\n */\nexport enum MowerFault {\n NoDeviceCode = 0, // INFO\n Tilted = 1, // ERROR\n Trapped = 2, // ERROR\n NarrowPathToStation = 3, // ERROR\n LeftWheel = 4, // ERROR\n RightWheel = 5, // ERROR\n LiftMotor = 6, // ERROR\n Cutter = 7, // ERROR\n SidedMotor = 8, // ERROR\n CrashPlate = 9, // ERROR\n Charging = 10, // ERROR\n BatteryOverheat = 11, // ERROR\n LidarCovered = 12, // ERROR\n LidarOverheatWithoutMap = 13, // ERROR\n LidarOverheatWithMap = 14, // ERROR\n LidarOverheat = 15, // ERROR\n LidarDirty = 16, // ERROR\n LidarAbnormal = 17, // ERROR\n LocationWeak = 18, // ERROR\n LocationLost = 19, // ERROR\n Sensor = 20, // ERROR\n InForbiddenArea = 21, // ERROR\n OutOfMap = 22, // ERROR\n EmergencyStop = 23, // ERROR\n BatteryLow = 24, // ERROR\n MapFileCrack = 25, // ERROR\n AwayFromMap = 26, // ERROR\n HumanDetected = 27, // ERROR\n BladeLoss = 28, // ERROR\n StationLoss = 29, // ERROR\n MaintainLoss = 30, // ERROR\n BackChargeFailed = 31, // WARNING\n DockingFailed = 32, // WARNING\n LocatingFailedWithMap = 33, // WARNING\n LocatingFailedWithoutMap = 34, // WARNING\n LocatingAbnormal = 35, // WARNING\n TaskStartFailed = 36, // WARNING\n PathImpassable = 37, // ERROR\n LidarDirtyWarning = 38, // WARNING (donor LIDAR_DIRTY at 38)\n CamDirty = 39, // WARNING\n CamAbnormal = 40, // WARNING\n CamCover = 41, // WARNING\n BatteryOverheatWarning = 42, // WARNING (donor BATTERY_OVERHEAT at 42)\n BatteryTempLow = 43, // WARNING\n AutobuildBorder = 44, // WARNING\n AutobuildSide = 45, // WARNING\n BorderFinish = 46, // INFO\n NewMap = 47, // INFO\n TaskFinish = 48, // INFO\n DestinationNotReachable = 49, // INFO\n TaskStart = 50, // INFO\n CruiseStart = 51, // INFO\n PointAndGoStart = 52, // INFO\n ScheduleStart = 53, // INFO\n BatteryLowReturning = 54, // INFO\n BatteryLowScheduleSuspend = 55, // INFO\n BadWeatherProtecting = 56, // INFO\n RainScheduleInterupted = 57, // INFO\n RainScheduleSuspend = 58, // INFO\n ForzenReturning = 59, // INFO\n FrozenScheduleSuspend = 60, // INFO\n NotDisturbReturning = 61, // INFO\n NotDisturbScheduleSuspend = 62, // INFO\n WorkingScheduleSuspend = 63, // INFO\n RemoteControlingScheduleSuspend = 64, // INFO\n EmergencyStoppedScheduleSuspend = 65, // INFO\n TopCoverOpenScheduleSuspend = 66, // INFO\n FaultModeScheduleSuspend = 67, // INFO\n ScheduleTimeout = 68, // INFO\n StationNotConnectedToWorkingArea = 69, // INFO\n ContinueFromBreakpoint = 70, // INFO\n IdleTimeoutReturning = 71, // INFO\n PauseTimeoutReturning = 72, // INFO\n TopCoverOpen = 73, // ERROR\n}\n","/**\n * Cast-free mower decoders. Re-exports the shared numeric primitives, and adds\n * the two structured-payload parsers the typed getters consume:\n * - parseTaskDescriptor : SCHEDULING_TASK (2:50) {t,d:{...}} -> typed task\n * - parseControlStatus : MOWER_CONTROL_STATUS (2:56) {status:[[zone,code]]}\n * Ported from antondaubert/dreame-mower property/{scheduling,mower_control}.py.\n * The binary pose-track geometry (1:4) is OUT OF SCOPE here (P5).\n */\nimport { MowerControlAction } from './enums.js';\nimport { enumLookup } from '../_shared/decode.js';\n\nexport { asNum, enumLookup } from '../_shared/decode.js';\n\n/** Typed scheduling task descriptor (subset of the donor TaskHandler fields). */\nexport interface MowerTaskDescriptor {\n taskType: string;\n executionActive: boolean;\n /** `d.o` — coverage target % or mode sentinel. */\n coverageTarget: number;\n taskActive: boolean;\n areaId: number[] | null;\n regionId: number[] | null;\n elapsedTime: number | null;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction numArrayOrNull(v: unknown): number[] | null {\n if (!Array.isArray(v)) {\n return null;\n }\n const out: number[] = [];\n for (const n of v) {\n if (typeof n !== 'number') {\n return null;\n }\n out.push(n);\n }\n return out;\n}\n\n/**\n * Parse the SCHEDULING_TASK descriptor (2:50). Required fields `d.exe`,\n * `d.o`, `d.status` must be present (matching the donor's KeyError-bubble);\n * optional area_id/region_id/time default to null. Returns null on any\n * malformed input — no throw, no cast.\n */\nexport function parseTaskDescriptor(value: unknown): MowerTaskDescriptor | null {\n if (!isRecord(value)) {\n return null;\n }\n const t = value['t'];\n const d = value['d'];\n if (typeof t !== 'string' || !isRecord(d)) {\n return null;\n }\n const exe = d['exe'];\n const o = d['o'];\n const status = d['status'];\n if (typeof exe !== 'boolean' || typeof o !== 'number' || typeof status !== 'boolean') {\n return null;\n }\n return {\n taskType: t === 'TASK' ? 'TASK' : 'UNKNOWN',\n executionActive: exe,\n coverageTarget: o,\n taskActive: status,\n areaId: numArrayOrNull(d['area_id']),\n regionId: numArrayOrNull(d['region_id']),\n elapsedTime: typeof d['time'] === 'number' ? d['time'] : null,\n };\n}\n\nconst controlLookup = enumLookup<MowerControlAction>([\n MowerControlAction.Queued,\n MowerControlAction.Continue,\n MowerControlAction.Completed,\n MowerControlAction.Pause,\n]);\n\n/** Map a raw 2:56 entry code to a MowerControlAction, or null. Cast-free. */\nexport function controlActionFor(code: number): MowerControlAction | null {\n return controlLookup(code);\n}\n\n/** Typed mower control status (2:56). */\nexport interface MowerControlState {\n action: MowerControlAction | null;\n statusCode: number | null;\n zones: number[][];\n}\n\n/**\n * Parse MOWER_CONTROL_STATUS (2:56). `status` is `[[zone_id, code], ...]`.\n * Primary action = the actively-mowing zone (code 0), else the first entry.\n * Empty array -> null action. Warn-and-tolerate: an UNKNOWN code does NOT drop\n * the whole structure — the zone is surfaced as-is and its `action` resolves to\n * null (only the primary's action goes null when the primary itself is unknown).\n * Still returns null on a malformed PAIR shape (non-array / <2 / non-numeric).\n * No cast.\n */\nexport function parseControlStatus(value: unknown): MowerControlState | null {\n if (!isRecord(value)) {\n return null;\n }\n const status = value['status'];\n if (!Array.isArray(status)) {\n return null;\n }\n if (status.length === 0) {\n return { action: null, statusCode: null, zones: [] };\n }\n const zones: number[][] = [];\n for (const entry of status) {\n const pair = numArrayOrNull(entry);\n if (pair === null || pair.length < 2) {\n return null;\n }\n // `numArrayOrNull` already guarantees an all-number array and the length\n // check guarantees >=2 elements, so the pair is pushed as-is (no per-index\n // undefined guard needed — that branch would be unreachable / dead).\n zones.push(pair);\n }\n const primary = zones.find((e) => e[1] === 0) ?? zones[0];\n const code = primary?.[1] ?? null;\n return {\n action: code === null ? null : controlActionFor(code),\n statusCode: code,\n zones,\n };\n}\n","/**\n * Per-model mower capability records + a resolver adapting the rich record into\n * the generic CapabilityResolver the BaseDevice expects (mirrors the vacuum\n * capability layer). The donor (antondaubert/dreame-mower) has no per-model\n * capability matrix for mowers, so the targeted-mowing flags are ASSUMED working\n * hypotheses from the donor command surface; verified:false until live-checked.\n */\nimport type { CapabilityResolver, DeviceCapabilities } from '../../device/capability.js';\n\n/** Rich mower capability record. `canX` = behaviour the model accepts. */\nexport interface MowerCapabilities {\n model: string;\n /** true only for live-confirmed records (false = assumed / fallback). */\n verified: boolean;\n /** Zone-selective mowing (2:50 o:102). */\n canMowZones: boolean;\n /** Edge / contour mowing (2:50 o:101). */\n canMowEdges: boolean;\n /** Spot mowing (2:50 o:103). */\n canMowSpots: boolean;\n /** All-area, map-targeted start (2:50 o:100). */\n canMowAllArea: boolean;\n /** Resume after pause (2:50 o:5). Generic to the mower action surface. */\n canResume: boolean;\n /** Has a scheduling task descriptor (2:50). */\n canSchedule: boolean;\n /** Fetches/parses the batched vector map (MAP.* JSON → zones/paths/contours). */\n canMap: boolean;\n}\n\nconst FALLBACK: Omit<MowerCapabilities, 'model'> = {\n verified: false,\n canMowZones: false,\n canMowEdges: false,\n canMowSpots: false,\n canMowAllArea: false,\n canResume: true,\n canSchedule: true,\n // Vector-map parsing is model-agnostic, but an unknown model leaves it false\n // until its firmware is verified to expose the batch device-data surface.\n canMap: false,\n};\n\n/** A1-family body (ASSUMED from the donor command surface; not live-verified). */\nconst A1_FAMILY: Omit<MowerCapabilities, 'model' | 'verified'> = {\n canMowZones: true,\n canMowEdges: true,\n canMowSpots: true,\n canMowAllArea: true,\n canResume: true,\n canSchedule: true,\n canMap: true,\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {\n Object.freeze(obj);\n for (const v of Object.values(obj)) {\n deepFreeze(v);\n }\n }\n return obj;\n}\n\nexport const MODEL_CAPABILITIES: Readonly<Record<string, MowerCapabilities>> = {\n /** Dreame A1 — the user's mower. ASSUMED from the donor command surface. */\n 'dreame.mower.p2255': { model: 'dreame.mower.p2255', verified: false, ...A1_FAMILY },\n};\n\nfor (const entry of Object.values(MODEL_CAPABILITIES)) {\n deepFreeze(entry);\n}\n\n/** Resolve a model to its rich mower capability record (frozen / fallback). */\nexport function getMowerCapabilities(model: string): MowerCapabilities {\n const known = MODEL_CAPABILITIES[model];\n if (known) {\n return known;\n }\n return deepFreeze({ model, ...FALLBACK });\n}\n\n/** Map the rich record's booleans -> the generic capability token set. */\nfunction tokensFor(c: MowerCapabilities): Set<string> {\n const tokens = new Set<string>();\n const add = (flag: boolean, token: string): void => {\n if (flag) {\n tokens.add(token);\n }\n };\n add(c.canMowZones, 'mow-zones');\n add(c.canMowEdges, 'mow-edges');\n add(c.canMowSpots, 'mow-spots');\n add(c.canMowAllArea, 'mow-all-area');\n add(c.canResume, 'resume');\n add(c.canSchedule, 'schedule');\n return tokens;\n}\n\nclass MowerCapabilitySet implements DeviceCapabilities {\n readonly model: string;\n readonly #tokens: ReadonlySet<string>;\n constructor(model: string, tokens: ReadonlySet<string>) {\n this.model = model;\n this.#tokens = tokens;\n }\n has(token: string): boolean {\n return this.#tokens.has(token);\n }\n list(): readonly string[] {\n return [...this.#tokens];\n }\n}\n\n/** Resolver the BaseDevice accepts via BaseDeviceInput.capabilities. */\nexport class MowerCapabilityResolver implements CapabilityResolver {\n resolve(model: string): DeviceCapabilities {\n return new MowerCapabilitySet(model, tokensFor(getMowerCapabilities(model)));\n }\n}\n","/**\n * Parser for mower vector map data from the Dreame batch device-data API.\n *\n * PORT of antondaubert/dreame-mower's `map_data_parser.py`. The batch API\n * returns map data split across numbered keys (`MAP.0`, `MAP.1`, …); these\n * chunks are concatenated in numeric order to form a complete JSON string. The\n * JSON carries polygon-based zone boundaries, navigation paths, contours and\n * metadata. `M_PATH.*` keys carry the mow-path trace the same chunked way.\n *\n * Wire values arrive as `unknown`; every shape is narrowed with type-guards\n * (no `as` casts). Malformed entries are skipped (warn-and-tolerate), mirroring\n * the donor's try/except. The Python `vector_map_to_map_data` bridge and the\n * internal recursive `maps: dict` cache are intentionally NOT ported — the SVG\n * renderer consumes the structured `MowerMap` directly.\n */\nimport type {\n MowerMap,\n MowerZone,\n MowerSpotArea,\n MowerPathEntry,\n MowerContour,\n MowerMapBoundary,\n MowerMowPath,\n MowerAvailableMap,\n MowerPoint,\n} from './types.js';\n\n/** Sentinel pair in M_PATH data marking a path-segment break. */\nconst PATH_SENTINEL_X = 32767;\nconst PATH_SENTINEL_Y = -32768;\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction asNumber(v: unknown, fallback: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : fallback;\n}\n\nfunction asString(v: unknown, fallback: string): string {\n return typeof v === 'string' ? v : fallback;\n}\n\n/** Translate the batch map index into the task-level region identifier. */\nfunction mapIdFromIndex(mapIndex: number): number {\n return mapIndex + 1;\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Reassemble chunked data from the batch API into a single string.\n *\n * PORT of `reassemble_map_chunks`. Keys like `MAP.0`, `MAP.1`, … `MAP.N` are\n * concatenated in NUMERIC order; the `MAP.info` key (metadata) is skipped.\n * Wire values are strings; non-string values are skipped defensively.\n *\n * @param batchData raw `get_batch_device_datas` response map\n * @param prefix key prefix to match, e.g. `'MAP'` or `'M_PATH'`\n * @returns concatenated string, or `null` if no matching keys are found\n */\nexport function reassembleMapChunks(\n batchData: Record<string, unknown>,\n prefix: string,\n): string | null {\n const pattern = new RegExp(`^${escapeRegex(prefix)}\\\\.(\\\\d+)$`);\n const chunks: Array<{ idx: number; value: string }> = [];\n for (const [key, value] of Object.entries(batchData)) {\n const match = pattern.exec(key);\n if (match && typeof value === 'string') {\n const idx = Number(match[1]);\n chunks.push({ idx, value });\n }\n }\n if (chunks.length === 0) {\n return null;\n }\n chunks.sort((a, b) => a.idx - b.idx);\n return chunks.map((c) => c.value).join('');\n}\n\n/** Parse a `MAP.info`/`M_PATH.info` digit string into a split offset (>=0). */\nfunction parseSplitPos(info: unknown): number {\n if (typeof info === 'string' && /^\\d+$/.test(info)) {\n return Number(info);\n }\n return 0;\n}\n\n/**\n * Extract the `value` array from a `{ dataType: 'Map', value: [...] }` block.\n * Returns `[]` unless the block is a Map with an array value, mirroring the\n * donor `_parse_polygon_list`.\n */\nfunction parsePolygonList(dataMap: unknown): readonly unknown[] {\n if (!isRecord(dataMap) || dataMap['dataType'] !== 'Map') {\n return [];\n }\n const value = dataMap['value'];\n return Array.isArray(value) ? value : [];\n}\n\n/** Convert `[{ x, y }, …]` wire points to `MowerPoint[]`, skipping malformed. */\nfunction extractPathCoords(pathList: unknown): MowerPoint[] {\n if (!Array.isArray(pathList)) {\n return [];\n }\n const out: MowerPoint[] = [];\n for (const p of pathList) {\n if (isRecord(p) && typeof p['x'] === 'number' && typeof p['y'] === 'number') {\n out.push({ x: p['x'], y: p['y'] });\n }\n }\n return out;\n}\n\n/**\n * Normalize a contour identifier (string `'1,0'` or two-element array) into a\n * two-integer tuple. Throws on an invalid length — caught by the batch parser.\n */\nexport function extractContourId(raw: unknown): readonly [number, number] {\n if (typeof raw === 'string') {\n const parts = raw.split(',').map((part) => part.trim());\n if (parts.length !== 2) {\n throw new Error(`Invalid contour id: ${raw}`);\n }\n const a = Number(parts[0]);\n const b = Number(parts[1]);\n if (!Number.isFinite(a) || !Number.isFinite(b)) {\n throw new Error(`Invalid contour id: ${raw}`);\n }\n return [Math.trunc(a), Math.trunc(b)];\n }\n if (Array.isArray(raw) && raw.length === 2) {\n const pair: readonly unknown[] = raw;\n const a = pair[0];\n const b = pair[1];\n if (typeof a === 'number' && typeof b === 'number') {\n return [Math.trunc(a), Math.trunc(b)];\n }\n }\n throw new Error(`Invalid contour id: ${JSON.stringify(raw)}`);\n}\n\n/** A `[id, data]` polygon entry on the wire. */\nfunction asEntry(entry: unknown): { id: unknown; data: Record<string, unknown> } | null {\n if (!Array.isArray(entry) || entry.length < 2) {\n return null;\n }\n const tuple: readonly unknown[] = entry;\n const data = tuple[1];\n if (!isRecord(data)) {\n return null;\n }\n return { id: tuple[0], data };\n}\n\nfunction parseZones(list: readonly unknown[], includeTimes: boolean): MowerZone[] {\n const out: MowerZone[] = [];\n for (const raw of list) {\n const entry = asEntry(raw);\n if (!entry) {\n continue;\n }\n const { id, data } = entry;\n out.push({\n zoneId: asNumber(id, 0),\n path: extractPathCoords(data['path']),\n name: asString(data['name'], ''),\n zoneType: asNumber(data['type'], 0),\n shapeType: includeTimes ? asNumber(data['shapeType'], 0) : 0,\n area: includeTimes ? asNumber(data['area'], 0) : 0,\n time: includeTimes ? asNumber(data['time'], 0) : 0,\n etime: includeTimes ? asNumber(data['etime'], 0) : 0,\n });\n }\n return out;\n}\n\nfunction parseSpotAreas(list: readonly unknown[]): MowerSpotArea[] {\n const out: MowerSpotArea[] = [];\n for (const raw of list) {\n const entry = asEntry(raw);\n if (!entry) {\n continue;\n }\n const { id, data } = entry;\n out.push({\n areaId: Math.trunc(asNumber(id, 0)),\n path: extractPathCoords(data['path']),\n name: asString(data['name'], ''),\n shapeType: asNumber(data['shapeType'], 0),\n area: asNumber(data['area'], 0),\n });\n }\n return out;\n}\n\nfunction parsePaths(list: readonly unknown[]): MowerPathEntry[] {\n const out: MowerPathEntry[] = [];\n for (const raw of list) {\n const entry = asEntry(raw);\n if (!entry) {\n continue;\n }\n const { id, data } = entry;\n out.push({\n pathId: asNumber(id, 0),\n path: extractPathCoords(data['path']),\n pathType: asNumber(data['type'], 0),\n });\n }\n return out;\n}\n\nfunction parseContours(list: readonly unknown[]): MowerContour[] {\n const out: MowerContour[] = [];\n for (const raw of list) {\n const entry = asEntry(raw);\n if (!entry) {\n continue;\n }\n const { id, data } = entry;\n out.push({\n contourId: extractContourId(id),\n path: extractPathCoords(data['path']),\n contourType: asNumber(data['type'], 0),\n shapeType: asNumber(data['shapeType'], 0),\n });\n }\n return out;\n}\n\nfunction parseBoundary(raw: unknown): MowerMapBoundary | null {\n if (!isRecord(raw)) {\n return null;\n }\n const { x1, y1, x2, y2 } = raw;\n if (\n typeof x1 !== 'number' ||\n typeof y1 !== 'number' ||\n typeof x2 !== 'number' ||\n typeof y2 !== 'number'\n ) {\n return null;\n }\n return { x1, y1, x2, y2 };\n}\n\n/**\n * Parse a single map JSON string into a `MowerMap`.\n *\n * PORT of `parse_mower_map`. `JSON.parse` may throw on malformed input and\n * `extractContourId` may throw on an invalid contour id — both bubble up to\n * `parseBatchMapData`, which catches and skips (the donor's try/except).\n */\nexport function parseMowerMap(mapJsonStr: string): MowerMap {\n const data: unknown = JSON.parse(mapJsonStr);\n if (!isRecord(data)) {\n throw new Error('Map JSON is not an object');\n }\n\n const mapIndex = asNumber(data['mapIndex'], 0);\n\n return {\n zones: parseZones(parsePolygonList(data['mowingAreas']), true),\n spotAreas: parseSpotAreas(parsePolygonList(data['spotAreas'])),\n forbiddenAreas: parseZones(parsePolygonList(data['forbiddenAreas']), false),\n paths: parsePaths(parsePolygonList(data['paths'])),\n contours: parseContours(parsePolygonList(data['contours'])),\n boundary: parseBoundary(data['boundary']),\n totalArea: asNumber(data['totalArea'], 0),\n name: asString(data['name'], ''),\n mapIndex,\n mapId: mapIdFromIndex(mapIndex),\n mowPaths: [],\n availableMaps: [],\n currentMapId: null,\n lastUpdated: Date.now(),\n };\n}\n\n/**\n * Parse `M_PATH.*` keys into mow-path traces.\n *\n * PORT of `parse_mow_paths`. Reassembles the chunks, applies the `M_PATH.info`\n * split offset, extracts every `[x,y]` pair via regex, splits on the\n * `[32767,-32768]` sentinel into segments, and scales the remaining\n * coordinates ×10 (decimeters → centimeters). Always returns at most one\n * `MowerMowPath` (zone 0), matching the donor.\n */\nexport function parseMowPaths(batchData: Record<string, unknown>): MowerMowPath[] {\n const reassembled = reassembleMapChunks(batchData, 'M_PATH');\n if (!reassembled) {\n return [];\n }\n\n const splitPos = parseSplitPos(batchData['M_PATH.info']);\n let raw = reassembled;\n if (splitPos > 0 && splitPos < raw.length) {\n raw = raw.slice(splitPos);\n }\n\n const trimmed = raw.trim();\n if (trimmed.length === 0 || trimmed === '[]') {\n return [];\n }\n\n const pairPattern = /\\[(-?\\d+),(-?\\d+)\\]/g;\n const rawPairs: Array<readonly [number, number]> = [];\n for (const m of raw.matchAll(pairPattern)) {\n rawPairs.push([Number(m[1]), Number(m[2])]);\n }\n if (rawPairs.length === 0) {\n return [];\n }\n\n const segments: MowerPoint[][] = [];\n let current: MowerPoint[] = [];\n for (const [x, y] of rawPairs) {\n if (x === PATH_SENTINEL_X && y === PATH_SENTINEL_Y) {\n if (current.length > 0) {\n segments.push(current);\n current = [];\n }\n } else {\n current.push({ x: x * 10, y: y * 10 });\n }\n }\n if (current.length > 0) {\n segments.push(current);\n }\n\n if (segments.length === 0) {\n return [];\n }\n return [{ zoneId: 0, segments }];\n}\n\n/** Parse one reassembled `MAP.*` part (a JSON array of map-json-strings). */\nfunction parseMapPart(part: string): MowerMap[] {\n const trimmed = part.trim();\n if (trimmed.length === 0) {\n return [];\n }\n let arr: unknown;\n try {\n arr = JSON.parse(trimmed);\n } catch {\n return [];\n }\n if (!Array.isArray(arr)) {\n return [];\n }\n const out: MowerMap[] = [];\n for (const item of arr) {\n if (typeof item !== 'string') {\n continue;\n }\n try {\n out.push(parseMowerMap(item));\n } catch {\n // Skip malformed map entries (donor warn-and-tolerate).\n }\n }\n return out;\n}\n\n/**\n * Parse a full batch device-data response into the primary `MowerMap`.\n *\n * PORT of `parse_batch_map_data`. Reassembles `MAP.*`, splits on the\n * `MAP.info` character length, JSON-parses each part as an array of\n * map-json-strings, parses each (skipping throwers), picks the `mapIndex === 0`\n * primary (else the first), builds `availableMaps`, attaches `mowPaths`, and\n * returns the primary. Returns `null` on an empty batch or no valid arrays.\n *\n * The donor's recursive `maps: dict` cache is intentionally NOT ported — it is\n * internal HA active-map bookkeeping; we surface `availableMaps` instead.\n */\nexport function parseBatchMapData(batchData: Record<string, unknown>): MowerMap | null {\n if (Object.keys(batchData).length === 0) {\n return null;\n }\n\n const rawMap = reassembleMapChunks(batchData, 'MAP');\n if (!rawMap) {\n return null;\n }\n\n const splitPos = parseSplitPos(batchData['MAP.info']);\n const parts =\n splitPos > 0 && splitPos < rawMap.length\n ? [rawMap.slice(0, splitPos), rawMap.slice(splitPos)]\n : [rawMap];\n\n const parsedMaps: MowerMap[] = [];\n for (const part of parts) {\n parsedMaps.push(...parseMapPart(part));\n }\n\n if (parsedMaps.length === 0) {\n return null;\n }\n\n const primary = parsedMaps.find((m) => m.mapIndex === 0) ?? parsedMaps[0];\n if (!primary) {\n return null;\n }\n\n const availableMaps: MowerAvailableMap[] = [...parsedMaps]\n .sort((a, b) => a.mapId - b.mapId)\n .map((m) => ({\n mapId: m.mapId,\n mapIndex: m.mapIndex,\n name: m.name,\n totalArea: m.totalArea,\n }));\n\n const mowPaths = parseMowPaths(batchData);\n\n return {\n ...primary,\n availableMaps,\n mowPaths,\n currentMapId: primary.mapId,\n };\n}\n","/**\n * Deterministic SVG renderer for a structured `MowerMap`.\n *\n * ADAPT of antondaubert/dreame-mower's `svg_map_generator.py` — only the\n * geometry path is kept. We port `calculate_bounds`, `coord_to_pixel`\n * (aspect-preserving, Y-flipped, centred), `svg_polygon` (≥3 points),\n * `svg_path_from_segments` (M/L with consecutive-pixel dedupe), and the\n * `create_svg_document`/`finish_svg_document` framing.\n *\n * The HA-coordinator-coupled chrome (legend, status overlay, timestamp, title,\n * historical-file handling, rotation-from-coordinator, live-tracking overlay,\n * mower-position circle) is DROPPED — it depends on a `coordinator`, a file\n * path and live device state we don't carry. The renderer takes only a\n * `MowerMap` + optional `{ width, height, padding }`.\n *\n * Zone `name`s are NOT rendered as text in v1 (keeps the output injection-safe\n * and minimal — no untrusted strings reach the SVG). Pure string building, no\n * deps, cast-free.\n */\nimport type { MowerMap, MowerPoint } from './types.js';\n\nexport interface RenderMowerSvgOptions {\n /** Canvas width in pixels. Default 1200. */\n width?: number;\n /** Canvas height in pixels. Default 1200. */\n height?: number;\n /** Padding around the map content in pixels. Default 50. */\n padding?: number;\n}\n\nconst DEFAULT_WIDTH = 1200;\nconst DEFAULT_HEIGHT = 1200;\nconst DEFAULT_PADDING = 50;\n\nconst BACKGROUND = '#f5f5f0';\nconst MAP_BOUNDARY = '#006400';\nconst MOWING_PATH = '#ffa500';\nconst NAV_PATH = '#b4b4b4';\nconst OBSTACLE_STROKE = '#ff4d00';\nconst OBSTACLE_FILL = '#ff4d0065';\nconst TEXT_COLOR = '#000000';\n\n/** Zone fill/outline palette — soft pastels matching the Dreame app. */\nconst ZONE_COLORS: ReadonlyArray<readonly [string, string]> = [\n ['#a4d291c8', '#86be73'],\n ['#a0c8dcc8', '#82aac8'],\n ['#f0c8aac8', '#dcaf8c'],\n ['#f0b4b4c8', '#dc9696'],\n ['#e6dca0c8', '#d2c882'],\n ['#beaadcc8', '#aa91c8'],\n ['#aad7d2c8', '#8cc3be'],\n ['#dcbea0c8', '#c8a582'],\n];\n\ninterface Bounds {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\n/** Sentinel coordinate value used as a path-segment break in the donor. */\nconst SENTINEL = 2147483647;\n\n/** Compute the bounding box for all points, ignoring sentinels. */\nfunction calculateBounds(allPoints: readonly MowerPoint[]): Bounds {\n const valid = allPoints.filter((p) => p.x !== SENTINEL && p.y !== SENTINEL);\n if (valid.length === 0) {\n return { minX: 0, minY: 0, maxX: 100, maxY: 100 };\n }\n let minX = valid[0]?.x ?? 0;\n let maxX = minX;\n let minY = valid[0]?.y ?? 0;\n let maxY = minY;\n for (const p of valid) {\n if (p.x < minX) minX = p.x;\n if (p.x > maxX) maxX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.y > maxY) maxY = p.y;\n }\n return { minX, minY, maxX, maxY };\n}\n\ninterface Pixel {\n px: number;\n py: number;\n}\n\n/**\n * Convert mower coordinates to image pixel coordinates: aspect-preserving\n * (smaller scale wins), centred in the available space, Y-flipped so the map\n * origin sits at the bottom-left.\n */\nfunction coordToPixel(\n point: MowerPoint,\n bounds: Bounds,\n width: number,\n height: number,\n padding: number,\n): Pixel {\n let { maxX, maxY } = bounds;\n const { minX, minY } = bounds;\n if (maxX === minX) {\n maxX = minX + 100;\n }\n if (maxY === minY) {\n maxY = minY + 100;\n }\n const coordWidth = maxX - minX;\n const coordHeight = maxY - minY;\n const availableWidth = width - 2 * padding;\n const availableHeight = height - 2 * padding;\n const scale = Math.min(availableWidth / coordWidth, availableHeight / coordHeight);\n const renderedWidth = coordWidth * scale;\n const renderedHeight = coordHeight * scale;\n const offsetX = padding + (availableWidth - renderedWidth) / 2;\n const offsetY = padding + (availableHeight - renderedHeight) / 2;\n const px = Math.trunc(offsetX + (point.x - minX) * scale);\n const py = Math.trunc(offsetY + (maxY - point.y) * scale);\n return { px, py };\n}\n\n/** SVG `<polygon>` for ≥3 points; `''` otherwise. */\nfunction svgPolygon(\n points: readonly MowerPoint[],\n bounds: Bounds,\n width: number,\n height: number,\n padding: number,\n fill: string,\n stroke: string,\n): string {\n if (points.length < 3) {\n return '';\n }\n const pixels = points\n .map((p) => coordToPixel(p, bounds, width, height, padding))\n .map((p) => `${p.px},${p.py}`)\n .join(' ');\n return `<polygon points=\"${pixels}\" fill=\"${fill}\" stroke=\"${stroke}\"/>`;\n}\n\n/**\n * SVG `<path>` (M/L) built from one or more segments, skipping consecutive\n * duplicate pixels and segments shorter than two points. `''` when nothing\n * renders.\n */\nfunction svgPathFromSegments(\n segments: readonly (readonly MowerPoint[])[],\n bounds: Bounds,\n width: number,\n height: number,\n padding: number,\n stroke: string,\n strokeWidth: number,\n dashed: boolean,\n): string {\n const parts: string[] = [];\n for (const segment of segments) {\n if (segment.length < 2) {\n continue;\n }\n const first = segment[0];\n if (!first) {\n continue;\n }\n let prev = coordToPixel(first, bounds, width, height, padding);\n parts.push(`M ${prev.px} ${prev.py}`);\n for (let i = 1; i < segment.length; i += 1) {\n const point = segment[i];\n if (!point) {\n continue;\n }\n const pixel = coordToPixel(point, bounds, width, height, padding);\n if (pixel.px !== prev.px || pixel.py !== prev.py) {\n parts.push(`L ${pixel.px} ${pixel.py}`);\n prev = pixel;\n }\n }\n }\n if (parts.length === 0) {\n return '';\n }\n const dash = dashed ? ' stroke-dasharray=\"10,5\"' : '';\n return `<path d=\"${parts.join(' ')}\" stroke=\"${stroke}\" stroke-width=\"${strokeWidth}\"${dash} fill=\"none\"/>`;\n}\n\n/**\n * Render a `MowerMap` into a deterministic SVG document string.\n *\n * Draw order (back to front): background, nav paths (dashed grey), zone fills\n * (per-zone pastel), zone outlines, mow-path tracks (orange), forbidden-area\n * polygons (red). An empty map renders a centred \"No map data available\"\n * fallback inside a closed `<svg>`.\n */\nexport function renderMowerSvg(map: MowerMap, opts: RenderMowerSvgOptions = {}): string {\n const width = opts.width ?? DEFAULT_WIDTH;\n const height = opts.height ?? DEFAULT_HEIGHT;\n const padding = opts.padding ?? DEFAULT_PADDING;\n\n const lines: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<svg width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\" xmlns=\"http://www.w3.org/2000/svg\">`,\n `<rect width=\"100%\" height=\"100%\" fill=\"${BACKGROUND}\"/>`,\n ];\n\n const allPoints: MowerPoint[] = [];\n for (const zone of map.zones) {\n allPoints.push(...zone.path);\n }\n for (const area of map.forbiddenAreas) {\n allPoints.push(...area.path);\n }\n for (const path of map.paths) {\n allPoints.push(...path.path);\n }\n for (const contour of map.contours) {\n allPoints.push(...contour.path);\n }\n for (const mp of map.mowPaths) {\n for (const seg of mp.segments) {\n allPoints.push(...seg);\n }\n }\n\n if (allPoints.length === 0) {\n lines.push(\n `<text x=\"${Math.trunc(width / 2)}\" y=\"${Math.trunc(height / 2)}\" ` +\n `font-family=\"Arial, sans-serif\" font-size=\"16\" fill=\"${TEXT_COLOR}\" ` +\n `text-anchor=\"middle\">No map data available</text>`,\n );\n lines.push('</svg>');\n return lines.join('\\n');\n }\n\n const bounds = calculateBounds(allPoints);\n const multiZone = map.zones.length > 1;\n\n // 0. Inter-zone navigation paths — dashed grey, behind everything.\n for (const navPath of map.paths) {\n const dashed = svgPathFromSegments(\n [navPath.path],\n bounds,\n width,\n height,\n padding,\n NAV_PATH,\n 3,\n true,\n );\n if (dashed) {\n lines.push(dashed);\n }\n }\n\n // 1. Zone fills (only when more than one zone, matching the donor).\n map.zones.forEach((zone, i) => {\n if (!multiZone) {\n return;\n }\n const palette = ZONE_COLORS[i % ZONE_COLORS.length];\n const [fill, outline] = palette ?? ZONE_COLORS[0] ?? ['#cccccc', '#888888'];\n const poly = svgPolygon(zone.path, bounds, width, height, padding, fill, outline);\n if (poly) {\n lines.push(poly);\n }\n });\n\n // 2. Zone boundary outlines.\n map.zones.forEach((zone, i) => {\n const outline = multiZone\n ? (ZONE_COLORS[i % ZONE_COLORS.length]?.[1] ?? MAP_BOUNDARY)\n : MAP_BOUNDARY;\n const boundary = svgPathFromSegments(\n [zone.path],\n bounds,\n width,\n height,\n padding,\n outline,\n 2,\n false,\n );\n if (boundary) {\n lines.push(boundary);\n }\n });\n\n // 3. Mow-path tracks — orange.\n for (const mp of map.mowPaths) {\n const track = svgPathFromSegments(\n mp.segments,\n bounds,\n width,\n height,\n padding,\n MOWING_PATH,\n 2,\n false,\n );\n if (track) {\n lines.push(track);\n }\n }\n\n // 4. Forbidden-area polygons — red-ish fill.\n for (const area of map.forbiddenAreas) {\n const poly = svgPolygon(\n area.path,\n bounds,\n width,\n height,\n padding,\n OBSTACLE_FILL,\n OBSTACLE_STROKE,\n );\n if (poly) {\n lines.push(poly);\n }\n }\n\n lines.push('</svg>');\n return lines.join('\\n');\n}\n","import { BaseDevice, type BaseDeviceInput } from '../../device/base-device.js';\nimport {\n MOWER_ACTION,\n MOWER_PROP,\n buildAllAreaPayload,\n buildEdgePayload,\n buildResumePayload,\n buildSpotPayload,\n buildZonePayload,\n} from './properties.js';\nimport {\n MowerChargingStatus,\n MowerControlAction,\n MowerFault,\n MowerStatus,\n MowerTaskStatus,\n} from './enums.js';\nimport {\n asNum,\n enumLookup,\n parseControlStatus,\n parseTaskDescriptor,\n type MowerControlState,\n type MowerTaskDescriptor,\n} from './decode.js';\nimport {\n MowerCapabilityResolver,\n getMowerCapabilities,\n type MowerCapabilities,\n} from './capabilities.js';\nimport { DreameError } from '../../transport/errors.js';\nimport {\n parseBatchMapData,\n renderMowerSvg,\n type MowerMap,\n type RenderMowerSvgOptions,\n} from './map/index.js';\n\n/**\n * Injectable batch-fetch seam. Given a device id + the batch property groups\n * (e.g. `['MAP','M_PATH']`), resolves the raw `{ 'MAP.0': …, 'MAP.info': … }`\n * chunk dict the firmware returns. The default wires `getBatchDeviceDatas` from\n * the cloud layer — but its live endpoint path is NOT yet recovered (see\n * `cloud/commands.ts`), so production callers MUST inject a working fetcher.\n * Tests inject a fake with no cast.\n */\nexport type BatchDeviceDataFetcher = (\n did: string,\n props: string[],\n) => Promise<Record<string, unknown>>;\n\n/** Default batch property groups the firmware exposes the vector map under. */\nexport const DEFAULT_MAP_PROPS: readonly string[] = ['MAP', 'M_PATH'];\n\n/** Mower-specific construction input: adds the injectable batch-fetch seam. */\nexport interface MowerDeviceInput extends BaseDeviceInput {\n /** Inject the batched device-data fetcher (maps). Tests pass a fake. */\n getBatchDeviceDatas?: BatchDeviceDataFetcher;\n}\n\nconst STATUS = enumLookup<MowerStatus>(\n Object.values(MowerStatus).filter((v): v is MowerStatus => typeof v === 'number'),\n);\nconst CHARGING = enumLookup<MowerChargingStatus>(\n Object.values(MowerChargingStatus).filter((v): v is MowerChargingStatus => typeof v === 'number'),\n);\nconst TASK_STATUS = enumLookup<MowerTaskStatus>(\n Object.values(MowerTaskStatus).filter((v): v is MowerTaskStatus => typeof v === 'number'),\n);\nconst FAULT = enumLookup<MowerFault>(\n Object.values(MowerFault).filter((v): v is MowerFault => typeof v === 'number'),\n);\n\n/** A typed Dreame-mower handle (state + capability-gated commands). */\nexport class MowerDevice extends BaseDevice {\n readonly #caps: MowerCapabilities;\n readonly #fetchBatch: BatchDeviceDataFetcher | null;\n #lastMap: MowerMap | null = null;\n\n constructor(input: MowerDeviceInput) {\n super({\n ...input,\n // Inject the mower resolver so the inherited generic `capabilities` getter\n // carries the mower token set (no banned cast).\n capabilities: input.capabilities ?? new MowerCapabilityResolver().resolve(input.device.model),\n });\n this.#caps = getMowerCapabilities(input.device.model);\n this.#fetchBatch = input.getBatchDeviceDatas ?? null;\n }\n\n /** Rich, mower-specific capability record. */\n get mowerCapabilities(): MowerCapabilities {\n return this.#caps;\n }\n\n #num(siid: number, piid: number): number | null {\n return asNum(this.getProperty(siid, piid)?.value);\n }\n\n // -- typed state --------------------------------------------------------\n get statusRaw(): number | null {\n return this.#num(MOWER_PROP.STATUS.siid, MOWER_PROP.STATUS.piid);\n }\n get status(): MowerStatus | null {\n return STATUS(this.statusRaw);\n }\n get battery(): number | null {\n return this.#num(MOWER_PROP.BATTERY.siid, MOWER_PROP.BATTERY.piid);\n }\n get chargingRaw(): number | null {\n return this.#num(MOWER_PROP.CHARGING_STATUS.siid, MOWER_PROP.CHARGING_STATUS.piid);\n }\n get charging(): MowerChargingStatus | null {\n return CHARGING(this.chargingRaw);\n }\n /** Docked => on the dock (Charging / ChargingComplete state). */\n get isDocked(): boolean {\n const s = this.status;\n return s === MowerStatus.Charging || s === MowerStatus.ChargingComplete;\n }\n get isMowing(): boolean {\n return this.status === MowerStatus.Mowing;\n }\n get taskStatusRaw(): number | null {\n return this.#num(MOWER_PROP.TASK_STATUS.siid, MOWER_PROP.TASK_STATUS.piid);\n }\n /**\n * TASK_STATUS (5:104) decoded to {@link MowerTaskStatus}, or null. The donor\n * names only code 7 (SpotIncomplete); codes 2/3/10/13 are documented \"Unknown\n * task status\" and intentionally surface as raw via {@link taskStatusRaw}.\n */\n get taskStatus(): MowerTaskStatus | null {\n return TASK_STATUS(this.taskStatusRaw);\n }\n get faultRaw(): number | null {\n return this.#num(MOWER_PROP.DEVICE_CODE.siid, MOWER_PROP.DEVICE_CODE.piid);\n }\n /**\n * DEVICE_CODE (2:2) decoded to a {@link MowerFault} (the donor's\n * BASE_DEVICE_CODES registry, 0..73), or null for a model-specific/undocumented\n * code — which still surfaces as the raw number via {@link faultRaw}.\n */\n get fault(): MowerFault | null {\n return FAULT(this.faultRaw);\n }\n /** Parsed scheduling task descriptor (2:50), or null. */\n get task(): MowerTaskDescriptor | null {\n return parseTaskDescriptor(\n this.getProperty(MOWER_PROP.SCHEDULING_TASK.siid, MOWER_PROP.SCHEDULING_TASK.piid)?.value,\n );\n }\n /**\n * Mowing coverage target / progress signal from the task descriptor (`d.o`).\n * This is the P4 progress surface; the byte-accurate pose-track % is P5.\n */\n get coverageTargetPct(): number | null {\n return this.task?.coverageTarget ?? null;\n }\n /** Parsed per-zone control status (2:56), or null. */\n get controlStatus(): MowerControlState | null {\n return parseControlStatus(\n this.getProperty(MOWER_PROP.MOWER_CONTROL_STATUS.siid, MOWER_PROP.MOWER_CONTROL_STATUS.piid)\n ?.value,\n );\n }\n get controlAction(): MowerControlAction | null {\n return this.controlStatus?.action ?? null;\n }\n\n // -- command helpers ----------------------------------------------------\n #requireCap(flag: boolean, op: string, feature: string): void {\n if (!flag) {\n throw new DreameError(`${op}: model ${this.model} does not support ${feature}`);\n }\n }\n\n /** Send a scheduling-task action (2:50) carrying a single opcode object. */\n #sendTask(payload: Record<string, unknown>): Promise<unknown> {\n return this.callAction(MOWER_PROP.SCHEDULING_TASK.siid, MOWER_PROP.SCHEDULING_TASK.piid, [\n payload,\n ]);\n }\n\n // -- no-arg commands ----------------------------------------------------\n /**\n * Start the generic mowing action (siid 5 aiid 1). Named `startMowing` — NOT\n * `start` — because `BaseDevice.start()` is the MQTT lifecycle method the\n * facade relies on; overriding it would break handle startup.\n */\n startMowing(): Promise<unknown> {\n return this.callAction(MOWER_ACTION.START_MOWING.siid, MOWER_ACTION.START_MOWING.aiid, []);\n }\n pause(): Promise<unknown> {\n return this.callAction(MOWER_ACTION.PAUSE.siid, MOWER_ACTION.PAUSE.aiid, []);\n }\n stop(): Promise<unknown> {\n return this.callAction(MOWER_ACTION.STOP.siid, MOWER_ACTION.STOP.aiid, []);\n }\n /** Send the mower to its dock (siid 5 aiid 3). */\n dock(): Promise<unknown> {\n return this.callAction(MOWER_ACTION.DOCK.siid, MOWER_ACTION.DOCK.aiid, []);\n }\n /**\n * Resume mowing after a pause. Encoded as the continueControl opcode\n * `{m:'a', p:0, o:5}` sent as the single in-param of the SCHEDULING_TASK\n * (2:50) action — NOT a siid-5 action. Mirrors the donor TASK_PAYLOAD_RESUME.\n */\n async resume(): Promise<unknown> {\n this.#requireCap(this.#caps.canResume, 'resume', 'resume');\n return this.#sendTask(buildResumePayload());\n }\n\n // -- targeted mowing ----------------------------------------------------\n /** All-area, map-targeted mowing (2:50 o:100). */\n async startMowingAllArea(mapId: number): Promise<unknown> {\n this.#requireCap(this.#caps.canMowAllArea, 'startMowingAllArea', 'all-area mowing');\n return this.#sendTask(buildAllAreaPayload(Math.trunc(mapId)));\n }\n /** Zone-selective mowing (2:50 o:102). */\n async startMowingZones(zoneIds: number[]): Promise<unknown> {\n this.#requireCap(this.#caps.canMowZones, 'startMowingZones', 'zone mowing');\n if (zoneIds.length === 0) {\n throw new RangeError('startMowingZones: zoneIds must not be empty');\n }\n return this.#sendTask(buildZonePayload(zoneIds.map((z) => Math.trunc(z))));\n }\n /** Edge / contour mowing (2:50 o:101). Contour ids are two-int pairs [[1,0]]. */\n async startMowingEdges(contourIds: number[][]): Promise<unknown> {\n this.#requireCap(this.#caps.canMowEdges, 'startMowingEdges', 'edge mowing');\n if (contourIds.length === 0) {\n throw new RangeError('startMowingEdges: contourIds must not be empty');\n }\n return this.#sendTask(buildEdgePayload(contourIds));\n }\n /** Spot mowing (2:50 o:103). */\n async startMowingSpots(spotAreaIds: number[]): Promise<unknown> {\n this.#requireCap(this.#caps.canMowSpots, 'startMowingSpots', 'spot mowing');\n if (spotAreaIds.length === 0) {\n throw new RangeError('startMowingSpots: spotAreaIds must not be empty');\n }\n return this.#sendTask(buildSpotPayload(spotAreaIds.map((s) => Math.trunc(s))));\n }\n\n /**\n * Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the\n * mower — reads {@link MowerDevice.DEFAULT_PROPS} from the cloud-cached\n * endpoint. After it resolves, every typed getter (status/battery/charging/\n * coverage/task/controlAction…) reflects the cached values, so a docked/\n * standby mower reports its state exactly as the Dreamehome app does.\n */\n async refreshFromCache(): Promise<void> {\n await this.refreshCachedProperties([...MowerDevice.DEFAULT_PROPS]);\n }\n\n // -- maps ---------------------------------------------------------------\n /** The most-recently-parsed map, or `null` until {@link getMap} succeeds. */\n get lastMap(): MowerMap | null {\n return this.#lastMap;\n }\n\n /**\n * Fetch the batched vector-map data, parse it into a {@link MowerMap}, cache\n * it as {@link lastMap}, and return it. Capability-gated on `canMap`.\n *\n * The batch fetcher is injected at construction (the live cloud endpoint path\n * is not yet recovered — see `cloud/commands.ts`). Rejects with\n * {@link DreameError} if no fetcher was injected or the batch yields no\n * parseable map (asleep mower / empty data).\n */\n async getMap(props: readonly string[] = DEFAULT_MAP_PROPS): Promise<MowerMap> {\n this.#requireCap(this.#caps.canMap, 'getMap', 'map parsing');\n if (!this.#fetchBatch) {\n throw new DreameError(\n 'getMap: no batch device-data fetcher injected (live endpoint unrecovered)',\n );\n }\n const batch = await this.#fetchBatch(this.deviceId, [...props]);\n const map = parseBatchMapData(batch);\n if (!map) {\n throw new DreameError('getMap: batch device-data contained no parseable map');\n }\n this.#lastMap = map;\n return map;\n }\n\n /**\n * Render the current map to a deterministic SVG string. Uses {@link lastMap}\n * when present, otherwise fetches first via {@link getMap}.\n */\n async mapSvg(opts?: RenderMowerSvgOptions): Promise<string> {\n const map = this.#lastMap ?? (await this.getMap());\n return renderMowerSvg(map, opts ?? {});\n }\n\n /** Props worth seeding on start() / polling — exported for the facade. */\n static readonly DEFAULT_PROPS = [\n MOWER_PROP.STATUS,\n MOWER_PROP.DEVICE_CODE,\n MOWER_PROP.BATTERY,\n MOWER_PROP.CHARGING_STATUS,\n MOWER_PROP.TASK_STATUS,\n MOWER_PROP.SCHEDULING_TASK,\n MOWER_PROP.MOWER_CONTROL_STATUS,\n ] as const;\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type { DreameDevice, DreameSession } from '../cloud/types.js';\nimport { login as defaultLogin, refresh as defaultRefresh } from '../auth/dreame-account.js';\nimport { listDevices as defaultListDevices } from '../cloud/devices.js';\nimport { BaseDevice } from '../device/base-device.js';\nimport { VacuumDevice } from '../models/vacuum/vacuum-device.js';\nimport { MowerDevice } from '../models/mower/mower-device.js';\nimport { TypedEmitter } from '../transport/typed-emitter.js';\nimport { DreameAuthError } from '../transport/errors.js';\nimport type { FetchImpl } from '../transport/http.js';\nimport type { NodreameOptions, DeviceEvent, StateChangedEvent } from './types.js';\n\nconst DEFAULT_REFRESH_LEEWAY_SECS = 100;\n\n/**\n * Device-type factory: pick the handle class by model prefix. Returns a\n * `BaseDevice` constructor (`VacuumDevice` / `MowerDevice` are subtypes, so the\n * return is covariant and needs no cast).\n */\nexport function deviceClassFor(model: string): typeof BaseDevice {\n if (model.startsWith('dreame.vacuum.')) {\n return VacuumDevice;\n }\n if (model.startsWith('dreame.mower.')) {\n return MowerDevice;\n }\n return BaseDevice;\n}\n\n/** Args the facade passes to `createDevice` (a seam for tests). */\nexport interface CreateDeviceArgs {\n device: DreameDevice;\n region: DreameRegion;\n sessionRef: () => DreameSession;\n}\n\n/** Injectable collaborators — defaults wire the real P1 modules. */\nexport interface NodreameDeps {\n login(input: {\n email: string;\n password: string;\n region: DreameRegion;\n country?: string;\n lang?: string;\n fetchImpl?: FetchImpl;\n }): Promise<DreameSession>;\n refresh(input: {\n refreshToken: string;\n region: DreameRegion;\n country?: string;\n lang?: string;\n fetchImpl?: FetchImpl;\n }): Promise<DreameSession>;\n listDevices(input: {\n session: DreameSession;\n region: DreameRegion;\n fetchImpl?: FetchImpl;\n }): Promise<DreameDevice[]>;\n createDevice(args: CreateDeviceArgs): BaseDevice;\n}\n\nfunction defaultDeps(opts: NodreameOptions): NodreameDeps {\n return {\n login: (input) => defaultLogin(input),\n refresh: (input) => defaultRefresh(input),\n listDevices: (input) => defaultListDevices(input),\n createDevice: (args) => {\n const DeviceClass = deviceClassFor(args.device.model);\n return new DeviceClass({\n device: args.device,\n region: args.region,\n sessionRef: args.sessionRef,\n ...(opts.fetchInitialValues !== undefined\n ? { fetchInitialValues: opts.fetchInitialValues }\n : {}),\n ...(opts.pollIntervalMs !== undefined ? { pollIntervalMs: opts.pollIntervalMs } : {}),\n });\n },\n };\n}\n\nexport type NodreameEvents = {\n /** Re-emitted device state change, tagged with the deviceId. */\n stateChanged: [StateChangedEvent];\n /** Re-emitted device event, tagged with the deviceId. */\n event: [DeviceEvent];\n error: [Error];\n};\n\n/** Public facade for the Dreamehome cloud. */\nexport class Nodreame extends TypedEmitter<NodreameEvents> {\n readonly #opts: NodreameOptions;\n readonly #deps: NodreameDeps;\n readonly #leewayMs: number;\n #session: DreameSession | null = null;\n #devices: BaseDevice[] = [];\n #closed = false;\n\n constructor(opts: NodreameOptions, deps?: NodreameDeps) {\n super();\n if (!opts.username || !opts.password) {\n throw new DreameAuthError('username and password are required');\n }\n this.#opts = opts;\n this.#deps = deps ?? defaultDeps(opts);\n this.#leewayMs = (opts.refreshLeewaySecs ?? DEFAULT_REFRESH_LEEWAY_SECS) * 1000;\n }\n\n get region(): DreameRegion {\n return this.#opts.region;\n }\n\n get session(): DreameSession | null {\n return this.#session;\n }\n\n get devices(): readonly BaseDevice[] {\n return this.#devices;\n }\n\n /** Authenticate and stash the single shared session. */\n async login(): Promise<DreameSession> {\n this.#session = await this.#deps.login({\n email: this.#opts.username,\n password: this.#opts.password,\n region: this.#opts.region,\n ...(this.#opts.country !== undefined ? { country: this.#opts.country } : {}),\n ...(this.#opts.lang !== undefined ? { lang: this.#opts.lang } : {}),\n ...(this.#opts.fetchImpl !== undefined ? { fetchImpl: this.#opts.fetchImpl } : {}),\n });\n return this.#session;\n }\n\n /** Return a valid session, refreshing proactively within the leeway window. */\n async ensureSession(): Promise<DreameSession> {\n const current = this.#session;\n if (!current) {\n return this.login();\n }\n if (Date.now() < current.expiresAt - this.#leewayMs) {\n return current;\n }\n if (current.refreshToken) {\n try {\n const next = await this.#deps.refresh({\n refreshToken: current.refreshToken,\n region: this.#opts.region,\n ...(this.#opts.country !== undefined ? { country: this.#opts.country } : {}),\n ...(this.#opts.lang !== undefined ? { lang: this.#opts.lang } : {}),\n ...(this.#opts.fetchImpl !== undefined ? { fetchImpl: this.#opts.fetchImpl } : {}),\n });\n await this.#adoptSession(next);\n return next;\n } catch {\n // fall through to a full re-login\n }\n }\n const fresh = await this.login();\n await this.#propagateSession(fresh);\n return fresh;\n }\n\n /** Discover devices and build a live handle per device. */\n async discoverDevices(): Promise<readonly BaseDevice[]> {\n if (this.#closed) {\n throw new DreameAuthError('client is closed');\n }\n const session = await this.ensureSession();\n const records = await this.#deps.listDevices({\n session,\n region: this.#opts.region,\n ...(this.#opts.fetchImpl !== undefined ? { fetchImpl: this.#opts.fetchImpl } : {}),\n });\n const handles = records.map((device) =>\n this.#deps.createDevice({\n device,\n region: this.#opts.region,\n sessionRef: () => this.#requireSession(),\n }),\n );\n for (const h of handles) {\n h.on('stateChanged', (e) => this.emit('stateChanged', e));\n h.on('event', (e) => this.emit('event', e));\n h.on('error', (err) => this.emit('error', err));\n await h.start();\n }\n // Close any handles from a previous discovery before adopting the new set —\n // each holds an open MQTT push + a poll timer that would otherwise leak.\n const previous = this.#devices;\n this.#devices = [...handles];\n await Promise.all(previous.map((d) => d.close()));\n return this.#devices;\n }\n\n /** Tear everything down: close every device push and clear timers. */\n async close(): Promise<void> {\n this.#closed = true;\n const devices = this.#devices;\n this.#devices = [];\n await Promise.all(devices.map((d) => d.close()));\n this.removeAllListeners();\n }\n\n #requireSession(): DreameSession {\n if (!this.#session) {\n throw new DreameAuthError('no active session — call login() first');\n }\n return this.#session;\n }\n\n async #adoptSession(session: DreameSession): Promise<void> {\n this.#session = session;\n await this.#propagateSession(session);\n }\n\n /** Push the refreshed token to every live device push. */\n async #propagateSession(session: DreameSession): Promise<void> {\n if (this.#closed) {\n return;\n }\n await Promise.all(this.#devices.map((d) => d.applySession(session)));\n }\n}\n","/**\n * Deep, cast-free anonymization of a value tree before it enters a DeviceDump.\n * Dumps are shared PUBLICLY, so every identity/secret/PII field is replaced with\n * {@link REDACTED}. Matching is by FIELD NAME (case-insensitive, substring) so a\n * `customName`/`accessToken`/`refresh_token` is caught regardless of casing or\n * surrounding context. Non-sensitive data (model, firmware, region, the numeric\n * property keys + their values, enum names) passes through untouched.\n *\n * Immutable: returns NEW objects/arrays; never mutates the input.\n */\n\n/** Placeholder substituted for any redacted value. */\nexport const REDACTED = '[redacted]';\n\n/**\n * Lower-cased field-name fragments that mark a sensitive field. A key matches\n * when its lower-cased form CONTAINS one of these fragments. Ordered roughly by\n * spec §5 grouping (identity/secrets, then location/PII, then free-text names).\n */\nconst SENSITIVE_KEY_FRAGMENTS: readonly string[] = [\n // identity / secrets\n 'did',\n 'uid',\n 'token', // accessToken, refreshToken, refresh_token, token_type stripped too (safe)\n 'mac',\n 'serial',\n 'email',\n 'account',\n 'password',\n 'passwd',\n 'secret',\n 'authorization',\n 'auth',\n 'credential',\n 'apikey',\n 'api_key',\n 'clientid',\n 'client_id',\n // location / PII\n 'gps',\n 'coordinate',\n 'latitude',\n 'longitude',\n 'lat',\n 'lon',\n 'lng',\n 'ssid',\n 'wifi',\n 'bssid',\n 'ipaddr',\n 'localip',\n 'binddomain',\n 'host',\n 'address',\n 'room',\n 'area_name',\n 'areaname',\n 'segmentname',\n 'segment_name',\n // map binary / geometry (location-revealing)\n 'map_info',\n 'mapinfo',\n 'mapblob',\n // free-text device names that may carry PII. NOTE: the bare fragment `name` is\n // intentionally NOT listed — it would over-match the catalog's command `name`\n // field (an enum-derived, non-sensitive label). The specific custom-name\n // fields below cover every PII-bearing case.\n 'customname',\n 'devicename',\n 'nickname',\n];\n\n/** Bare `ip` is matched exactly (substring-`ip` would over-match e.g. `equip`). */\nconst EXACT_SENSITIVE_KEYS: ReadonlySet<string> = new Set(['ip']);\n\nfunction isSensitiveKey(key: string): boolean {\n const lower = key.toLowerCase();\n if (EXACT_SENSITIVE_KEYS.has(lower)) {\n return true;\n }\n return SENSITIVE_KEY_FRAGMENTS.some((frag) => lower.includes(frag));\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/** Recursively scrub a value. Sensitive KEYS short-circuit to {@link REDACTED}. */\nexport function redact(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((v) => redact(v));\n }\n if (isPlainObject(value)) {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n out[k] = isSensitiveKey(k) ? REDACTED : redact(v);\n }\n return out;\n }\n return value;\n}\n","import { z } from 'zod';\n\n/**\n * The SHARED diagnostic-dump format (spec §7). `nodedreame` and `nodewitt` fill\n * the SAME shape so dumps are diffable. {@link DeviceDump} is the public output\n * type; {@link DeviceDumpSchema} validates a built dump before export.\n *\n * `library` is a union for the shared format; this library always emits\n * `'nodedreame'`. The schema is the single source of truth — {@link DeviceDump}\n * is `z.infer` of it, so the runtime validator and the public type never drift.\n */\n\n/** A raw MIoT property value as it appears in a dump (always JSON-scalar). */\nexport type DumpScalar = string | number | boolean;\n\nconst DumpScalarSchema = z.union([z.string(), z.number(), z.boolean()]);\n\nconst PropertyObservationSchema = z.object({\n values: z.array(DumpScalarSchema),\n unmapped: z.array(DumpScalarSchema),\n enum: z.string().optional(),\n count: z.number(),\n firstSeen: z.number(),\n lastSeen: z.number(),\n});\n\nconst EventObservationSchema = z.object({\n at: z.number(),\n type: z.string(),\n data: z.unknown().optional(),\n});\n\nconst RawFrameSchema = z.object({\n at: z.number(),\n source: z.string(),\n payload: z.unknown(),\n});\n\nconst CommandSchema = z.object({\n name: z.string(),\n siid: z.number().optional(),\n aiid: z.number().optional(),\n});\n\nconst SensorSchema = z.object({\n model: z.string(),\n channel: z.number().optional(),\n});\n\nexport const DeviceDumpSchema = z.object({\n schemaVersion: z.literal(1),\n library: z.union([z.literal('nodedreame'), z.literal('nodewitt')]),\n libraryVersion: z.string(),\n device: z.object({\n model: z.string(),\n firmware: z.string().optional(),\n region: z.string().optional(),\n type: z.string().optional(),\n }),\n observations: z.object({\n properties: z.record(z.string(), PropertyObservationSchema),\n events: z.array(EventObservationSchema),\n rawFrames: z.array(RawFrameSchema).optional(),\n }),\n catalog: z.object({\n commands: z.array(CommandSchema).optional(),\n capabilities: z.record(z.string(), z.unknown()).optional(),\n sensors: z.array(SensorSchema).optional(),\n }),\n meta: z.object({\n startedAt: z.number(),\n durationMs: z.number(),\n generatedAt: z.number(),\n }),\n});\n\n/** Public shared dump type — inferred from the schema so the two never drift. */\nexport type DeviceDump = z.infer<typeof DeviceDumpSchema>;\n","import { BaseDevice } from '../device/base-device.js';\nimport { VacuumDevice } from '../models/vacuum/vacuum-device.js';\nimport { MowerDevice } from '../models/mower/mower-device.js';\nimport { VACUUM_ACTION } from '../models/vacuum/properties.js';\nimport { MOWER_ACTION } from '../models/mower/properties.js';\nimport { enumLookup } from '../models/_shared/decode.js';\nimport {\n ChargingStatus,\n CleaningMode,\n MiotState,\n SuctionLevel,\n TaskStatus,\n WaterVolume,\n} from '../models/vacuum/enums.js';\nimport {\n MowerChargingStatus,\n MowerFault,\n MowerStatus,\n MowerTaskStatus,\n} from '../models/mower/enums.js';\nimport { redact } from './redact.js';\nimport { LIBRARY_VERSION } from '../support/version.js';\nimport type { DeviceEvent, PropertyChangedEvent } from '../api/types.js';\nimport type { Nodreame } from '../api/nodreame.js';\nimport { DeviceDumpSchema, type DeviceDump } from './dump-format.js';\n\n/** The `catalog` slice of a {@link DeviceDump}. */\ntype DumpCatalog = DeviceDump['catalog'];\ntype DumpCommand = NonNullable<DumpCatalog['commands']>[number];\n\n/**\n * Static command list for a device family, read from the model's `*_ACTION` map.\n * Cast-free: the maps are plain records of `{ siid, aiid }`. NEVER executes one.\n */\nfunction commandsForFamily(family: 'vacuum' | 'mower' | 'device'): DumpCommand[] {\n const commands: DumpCommand[] = [];\n if (family === 'vacuum') {\n for (const [name, ref] of Object.entries(VACUUM_ACTION)) {\n commands.push({ name, siid: ref.siid, aiid: ref.aiid });\n }\n } else if (family === 'mower') {\n for (const [name, ref] of Object.entries(MOWER_ACTION)) {\n commands.push({ name, siid: ref.siid, aiid: ref.aiid });\n }\n }\n return commands;\n}\n\n/**\n * Build the STATIC catalog for a device: its declared command/action map +\n * resolved capability tokens. Reads only static maps + the capability getter —\n * NEVER invokes an action.\n */\nexport function buildCatalog(device: BaseDevice): DumpCatalog {\n const family =\n device instanceof VacuumDevice ? 'vacuum' : device instanceof MowerDevice ? 'mower' : 'device';\n return {\n commands: commandsForFamily(family),\n capabilities: { tokens: [...device.capabilities.list()] },\n };\n}\n\n/** A property decoder: the enum NAME + a membership test for a raw number. */\ninterface KeyDecoder {\n enumName: string;\n isMember(raw: number): boolean;\n}\n\nfunction decoderFor<E extends number>(enumName: string, members: readonly E[]): KeyDecoder {\n const lookup = enumLookup<E>(members);\n return { enumName, isMember: (raw) => lookup(raw) !== null };\n}\n\n/**\n * Per-property decoder table for the VACUUM family, keyed by `\"siid.piid\"`.\n * Mirrors the `enumLookup` wiring the vacuum device uses for its `*Raw`/decoded\n * getters. Members are extracted cast-free via the predicate `filter` (identical\n * to `vacuum-device.ts`), so a raw number is never branded with a banned cast.\n */\nexport function vacuumDecoders(): Record<string, KeyDecoder> {\n return {\n '2.1': decoderFor(\n 'MiotState',\n Object.values(MiotState).filter((v): v is MiotState => typeof v === 'number'),\n ),\n '3.2': decoderFor(\n 'ChargingStatus',\n Object.values(ChargingStatus).filter((v): v is ChargingStatus => typeof v === 'number'),\n ),\n '4.4': decoderFor(\n 'SuctionLevel',\n Object.values(SuctionLevel).filter((v): v is SuctionLevel => typeof v === 'number'),\n ),\n '4.5': decoderFor(\n 'WaterVolume',\n Object.values(WaterVolume).filter((v): v is WaterVolume => typeof v === 'number'),\n ),\n '2.6': decoderFor(\n 'CleaningMode',\n Object.values(CleaningMode).filter((v): v is CleaningMode => typeof v === 'number'),\n ),\n '4.1': decoderFor(\n 'TaskStatus',\n Object.values(TaskStatus).filter((v): v is TaskStatus => typeof v === 'number'),\n ),\n };\n}\n\n/**\n * Per-property decoder table for the MOWER family, keyed by `\"siid.piid\"`.\n * Note vacuum and mower BOTH use keys `2.1`/`3.2` for DIFFERENT enums, so the\n * family-correct table MUST be injected — there is no shared default.\n */\nexport function mowerDecoders(): Record<string, KeyDecoder> {\n return {\n '2.1': decoderFor(\n 'MowerStatus',\n Object.values(MowerStatus).filter((v): v is MowerStatus => typeof v === 'number'),\n ),\n '3.2': decoderFor(\n 'MowerChargingStatus',\n Object.values(MowerChargingStatus).filter(\n (v): v is MowerChargingStatus => typeof v === 'number',\n ),\n ),\n '5.104': decoderFor(\n 'MowerTaskStatus',\n Object.values(MowerTaskStatus).filter((v): v is MowerTaskStatus => typeof v === 'number'),\n ),\n '2.2': decoderFor(\n 'MowerFault',\n Object.values(MowerFault).filter((v): v is MowerFault => typeof v === 'number'),\n ),\n };\n}\n\ntype DumpScalar = string | number | boolean;\ntype PropertyObservation = DeviceDump['observations']['properties'][string];\n\nfunction toScalar(value: unknown): DumpScalar | null {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n return value;\n }\n return null;\n}\n\ninterface AccEntry {\n values: DumpScalar[];\n unmapped: DumpScalar[];\n count: number;\n firstSeen: number;\n lastSeen: number;\n}\n\n/**\n * Accumulates per-property `(siid,piid)` → `\"siid.piid\"` observations: the\n * DISTINCT raw values seen (insertion order), a total `count`, first/last seen\n * timestamps, the mapped `enum` name (when a decoder is injected for the key),\n * and the subset of numeric values that DECODE TO NO KNOWN ENUM (`unmapped` —\n * the core crowd-sourcing signal). The family-correct decoder table is injected\n * at construction; a bare accumulator (no decoders) records values but never\n * flags unmapped.\n */\nexport class PropertyAccumulator {\n readonly #decoders: Record<string, KeyDecoder>;\n readonly #entries = new Map<string, AccEntry>();\n\n constructor(decoders: Record<string, KeyDecoder> = {}) {\n this.#decoders = decoders;\n }\n\n record(siid: number, piid: number, rawValue: unknown, at: number): void {\n const key = `${siid}.${piid}`;\n const scalar = toScalar(rawValue);\n let entry = this.#entries.get(key);\n if (!entry) {\n entry = { values: [], unmapped: [], count: 0, firstSeen: at, lastSeen: at };\n this.#entries.set(key, entry);\n }\n entry.count += 1;\n entry.lastSeen = at;\n if (scalar === null) {\n return; // non-scalar (object/array/null) — recorded as an observation count only\n }\n if (!entry.values.includes(scalar)) {\n entry.values.push(scalar);\n }\n const decoder = this.#decoders[key];\n if (decoder && typeof scalar === 'number' && !decoder.isMember(scalar)) {\n if (!entry.unmapped.includes(scalar)) {\n entry.unmapped.push(scalar);\n }\n }\n }\n\n snapshot(): Record<string, PropertyObservation> {\n const out: Record<string, PropertyObservation> = {};\n for (const [key, e] of this.#entries) {\n const decoder = this.#decoders[key];\n const base: PropertyObservation = {\n values: [...e.values],\n unmapped: [...e.unmapped],\n count: e.count,\n firstSeen: e.firstSeen,\n lastSeen: e.lastSeen,\n };\n out[key] = decoder ? { ...base, enum: decoder.enumName } : base;\n }\n return out;\n }\n}\n\n/** Tunables for a {@link Dumper}. All optional. */\nexport interface DumperOptions {\n /** Capture raw event/property frames into `observations.rawFrames`. Default false. */\n captureRawFrames?: boolean;\n /** Cap on retained raw frames (ring). Default 500. */\n maxRawFrames?: number;\n /** Periodic cloud-shadow refresh interval (ms). Default 30000; 0 disables. */\n refreshIntervalMs?: number;\n}\n\n/**\n * The READ-ONLY slice of a device the {@link Dumper} consumes. Declaring it as\n * an interface lets tests inject a fake with no cast (mirrors `PushLike`). A real\n * `BaseDevice`/`VacuumDevice`/`MowerDevice` structurally satisfies it. NOTE it\n * exposes NO action/command method — the dumper literally cannot execute one.\n */\nexport interface DumperDevice {\n readonly model: string;\n readonly deviceId: string;\n readonly capabilities: { list(): readonly string[]; has(t: string): boolean; model: string };\n on(event: 'propertyChanged', cb: (e: PropertyChangedEvent) => void): this;\n on(event: 'event', cb: (e: DeviceEvent) => void): this;\n off(event: 'propertyChanged', cb: (e: PropertyChangedEvent) => void): this;\n off(event: 'event', cb: (e: DeviceEvent) => void): this;\n /** Cloud-shadow read; absent on a bare BaseDevice. Feature-detected. */\n refreshFromCache?(): Promise<void>;\n}\n\nconst DEFAULT_REFRESH_INTERVAL_MS = 30000;\nconst DEFAULT_MAX_RAW_FRAMES = 500;\n\ntype RawFrame = NonNullable<DeviceDump['observations']['rawFrames']>[number];\n\n/**\n * A passive, READ-ONLY observer that records what a Dreame device exposes while\n * it operates. {@link start} hooks the device's `propertyChanged`/`event` stream,\n * does an initial + periodic cloud-shadow {@link DumperDevice.refreshFromCache}\n * read (feature-detected), and snapshots the static catalog at construction.\n * {@link stop} detaches every listener + clears the timer (idempotent, no leaks).\n * {@link export} assembles the ANONYMIZED, zod-valid {@link DeviceDump}. The\n * dumper NEVER executes a device command/action.\n */\nexport class Dumper {\n readonly #device: DumperDevice;\n readonly #opts: { captureRawFrames: boolean; maxRawFrames: number; refreshIntervalMs: number };\n readonly #acc: PropertyAccumulator;\n readonly #events: DeviceDump['observations']['events'] = [];\n readonly #rawFrames: RawFrame[] = [];\n readonly #catalog: DeviceDump['catalog'];\n readonly #type: string;\n readonly #onProperty: (e: PropertyChangedEvent) => void;\n readonly #onEvent: (e: DeviceEvent) => void;\n #timer: ReturnType<typeof setInterval> | null = null;\n #startedAt = 0;\n #started = false;\n\n constructor(\n device: DumperDevice,\n catalog: DeviceDump['catalog'],\n type: string,\n decoders: Record<string, KeyDecoder>,\n options: DumperOptions = {},\n ) {\n this.#device = device;\n this.#catalog = catalog;\n this.#type = type;\n this.#acc = new PropertyAccumulator(decoders);\n this.#opts = {\n captureRawFrames: options.captureRawFrames ?? false,\n maxRawFrames: options.maxRawFrames ?? DEFAULT_MAX_RAW_FRAMES,\n refreshIntervalMs: options.refreshIntervalMs ?? DEFAULT_REFRESH_INTERVAL_MS,\n };\n // Bound listeners stored once so `off()` removes the EXACT same reference.\n this.#onProperty = (e): void => {\n this.#acc.record(e.siid, e.piid, e.value, Date.now());\n this.#pushRaw('mqtt:property', e);\n };\n this.#onEvent = (e): void => {\n this.#events.push({\n at: Date.now(),\n type: `${e.siid}.${e.eiid}`,\n data: { arguments: e.arguments },\n });\n this.#pushRaw('mqtt:event', e);\n };\n }\n\n /** Attach to the live stream + arm the periodic shadow refresh. Idempotent. */\n async start(): Promise<void> {\n if (this.#started) {\n return;\n }\n this.#started = true;\n this.#startedAt = Date.now();\n this.#device.on('propertyChanged', this.#onProperty);\n this.#device.on('event', this.#onEvent);\n await this.#refresh();\n if (this.#opts.refreshIntervalMs > 0 && this.#device.refreshFromCache) {\n this.#timer = setInterval(() => void this.#refresh(), this.#opts.refreshIntervalMs);\n this.#timer.unref?.();\n }\n }\n\n /** Detach every listener + clear the timer. Idempotent. */\n stop(): Promise<void> {\n if (!this.#started) {\n return Promise.resolve();\n }\n this.#started = false;\n if (this.#timer) {\n clearInterval(this.#timer);\n this.#timer = null;\n }\n this.#device.off('propertyChanged', this.#onProperty);\n this.#device.off('event', this.#onEvent);\n return Promise.resolve();\n }\n\n /** Build the anonymized, schema-valid dump. Works live or after {@link stop}. */\n export(): DeviceDump {\n const now = Date.now();\n const dump: DeviceDump = {\n schemaVersion: 1,\n library: 'nodedreame',\n libraryVersion: LIBRARY_VERSION,\n device: {\n model: this.#device.model,\n type: this.#type,\n },\n observations: {\n properties: this.#acc.snapshot(),\n events: [...this.#events],\n ...(this.#opts.captureRawFrames ? { rawFrames: this.#redactFrames() } : {}),\n },\n catalog: this.#catalog,\n meta: {\n startedAt: this.#startedAt,\n durationMs: Math.max(0, now - this.#startedAt),\n generatedAt: now,\n },\n };\n // redact(dump) returns `unknown`; DeviceDumpSchema.parse re-types it back to\n // DeviceDump with ZERO cast (Zod's parse return type IS DeviceDump).\n const anonymized = redact(dump);\n return DeviceDumpSchema.parse(anonymized);\n }\n\n /** Deterministic pretty JSON of {@link export}. */\n exportJson(): string {\n return JSON.stringify(this.export(), null, 2);\n }\n\n async #refresh(): Promise<void> {\n if (this.#device.refreshFromCache) {\n try {\n await this.#device.refreshFromCache();\n } catch {\n // read-only best-effort — a shadow-read failure must never crash a dump.\n }\n }\n }\n\n #pushRaw(source: string, payload: unknown): void {\n if (!this.#opts.captureRawFrames) {\n return;\n }\n this.#rawFrames.push({ at: Date.now(), source, payload });\n if (this.#rawFrames.length > this.#opts.maxRawFrames) {\n this.#rawFrames.shift();\n }\n }\n\n #redactFrames(): RawFrame[] {\n return this.#rawFrames.map((f) => ({ at: f.at, source: f.source, payload: redact(f.payload) }));\n }\n}\n\n/**\n * The device family. A real device is classified by `instanceof`; a non-device\n * target (e.g. a test fake) falls back to its `model` prefix so the\n * family-correct decoder table/type still applies. Unknown → `'device'`.\n */\nfunction familyOf(device: DumperDevice): 'vacuum' | 'mower' | 'device' {\n if (device instanceof VacuumDevice) {\n return 'vacuum';\n }\n if (device instanceof MowerDevice) {\n return 'mower';\n }\n if (!(device instanceof BaseDevice)) {\n if (device.model.startsWith('dreame.vacuum.')) {\n return 'vacuum';\n }\n if (device.model.startsWith('dreame.mower.')) {\n return 'mower';\n }\n }\n return 'device';\n}\n\nfunction decodersFor(device: DumperDevice): Record<string, KeyDecoder> {\n switch (familyOf(device)) {\n case 'vacuum':\n return vacuumDecoders();\n case 'mower':\n return mowerDecoders();\n default:\n return {};\n }\n}\n\nfunction typeFor(device: DumperDevice): string {\n return familyOf(device);\n}\n\nfunction catalogFor(device: DumperDevice): DeviceDump['catalog'] {\n // Family-based: a real device is classified by `instanceof` inside familyOf, a\n // test fake by model prefix. Equivalent to buildCatalog for a real BaseDevice,\n // but avoids re-narrowing to the generic BaseDevice<any>.\n return {\n commands: commandsForFamily(familyOf(device)),\n capabilities: { tokens: [...device.capabilities.list()] },\n };\n}\n\n/**\n * Create a read-only {@link Dumper} for one device. The family-correct decoder\n * table + catalog + `device.type` are chosen via cast-free `instanceof` checks;\n * an unknown family records values with no enum/unmapped detection.\n */\nexport function createDumper(target: DumperDevice, options?: DumperOptions): Dumper {\n return new Dumper(\n target,\n catalogFor(target),\n typeFor(target),\n decodersFor(target),\n options ?? {},\n );\n}\n\n/**\n * Fan-out: one {@link Dumper} per discovered device on a {@link Nodreame} client\n * (read-only). Each `BaseDevice` structurally satisfies {@link DumperDevice}.\n */\nexport function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[] {\n return client.devices.map((d) => createDumper(d, options));\n}\n"],"mappings":"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","names":["MODEL_CAPABILITIES","undiciFetch","import_zod","mqtt","key","MiotState","ChargingStatus","SuctionLevel","WaterVolume","CleaningMode","MiotError","TaskStatus","import_node_crypto","key","key","z","MowerStatus","MowerChargingStatus","MowerControlAction","MowerTaskStatus","MowerFault","FALLBACK","deepFreeze","MODEL_CAPABILITIES","tokensFor","isRecord","key","CHARGING","z","key","import_zod","key"]}