@apocaliss92/nodedreame 1.12.2 → 1.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +136 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +140 -45
- package/dist/index.d.ts +140 -45
- package/dist/index.js +134 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -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/connectivity.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/ai-detection.ts","../src/models/vacuum/auto-switch.ts","../src/models/vacuum/consumables.ts","../src/models/vacuum/capabilities.ts","../src/video/schemas.ts","../src/video/profile.ts","../src/video/client.ts","../src/video/aliyun/constants.ts","../src/video/aliyun/identity.ts","../src/video/aliyun/signing.ts","../src/video/aliyun/vision.ts","../src/video/aliyun/session.ts","../src/video/camera-availability.ts","../src/video/monitor/constants.ts","../src/video/monitor/protocol.ts","../src/video/monitor/controller.ts","../src/models/vacuum/map/envelope.ts","../src/models/vacuum/map/header.ts","../src/models/vacuum/map/tail.ts","../src/models/vacuum/map/segment-types.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/merge.ts","../src/models/vacuum/map/decode.ts","../src/models/vacuum/map/render.ts","../src/models/vacuum/map/wifi.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","../src/video/monitor/detections.ts","../src/video/media/lv-rtmp-client.ts","../src/video/media/amf0.ts","../src/video/media/chunk.ts","../src/video/media/flv.ts","../src/video/media/g711.ts","../src/video/media/camera-stream.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 { isAuthRefusedError } from './transport/mqtt-push.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, WifiSignalMapInput } 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';\nexport {\n AI_FEATURE_BIT,\n AI_FEATURE_JSON_KEY,\n decodeAiFeature,\n encodeAiFeatureWrite,\n} from './models/vacuum/ai-detection.js';\nexport type { DreameAiFeature, AiDetectionRaw } from './models/vacuum/ai-detection.js';\nexport {\n AUTO_SWITCH_JSON_KEY,\n decodeAutoSwitch,\n decodeAutoSwitchAll,\n supportedAutoSwitchKeys,\n encodeAutoSwitchWrite,\n} from './models/vacuum/auto-switch.js';\nexport type { AutoSwitchKey, AutoSwitchRaw } from './models/vacuum/auto-switch.js';\nexport {\n VACUUM_CONSUMABLES,\n consumableSpec,\n isDreameConsumableKey,\n} from './models/vacuum/consumables.js';\nexport type {\n ConsumableSpec,\n ConsumableReading,\n DreameConsumableKey,\n} from './models/vacuum/consumables.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 mowerFaultSeverity,\n} from './models/mower/enums.js';\nexport type { MowerFaultSeverity } 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// CMS consumables (blade/brush/maintenance) — typed readings + the parser, so\n// camstack can surface them on the `consumables` cap.\nexport {\n parseMowerConsumables,\n extractMowerConsumableValues,\n mowerConsumableIndex,\n parseMowerHeartbeat,\n parseMowingProgress,\n MOWER_TASK_SUBSTATES,\n} from './models/mower/decode.js';\nexport type { MowerMowingProgress } from './models/mower/decode.js';\nexport type {\n MowerConsumableKey,\n MowerConsumableReading,\n MowerHeartbeat,\n MowerTaskSubState,\n} 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, MapColorScheme } 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\n// --- Video control-plane (X40/X50 camera devices) ------------------------\nexport {\n getVideoAccessToken,\n getAliyunAuthCode,\n getVideoFamilyId,\n getDeviceVideoProfile,\n explainNoCameraChannel,\n toVideoProfile,\n} from './video/index.js';\nexport type {\n VideoRequestInput,\n VideoVendor,\n VideoAccessToken,\n DeviceVideoProfile,\n} from './video/index.js';\n\n// --- Aliyun LinkVisual media plane -----------------------------------------\nexport {\n DreameVideoSession,\n streamQuery,\n loginByOauth,\n createIotSession,\n signApiGatewayRequest,\n ALIYUN_REGION_ID,\n} from './video/index.js';\nexport type {\n DreameVideoSessionInput,\n StreamInfo,\n StreamQueryInput,\n OaSession,\n IotSession,\n} from './video/index.js';\n\n// --- Camera monitor control-plane + autonomous cold-start ------------------\nexport {\n DreameCameraController,\n MONITOR_SIID,\n MONITOR_AIID,\n MONITOR_PIID,\n MONITOR_VENDOR_TOKEN,\n VACUUM_SIID,\n VACUUM_MOVE,\n PET_SOUNDS,\n VACUUM_ACTIONS,\n makeMonitorSession,\n hashAccessCode,\n parsePersonFollow,\n parseObstacleData,\n remoteDriveValue,\n DRIVE_DIRECTIONS,\n} from './video/index.js';\nexport type {\n DreameCameraControllerInput,\n CameraStreamHandle,\n MonitorActionCaller,\n RelayMinter,\n DetectionBox,\n PersonFollowDetection,\n ObstacleDetection,\n DriveDirection,\n PetSound,\n VacuumActionKey,\n} from './video/index.js';\n\n// --- Camera media pipeline (RTMP relay -> H.264/AAC frames) -----------------\nexport {\n DreameCameraStream,\n LvRtmpClient,\n parseRelayUrl,\n LV_STATUS,\n TALK_AUDIO_HEADER_G711A,\n pcm16ToALaw,\n pcm16leToALaw,\n avccToAnnexB,\n aacToAdts,\n parseAvcConfig,\n parseAacConfig,\n aacSampleRate,\n} from './video/index.js';\nexport type {\n DreameCameraStreamInput,\n DreameCameraStreamEvents,\n FrameSource,\n FrameSourceFactory,\n LvRtmpClientOptions,\n VideoAccessUnitEvent,\n AudioInfoEvent,\n StatusEvent,\n AvcConfig,\n AacConfig,\n} from './video/index.js';\n\n// --- Raw device-list access (connectivity/diagnostics) --------------------\nexport { listDevices } from './cloud/devices.js';\nexport type { ListDevicesInput } from './cloud/devices.js';\nexport { parseConnectivity } from './cloud/connectivity.js';\nexport type { DeviceConnectivity, BrokerEndpoint } from './cloud/connectivity.js';\n\n// --- WiFi signal map (coverage heatmap + current-signal at robot pose) -----\nexport { decodeWifiSignalMap, renderWifiSignalPng } from './models/vacuum/map/index.js';\nexport type { WifiSignalMap, RenderWifiSignalPngOptions } from './models/vacuum/map/index.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.12.2';\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 * Upper bound for the sendCommand `id`. The Dreame cloud's device-side\n * correlation id is a NARROW integer field: verified live on\n * `dreame.mower.p2255` that ids up to 2^24 (16_777_216) round-trip fine but ids\n * ≳ 1e8 make the cloud return code 80001 \"device offline / 指令发送超时\" (the\n * oversized id never reaches/echoes from the device, so the cloud reports it\n * unreachable). A 31-bit random id therefore broke EVERY mower command. Keep ids\n * comfortably under that ceiling.\n */\nconst MAX_REQUEST_ID = 0xffffff;\n\n/** Monotonic counter, seeded with a small random base (mirrors the Dreame app's\n * `random(1,100)` start). Module-scoped so concurrent fan-out never collides. */\nlet nextRequestId = Math.floor(Math.random() * 100) + 1;\n\n/**\n * Next `id` for a sendCommand envelope — a small, monotonically increasing\n * integer that wraps under {@link MAX_REQUEST_ID}. MUST stay small: see\n * {@link MAX_REQUEST_ID} for why a large id makes the cloud 80001.\n */\nexport function randomRequestId(): number {\n nextRequestId = nextRequestId >= MAX_REQUEST_ID ? 1 : nextRequestId + 1;\n return nextRequestId;\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().nullish(),\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().nullish(),\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().nullish(),\n data: z.array(CachedPropEntrySchema).optional(),\n })\n .passthrough();\nexport type CachedPropsResponse = z.infer<typeof CachedPropsResponseSchema>;\n\n/**\n * Batched device-data read response (`dreame-user-iot/iotuserdata/getDeviceData`).\n * `data` is a flat dict of hierarchical keys (`MAP.0`, `MAP.info`, `M_PATH.0`,\n * `SETTINGS.*`, `SCHEDULE.*`, …) → string/number chunk values. Permissive: the\n * exact key set is device/firmware-specific; on an error code `data` may be\n * absent entirely.\n */\nexport const BatchDeviceDataResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z.record(z.string(), z.unknown()).optional(),\n })\n .passthrough();\nexport type BatchDeviceDataResponse = z.infer<typeof BatchDeviceDataResponseSchema>;\n\nexport const SendCommandResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\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","/**\n * Connectivity / diagnostic info distilled from a device-list record.\n *\n * Scope note: the Dreame cloud device record exposes reachability + identity +\n * the assigned MQTT broker, but NOT Wi-Fi RSSI / SSID / local IP — those are\n * native-firmware (miIO-style) details the cloud API does not return for these\n * vacuums. So `signalStrength`/`ssid`/`ip` are intentionally absent here; this\n * surfaces everything the cloud actually provides.\n */\n\n/** The device's assigned MQTT broker endpoint (from `bindDomain`). */\nexport interface BrokerEndpoint {\n readonly host: string;\n readonly port: number | null;\n}\n\nexport interface DeviceConnectivity {\n /** Whether the cloud considers the device online (LWT/online flag). */\n readonly online: boolean;\n /** Physical link type reported by the cloud (e.g. `\"WIFI\"`), or null. */\n readonly connectionType: string | null;\n /** Device MAC address, or null. */\n readonly mac: string | null;\n /** Assigned MQTT broker (`bindDomain` split into host/port), or null. */\n readonly broker: BrokerEndpoint | null;\n /** Cloud region the device is bound to (e.g. `\"eu\"`), or null. */\n readonly region: string | null;\n /** Cloud media/IoT vendor (e.g. `\"ali\"`), or null. */\n readonly cloudVendor: string | null;\n /** Firmware version (`ver`), or null. */\n readonly firmwareVersion: string | null;\n /** Serial number (`sn`), or null. */\n readonly serialNumber: string | null;\n /** Sub-model code, or null. */\n readonly subModel: string | null;\n /** Battery percentage 0–100 from the cloud snapshot, or null. */\n readonly battery: number | null;\n /** Latest MIoT status int from the cloud snapshot, or null. */\n readonly statusCode: number | null;\n}\n\nconst str = (v: unknown): string | null => (typeof v === 'string' && v.length > 0 ? v : null);\nconst numOrNull = (v: unknown): number | null => (typeof v === 'number' && Number.isFinite(v) ? v : null);\n\n/** Split a `bindDomain` like `\"10000.mt.eu.iot.dreame.tech:19973\"` into host/port. */\nfunction parseBroker(bindDomain: unknown): BrokerEndpoint | null {\n const s = str(bindDomain);\n if (!s) return null;\n const idx = s.lastIndexOf(':');\n if (idx < 0) return { host: s, port: null };\n const port = Number(s.slice(idx + 1));\n return { host: s.slice(0, idx), port: Number.isFinite(port) ? port : null };\n}\n\n/** Parse the connectivity view from a raw device-list record. */\nexport function parseConnectivity(raw: Record<string, unknown>): DeviceConnectivity {\n const deviceInfo = (raw['deviceInfo'] ?? {}) as Record<string, unknown>;\n return {\n online: raw['online'] === true,\n connectionType: str(deviceInfo['scType']),\n mac: str(raw['mac']),\n broker: parseBroker(raw['bindDomain']),\n region: str(raw['region']),\n cloudVendor: str(raw['vendor']),\n firmwareVersion: str(raw['ver']),\n serialNumber: str(raw['sn']),\n subModel: str(raw['subModel']),\n battery: numOrNull(raw['battery']),\n statusCode: numOrNull(raw['latestStatus']),\n };\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';\nimport { parseConnectivity } from './connectivity.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 connectivity: parseConnectivity(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 BatchDeviceDataResponseSchema,\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.*` chunk\n * keys, plus `SETTINGS.*` / `SCHEDULE.*` / `OTA_INFO.*` groups) for a set of\n * property groups.\n *\n * Hits `dreame-user-iot/iotuserdata/getDeviceData` — the SAME account-auth host\n * as {@link getCachedProperties} (NOT the `device/sendCommand` envelope), so it\n * resolves the cloud-stored data for a standby/sleeping robot. Body is\n * `{ did, model: props }` where `model` (the firmware's spelling) carries the\n * requested key groups (e.g. `['MAP','M_PATH']`); an empty list returns every\n * group. The response `data` is a flat dict of `KEY.idx` chunk values that\n * {@link import('../models/mower/map/parser.js').parseBatchMapData} reassembles.\n *\n * Endpoint recovered from the Tasshack `dreame-vacuum` protocol table\n * (`_strings[23]/[26]/[44]` = `dreame-user-iot/iotuserdata/getDeviceData`) and\n * the donor `antondaubert/dreame-mower` device-data analysis tool.\n */\nexport async function getBatchDeviceDatas(\n base: CommonInput,\n props: string[],\n opts: CallOptions = {},\n): Promise<Record<string, unknown>> {\n const ctx = base.ctx ?? RequestContext.from({ ...base, host: base.apiHost });\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/iotuserdata/getDeviceData',\n accessToken: base.session.accessToken,\n body: { did: base.did, model: props },\n context: 'batch device data',\n ...(signal !== undefined ? { signal } : {}),\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n\n const parsed = BatchDeviceDataResponseSchema.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 `batch device data rejected: code=${parsed.code} msg=${parsed.msg ?? '?'}`,\n 200,\n parsed,\n );\n }\n return parsed.data ?? {};\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 /** MQTT-level keep-alive in seconds: the client PINGREQs the broker this often\n * so an idle connection is held open (and a dead one detected) at the protocol\n * layer — the connection's keep-alive belongs here, in the library, not in a\n * consumer-side property poll. */\n keepalive: number;\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 /** MQTT keep-alive (PINGREQ cadence) in seconds. Default 60. `0` disables. */\n keepaliveSeconds?: 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 * Called when the broker refuses a CONNECT with an auth error — a CONNACK\n * whose return code is 5 (\"Not authorized\") or 4 (\"Bad username or password\")\n * on MQTT 3.1.1 (135/134 on MQTT 5). This happens when the access token the\n * push connects with has gone stale server-side. The callback MUST resolve to\n * a freshly-minted session; the push adopts its token and reconnects with it\n * instead of hopelessly replaying the stale one. Wired by the facade to\n * `Nodreame.reauthenticate()`. When omitted, an auth-refused reconnect falls\n * back to the plain backoff loop (legacy behaviour).\n */\n onAuthFailure?: () => Promise<DreameSession>;\n /**\n * Maximum number of CONSECUTIVE auth-refused re-authentications before the\n * push stops re-logging-in and falls back to a plain backoff reconnect. The\n * counter resets to 0 on the next successful connect. Guards against a tight\n * re-login loop when the credentials are genuinely bad. Default 3.\n *\n * NOTE: this cap is PER-PUSH, so a fleet of N devices could in principle make\n * up to `maxAuthRetries * N` re-auth calls on an account-wide invalidation. In\n * practice the facade bounds this: `Nodreame.reauthenticate()` coalesces\n * concurrent refreshes into ONE grant and propagates the fresh token to every\n * device, so the first device's re-auth heals the whole fleet before the\n * others exhaust their own budgets.\n */\n maxAuthRetries?: number;\n}\n\n/**\n * True when `err` (or an error in its `cause` chain) is an MQTT CONNACK\n * auth-refusal. mqtt.js raises an `ErrorWithReasonCode` carrying a numeric\n * `code`: MQTT 3.1.1 uses 4 (bad user/pass) / 5 (not authorized); MQTT 5 uses\n * 134 / 135. We also match the human-readable message as a belt-and-braces\n * fallback in case the code is not surfaced through a wrapper.\n */\nexport function isAuthRefusedError(err: unknown): boolean {\n let cur: unknown = err;\n for (let depth = 0; cur != null && depth < 6; depth += 1) {\n if (typeof cur !== 'object') {\n break;\n }\n const code = (cur as { code?: unknown }).code;\n if (code === 4 || code === 5 || code === 134 || code === 135) {\n return true;\n }\n const message = (cur as { message?: unknown }).message;\n if (\n typeof message === 'string' &&\n /not authorized|bad user ?name|bad username or password|bad password/i.test(message)\n ) {\n return true;\n }\n cur = (cur as { cause?: unknown }).cause;\n }\n return false;\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 #keepaliveSeconds: number;\n readonly #rejectUnauthorized: boolean;\n readonly #onAuthFailure: (() => Promise<DreameSession>) | null;\n readonly #maxAuthRetries: number;\n #topic: string;\n #client: MqttLikeClient | null = null;\n #closed = false;\n #tearingDown = false;\n #reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n /** Consecutive auth-refused re-auths since the last successful connect. */\n #authFailureCount = 0;\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.#keepaliveSeconds = input.keepaliveSeconds ?? 60;\n this.#rejectUnauthorized = input.rejectUnauthorized ?? false;\n this.#onAuthFailure = input.onAuthFailure ?? null;\n this.#maxAuthRetries = input.maxAuthRetries ?? 3;\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 try {\n await this.#connectAndSubscribe();\n } catch {\n // The fresh token failed to connect transiently (broker has not yet\n // indexed the rotated token, a network blip, etc.). Arm a bounded\n // reconnect instead of leaving the device dark until the next rotation.\n // `#connectAndSubscribe` already ended + nulled its client, so this\n // schedules a clean retry. Resolve rather than reject so a propagation\n // (allSettled) caller records this device as handled — the reconnect\n // timer is now driving recovery.\n this.#scheduleReconnect();\n }\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 keepalive: this.#keepaliveSeconds,\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 try {\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 // A clean connect+subscribe proves the current token is good again:\n // reset the auth-failure budget so a LATER staleness can self-heal.\n this.#authFailureCount = 0;\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 } catch (err) {\n // The connect/subscribe failed AFTER the TCP socket may already be\n // ESTABLISHED (auth or subscribe rejected post-handshake — e.g. a stale\n // token after a re-login). Force-close it now so the socket + client\n // object are freed. Without this, the caller (`open`/`refreshSession`/the\n // reconnect loop) would orphan the client — `#scheduleReconnect` nulls\n // `#client` without `end()`, leaking one ESTABLISHED connection (and its\n // buffers) per failed attempt, which snowballs once the broker starts\n // rejecting the pile-up. Detach our close handler first so the forced\n // end() does not re-arm a reconnect.\n client.removeListener('close', this.#onClose);\n try {\n client.end(true, {}, () => undefined);\n } catch {\n // best-effort — a half-open client may throw on end(); ignore.\n }\n if (this.#client === client) {\n this.#client = null;\n }\n throw err;\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.#attemptReconnect();\n }, this.#backoffMs);\n }\n\n /**\n * One reconnect attempt with reactive auth self-heal. If the broker refuses\n * the CONNECT with an auth error (a stale access token) and an\n * `onAuthFailure` provider is wired and the re-auth budget is not exhausted,\n * mint a fresh session and retry with the new token instead of replaying the\n * old one. Any other failure (or an exhausted budget) falls back to the plain\n * backoff reconnect so bad credentials cannot hot-loop re-login.\n */\n async #attemptReconnect(): Promise<void> {\n try {\n await this.#connectAndSubscribe();\n } catch (err) {\n if (this.#closed) {\n return;\n }\n if (\n this.#onAuthFailure &&\n isAuthRefusedError(err) &&\n this.#authFailureCount < this.#maxAuthRetries\n ) {\n await this.#reauthenticateAndReconnect();\n return;\n }\n // Non-auth error, or the re-auth cap is exhausted: surface it and fall\n // back to a plain backoff reconnect (no re-login storm).\n this.emit('error', err instanceof Error ? err : new DreameTransportError(String(err)));\n this.#scheduleReconnect();\n }\n }\n\n /** Mint a fresh session via `onAuthFailure`, adopt its token, and reconnect. */\n async #reauthenticateAndReconnect(): Promise<void> {\n this.#authFailureCount += 1;\n let session: DreameSession;\n try {\n session = await this.#onAuthFailure!();\n } catch (err) {\n // Re-authentication itself failed: surface it and retry on a backoff.\n this.emit('error', err instanceof Error ? err : new DreameTransportError(String(err)));\n // NEVER orphan a live client: `onAuthFailure` propagates the new session\n // through `refreshSession()`, which may have ALREADY reconnected this push\n // before the (later) rejection surfaced. `#scheduleReconnect()` would null\n // that healthy client without ending it — a leaked socket + a duplicate\n // connection. Only re-arm when we have no live client to preserve.\n if (!this.#client) {\n this.#scheduleReconnect();\n }\n return;\n }\n if (this.#closed) {\n return;\n }\n // Adopt the fresh token + rebuild the topic (uid could differ). NOTE:\n // `onAuthFailure` typically propagates the new session back through\n // `refreshSession()`, which ALREADY tears down and reconnects this push —\n // in that case `#client` is live and we must NOT open a second connection.\n this.#session = session;\n this.#topic = buildStatusTopic(this.#device, this.#session.uid, this.#region);\n if (this.#client) {\n return;\n }\n // No propagated reconnect happened — reconnect ourselves on the new token.\n void this.#attemptReconnect();\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 /** Number of listeners registered for `event`. */\n listenerCount<K extends keyof Events & string>(event: K): number {\n return this.#emitter.listenerCount(event);\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(\n device: DreameDevice,\n session: DreameSession,\n region: DreameRegion,\n onAuthFailure?: () => Promise<DreameSession>,\n ): 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, onAuthFailure) =>\n new DreamePush({\n device,\n session,\n region,\n ...(onAuthFailure !== undefined ? { onAuthFailure } : {}),\n }),\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 /**\n * Force-refresh callback handed to the underlying push so it can self-heal a\n * broker CONNACK auth-refusal (stale token) by minting a fresh session.\n * Wired by the facade to `Nodreame.reauthenticate()`.\n */\n onAuthFailure?: () => Promise<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 #onAuthFailure: (() => Promise<DreameSession>) | undefined;\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.#onAuthFailure = input.onAuthFailure;\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(\n this.#device,\n this.#sessionRef(),\n this.#region,\n this.#onAuthFailure,\n );\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 /**\n * AI obstacle-detection toggle bundle (Tasshack types.py:1609 — `AI_DETECTION`,\n * siid 4 piid 22). The value packs ALL AI-obstacle switches into ONE property,\n * encoded as EITHER an int bitmask (`DreameVacuumAIProperty`) OR a JSON string\n * (`DreameVacuumStrAIProperty`) depending on firmware. Decode/encode per-feature\n * via `ai-detection.ts` ({@link decodeAiFeature} / {@link encodeAiFeatureWrite}).\n */\n AI_DETECTION: { siid: 4, piid: 22 } as const,\n /**\n * Auto-switch settings bundle (Tasshack types.py — `AUTO_SWITCH_SETTINGS`,\n * siid 4 piid 50). A JSON string packing every secondary toggle/setting as\n * `[{\"k\":<key>,\"v\":<int>}, …]`. Decode/encode per-setting via `auto-switch.ts`.\n */\n AUTO_SWITCH_SETTINGS: { siid: 4, piid: 50 } 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 /**\n * Map \"PATH\" push — the OSS object name of the latest map frame the robot\n * uploaded (the value passed to {@link VacuumDevice.getMap}). Arrives via the\n * MQTT push during/after cleaning; NOT in `DEFAULT_PROPS` because it is not\n * polled (it is pushed). Read it through {@link VacuumDevice.mapFilename}.\n */\n MAP_PATH: { siid: 6, piid: 3 } 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 * AI obstacle-detection codec. The robot packs ALL AI-obstacle toggles into a\n * SINGLE MIoT property — `AI_DETECTION` (siid 4 piid 22) — whose value is EITHER\n * an `int` bitmask OR a `str` JSON object, depending on firmware generation.\n * Ported from the Tasshack `dreame-vacuum` HA integration (v2.0.0b25):\n * - INT ←→ `DreameVacuumAIProperty` (bitwise flags)\n * - JSON ←→ `DreameVacuumStrAIProperty` ({ \"<key>\": bool })\n *\n * This module exposes per-feature decode + a read-modify-write encode that\n * preserves the other features, so a consumer can present each AI toggle as an\n * independent boolean without knowing which on-wire encoding the model uses.\n */\n\n/** Canonical AI-obstacle feature keys. A given model exposes a subset. */\nexport type DreameAiFeature =\n | 'furnitureDetection'\n | 'obstacleDetection'\n | 'obstaclePicture'\n | 'fluidDetection'\n | 'petDetection'\n | 'obstacleImageUpload'\n | 'petAvoidance'\n | 'fuzzyObstacleDetection'\n | 'petPicture'\n | 'petFocusedDetection'\n | 'largeParticlesBoost'\n | 'humanDetection';\n\n/**\n * Bit value in the INT-encoded `AI_DETECTION` (Tasshack `DreameVacuumAIProperty`).\n * A feature absent here has no int representation (JSON-only).\n */\nexport const AI_FEATURE_BIT: Readonly<Partial<Record<DreameAiFeature, number>>> = {\n furnitureDetection: 1,\n obstacleDetection: 2,\n obstaclePicture: 4,\n fluidDetection: 8,\n petDetection: 16,\n obstacleImageUpload: 32,\n // AI_IMAGE = 64 is an internal flag, not a user toggle — intentionally omitted.\n petAvoidance: 128,\n fuzzyObstacleDetection: 256,\n petPicture: 512,\n petFocusedDetection: 1024,\n largeParticlesBoost: 2048,\n};\n\n/**\n * Key in the JSON-string-encoded `AI_DETECTION` (Tasshack\n * `DreameVacuumStrAIProperty`). A feature absent here has no JSON representation.\n */\nexport const AI_FEATURE_JSON_KEY: Readonly<Partial<Record<DreameAiFeature, string>>> = {\n obstacleDetection: 'obstacle_detect_switch',\n obstacleImageUpload: 'obstacle_app_display_switch',\n petDetection: 'whether_have_pet',\n humanDetection: 'human_detect_switch',\n furnitureDetection: 'furniture_detect_switch',\n fluidDetection: 'fluid_detect_switch',\n};\n\n/** The raw on-wire `AI_DETECTION` value: an int bitmask, a JSON string, or absent. */\nexport type AiDetectionRaw = number | string | null;\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** Parse a JSON-string AI payload into an object, or null when not an object. */\nfunction parseJsonPayload(raw: string): Record<string, unknown> | null {\n try {\n const parsed: unknown = JSON.parse(raw);\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n/** Coerce a JSON value (bool | 1/0 | \"1\"/\"0\") to a boolean, or null when ambiguous. */\nfunction coerceBool(v: unknown): boolean | null {\n if (typeof v === 'boolean') return v;\n if (v === 1 || v === '1') return true;\n if (v === 0 || v === '0') return false;\n return null;\n}\n\n/**\n * Decode ONE AI feature's on/off state from the raw `AI_DETECTION` value,\n * transparently handling both the int-bitmask and JSON-string encodings.\n * Returns `null` when the value is absent, the encoding cannot represent the\n * feature, or the JSON payload omits the key.\n */\nexport function decodeAiFeature(raw: AiDetectionRaw, feature: DreameAiFeature): boolean | null {\n if (typeof raw === 'number') {\n const bit = AI_FEATURE_BIT[feature];\n if (bit === undefined) return null;\n return (raw & bit) === bit;\n }\n if (typeof raw === 'string') {\n const key = AI_FEATURE_JSON_KEY[feature];\n if (key === undefined) return null;\n const payload = parseJsonPayload(raw);\n if (payload === null) return null;\n return coerceBool(payload[key]);\n }\n return null;\n}\n\n/**\n * Compute the value to WRITE back to `AI_DETECTION` after toggling ONE feature,\n * preserving the on-wire encoding and the other features. Mirrors Tasshack\n * `set_ai_detection`: an int payload is the full new bitmask; a JSON payload\n * carries ONLY the changed key (the device merges it server-side).\n *\n * Throws when the current value/encoding is unknown (a blind read-modify-write\n * could clobber the other features) or the feature has no representation in the\n * active encoding.\n */\nexport function encodeAiFeatureWrite(\n raw: AiDetectionRaw,\n feature: DreameAiFeature,\n value: boolean,\n): number | string {\n if (typeof raw === 'number') {\n const bit = AI_FEATURE_BIT[feature];\n if (bit === undefined) {\n throw new Error(\n `encodeAiFeatureWrite: feature \"${feature}\" has no int-bitmask representation`,\n );\n }\n return value ? raw | bit : raw & ~bit;\n }\n if (typeof raw === 'string') {\n const key = AI_FEATURE_JSON_KEY[feature];\n if (key === undefined) {\n throw new Error(`encodeAiFeatureWrite: feature \"${feature}\" has no JSON-key representation`);\n }\n return JSON.stringify({ [key]: value });\n }\n throw new Error(\n `encodeAiFeatureWrite: AI_DETECTION value not yet known — cannot safely toggle \"${feature}\"`,\n );\n}\n","/**\n * Auto-switch settings codec. The robot packs a large bundle of secondary\n * cleaning toggles + multi-value settings into a SINGLE MIoT property —\n * `AUTO_SWITCH_SETTINGS` (siid 4 piid 50) — whose value is a JSON string.\n * Ported from the Tasshack `dreame-vacuum` HA integration (v2.0.0b25,\n * `DreameVacuumAutoSwitchProperty` + `set_auto_switch_property`):\n *\n * - READ : a JSON LIST `[{\"k\":\"LessColl\",\"v\":1}, …]` (or a single\n * `{\"k\":…,\"v\":…}` object) — every supported key/value pair.\n * - WRITE : a SINGLE `{\"k\":\"LessColl\",\"v\":0}` object carrying ONLY the\n * changed key; the device merges it server-side.\n *\n * Unlike `AI_DETECTION` this family is JSON-ONLY (no int-bitmask variant) and the\n * values are arbitrary ints (booleans 0/1, small enums, and `-1`/`-n` \"not\n * applicable\" sentinels) — the codec returns the raw int and leaves the\n * boolean-vs-enum interpretation to the caller. Presence is data-driven: a key is\n * \"supported\" iff it appears in the decoded payload (exactly how Tasshack derives\n * `auto_switch_data`).\n */\n\n/** Canonical auto-switch keys. A given model reports a subset. */\nexport type AutoSwitchKey =\n | 'collisionAvoidance'\n | 'fillLight'\n | 'autoDrying'\n | 'stainAvoidance'\n | 'moppingType'\n | 'cleanGenius'\n | 'widerCornerCoverage'\n | 'floorDirectionCleaning'\n | 'petFocusedCleaning'\n | 'autoRecleaning'\n | 'autoRewashing'\n | 'mopPadSwing'\n | 'autoCharging'\n | 'humanFollow'\n | 'maxSuctionPower'\n | 'smartDrying'\n | 'drainageConfirmResult'\n | 'drainageTestResult'\n | 'hotWashing'\n | 'uvSterilization'\n | 'cleaningRoute'\n | 'customMoppingMode'\n | 'moppingMode'\n | 'selfCleanFrequency'\n | 'intensiveCarpetCleaning'\n | 'gapCleaningExtension'\n | 'moppingUnderFurnitures'\n | 'ultraCleanMode'\n | 'streamingVoicePrompt'\n | 'mopExtend'\n | 'mopExtendFrequency'\n | 'sideReach'\n | 'intelligentStainCleaning';\n\n/**\n * Short on-wire key for each setting inside the `AUTO_SWITCH_SETTINGS` JSON\n * (Tasshack `DreameVacuumAutoSwitchProperty`). These opaque strings are the\n * device's own identifiers and must match byte-for-byte.\n */\nexport const AUTO_SWITCH_JSON_KEY: Readonly<Record<AutoSwitchKey, string>> = {\n collisionAvoidance: 'LessColl',\n fillLight: 'FillinLight',\n autoDrying: 'AutoDry',\n stainAvoidance: 'StainIdentify',\n moppingType: 'CleanType',\n cleanGenius: 'SmartHost',\n widerCornerCoverage: 'MeticulousTwist',\n floorDirectionCleaning: 'MaterialDirectionClean',\n petFocusedCleaning: 'PetPartClean',\n autoRecleaning: 'SmartAutoMop',\n autoRewashing: 'SmartAutoWash',\n mopPadSwing: 'MopScalable',\n autoCharging: 'SmartCharge',\n humanFollow: 'MonitorHumanFollow',\n maxSuctionPower: 'SuctionMax',\n smartDrying: 'SmartDrying',\n drainageConfirmResult: 'FluctuationConfirmResult',\n drainageTestResult: 'FluctuationTestResult',\n hotWashing: 'HotWash',\n uvSterilization: 'UVLight',\n cleaningRoute: 'CleanRoute',\n customMoppingMode: 'MopEffectSwitch',\n moppingMode: 'MopEffectState',\n selfCleanFrequency: 'BackWashType',\n intensiveCarpetCleaning: 'CarpetFineClean',\n gapCleaningExtension: 'LacuneMopScalable',\n moppingUnderFurnitures: 'MopScalable2',\n ultraCleanMode: 'SuperWash',\n streamingVoicePrompt: 'MonitorPromptLevel',\n mopExtend: 'MopExtrSwitch',\n mopExtendFrequency: 'ExtrFreq',\n sideReach: 'SbrushExtrSwitch',\n intelligentStainCleaning: 'HeavyStainSmart',\n};\n\n/** Reverse map: on-wire key → canonical key (built once). */\nconst JSON_KEY_TO_CANONICAL: Readonly<Record<string, AutoSwitchKey>> = Object.fromEntries(\n (Object.entries(AUTO_SWITCH_JSON_KEY) as [AutoSwitchKey, string][]).map(([k, v]) => [v, k]),\n);\n\n/** The raw on-wire `AUTO_SWITCH_SETTINGS` value: a JSON string, or absent. */\nexport type AutoSwitchRaw = string | null;\n\n/** Coerce a JSON value (number | numeric string) to a finite int, or null. */\nfunction coerceInt(v: unknown): number | null {\n if (typeof v === 'number') return Number.isFinite(v) ? v : null;\n if (typeof v === 'string' && v.trim() !== '') {\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n\n/**\n * Parse the raw `AUTO_SWITCH_SETTINGS` value into a `{ on-wire key → int }` map.\n * Accepts the list form (`[{\"k\":…,\"v\":…}, …]`) and the single-object form\n * (`{\"k\":…,\"v\":…}`). Returns null when the value is absent or unparseable.\n */\nfunction parseAutoSwitchMap(raw: AutoSwitchRaw): Map<string, number> | null {\n if (typeof raw !== 'string' || raw.length <= 2) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n const out = new Map<string, number>();\n const add = (entry: unknown): void => {\n if (entry === null || typeof entry !== 'object') return;\n const rec = entry as Record<string, unknown>;\n if (typeof rec['k'] !== 'string') return;\n const value = coerceInt(rec['v']);\n if (value !== null) out.set(rec['k'], value);\n };\n if (Array.isArray(parsed)) {\n for (const entry of parsed) add(entry);\n } else {\n add(parsed);\n }\n return out.size > 0 ? out : null;\n}\n\n/**\n * Decode ONE auto-switch setting's int value from the packed\n * `AUTO_SWITCH_SETTINGS` payload. Returns `null` when the value is absent or the\n * key is not present in the payload (i.e. unsupported by this model/firmware).\n */\nexport function decodeAutoSwitch(raw: AutoSwitchRaw, key: AutoSwitchKey): number | null {\n const map = parseAutoSwitchMap(raw);\n if (map === null) return null;\n const jsonKey = AUTO_SWITCH_JSON_KEY[key];\n return map.has(jsonKey) ? (map.get(jsonKey) ?? null) : null;\n}\n\n/**\n * The auto-switch keys the payload actually reports (presence-driven — mirrors\n * Tasshack `auto_switch_data`). On-wire keys with no canonical mapping (newer\n * firmware extras like `MopFullyScalable`) are skipped. Order follows\n * {@link AUTO_SWITCH_JSON_KEY} for determinism.\n */\nexport function supportedAutoSwitchKeys(raw: AutoSwitchRaw): readonly AutoSwitchKey[] {\n const map = parseAutoSwitchMap(raw);\n if (map === null) return [];\n const present: AutoSwitchKey[] = [];\n for (const key of Object.keys(AUTO_SWITCH_JSON_KEY) as AutoSwitchKey[]) {\n if (map.has(AUTO_SWITCH_JSON_KEY[key])) present.push(key);\n }\n return present;\n}\n\n/** All `{ canonical key → int }` pairs the payload reports (canonical keys only). */\nexport function decodeAutoSwitchAll(\n raw: AutoSwitchRaw,\n): Readonly<Partial<Record<AutoSwitchKey, number>>> {\n const map = parseAutoSwitchMap(raw);\n if (map === null) return {};\n const out: Partial<Record<AutoSwitchKey, number>> = {};\n for (const [jsonKey, value] of map) {\n const canonical = JSON_KEY_TO_CANONICAL[jsonKey];\n if (canonical !== undefined) out[canonical] = value;\n }\n return out;\n}\n\n/**\n * Compute the value to WRITE to `AUTO_SWITCH_SETTINGS` for ONE setting. Mirrors\n * Tasshack `set_auto_switch_property`: a SINGLE `{\"k\":<key>,\"v\":<int>}` object\n * (NOT the full list) — the device merges it server-side, so the other settings\n * are preserved without a read-modify-write.\n */\nexport function encodeAutoSwitchWrite(key: AutoSwitchKey, value: number): string {\n return JSON.stringify({ k: AUTO_SWITCH_JSON_KEY[key], v: Math.trunc(value) });\n}\n","/**\n * Vacuum consumable/maintenance map (ported from Tasshack `dreame-vacuum`\n * v2.0.0b25 — property + action mappings). Each consumable lives on its own MIoT\n * service `siid`: a remaining-life `%` property (`siid/piid`) and a \"reset to\n * 100%\" action (`siid/aiid 1`). A model exposes only a SUBSET — which is\n * discovered by PRESENCE (the device reports the life property) rather than a\n * hardcoded per-model list, mirroring how the HA integration derives support.\n */\n\n/** Stable consumable keys (kebab-case so a consumer can use them verbatim). */\nexport type DreameConsumableKey =\n | 'main-brush'\n | 'side-brush'\n | 'filter'\n | 'sensor'\n | 'tank-filter'\n | 'mop-pad'\n | 'silver-ion'\n | 'detergent'\n | 'squeegee'\n | 'deodorizer'\n | 'wheel'\n | 'scale-inhibitor'\n | 'dust-bag';\n\n/** One consumable's wire coordinates: remaining-life property + optional reset action. */\nexport interface ConsumableSpec {\n readonly key: DreameConsumableKey;\n readonly label: string;\n /** Remaining-life % property (0..100). */\n readonly life: { readonly siid: number; readonly piid: number };\n /** \"Mark replaced / reset life\" action, or null when the model exposes none. */\n readonly reset: { readonly siid: number; readonly aiid: number } | null;\n}\n\n/**\n * Every consumable nodedreame knows. The reset action shares the consumable's\n * service `siid` with `aiid 1` (Tasshack action map). `dust-bag` has a life\n * property but no reset action.\n */\nexport const VACUUM_CONSUMABLES: readonly ConsumableSpec[] = [\n {\n key: 'main-brush',\n label: 'Main Brush',\n life: { siid: 9, piid: 2 },\n reset: { siid: 9, aiid: 1 },\n },\n {\n key: 'side-brush',\n label: 'Side Brush',\n life: { siid: 10, piid: 2 },\n reset: { siid: 10, aiid: 1 },\n },\n { key: 'filter', label: 'Filter', life: { siid: 11, piid: 1 }, reset: { siid: 11, aiid: 1 } },\n { key: 'sensor', label: 'Sensor', life: { siid: 16, piid: 1 }, reset: { siid: 16, aiid: 1 } },\n {\n key: 'tank-filter',\n label: 'Tank Filter',\n life: { siid: 17, piid: 1 },\n reset: { siid: 17, aiid: 1 },\n },\n { key: 'mop-pad', label: 'Mop Pad', life: { siid: 18, piid: 1 }, reset: { siid: 18, aiid: 1 } },\n {\n key: 'silver-ion',\n label: 'Silver-ion',\n life: { siid: 19, piid: 2 },\n reset: { siid: 19, aiid: 1 },\n },\n {\n key: 'detergent',\n label: 'Detergent',\n life: { siid: 20, piid: 1 },\n reset: { siid: 20, aiid: 1 },\n },\n { key: 'squeegee', label: 'Squeegee', life: { siid: 24, piid: 1 }, reset: { siid: 24, aiid: 1 } },\n {\n key: 'deodorizer',\n label: 'Deodorizer',\n life: { siid: 29, piid: 2 },\n reset: { siid: 29, aiid: 1 },\n },\n { key: 'wheel', label: 'Wheel', life: { siid: 30, piid: 2 }, reset: { siid: 30, aiid: 1 } },\n {\n key: 'scale-inhibitor',\n label: 'Scale Inhibitor',\n life: { siid: 31, piid: 2 },\n reset: { siid: 31, aiid: 1 },\n },\n { key: 'dust-bag', label: 'Dust Bag', life: { siid: 27, piid: 17 }, reset: null },\n] as const;\n\n/** Lookup a consumable spec by key (undefined for an unknown key). */\nexport function consumableSpec(key: DreameConsumableKey): ConsumableSpec | undefined {\n return VACUUM_CONSUMABLES.find((c) => c.key === key);\n}\n\n/** Narrow an arbitrary string to a known {@link DreameConsumableKey} — lets a\n * string-keyed consumer (e.g. a generic consumables cap) validate before\n * calling the typed reset path with no cast. */\nexport function isDreameConsumableKey(key: string): key is DreameConsumableKey {\n return VACUUM_CONSUMABLES.some((c) => c.key === key);\n}\n\n/** A resolved consumable reading: which consumable, its remaining life %, and\n * whether a reset action exists. Only the consumables the model REPORTS appear. */\nexport interface ConsumableReading {\n readonly key: DreameConsumableKey;\n readonly label: string;\n readonly leftPct: number;\n readonly resettable: boolean;\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 { DreameAiFeature } from './ai-detection.js';\nimport type { CapabilityResolver, DeviceCapabilities } from '../../device/capability.js';\n\n/**\n * AI-obstacle features the X50 family exposes (live-verified on `dreame.vacuum.r2538z`\n * via the Dreamehome/HA superset 2026-06-29). `petPicture`/`petFocusedDetection` read\n * `unavailable` on this model, so they are intentionally excluded.\n */\nconst X50_AI_FEATURES: readonly DreameAiFeature[] = [\n 'obstacleDetection',\n 'petDetection',\n 'furnitureDetection',\n 'fluidDetection',\n 'obstaclePicture',\n 'obstacleImageUpload',\n 'fuzzyObstacleDetection',\n];\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 * The AI-obstacle toggles this model exposes (subset of {@link DreameAiFeature}),\n * each surfaced as an independent boolean from the packed `AI_DETECTION` property.\n * Empty when `hasAiObstacleDetection` is false.\n */\n supportedAiFeatures: readonly DreameAiFeature[];\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 // An unknown model advertises no AI toggles until its feature set is verified.\n supportedAiFeatures: [],\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 supportedAiFeatures: X50_AI_FEATURES,\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","import { z } from 'zod';\n\n/**\n * The third-video endpoints wrap their payload in a double `data` envelope:\n * `{ code, success, msg, data: { requestId, data: <payload> } }`. `code === 0`\n * means success (already enforced by the HTTP layer); these schemas narrow the\n * payload. All are permissive (`.passthrough()`) — the cloud adds fields freely.\n */\n\n/** `tx/user/accesstoken` → `data.data = { token, userId, expireAt }` (expireAt: epoch seconds). */\nexport const VideoAccessTokenResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z\n .object({\n data: z\n .object({\n token: z.string(),\n userId: z.union([z.string(), z.number()]).optional(),\n expireAt: z.number().optional(),\n })\n .passthrough(),\n })\n .passthrough(),\n })\n .passthrough();\nexport type VideoAccessTokenResponse = z.infer<typeof VideoAccessTokenResponseSchema>;\n\n/** `aliIot/getAuthCodeV3` → `data` is a hex string (the Aliyun authCode blob). */\nexport const AuthCodeResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z.string(),\n })\n .passthrough();\nexport type AuthCodeResponse = z.infer<typeof AuthCodeResponseSchema>;\n\n/** `tx/mgr/family/getFamilyId` → `data.data.familyId`. */\nexport const FamilyIdResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z\n .object({ data: z.object({ familyId: z.string() }).passthrough() })\n .passthrough(),\n })\n .passthrough();\nexport type FamilyIdResponse = z.infer<typeof FamilyIdResponseSchema>;\n\n/** Nested `deviceInfo` block within a device record, carrying the video vendor hints. */\nexport const DeviceVideoInfoSchema = z\n .object({\n model: z.string().optional(),\n displayName: z.string().optional(),\n permit: z.string().optional(),\n videoDynamicVendor: z.boolean().optional(),\n defaultVendors: z.array(z.string()).optional(),\n productId: z.union([z.string(), z.number()]).optional(),\n })\n .passthrough();\n\n/** A device record as returned by `device/info` (`data` = single record). */\nexport const DeviceInfoRecordSchema = z\n .object({\n did: z.union([z.string(), z.number()]).optional(),\n model: z.string().optional(),\n customName: z.string().optional(),\n vendor: z.string().optional(),\n online: z.boolean().optional(),\n lwt: z.number().optional(),\n property: z.string().optional(),\n bindDomain: z.string().optional(),\n deviceInfo: DeviceVideoInfoSchema.optional(),\n })\n .passthrough();\nexport type DeviceInfoRecord = z.infer<typeof DeviceInfoRecordSchema>;\n\nexport const DeviceInfoResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: DeviceInfoRecordSchema,\n })\n .passthrough();\nexport type DeviceInfoResponse = z.infer<typeof DeviceInfoResponseSchema>;\n","import type { DeviceInfoRecord } from './schemas.js';\nimport type { DeviceVideoProfile, VideoVendor } from './types.js';\n\nconst KNOWN_VENDORS: readonly string[] = ['tx', 'ali'];\n\nfunction asVendor(v: string | undefined | null): VideoVendor | null {\n return v === 'tx' || v === 'ali' ? v : null;\n}\n\n/** Parse the stringified `property` blob for the fields we care about (iotId, lwt). */\nfunction parseProperty(property: string | undefined): { iotId: string | null; lwt: number | null } {\n if (!property) {\n return { iotId: null, lwt: null };\n }\n try {\n const p = JSON.parse(property) as { iotId?: unknown; lwt?: unknown };\n return {\n iotId: typeof p.iotId === 'string' && p.iotId.length > 0 ? p.iotId : null,\n lwt: typeof p.lwt === 'number' ? p.lwt : null,\n };\n } catch {\n return { iotId: null, lwt: null };\n }\n}\n\n/**\n * Derive a {@link DeviceVideoProfile} from a raw device record. Pure — no I/O —\n * so it is unit-testable against captured records. `videoCapable` is true when\n * the cloud marks the device `permit: \"video\"` or lists any supported vendor.\n */\nexport function toVideoProfile(record: DeviceInfoRecord): DeviceVideoProfile {\n const info = record.deviceInfo ?? {};\n const supportedVendors = (info.defaultVendors ?? []).filter((v): v is VideoVendor =>\n KNOWN_VENDORS.includes(v),\n );\n const prop = parseProperty(record.property);\n const online = record.online === true || record.lwt === 1 || prop.lwt === 1;\n return {\n did: String(record.did ?? ''),\n model: String(record.model ?? info.model ?? ''),\n displayName: info.displayName ?? null,\n videoCapable: info.permit === 'video' || supportedVendors.length > 0,\n currentVendor: asVendor(record.vendor),\n supportedVendors,\n dynamicVendor: info.videoDynamicVendor === true,\n iotId: prop.iotId,\n online,\n };\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type { DreameSession } from '../cloud/types.js';\nimport {\n httpPostJsonBody,\n RequestContext,\n type BaseResponse,\n type FetchImpl,\n} from '../transport/http.js';\nimport {\n AuthCodeResponseSchema,\n DeviceInfoResponseSchema,\n FamilyIdResponseSchema,\n VideoAccessTokenResponseSchema,\n} from './schemas.js';\nimport { toVideoProfile } from './profile.js';\nimport type { DeviceVideoProfile, VideoAccessToken } from './types.js';\n\n/** Path prefixes for the two video control-plane services on the region host. */\nconst P_THIRD_VIDEO = '/dreame-third-video';\nconst P_SMARTHOME = '/dreame-smarthome';\nconst P_USER_IOT = '/dreame-user-iot';\n\n/** `os` form value the app sends; the cloud accepts any of android/iOS/1/2. */\nconst DEFAULT_OS = 'android';\n\n/** Shared inputs for every video control-plane call. Mirrors `listDevices`. */\nexport interface VideoRequestInput {\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\nfunction resolveCtx(input: VideoRequestInput): RequestContext {\n return input.ctx ?? RequestContext.from({ ...input, host: input.apiHost });\n}\n\nfunction passthrough(input: VideoRequestInput): {\n accessToken: string;\n signal?: AbortSignal;\n timeoutMs?: number;\n} {\n return {\n accessToken: input.session.accessToken,\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n };\n}\n\n/**\n * Mint the short-lived third-video access token. Required as a body field by the\n * device-scoped video calls (getIdentity, getP2PInfo, …).\n */\nexport async function getVideoAccessToken(input: VideoRequestInput): Promise<VideoAccessToken> {\n const ctx = resolveCtx(input);\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_THIRD_VIDEO}/tx/user/accesstoken`,\n body: { os: DEFAULT_OS },\n context: 'video accesstoken',\n ...passthrough(input),\n });\n const parsed = VideoAccessTokenResponseSchema.parse(raw);\n const d = parsed.data.data;\n return {\n token: d.token,\n userId: d.userId !== undefined ? String(d.userId) : null,\n // Cloud reports expiry in epoch-seconds; expose epoch-ms like DreameSession.\n expiresAt: d.expireAt !== undefined ? d.expireAt * 1000 : 0,\n };\n}\n\n/**\n * Fetch the Aliyun authCode blob (`getAuthCodeV3`). This opaque hex string is the\n * material exchanged with Aliyun IoT to obtain a LinkVisual identity — the first\n * step of the Aliyun media path.\n */\nexport async function getAliyunAuthCode(input: VideoRequestInput): Promise<string> {\n const ctx = resolveCtx(input);\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_SMARTHOME}/aliIot/getAuthCodeV3`,\n body: { os: DEFAULT_OS },\n context: 'aliyun authCode',\n ...passthrough(input),\n });\n return AuthCodeResponseSchema.parse(raw).data;\n}\n\n/**\n * Fetch the account's video `familyId` (used to scope some vendor calls). This\n * endpoint requires the third-video access token in the body — pass one via\n * `videoToken`, otherwise a fresh one is minted with {@link getVideoAccessToken}.\n */\nexport async function getVideoFamilyId(\n input: VideoRequestInput & { videoToken?: string },\n): Promise<string> {\n const ctx = resolveCtx(input);\n const videoToken = input.videoToken ?? (await getVideoAccessToken({ ...input, ctx })).token;\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_THIRD_VIDEO}/tx/mgr/family/getFamilyId`,\n body: { accesstoken: videoToken, os: DEFAULT_OS },\n context: 'video familyId',\n ...passthrough(input),\n });\n return FamilyIdResponseSchema.parse(raw).data.data.familyId;\n}\n\n/**\n * Resolve a device's video profile (vendor, supported vendors, Aliyun iotId,\n * capability) from its cloud record. `did` is the Dreame device id.\n */\nexport async function getDeviceVideoProfile(\n input: VideoRequestInput & { did: string },\n): Promise<DeviceVideoProfile> {\n const ctx = resolveCtx(input);\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_USER_IOT}/iotuserbind/device/info`,\n body: { did: input.did },\n context: 'device video info',\n ...passthrough(input),\n });\n return toVideoProfile(DeviceInfoResponseSchema.parse(raw).data);\n}\n","import type { DreameRegion } from '../../auth/config.js';\n\n/**\n * Aliyun IoT App SDK credentials for the Dreamehome app. These are app-global\n * constants baked into the shipped Dreamehome binary — NOT a user secret. The\n * appKey is public and the appSecret only signs the app's own API-Gateway\n * requests (identically for every install); they rotate only with a new app\n * release. Verified against the live x-ca signature (see signing.test.ts).\n * (SAST note: shipped public client credentials, not a leaked user secret.)\n */\nexport const ALIYUN_APP_KEY = '33974273';\nexport const ALIYUN_APP_SECRET = '65a62f03ae5eda97af962fecc2e3efa6';\n\n/** Aliyun IoT region id per Dreame region. Only `eu-central-1` is verified live. */\nexport const ALIYUN_REGION_ID: Record<DreameRegion, string> = {\n eu: 'eu-central-1',\n de: 'eu-central-1',\n us: 'us-east-1',\n cn: 'cn-shanghai',\n ru: 'eu-central-1',\n sg: 'ap-southeast-1',\n in: 'ap-southeast-1',\n tw: 'cn-shanghai',\n};\n\n/** IoT App identity/session host (createSessionByAuthCode, connect). */\nexport function livingAccountHost(regionId: string): string {\n return `living-account.${regionId}.aliyuncs.com`;\n}\n\n/** IoT API gateway host (vision/customer/*). */\nexport function apiIotHost(regionId: string): string {\n return `${regionId}.api-iot.aliyuncs.com`;\n}\n\nexport const VISION_STREAM_QUERY = '/vision/customer/stream/query';\n","import { randomUUID } from 'node:crypto';\nimport { z } from 'zod';\nimport { defaultFetch, type FetchImpl } from '../../transport/fetch.js';\nimport { DreameApiError } from '../../transport/errors.js';\nimport { ALIYUN_APP_KEY, ALIYUN_APP_SECRET, apiIotHost, livingAccountHost } from './constants.js';\nimport { signApiGatewayRequest } from './signing.js';\n\n/** Aliyun OpenAccount session from loginbyoauth. */\nexport interface OaSession {\n sid: string;\n refreshToken: string;\n openId: string;\n}\n\n/** Aliyun IoT session (the iotToken used by every LinkVisual call). */\nexport interface IotSession {\n iotToken: string;\n refreshToken: string;\n identityId: string;\n /** Epoch-ms at which iotToken expires. */\n expiresAt: number;\n}\n\nconst LoginByOauthSchema = z\n .object({\n data: z\n .object({\n data: z\n .object({\n loginSuccessResult: z\n .object({\n sid: z.string(),\n refreshToken: z.string().optional(),\n token: z.string().optional(),\n openAccount: z.object({ openId: z.string().optional() }).passthrough().optional(),\n })\n .passthrough(),\n })\n .passthrough(),\n })\n .passthrough(),\n })\n .passthrough();\n\nconst CreateSessionSchema = z\n .object({\n code: z.number().optional(),\n message: z.string().nullish(),\n data: z\n .object({\n iotToken: z.string(),\n refreshToken: z.string().optional(),\n identityId: z.string().optional(),\n iotTokenExpire: z.number().optional(),\n })\n .passthrough(),\n })\n .passthrough();\n\nasync function postJson(url: string, headers: Record<string, string>, body: string, fetchImpl: FetchImpl, ctx: string): Promise<unknown> {\n const res = await fetchImpl(url, { method: 'POST', headers, body });\n const text = await res.text();\n try {\n return JSON.parse(text);\n } catch {\n throw new DreameApiError(`${ctx} returned non-JSON (status ${res.status})`, res.status);\n }\n}\n\nexport interface LoginByOauthInput {\n authCode: string;\n regionId: string;\n deviceId?: string;\n utdid?: string;\n fetchImpl?: FetchImpl;\n}\n\n/** Exchange the Dreame authCode for an Aliyun OpenAccount session (step 2). */\nexport async function loginByOauth(input: LoginByOauthInput): Promise<OaSession> {\n const host = livingAccountHost(input.regionId);\n const riskControlInfo = {\n createIfNotExist: 'true', sdkVersion: '3.4.0.27', openaccountVersion: '3.4.0.27',\n platformName: 'iOS', netType: 'wifi', brand: 'Apple', USE_H5_NC: 'true',\n deviceId: input.deviceId ?? randomUUID(), locale: 'en-US', platformVersion: '26.5.2',\n platform: 'iPhone', appKey: ALIYUN_APP_KEY, USE_OA_PWD_ENCRYPT: 'true',\n model: 'iPhone16,2', utdid: input.utdid ?? randomUUID().replace(/-/g, '').slice(0, 20),\n appVersion: '1895',\n };\n const value = JSON.stringify({\n riskControlInfo, oauthPlateform: 23, oauthAppKey: ALIYUN_APP_KEY, authCode: input.authCode,\n });\n const signed = signApiGatewayRequest({\n method: 'POST', host, path: '/api/prd/loginbyoauth.json',\n appKey: ALIYUN_APP_KEY, appSecret: ALIYUN_APP_SECRET,\n form: { loginByOauthRequest: value },\n });\n const json = await postJson(`https://${host}/api/prd/loginbyoauth.json`, signed.headers, signed.body,\n input.fetchImpl ?? defaultFetch, 'loginbyoauth');\n const r = LoginByOauthSchema.parse(json).data.data.loginSuccessResult;\n return {\n sid: r.sid,\n refreshToken: r.refreshToken ?? '',\n openId: r.openAccount?.openId ?? '',\n };\n}\n\nexport interface CreateIotSessionInput {\n /** The OpenAccount `sid` from {@link loginByOauth}. */\n sid: string;\n regionId: string;\n fetchImpl?: FetchImpl;\n}\n\n/** Exchange the OA `sid` for an Aliyun IoT session / iotToken (step 3). */\nexport async function createIotSession(input: CreateIotSessionInput): Promise<IotSession> {\n const host = apiIotHost(input.regionId);\n const path = '/account/createSessionByAuthCode';\n const payload = {\n id: randomUUID().toUpperCase(),\n params: { request: { appKey: ALIYUN_APP_KEY, authCode: input.sid, accountType: 'OA_SESSION' } },\n request: { apiVer: '1.0.4', language: 'en-US', appKey: ALIYUN_APP_KEY },\n version: '1.0.0',\n };\n const signed = signApiGatewayRequest({\n method: 'POST', host, path, appKey: ALIYUN_APP_KEY, appSecret: ALIYUN_APP_SECRET,\n json: payload, extraCaHeaders: { 'X-Ca-Stage': 'RELEASE' },\n });\n const json = await postJson(`https://${host}${path}`, signed.headers, signed.body,\n input.fetchImpl ?? defaultFetch, 'createSessionByAuthCode');\n const parsed = CreateSessionSchema.parse(json);\n if (parsed.code !== 200 && parsed.code !== 0) {\n throw new DreameApiError(`createSession rejected: code=${parsed.code} msg=${parsed.message ?? '?'}`, 200, parsed);\n }\n const d = parsed.data;\n return {\n iotToken: d.iotToken,\n refreshToken: d.refreshToken ?? '',\n identityId: d.identityId ?? '',\n expiresAt: Date.now() + (d.iotTokenExpire ?? 72000) * 1000,\n };\n}\n","import { createHash, createHmac } from 'node:crypto';\n\n/**\n * Aliyun API Gateway (\"x-ca\") request signing, HmacSHA1. Used by the LinkVisual\n * video endpoints the Dreamehome app talks to (living-account + api-iot hosts).\n *\n * The string to sign is:\n * METHOD\\n Accept\\n Content-MD5\\n Content-Type\\n Date\\n <signed-headers-block> <resource>\n * where the signed-headers block is `Name:Value\\n` for each header named in\n * `X-Ca-Signature-Headers`, sorted case-insensitively, and `resource` is the\n * path plus, for form bodies, the `?`-joined sorted form params.\n *\n * Verified byte-for-byte against a captured connect.json request (see tests).\n */\n\nexport interface SignedRequest {\n headers: Record<string, string>;\n body: string;\n}\n\nexport interface SignInput {\n method: 'POST' | 'GET';\n host: string;\n path: string;\n appKey: string;\n appSecret: string;\n /** JSON payload → sent as the raw body with a Content-MD5. */\n json?: unknown;\n /** Form fields → sent as application/x-www-form-urlencoded and folded into the resource. */\n form?: Record<string, string>;\n /** Extra x-ca headers to sign (e.g. `{ 'X-Ca-Stage': 'RELEASE' }`). */\n extraCaHeaders?: Record<string, string>;\n /** Overrides for deterministic tests. */\n nonce?: string;\n timestamp?: number;\n date?: string;\n}\n\nconst CT_JSON = 'application/json; charset=UTF-8';\nconst CT_FORM = 'application/x-www-form-urlencoded; charset=UTF-8';\n\nfunction md5Base64(s: string): string {\n return createHash('md5').update(s, 'utf8').digest('base64');\n}\n\n/** Build the x-ca headers + body for a request. */\nexport function signApiGatewayRequest(input: SignInput): SignedRequest {\n const accept = CT_JSON;\n const contentType = input.json !== undefined ? CT_JSON : CT_FORM;\n const date = input.date ?? new Date().toUTCString();\n\n let body: string;\n let contentMd5 = '';\n let formParams: Record<string, string> | undefined;\n if (input.json !== undefined) {\n body = JSON.stringify(input.json);\n contentMd5 = md5Base64(body);\n } else {\n const form = input.form ?? {};\n body = Object.entries(form)\n .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)\n .join('&');\n formParams = form;\n }\n\n const caHeaders: Record<string, string> = {\n 'X-Ca-Key': input.appKey,\n 'X-Ca-Nonce': input.nonce ?? crypto.randomUUID().toUpperCase(),\n 'X-Ca-Timestamp': String(input.timestamp ?? Date.now()),\n 'X-Ca-Version': '1',\n ...(input.extraCaHeaders ?? {}),\n };\n\n const signedHeaderNames = Object.keys(caHeaders).sort((a, b) =>\n a.toLowerCase() < b.toLowerCase() ? -1 : a.toLowerCase() > b.toLowerCase() ? 1 : 0,\n );\n const headerBlock = signedHeaderNames.map((k) => `${k}:${caHeaders[k]}\\n`).join('');\n\n let resource = input.path;\n if (formParams && Object.keys(formParams).length > 0) {\n const sorted = Object.keys(formParams).sort();\n resource += '?' + sorted.map((k) => `${k}=${formParams[k]}`).join('&');\n }\n\n const stringToSign = `${input.method}\\n${accept}\\n${contentMd5}\\n${contentType}\\n${date}\\n${headerBlock}${resource}`;\n const signature = createHmac('sha1', input.appSecret).update(stringToSign, 'utf8').digest('base64');\n\n const headers: Record<string, string> = {\n 'Content-Type': contentType,\n Accept: accept,\n Date: date,\n 'X-Ca-Signature': signature,\n 'X-Ca-Signature-Method': 'HmacSHA1',\n 'X-Ca-Signature-Headers': signedHeaderNames.join(','),\n ...caHeaders,\n };\n if (contentMd5) {\n headers['Content-MD5'] = contentMd5;\n }\n return { headers, body };\n}\n","import { z } from 'zod';\nimport { defaultFetch, type FetchImpl } from '../../transport/fetch.js';\nimport { DreameApiError, DreameDeviceOfflineError } from '../../transport/errors.js';\n\n/** LinkVisual code meaning the device's video agent isn't connected yet (waking). */\nexport const VISION_DEVICE_OFFLINE = 9201;\nimport {\n ALIYUN_APP_KEY,\n ALIYUN_APP_SECRET,\n apiIotHost,\n VISION_STREAM_QUERY,\n} from './constants.js';\nimport { signApiGatewayRequest } from './signing.js';\n\n/** LinkVisual live-stream descriptor returned by /vision/customer/stream/query. */\nexport interface StreamInfo {\n /** Connection strategies the cloud offers, e.g. [\"Direct\",\"Relay\",\"NAT\"]. */\n typeList: string[];\n /** RTMP relay URL. Plain (playable directly) when requested with relayEncrypted:false. */\n relayUrl: string | null;\n /** AES key/iv for the relay payload — present (non-empty) only when relayEncrypted:true. */\n relayDecryptKey: { iv: string; key: string } | null;\n /** P2P (Direct/NAT) parameters: STUN/TURN + the WebSocket signaling URL. */\n p2pInfo: {\n stunUrl?: string;\n alterStunUrl?: string;\n signalUrl?: string;\n portDetectStunUrlList?: string[];\n } | null;\n supportVisionPtz?: boolean;\n}\n\nconst StreamQueryResponseSchema = z\n .object({\n code: z.number().optional(),\n message: z.string().nullish(),\n data: z\n .object({\n typeList: z.array(z.string()).optional(),\n relayUrl: z.string().optional(),\n relayDecryptKey: z\n .object({ iv: z.string().optional(), key: z.string().optional() })\n .passthrough()\n .optional(),\n p2pInfo: z\n .object({\n stunUrl: z.string().optional(),\n alterStunUrl: z.string().optional(),\n signalUrl: z.string().optional(),\n portDetectStunUrlList: z.array(z.string()).optional(),\n })\n .passthrough()\n .optional(),\n supportVisionPtz: z.boolean().optional(),\n })\n .passthrough()\n .optional(),\n })\n .passthrough();\n\nexport interface StreamQueryInput {\n /** Aliyun IoT region id, e.g. `eu-central-1`. */\n regionId: string;\n /** Short-lived IoT identity token (from createSessionByAuthCode). */\n iotToken: string;\n /** LinkVisual device id. */\n iotId: string;\n /**\n * When false (default) the relay RTMP carries a PLAIN (unencrypted) FLV, so it\n * can be handed straight to ffmpeg. When true the payload is AES-encrypted and\n * `relayDecryptKey` is returned.\n */\n relayEncrypted?: boolean;\n /** 0 = HD main stream (default). */\n streamType?: number;\n appKey?: string;\n appSecret?: string;\n fetchImpl?: FetchImpl;\n signal?: AbortSignal;\n}\n\n/**\n * Query the LinkVisual live stream for a device. Returns the relay RTMP URL and\n * P2P parameters. Note: each returned relay token is single-use — request a\n * fresh one per consumer.\n */\nexport async function streamQuery(input: StreamQueryInput): Promise<StreamInfo> {\n const appKey = input.appKey ?? ALIYUN_APP_KEY;\n const appSecret = input.appSecret ?? ALIYUN_APP_SECRET;\n const host = apiIotHost(input.regionId);\n const payload = {\n id: crypto.randomUUID().toUpperCase(),\n params: {\n streamType: input.streamType ?? 0,\n needDomainName: true,\n relayEncryptType: 0,\n clientType: 'iOS',\n iotId: input.iotId,\n enablePortPredict: true,\n enableWebSocket: true,\n forceIFrame: true,\n cacheDuration: 3000,\n relayEncrypted: input.relayEncrypted ?? false,\n },\n request: { language: 'en-US', appKey, iotToken: input.iotToken, apiVer: '2.2.0' },\n version: '1.0.0',\n };\n const signed = signApiGatewayRequest({\n method: 'POST',\n host,\n path: VISION_STREAM_QUERY,\n appKey,\n appSecret,\n json: payload,\n extraCaHeaders: { 'X-Ca-Stage': 'RELEASE' },\n });\n\n const fetchImpl = input.fetchImpl ?? defaultFetch;\n const res = await fetchImpl(`https://${host}${VISION_STREAM_QUERY}`, {\n method: 'POST',\n headers: signed.headers,\n body: signed.body,\n ...(input.signal ? { signal: input.signal } : {}),\n });\n const text = await res.text();\n let json: unknown;\n try {\n json = JSON.parse(text);\n } catch {\n throw new DreameApiError(`stream/query returned non-JSON (status ${res.status})`, res.status);\n }\n const parsed = StreamQueryResponseSchema.parse(json);\n if (parsed.code === VISION_DEVICE_OFFLINE) {\n // The device's video agent is not connected yet — querying wakes it via the\n // LinkVisual cloud, so this is retryable (see DreameVideoSession.getStreamInfo).\n throw new DreameDeviceOfflineError(\n `stream/query: device video agent offline (code ${VISION_DEVICE_OFFLINE}) — retry to wake`,\n res.status,\n parsed,\n );\n }\n if (parsed.code !== 200 && parsed.code !== 0) {\n throw new DreameApiError(\n `stream/query rejected: code=${parsed.code} msg=${parsed.message ?? '?'}`,\n res.status,\n parsed,\n );\n }\n const d = parsed.data ?? {};\n const dk = d.relayDecryptKey;\n const hasKey = dk && dk.iv && dk.key;\n const p = d.p2pInfo;\n const p2pInfo: StreamInfo['p2pInfo'] = p\n ? {\n ...(p.stunUrl !== undefined ? { stunUrl: p.stunUrl } : {}),\n ...(p.alterStunUrl !== undefined ? { alterStunUrl: p.alterStunUrl } : {}),\n ...(p.signalUrl !== undefined ? { signalUrl: p.signalUrl } : {}),\n ...(p.portDetectStunUrlList !== undefined\n ? { portDetectStunUrlList: p.portDetectStunUrlList }\n : {}),\n }\n : null;\n return {\n typeList: d.typeList ?? [],\n relayUrl: d.relayUrl ?? null,\n relayDecryptKey: hasKey ? { iv: dk.iv as string, key: dk.key as string } : null,\n p2pInfo,\n ...(d.supportVisionPtz !== undefined ? { supportVisionPtz: d.supportVisionPtz } : {}),\n };\n}\n","import type { DreameRegion } from '../../auth/config.js';\nimport type { DreameSession } from '../../cloud/types.js';\nimport type { FetchImpl } from '../../transport/http.js';\nimport { getAliyunAuthCode } from '../client.js';\nimport { ALIYUN_REGION_ID } from './constants.js';\nimport { DreameDeviceOfflineError } from '../../transport/errors.js';\nimport { createIotSession, loginByOauth, type IotSession } from './identity.js';\nimport { streamQuery, type StreamInfo } from './vision.js';\n\n/** Default budget to wake a cold device via retried stream/query. */\nconst DEFAULT_WAKE_TIMEOUT_MS = 30_000;\nconst DEFAULT_WAKE_POLL_MS = 2_000;\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n/** Refresh the IoT session this many ms before it actually expires. */\nconst REFRESH_LEEWAY_MS = 60_000;\n\nexport interface DreameVideoSessionInput {\n /** An authenticated Dreame account session (from `Nodreame`). */\n session: DreameSession;\n region: DreameRegion;\n fetchImpl?: FetchImpl;\n}\n\n/**\n * High-level video session for a Dreame account. Establishes and caches the\n * Aliyun IoT identity (iotToken), and mints fresh LinkVisual RTMP stream URLs\n * on demand. Each returned relay URL is single-use — call {@link getStreamInfo}\n * (or {@link getRtmpUrl}) once per consumer.\n *\n * The plain (unencrypted) relay URL is a standard RTMP stream: hand it straight\n * to ffmpeg / scrypted / camstack. No local muxing or RTSP server required here.\n */\nexport class DreameVideoSession {\n readonly #session: DreameSession;\n readonly #region: DreameRegion;\n readonly #regionId: string;\n readonly #fetchImpl: FetchImpl | undefined;\n #iot: IotSession | null = null;\n #inFlight: Promise<IotSession> | null = null;\n\n constructor(input: DreameVideoSessionInput) {\n this.#session = input.session;\n this.#region = input.region;\n this.#regionId = ALIYUN_REGION_ID[input.region];\n this.#fetchImpl = input.fetchImpl;\n }\n\n /** The resolved Aliyun IoT region id (e.g. `eu-central-1`). */\n get regionId(): string {\n return this.#regionId;\n }\n\n /** Mint (or reuse) a valid Aliyun IoT session, refreshing before expiry. */\n async ensureIotSession(): Promise<IotSession> {\n const cur = this.#iot;\n if (cur && Date.now() < cur.expiresAt - REFRESH_LEEWAY_MS) {\n return cur;\n }\n if (this.#inFlight) {\n return this.#inFlight;\n }\n this.#inFlight = this.#mint();\n try {\n this.#iot = await this.#inFlight;\n return this.#iot;\n } finally {\n this.#inFlight = null;\n }\n }\n\n async #mint(): Promise<IotSession> {\n const base = {\n session: this.#session,\n region: this.#region,\n ...(this.#fetchImpl ? { fetchImpl: this.#fetchImpl } : {}),\n };\n const authCode = await getAliyunAuthCode(base);\n const oa = await loginByOauth({\n authCode,\n regionId: this.#regionId,\n ...(this.#fetchImpl ? { fetchImpl: this.#fetchImpl } : {}),\n });\n return createIotSession({\n sid: oa.sid,\n regionId: this.#regionId,\n ...(this.#fetchImpl ? { fetchImpl: this.#fetchImpl } : {}),\n });\n }\n\n /**\n * Mint a fresh LinkVisual stream descriptor for a device. `encrypted:false`\n * (default) yields a plain RTMP relay URL directly playable by ffmpeg.\n *\n * A camera that has been idle reports \"device offline\" until the query wakes\n * its video agent via the LinkVisual cloud. This transparently retries on that\n * (up to `wakeTimeoutMs`, default 30s) so a single call returns a live stream;\n * pass `wakeTimeoutMs: 0` to fail fast instead.\n */\n async getStreamInfo(\n iotId: string,\n opts?: { encrypted?: boolean; wakeTimeoutMs?: number; pollMs?: number },\n ): Promise<StreamInfo> {\n const deadline = Date.now() + (opts?.wakeTimeoutMs ?? DEFAULT_WAKE_TIMEOUT_MS);\n const pollMs = opts?.pollMs ?? DEFAULT_WAKE_POLL_MS;\n for (;;) {\n const iot = await this.ensureIotSession();\n try {\n return await streamQuery({\n regionId: this.#regionId,\n iotToken: iot.iotToken,\n iotId,\n relayEncrypted: opts?.encrypted ?? false,\n ...(this.#fetchImpl ? { fetchImpl: this.#fetchImpl } : {}),\n });\n } catch (err) {\n if (err instanceof DreameDeviceOfflineError && Date.now() + pollMs < deadline) {\n await sleep(pollMs);\n continue;\n }\n throw err;\n }\n }\n }\n\n /**\n * Convenience: mint a fresh plain RTMP relay URL for a device, or `null` when\n * the device is not currently streaming (no relay available).\n */\n async getRtmpUrl(iotId: string): Promise<string | null> {\n const info = await this.getStreamInfo(iotId, { encrypted: false });\n return info.relayUrl;\n }\n}\n","import type { DeviceVideoProfile } from './types.js';\n\n/**\n * Why this device cannot open a camera channel — or `null` when it can.\n *\n * The camera path needs one thing from the cloud: the `iotId`, which is the\n * CHANNEL id `startMonitor` addresses (for both vendors — see\n * `startMonitorParams`). When it is absent the call cannot proceed, and for a\n * long time that was reported as a single sentence covering four different\n * situations:\n *\n * \"device has no LinkVisual iotId; not a camera device or not provisioned\"\n *\n * A CamStack operator met it on a robot whose camera they had just enabled in\n * the Dreame app (2026-09-16). Working out which of the four they were in\n * meant reading this library's source — the sentence names a MISSING FIELD,\n * not a cause, and the profile sitting right next to it already knows whether\n * the device is a camera at all, whether it is online, and which vendors it is\n * on and supports.\n *\n * The four states want four different actions:\n *\n * - not a camera → wrong device; nothing done here will help\n * - offline → wait; the binding is published BY the device\n * - camera, online, no channel → open it once in the vendor app, which is\n * what creates the binding\n * - channel present → not this function's business\n *\n * Pure, so it is testable against captured profiles without a cloud.\n */\nexport function explainNoCameraChannel(profile: DeviceVideoProfile): string | null {\n if (profile.iotId) {\n return null;\n }\n const who = `${profile.model || 'unknown model'} (${profile.did || 'no did'})`;\n if (!profile.videoCapable) {\n return (\n `${who} is not a camera: the cloud lists no video permit and no supported ` +\n `video vendor for it, so there is no channel to open.`\n );\n }\n if (!profile.online) {\n return (\n `${who} is a camera but is OFFLINE. The video binding is published by the ` +\n `device itself, so there is nothing to read until it connects — this is ` +\n `not a provisioning fault.`\n );\n }\n const supports = profile.supportedVendors.length > 0 ? profile.supportedVendors.join(', ') : 'none';\n // ON A VENDOR WE DO NOT SPEAK.\n //\n // `iotId` is read from the ALIYUN bind endpoint (`iotuserbind/device/info`),\n // so a device provisioned on Tencent has none BY CONSTRUCTION — it is not\n // missing, it does not live there. Measured on a live X50 on 2026-09-16\n // while its owner was watching the stream in the Dreame app: `vendor: 'tx'`,\n // `supports: ['tx','ali']`, `iotId: null`.\n //\n // Telling that owner to \"open it once in the app to create the binding\" is\n // advice that cannot work, and it was given — while the app was streaming.\n // A diagnostic that sends someone to do a useless thing is worse than one\n // that says nothing, so this case says what is actually true.\n if (profile.currentVendor === 'tx') {\n return (\n `${who} is a camera and online, but is provisioned on the TENCENT video ` +\n `vendor (vendor: tx; supports: ${supports}), and this library implements ` +\n `only the Aliyun LinkVisual path — the channel id it needs lives on ` +\n `Aliyun and a tx device has none. Re-pairing or re-opening the camera in ` +\n `the app will not change this.`\n );\n }\n // A camera, online, on no vendor at all: it has never bound to the video\n // service. Streaming it once in the vendor app is what creates the binding —\n // and here that advice is right.\n const on = profile.currentVendor ?? 'none';\n return (\n `${who} is a camera and online, but has no video channel yet ` +\n `(vendor: ${on}; supports: ${supports}). The device has not bound to the ` +\n `video service — open its camera once in the Dreame app to create the ` +\n `binding, then retry.`\n );\n}\n","/**\n * MIoT surface of the Dreame camera service (SIID 10001), reversed byte-exact\n * from the r2538z (X50) React-Native plugin `Monitor` model. Every camera\n * control the app issues is a MIoT `action` on this service; the values below\n * are the verbatim aiid/piid maps from the bundle.\n */\n\n/** The camera/monitor service id. Same across the X40/X50 vacuum class. */\nexport const MONITOR_SIID = 10001;\n\n/** Action instance ids on {@link MONITOR_SIID}. */\nexport const MONITOR_AIID = {\n /** Stream lifecycle + keep-alive (monitor start/end, keep_alive). */\n CAMERA_OPERATE: 1,\n /** Two-way intercom start/end. */\n VOICE_OPERATE: 2,\n /** Generic property-scoped operations. */\n PROPERTY_OPERATE: 3,\n /** Privacy access-code gate (launch/verify/set/reset). */\n ACCESS_CODE_OPERATE: 4,\n /** Video-vendor SDK init / switch. */\n VIDEO_VENDOR: 7,\n} as const;\n\n/** Property instance ids on {@link MONITOR_SIID}. */\nexport const MONITOR_PIID = {\n MONITOR_STATUS: 1,\n MONITOR_AUDIO_STATUS: 2,\n MONITOR_RECORD_STATUS: 4,\n TAKE_PHOTO: 5,\n KEEP_ALIVE: 6,\n MONITOR_FAULTS: 7,\n FILL_LIGHT_SET: 9,\n CAMERA_LIGHT_SWITCH: 10,\n VIDEO_VENDOR_STATUS: 11,\n GET_PROPERTY: 99,\n PERSON_DATA: 110,\n VIDEO_OBSTACLE_SWITCH: 111,\n VIDEO_OBSTACLE_DATA: 112,\n MONITOR_TASK_STATUS: 103,\n UPLOAD_STATUS: 1003,\n GET_ACCESS_CODE: 1100,\n SET_ACCESS_CODE: 1101,\n VERIFY_ACCESS_CODE: 1102,\n MONITOR_REST_MODE: 1103,\n SET_ACCESS_HOT_STATUS: 1104,\n MONITOR_SPACE: 2003,\n} as const;\n\n/**\n * The `token` field the `startMonitor` action expects, keyed by vendor. Aliyun\n * LinkVisual uses the literal `\"alify\"`; Tencent uses `\"tx\"`. (Agora passes an\n * RTC access token instead and is not modelled here.)\n */\nexport const MONITOR_VENDOR_TOKEN: Record<'ali' | 'tx', string> = {\n ali: 'alify',\n tx: 'tx',\n};\n\n/** Fill-light: values 40..100 are manual %, below 40 (or 101) select auto. */\nexport const FILL_LIGHT_MIN = 40;\n\n/**\n * The vacuum work service (SIID 4). The camera has no gimbal — \"PTZ\" and the\n * camera-adjacent controls (remote drive, go-to-point, person-follow) drive the\n * robot itself through this service.\n */\nexport const VACUUM_SIID = 4;\nexport const VACUUM_MOVE = {\n /** Remote-drive state property (set_properties), sent ~1 Hz while held. */\n REMOTE_STATE_PIID: 15,\n /** Work-mode action instance (go-to-point, cruise, person-follow start). */\n WORK_AIID: 1,\n /** Work-mode stop action instance (person-follow stop). */\n STOP_AIID: 2,\n /** piid carrying the work-mode selector value in the WORK action. */\n MODE_PIID: 1,\n /** piid carrying the point-info JSON in the WORK action. */\n POINT_PIID: 10,\n /** Work-mode selector values. */\n MODE_SPOT: 23, // go to point (MonitorSpotMode)\n MODE_CRUISE: 22, // cruise / patrol / find-pet\n MODE_PERSON_FOLLOW: 26,\n MODE_SPOT_CLEAN: 20, // spot-clean the current position (SpotClean)\n /** Auto-switch setter property (fill-light auto/full toggle lives here). */\n AUTO_SWITCH_PIID: 50,\n} as const;\n\n/** Return-to-dock / charge action (battery service). */\nexport const VACUUM_CHARGE = { siid: 3, aiid: 1 } as const;\n\n/**\n * Voice/audio service (SIID 7). aiid 1 = position/locate (robot beeps), aiid 2 =\n * playSound (play a sound clip by id, carried in piid 4).\n */\nexport const VOICE_SIID = 7;\nexport const VOICE_LOCATE_AIID = 1;\nexport const VOICE_PLAY_SOUND_AIID = 2;\nexport const VOICE_SOUND_PIID = 4;\n/** Locate action — the robot beeps so you can find it. */\nexport const VACUUM_LOCATE = { siid: VOICE_SIID, aiid: VOICE_LOCATE_AIID } as const;\n\n/** Pet-teasing sound clips playable via {@link VOICE_PLAY_SOUND_AIID}. */\nexport const PET_SOUNDS = {\n meow: 684,\n bark: 685,\n footsteps: 686,\n purring: 687,\n tickTock: 688,\n} as const;\nexport type PetSound = keyof typeof PET_SOUNDS;\n\n/** Common whole-robot actions (usable as camera presets). */\nexport const VACUUM_ACTIONS = {\n startClean: { siid: 2, aiid: 1 },\n pauseClean: { siid: 2, aiid: 2 },\n stopClean: { siid: 4, aiid: 2 },\n dockWash: { siid: 4, aiid: 4 },\n autoEmpty: { siid: 15, aiid: 1 },\n} as const;\nexport type VacuumActionKey = keyof typeof VACUUM_ACTIONS;\n","/**\n * Pure protocol helpers for the camera monitor service — no I/O, so every wire\n * shape here is unit-testable in isolation. The device wraps each action value\n * as `JSON.stringify({ ...params, session })`, where `session` is a per-session\n * correlation token; these builders reproduce that envelope exactly.\n */\nimport { createHash } from 'node:crypto';\nimport { MONITOR_PIID, MONITOR_VENDOR_TOKEN } from './constants.js';\n\n/** One `in[]` entry of a MIoT action: a piid plus a JSON-encoded value. */\nexport interface MonitorActionInput {\n readonly piid: number;\n readonly value: string;\n}\n\n/** The parsed shape of a device action reply (`callAction` resolves to this). */\nexport interface MonitorActionResult {\n readonly code?: number;\n readonly out?: ReadonlyArray<{ piid?: number; value?: unknown }>;\n readonly result?: { out?: ReadonlyArray<{ piid?: number; value?: unknown }> };\n}\n\n/**\n * Per-session correlation token, exactly as the app's `Monitor.updateSession()`:\n * `MD5(accountId + \"_\" + epochMillis)`. Pass an explicit `now` for deterministic\n * tests. `accountId` is the Dreame account uid.\n */\nexport function makeMonitorSession(accountId: string, now: number = Date.now()): string {\n return createHash('md5').update(`${accountId}_${now}`).digest('hex');\n}\n\n/** SHA-256 hex of a privacy access code, as `Monitor.verifyAccessCode` sends it. */\nexport function hashAccessCode(code: string): string {\n return createHash('sha256').update(code).digest('hex');\n}\n\n/**\n * Build a single action `in[]` entry: merge `params` with the `session` (session\n * wins on key clash, matching the plugin) and JSON-encode. `params` of `null`\n * yields just `{ session }`.\n */\nexport function buildActionInput(\n piid: number,\n params: Record<string, unknown> | null,\n session: string,\n): MonitorActionInput {\n const value = JSON.stringify({ ...(params ?? {}), session });\n return { piid, value };\n}\n\n/** `startMonitor` value params for a given vendor + channel (iotId). */\nexport function startMonitorParams(\n iotId: string,\n vendor: 'ali' | 'tx',\n area: string,\n): Record<string, unknown> {\n return {\n token: MONITOR_VENDOR_TOKEN[vendor],\n channelId: iotId,\n area,\n operType: 'monitor',\n operation: 'start',\n };\n}\n\n/** `stopMonitor` value params. */\nexport function stopMonitorParams(): Record<string, unknown> {\n return { operType: 'monitor', operation: 'end' };\n}\n\n/** `keep_alive` value params. `videoStatus` is `preparing` until the relay is up, then `opened`. */\nexport function keepAliveParams(videoStatus: 'preparing' | 'opened'): Record<string, unknown> {\n return { operType: 'keep_alive', videoStatus };\n}\n\n/** `getAccessCodeLaunch` params — primes the camera pipeline and reports PIN state. */\nexport function accessCodeLaunchParams(): Record<string, unknown> {\n return { open: true };\n}\n\n/** `verifyAccessCode` params from a plaintext code. `lazymode` mirrors the app default. */\nexport function verifyAccessCodeParams(code: string, lazymode = 0): Record<string, unknown> {\n return { oldcode: hashAccessCode(code), lazymode };\n}\n\n/** `intercom` start params. `phone:1` marks a video-call intercom. */\nexport function intercomStartParams(opts?: {\n needRecordSound?: boolean;\n videoCall?: boolean;\n}): Record<string, unknown> {\n return {\n operType: 'intercom',\n operation: 'start',\n ...(opts?.needRecordSound !== undefined ? { need_record_sound: opts.needRecordSound } : {}),\n ...(opts?.videoCall ? { phone: 1 } : {}),\n };\n}\n\n/** `intercom` stop params. */\nexport function intercomStopParams(): Record<string, unknown> {\n return { operType: 'intercom', operation: 'end' };\n}\n\n/** Fill-light brightness params. Value is sent as a STRING, per the plugin. */\nexport function fillLightParams(value: number): Record<string, unknown> {\n return { value: String(value) };\n}\n\n/** Device-side snapshot (`takephoto`) params. */\nexport function takePhotoParams(): Record<string, unknown> {\n return { operType: 'takephoto', operation: 'start' };\n}\n\n/**\n * Remote-drive value (siid 4 / piid 15), sent ~1 Hz while a direction is held.\n * `spdv` = forward speed (200 fwd, 0 stop), `spdw` = turn (45 left, -45 right,\n * 180 turn-around, 0 straight). Pass an explicit `now` for deterministic tests.\n */\nexport function remoteDriveValue(\n spdv: number,\n spdw: number,\n now: number = Date.now(),\n): string {\n return JSON.stringify({\n spdv,\n spdw,\n audio: 'false',\n random: Math.floor(Math.random() * 1000),\n timestamp: now,\n });\n}\n\n/** Named remote-drive directions, mapping to (spdv, spdw). */\nexport const DRIVE_DIRECTIONS = {\n forward: [200, 0],\n left: [0, 45],\n right: [0, -45],\n turnAround: [0, 180],\n stop: [0, 0],\n} as const;\nexport type DriveDirection = keyof typeof DRIVE_DIRECTIONS;\n\n/** The device business `code` from an action reply (0 = accepted); `null` if absent. */\nexport function actionCode(res: unknown): number | null {\n const r = res as MonitorActionResult | null | undefined;\n return typeof r?.code === 'number' ? r.code : null;\n}\n\n/** First `out[0].value` from an action reply (handles both `out` and `result.out`). */\nexport function firstOutValue(res: unknown): unknown {\n const r = res as MonitorActionResult | null | undefined;\n const out = r?.out ?? r?.result?.out;\n return Array.isArray(out) && out.length > 0 ? out[0]?.value : undefined;\n}\n","/**\n * Autonomous cold-start of a Dreame vacuum camera stream.\n *\n * Reproduces the exact sequence the r2538z (X50) app runs when you open the\n * camera — verified end-to-end against a deep-cold device:\n *\n * 1. getAccessCodeLaunch (aiid 4 / piid 1100 {open:true}) — primes the pipeline\n * 2. initCameraSdk (aiid 7 / piid 11 {}) — boots the on-device video agent\n * 3. startMonitor (aiid 1 / piid 1 …start) — on code:-1 with a PIN set,\n * verifyAccessCode (aiid 4 / piid 1102 sha256(PIN)) — satisfy the privacy gate,\n * startMonitor (retry) — now accepted (code:0 + encryptionKey)\n * 4. keep_alive loop (aiid 1 / piid 6) — every ~10s, or the device tears down\n * 5. stream/query (Aliyun LinkVisual) — mint the plain RTMP relay URL\n *\n * The resulting relay URL is a standard RTMP stream (H.264 + AAC) any consumer\n * (scrypted / camstack / ffmpeg) can pull directly. Call {@link close} to stop\n * the keep-alive loop and release the monitor on the device.\n */\nimport { DreameError } from '../../transport/errors.js';\nimport {\n MONITOR_AIID,\n MONITOR_PIID,\n MONITOR_SIID,\n VACUUM_SIID,\n VACUUM_MOVE,\n VACUUM_CHARGE,\n VACUUM_LOCATE,\n VACUUM_ACTIONS,\n VOICE_SIID,\n VOICE_PLAY_SOUND_AIID,\n VOICE_SOUND_PIID,\n PET_SOUNDS,\n type PetSound,\n type VacuumActionKey,\n} from './constants.js';\nimport {\n accessCodeLaunchParams,\n actionCode,\n buildActionInput,\n fillLightParams,\n firstOutValue,\n intercomStartParams,\n intercomStopParams,\n keepAliveParams,\n makeMonitorSession,\n remoteDriveValue,\n startMonitorParams,\n stopMonitorParams,\n takePhotoParams,\n verifyAccessCodeParams,\n DRIVE_DIRECTIONS,\n type DriveDirection,\n} from './protocol.js';\n\n/**\n * MIoT device transport the controller drives — satisfied by a nodedreame device\n * handle (VacuumDevice). `setProperty` is optional so callAction-only fakes still\n * satisfy the interface; the robot-drive helpers require it.\n */\nexport interface MonitorActionCaller {\n callAction(siid: number, aiid: number, input: unknown[]): Promise<unknown>;\n setProperty?(write: { siid: number; piid: number; value: unknown }): Promise<unknown>;\n}\n\n/** Minimal relay minter — satisfied by {@link DreameVideoSession}. */\nexport interface RelayMinter {\n getStreamInfo(\n iotId: string,\n opts?: { encrypted?: boolean; wakeTimeoutMs?: number; pollMs?: number },\n ): Promise<{ relayUrl: string | null }>;\n}\n\nexport interface DreameCameraControllerInput {\n /** MIoT action transport (the device handle). */\n readonly device: MonitorActionCaller;\n /** Aliyun relay minter (a {@link DreameVideoSession}). */\n readonly relay: RelayMinter;\n /** LinkVisual channel id for this device (`iotId`). */\n readonly iotId: string;\n /** Dreame account uid — seeds the per-session correlation token. */\n readonly accountId: string;\n /** Video vendor. Only `ali` mints an RTMP relay; defaults to `ali`. */\n readonly vendor?: 'ali' | 'tx';\n /** Privacy access code (PIN) set at pairing, if any. Required to wake a coded camera. */\n readonly accessCode?: string;\n /** Aliyun region code the device reports as `area`; defaults to `'4'`. */\n readonly area?: string;\n /** Keep-alive cadence in ms (app default 10s). */\n readonly keepAliveIntervalMs?: number;\n /** Max time to wait for the relay to come up while the device wakes. */\n readonly wakeTimeoutMs?: number;\n /** Reported when a keep-alive tick fails (the stream is likely dying). */\n readonly onKeepAliveError?: (err: unknown) => void;\n /** Optional line logger (nodelink-style). Defaults to no-op. */\n readonly log?: (line: string) => void;\n}\n\n/** A live camera stream: the pullable relay URL plus the device's per-stream key. */\nexport interface CameraStreamHandle {\n /** Plain RTMP relay URL (H.264 + AAC). Single stream; re-open for another consumer. */\n readonly rtmpUrl: string;\n /** Device-generated per-session encryption key echoed by `startMonitor`. */\n readonly encryptionKey: string | null;\n}\n\nconst DEFAULT_KEEP_ALIVE_MS = 10_000;\nconst DEFAULT_AREA = '4';\n\nexport class DreameCameraController {\n readonly #device: MonitorActionCaller;\n readonly #relay: RelayMinter;\n readonly #iotId: string;\n readonly #accountId: string;\n readonly #vendor: 'ali' | 'tx';\n readonly #accessCode: string | undefined;\n readonly #area: string;\n readonly #keepAliveIntervalMs: number;\n readonly #wakeTimeoutMs: number | undefined;\n readonly #onKeepAliveError: ((err: unknown) => void) | undefined;\n readonly #log: (line: string) => void;\n\n #session: string | null = null;\n #keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n #opened = false;\n\n constructor(input: DreameCameraControllerInput) {\n this.#device = input.device;\n this.#relay = input.relay;\n this.#iotId = input.iotId;\n this.#accountId = input.accountId;\n this.#vendor = input.vendor ?? 'ali';\n this.#accessCode = input.accessCode;\n this.#area = input.area ?? DEFAULT_AREA;\n this.#keepAliveIntervalMs = input.keepAliveIntervalMs ?? DEFAULT_KEEP_ALIVE_MS;\n this.#wakeTimeoutMs = input.wakeTimeoutMs;\n this.#onKeepAliveError = input.onKeepAliveError;\n this.#log = input.log ?? ((): void => {});\n }\n\n #logLine(msg: string): void {\n this.#log(`[nodedreame:monitor] ${msg}`);\n }\n\n /** True while a monitor session is active (between {@link open} and {@link close}). */\n get isOpen(): boolean {\n return this.#opened;\n }\n\n /** Send one camera action, resolving to the raw device reply. */\n async #action(aiid: number, piid: number, params: Record<string, unknown> | null): Promise<unknown> {\n if (!this.#session) {\n throw new DreameError('camera controller has no session; call open() first');\n }\n const input = buildActionInput(piid, params, this.#session);\n return this.#device.callAction(MONITOR_SIID, aiid, [input]);\n }\n\n /**\n * Run the full cold-start sequence and return a live relay URL. Starts the\n * keep-alive loop; the caller MUST {@link close} when done to release the\n * device. Throws if the monitor cannot be started or no relay comes up.\n */\n async open(): Promise<CameraStreamHandle> {\n if (this.#opened) {\n throw new DreameError('camera stream already open');\n }\n this.#session = makeMonitorSession(this.#accountId);\n this.#logLine(`open: vendor=${this.#vendor} iotId=${this.#iotId} area=${this.#area}`);\n\n // 1) Prime the pipeline + boot the on-device video agent (best-effort:\n // failures here are non-fatal, startMonitor is the real gate).\n await this.#tryPrime();\n\n // 2) startMonitor — retry once behind the privacy gate if refused.\n const startRes = await this.#startMonitorWithGate();\n const encryptionKey = asString(firstOutValue(startRes));\n this.#logLine(`startMonitor accepted (encryptionKey=${encryptionKey ? 'yes' : 'no'})`);\n\n // 3) keep the monitor alive from now on (device tears down without it).\n this.#opened = true;\n this.#startKeepAlive();\n\n // 4) mint the relay (transparently retries while the device wakes).\n let relayUrl: string | null;\n try {\n const info = await this.#relay.getStreamInfo(this.#iotId, {\n encrypted: false,\n ...(this.#wakeTimeoutMs !== undefined ? { wakeTimeoutMs: this.#wakeTimeoutMs } : {}),\n });\n relayUrl = info.relayUrl;\n } catch (err) {\n this.#logLine(`relay mint failed: ${err instanceof Error ? err.message : String(err)}`);\n await this.close();\n throw err;\n }\n if (!relayUrl) {\n await this.close();\n throw new DreameError('monitor started but no relay URL was returned');\n }\n this.#logLine('relay up');\n\n return { rtmpUrl: relayUrl, encryptionKey };\n }\n\n async #tryPrime(): Promise<void> {\n try {\n await this.#action(MONITOR_AIID.ACCESS_CODE_OPERATE, MONITOR_PIID.GET_ACCESS_CODE, accessCodeLaunchParams());\n } catch {\n /* non-fatal */\n }\n try {\n await this.#action(MONITOR_AIID.VIDEO_VENDOR, MONITOR_PIID.VIDEO_VENDOR_STATUS, null);\n } catch {\n /* non-fatal */\n }\n }\n\n async #startMonitorWithGate(): Promise<unknown> {\n const params = startMonitorParams(this.#iotId, this.#vendor, this.#area);\n let res = await this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.MONITOR_STATUS, params);\n if (actionCode(res) === 0) {\n return res;\n }\n this.#logLine(`startMonitor refused (code ${actionCode(res) ?? '?'}); verifying access code`);\n // Refused: the camera's per-session privacy gate needs the PIN verified.\n if (this.#accessCode) {\n await this.#action(\n MONITOR_AIID.ACCESS_CODE_OPERATE,\n MONITOR_PIID.VERIFY_ACCESS_CODE,\n verifyAccessCodeParams(this.#accessCode),\n );\n res = await this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.MONITOR_STATUS, params);\n }\n if (actionCode(res) !== 0) {\n const code = actionCode(res);\n throw new DreameError(\n `startMonitor refused (code ${code ?? '?'})` +\n (this.#accessCode ? '' : '; device likely requires an accessCode'),\n );\n }\n return res;\n }\n\n #startKeepAlive(): void {\n this.#stopKeepAlive();\n this.#keepAliveTimer = setInterval(() => {\n void this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.KEEP_ALIVE, keepAliveParams('opened')).catch(\n (err: unknown) => this.#onKeepAliveError?.(err),\n );\n }, this.#keepAliveIntervalMs);\n // Don't hold the event loop open on the keep-alive timer alone.\n this.#keepAliveTimer.unref?.();\n }\n\n #stopKeepAlive(): void {\n if (this.#keepAliveTimer) {\n clearInterval(this.#keepAliveTimer);\n this.#keepAliveTimer = null;\n }\n }\n\n /**\n * Mint a FRESH single-use RTMP relay URL for the already-open monitor. Each\n * consumer needs its own relay URL, so call this once per ffmpeg/consumer.\n * Throws if the stream is not open.\n */\n async mintRelayUrl(): Promise<string> {\n if (!this.#opened) {\n throw new DreameError('camera stream is not open; call open() first');\n }\n const info = await this.#relay.getStreamInfo(this.#iotId, {\n encrypted: false,\n ...(this.#wakeTimeoutMs !== undefined ? { wakeTimeoutMs: this.#wakeTimeoutMs } : {}),\n });\n if (!info.relayUrl) {\n throw new DreameError('no relay URL available for the open monitor');\n }\n return info.relayUrl;\n }\n\n /** Start the two-way intercom session (control plane only; see module docs on mic uplink). */\n async startIntercom(opts?: { needRecordSound?: boolean; videoCall?: boolean }): Promise<unknown> {\n return this.#action(MONITOR_AIID.VOICE_OPERATE, MONITOR_PIID.MONITOR_AUDIO_STATUS, intercomStartParams(opts));\n }\n\n /** Stop the two-way intercom session. */\n async stopIntercom(): Promise<unknown> {\n return this.#action(MONITOR_AIID.VOICE_OPERATE, MONITOR_PIID.MONITOR_AUDIO_STATUS, intercomStopParams());\n }\n\n /** Set the fill-light brightness (40–100 = manual %, below 40 / 101 = auto). */\n async setFillLight(value: number): Promise<unknown> {\n return this.#action(MONITOR_AIID.PROPERTY_OPERATE, MONITOR_PIID.FILL_LIGHT_SET, fillLightParams(value));\n }\n\n /**\n * Device-side snapshot: the robot captures a still and uploads it to cloud\n * storage (read the result back from the `uploadStatus` property). Prefer a\n * frame-grab from the live stream for an instant image.\n */\n async takePhoto(): Promise<unknown> {\n return this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.TAKE_PHOTO, takePhotoParams());\n }\n\n /**\n * Remote-drive the robot (\"PTZ\" substitute — the camera is fixed, the robot\n * moves). `spdv` = forward speed, `spdw` = turn; send repeatedly (~1 Hz) while\n * a direction is held, then `stop`. Only valid while the stream is open.\n */\n async drive(spdv: number, spdw: number): Promise<unknown> {\n this.#requireSession();\n if (!this.#device.setProperty) {\n throw new DreameError('device does not support setProperty; cannot remote-drive');\n }\n return this.#device.setProperty({\n siid: VACUUM_SIID,\n piid: VACUUM_MOVE.REMOTE_STATE_PIID,\n value: remoteDriveValue(spdv, spdw),\n });\n }\n\n /** Remote-drive by named direction (forward/left/right/turnAround/stop). */\n async driveDirection(direction: DriveDirection): Promise<unknown> {\n const [spdv, spdw] = DRIVE_DIRECTIONS[direction];\n return this.drive(spdv, spdw);\n }\n\n /** Start person-follow mode (robot tracks a detected person). */\n async startPersonFollow(): Promise<unknown> {\n this.#requireSession();\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.WORK_AIID, [\n { piid: VACUUM_MOVE.MODE_PIID, value: VACUUM_MOVE.MODE_PERSON_FOLLOW },\n ]);\n }\n\n /**\n * Universal stop for the current work mode — stops person-follow, spot-clean,\n * go-to-point, cruise, etc. (siid 4 / aiid 2).\n */\n async stopWork(): Promise<unknown> {\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.STOP_AIID, []);\n }\n\n /** Stop person-follow mode (alias for {@link stopWork}). */\n async stopPersonFollow(): Promise<unknown> {\n return this.stopWork();\n }\n\n /** Spot-clean the robot's current position (WorkMode SpotClean). */\n async spotClean(): Promise<unknown> {\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.WORK_AIID, [\n { piid: VACUUM_MOVE.MODE_PIID, value: VACUUM_MOVE.MODE_SPOT_CLEAN },\n ]);\n }\n\n /** Play one of the robot's sound clips (e.g. pet sounds) by id. */\n async playSound(soundId: number): Promise<unknown> {\n return this.#device.callAction(VOICE_SIID, VOICE_PLAY_SOUND_AIID, [\n { piid: VOICE_SOUND_PIID, value: soundId },\n ]);\n }\n\n /** Play a named pet-teasing sound (meow/bark/footsteps/purring/tickTock). */\n async playPetSound(sound: PetSound): Promise<unknown> {\n return this.playSound(PET_SOUNDS[sound]);\n }\n\n /** Run a common whole-robot action (startClean/pauseClean/stopClean/dockWash/autoEmpty). */\n async runVacuumAction(action: VacuumActionKey): Promise<unknown> {\n const a = VACUUM_ACTIONS[action];\n return this.#device.callAction(a.siid, a.aiid, []);\n }\n\n /** Find-pet: cruise the home looking for the pet. */\n async findPet(): Promise<unknown> {\n this.#requireSession();\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.WORK_AIID, [\n { piid: VACUUM_MOVE.MODE_PIID, value: VACUUM_MOVE.MODE_CRUISE },\n { piid: VACUUM_MOVE.POINT_PIID, value: JSON.stringify({ findpet: '' }) },\n ]);\n }\n\n /** Send the robot to a map point (`spoint` current, `tpoint` targets). */\n async goToPoint(spoint: number[][], tpoint: number[][]): Promise<unknown> {\n this.#requireSession();\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.WORK_AIID, [\n { piid: VACUUM_MOVE.MODE_PIID, value: VACUUM_MOVE.MODE_SPOT },\n { piid: VACUUM_MOVE.POINT_PIID, value: JSON.stringify({ spoint, tpoint }) },\n ]);\n }\n\n /** Toggle the fill light between auto (`full=false`) and full-on (`full=true`). */\n async setFillLightAuto(full: boolean): Promise<unknown> {\n if (!this.#device.setProperty) {\n throw new DreameError('device does not support setProperty; cannot toggle fill light');\n }\n return this.#device.setProperty({\n siid: VACUUM_SIID,\n piid: VACUUM_MOVE.AUTO_SWITCH_PIID,\n value: JSON.stringify({ k: 'FillinLight', v: full ? 1 : 0 }),\n });\n }\n\n /**\n * Send the robot back to its dock to charge (\"go home\"). Works regardless of\n * stream state — usable as a standalone command or a PTZ \"home\" preset.\n */\n async returnToDock(): Promise<unknown> {\n return this.#device.callAction(VACUUM_CHARGE.siid, VACUUM_CHARGE.aiid, []);\n }\n\n /** Locate the robot — it beeps. Works regardless of stream state. */\n async locate(): Promise<unknown> {\n return this.#device.callAction(VACUUM_LOCATE.siid, VACUUM_LOCATE.aiid, []);\n }\n\n #requireSession(): void {\n if (!this.#opened) {\n throw new DreameError('camera stream is not open; call open() first');\n }\n }\n\n /** Stop the keep-alive loop and release the monitor on the device. Idempotent. */\n async close(): Promise<void> {\n this.#stopKeepAlive();\n if (!this.#opened) {\n this.#session = null;\n return;\n }\n this.#opened = false;\n this.#logLine('close: stopping monitor');\n try {\n await this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.MONITOR_STATUS, stopMonitorParams());\n } finally {\n this.#session = null;\n }\n }\n}\n\nfunction asString(v: unknown): string | null {\n return typeof v === 'string' ? v : null;\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 let inflated: Buffer;\n try {\n inflated = zlib.inflateSync(bytes);\n } catch (err) {\n throw new MapDecodeError('envelope: zlib inflate failed', { cause: err });\n }\n\n // Some models/firmwares DOUBLE-wrap live-clean frames: the first inflate yields\n // a URL-safe base64 STRING of ANOTHER zlib stream rather than the binary frame\n // (verified on dreame.vacuum.r2538z — head decodes to the `78 xx` zlib magic).\n // Peel extra base64→zlib layers until the payload is binary (bounded, so a\n // pathological input can't loop). A genuine binary frame is NOT entirely\n // base64-text (its int16 header fields + 0x00 padding fail the check), so the\n // single-wrap path is untouched.\n let guard = 0;\n while (guard < MAX_EXTRA_WRAP_LAYERS && looksLikeBase64Zlib(inflated)) {\n const text = inflated.toString('latin1').replace(/-/g, '+').replace(/_/g, '/');\n try {\n inflated = zlib.inflateSync(Buffer.from(text, 'base64'));\n } catch (err) {\n throw new MapDecodeError('envelope: nested zlib inflate failed', { cause: err });\n }\n guard += 1;\n }\n return inflated;\n}\n\n/** Upper bound on nested base64+zlib layers peeled by {@link unwrapEnvelope}. */\nconst MAX_EXTRA_WRAP_LAYERS = 4;\n\n/**\n * Heuristic: does this buffer look like a base64-text wrapper around ANOTHER\n * zlib stream (rather than a binary map frame)? True only when the WHOLE buffer\n * is URL-safe-base64 characters AND a short prefix decodes to the zlib magic\n * byte `0x78`. A binary frame contains non-base64 bytes (header int16s / `0x00`\n * padding), so it never matches.\n */\nexport function looksLikeBase64Zlib(buf: Buffer): boolean {\n if (buf.length < 8) return false;\n for (const b of buf) {\n const isB64 =\n (b >= 0x41 && b <= 0x5a) || // A-Z\n (b >= 0x61 && b <= 0x7a) || // a-z\n (b >= 0x30 && b <= 0x39) || // 0-9\n b === 0x2b ||\n b === 0x2f || // + /\n b === 0x2d ||\n b === 0x5f || // - _ (url-safe)\n b === 0x3d; // =\n if (!isB64) return false;\n }\n try {\n const prefix = buf.subarray(0, 16).toString('latin1').replace(/-/g, '+').replace(/_/g, '/');\n const decoded = Buffer.from(prefix, 'base64');\n return decoded.length >= 2 && decoded[0] === 0x78;\n } catch {\n return false;\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 * Dreame room SEGMENT-TYPE → display-name table. A robot segment carries a\n * `type` code in its `seg_inf` record; the Dreamehome app (and the HA\n * `dreame_vacuum` integration) render a localized DEFAULT name from that code\n * when the user has NOT assigned a custom name. A custom-named room is stored\n * with `type === 0` plus a base64 `name`. Ported verbatim from the donor\n * `dreame/types.py SEGMENT_TYPE_CODE_TO_NAME`.\n */\nexport const SEGMENT_TYPE_CODE_TO_NAME: Readonly<Record<number, string>> = {\n 0: 'Room',\n 1: 'Living Room',\n 2: 'Primary Bedroom',\n 3: 'Study',\n 4: 'Kitchen',\n 5: 'Dining Hall',\n 6: 'Bathroom',\n 7: 'Balcony',\n 8: 'Corridor',\n 9: 'Utility Room',\n 10: 'Closet',\n 11: 'Meeting Room',\n 12: 'Office',\n 13: 'Fitness Area',\n 14: 'Recreation Area',\n 15: 'Secondary Bedroom',\n};\n\n/** Decode a base64 segment name to UTF-8, or `null` on malformed input. */\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/**\n * Resolve a segment's display name, mirroring the donor `Segment.set_name`:\n * 1. a known room `type` (≠ 0) wins → its localized default name, suffixed\n * `\" {index+1}\"` for the 2nd+ room of the same type (e.g. `Bedroom 2`);\n * 2. otherwise a user-assigned base64 `customName` (stored with `type === 0`);\n * 3. otherwise `null` (the caller renders a `Room <id>` fallback).\n *\n * @param type `seg_inf.<id>.type` room-category code (0 = custom/none).\n * @param index `seg_inf.<id>.index` ordinal among same-type rooms.\n * @param customName `seg_inf.<id>.name` — base64-encoded user name, if any.\n */\nexport function resolveSegmentName(\n type: number | undefined,\n index: number | undefined,\n customName: string | undefined,\n): string | null {\n if (type !== undefined && type !== 0 && SEGMENT_TYPE_CODE_TO_NAME[type] !== undefined) {\n const base = SEGMENT_TYPE_CODE_TO_NAME[type];\n return index !== undefined && index > 0 ? `${base} ${index + 1}` : base;\n }\n if (typeof customName === 'string' && customName.length > 0) {\n return safeBase64ToUtf8(customName);\n }\n return null;\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';\nimport { resolveSegmentName } from './segment-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 // Mirror the donor: a room-type code (≠0) yields a localized default name\n // (e.g. \"Kitchen\"), else the user's custom base64 name, else null.\n name: resolveSegmentName(meta?.type, meta?.index, meta?.name),\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","/**\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 * P-frame merging.\n *\n * The live channel emits a base I-frame (full snapshot, OSS-fetched per\n * Phase 0 finding) followed by sequential P-frames carrying byte-add\n * deltas plus updated robot/dock pose, latest obstacle list, and an\n * incremental `tr` path fragment. To keep an up-to-date renderable map,\n * each P-frame must be merged onto the running state.\n *\n * Strategy (matches Tasshack `dreame/map.py:5018-5070`):\n * - allocate a new pixel buffer sized to the union of (prev bbox,\n * P-frame bbox) in world-frame mm\n * - copy prev pixels into the new buffer at the offset of prev's\n * world-frame origin within the union\n * - byte-add each P-frame pixel into the new buffer at its offset\n * (`out[i] = (out[i] + p[i]) & 0xFF`, intentional 8-bit wrap)\n * - re-stamp the result as `frame_type = I` (73). The pixel grid is\n * now an absolute classification, not a delta — and `decodeVacuumMap`\n * deliberately skips pixel decode on P-frames, so the merged buffer\n * must look like an I-frame to round-trip cleanly\n * - merge JSON tail: prefer P's keys (latest state), but APPEND P's\n * `tr` to prev's (the path is incremental), and fall back to prev's\n * `seg_inf` when P doesn't carry one (most P-frames don't)\n * - advance `frame_id` to P's, keep `map_id`\n *\n * The result is a synthetic inflated frame buffer that\n * `decodeVacuumMap(buffer)` consumes normally. No envelope wrap.\n *\n * For sequence enforcement: `mergePFrame` throws `OutOfOrderFrameError`\n * when `pframe.frameId !== prev.frameId + 1`. The frame-orchestration\n * follow-up uses this to queue or trigger a re-request.\n */\n\nimport { FRAME_TYPE, HEADER_SIZE, MapDecodeError, unwrapEnvelope } from './envelope.js';\nimport { parseMapHeader } from './header.js';\nimport { parseMapJsonTail, sliceTailText } from './tail.js';\nimport type { VacuumMapDecodeOptions, MapTail } from './types.js';\n\nexport class OutOfOrderFrameError extends Error {\n readonly expectedFrameId: number;\n readonly actualFrameId: number;\n constructor(expectedFrameId: number, actualFrameId: number) {\n super(`map: out-of-order P-frame (expected frame_id=${expectedFrameId}, got ${actualFrameId})`);\n this.name = 'OutOfOrderFrameError';\n this.expectedFrameId = expectedFrameId;\n this.actualFrameId = actualFrameId;\n }\n}\n\n/**\n * Merge a P-frame onto the previous (I-frame or already-merged) inflated\n * frame buffer. Returns a new inflated buffer re-stamped as `frame_type=I`.\n *\n * Both inputs must already be unwrapped (`unwrapEnvelope` applied). For\n * envelope-string inputs use `mergePFrameEnvelope` or `applyVacuumPFrame`.\n */\nexport function mergePFrame(prevInflated: Buffer, pFrameInflated: Buffer): Buffer {\n const prevHeader = parseMapHeader(prevInflated);\n const pHeader = parseMapHeader(pFrameInflated);\n\n if (pHeader.frameType !== 'P') {\n throw new MapDecodeError(`mergePFrame: expected P-frame, got frame_type=${pHeader.frameType}`);\n }\n if (pHeader.mapId !== prevHeader.mapId) {\n throw new MapDecodeError(\n `mergePFrame: map_id mismatch (prev=${prevHeader.mapId}, P=${pHeader.mapId}) — request a fresh I-frame`,\n );\n }\n if (pHeader.frameId !== prevHeader.frameId + 1) {\n throw new OutOfOrderFrameError(prevHeader.frameId + 1, pHeader.frameId);\n }\n if (prevHeader.gridSize !== pHeader.gridSize && pHeader.width > 0 && pHeader.height > 0) {\n throw new MapDecodeError(\n `mergePFrame: grid_size changed mid-stream (${prevHeader.gridSize} → ${pHeader.gridSize})`,\n );\n }\n\n const prevTail = parseMapJsonTail(sliceTailText(prevInflated, prevHeader));\n const pTail = parseMapJsonTail(sliceTailText(pFrameInflated, pHeader));\n\n // ── compute union bbox ──────────────────────────────────────────────\n // tail.origin and header.left/top agree on r2532a fsm:1 captures —\n // prefer the tail value when present, fall back to the header.\n const prevLeft = prevTail.origin?.[0] ?? prevHeader.left;\n const prevTop = prevTail.origin?.[1] ?? prevHeader.top;\n const grid = prevHeader.gridSize;\n const prevRight = prevLeft + prevHeader.width * grid;\n const prevBottom = prevTop + prevHeader.height * grid;\n\n // P-frames with width=0,height=0 represent \"no spatial change\" — robot\n // moved or obstacle list updated, but no pixel deltas. Skip the bbox\n // union and the byte-add loop in that case.\n const hasPixelDelta = pHeader.width > 0 && pHeader.height > 0;\n let unionLeft = prevLeft;\n let unionTop = prevTop;\n let unionWidth = prevHeader.width;\n let unionHeight = prevHeader.height;\n let pLeft = 0;\n let pTop = 0;\n\n if (hasPixelDelta) {\n pLeft = pTail.origin?.[0] ?? pHeader.left;\n pTop = pTail.origin?.[1] ?? pHeader.top;\n const pRight = pLeft + pHeader.width * grid;\n const pBottom = pTop + pHeader.height * grid;\n\n if ((pLeft - prevLeft) % grid !== 0 || (pTop - prevTop) % grid !== 0) {\n throw new MapDecodeError(\n `mergePFrame: P-frame origin not aligned to prev grid (offset=${pLeft - prevLeft},${pTop - prevTop} vs grid=${grid})`,\n );\n }\n\n unionLeft = Math.min(prevLeft, pLeft);\n unionTop = Math.min(prevTop, pTop);\n const unionRight = Math.max(prevRight, pRight);\n const unionBottom = Math.max(prevBottom, pBottom);\n unionWidth = (unionRight - unionLeft) / grid;\n unionHeight = (unionBottom - unionTop) / grid;\n }\n\n // ── allocate new pixel grid + copy prev pixels in ───────────────────\n const newPixels = Buffer.alloc(unionWidth * unionHeight);\n const prevPixelEnd = HEADER_SIZE + prevHeader.width * prevHeader.height;\n if (prevInflated.length < prevPixelEnd) {\n throw new MapDecodeError(\n `mergePFrame: prev buffer truncated (need ${prevPixelEnd} bytes for header+pixels, got ${prevInflated.length})`,\n );\n }\n const prevPixels = prevInflated.subarray(HEADER_SIZE, prevPixelEnd);\n const prevDxPx = (prevLeft - unionLeft) / grid;\n const prevDyPx = (prevTop - unionTop) / grid;\n for (let y = 0; y < prevHeader.height; y++) {\n const srcOff = y * prevHeader.width;\n const dstOff = (prevDyPx + y) * unionWidth + prevDxPx;\n prevPixels.copy(newPixels, dstOff, srcOff, srcOff + prevHeader.width);\n }\n\n // ── byte-add P pixels at their offset ───────────────────────────────\n if (hasPixelDelta) {\n const pPixelEnd = HEADER_SIZE + pHeader.width * pHeader.height;\n if (pFrameInflated.length < pPixelEnd) {\n throw new MapDecodeError(\n `mergePFrame: P buffer truncated (need ${pPixelEnd} bytes for header+pixels, got ${pFrameInflated.length})`,\n );\n }\n const pPixels = pFrameInflated.subarray(HEADER_SIZE, pPixelEnd);\n const pDxPx = (pLeft - unionLeft) / grid;\n const pDyPx = (pTop - unionTop) / grid;\n for (let y = 0; y < pHeader.height; y++) {\n const dstRow = (pDyPx + y) * unionWidth + pDxPx;\n const srcRow = y * pHeader.width;\n for (let x = 0; x < pHeader.width; x++) {\n newPixels[dstRow + x] = (newPixels[dstRow + x]! + pPixels[srcRow + x]!) & 0xff;\n }\n }\n }\n\n // ── build new header (re-stamped as I-frame, union bbox) ────────────\n const newHeader = Buffer.alloc(HEADER_SIZE);\n newHeader.writeInt16LE(prevHeader.mapId, 0);\n newHeader.writeInt16LE(pHeader.frameId, 2);\n newHeader[4] = FRAME_TYPE.I;\n newHeader.writeInt16LE(pHeader.robotX, 5);\n newHeader.writeInt16LE(pHeader.robotY, 7);\n newHeader.writeInt16LE(pHeader.robotA, 9);\n newHeader.writeInt16LE(pHeader.chargerX, 11);\n newHeader.writeInt16LE(pHeader.chargerY, 13);\n newHeader.writeInt16LE(pHeader.chargerA, 15);\n newHeader.writeInt16LE(grid, 17);\n newHeader.writeInt16LE(unionWidth, 19);\n newHeader.writeInt16LE(unionHeight, 21);\n newHeader.writeInt16LE(unionLeft, 23);\n newHeader.writeInt16LE(unionTop, 25);\n\n // ── merge JSON tail ─────────────────────────────────────────────────\n const mergedTail = mergeTails(prevTail, pTail, unionLeft, unionTop);\n const tailBytes = Buffer.from(JSON.stringify(mergedTail), 'utf8');\n\n return Buffer.concat([newHeader, newPixels, tailBytes]);\n}\n\n/**\n * Convenience wrapper: accept envelope strings (or raw buffers) for both\n * inputs and return the merged inflated buffer.\n */\nexport function mergePFrameEnvelope(\n prev: Buffer | string,\n pframe: Buffer | string,\n prevOpts?: VacuumMapDecodeOptions,\n pframeOpts?: VacuumMapDecodeOptions,\n): Buffer {\n const prevBuf = typeof prev === 'string' ? unwrapEnvelope(prev, prevOpts) : prev;\n const pBuf = typeof pframe === 'string' ? unwrapEnvelope(pframe, pframeOpts) : pframe;\n return mergePFrame(prevBuf, pBuf);\n}\n\n/**\n * Construct the merged JSON tail. P-frame's tail is taken as the base\n * (it carries the latest robot/dock/obstacle/etc. state); a few keys\n * need explicit treatment:\n *\n * - `origin` is overwritten with the union origin\n * - `tr` (cleaning path) is concatenated: `prev.tr + p.tr`. P-frame\n * uses lowercase `l` for line continuation; `parsePathTr` already\n * normalises that, so plain concatenation is correct\n * - `seg_inf` falls back to prev's when P doesn't supply one (typical;\n * P-frames re-send seg_inf only on segment metadata changes)\n * - `sa` (active segments) similarly falls back to prev's when absent\n */\nfunction mergeTails(\n prev: MapTail,\n p: MapTail,\n unionLeft: number,\n unionTop: number,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...p };\n merged.origin = [unionLeft, unionTop];\n\n const prevTr = typeof prev.tr === 'string' ? prev.tr : '';\n const pTr = typeof p.tr === 'string' ? p.tr : '';\n if (prevTr || pTr) {\n merged.tr = prevTr + pTr;\n } else {\n delete merged.tr;\n }\n\n if (!('seg_inf' in p) && 'seg_inf' in prev) {\n merged.seg_inf = prev.seg_inf;\n }\n if (!('sa' in p) && 'sa' in prev) {\n merged.sa = prev.sa;\n }\n // User-defined geometry + persistent overlay blocks are\n // configuration, not live state — most P-frames don't re-send any\n // of them. Fall back to prev when absent so the running state\n // retains user-defined geometry / cleaned-area / saved-map across\n // the chain. To wire a new persistent tail key (e.g. `vw.cliff`\n // when we get a fixture), add it here and to `MapTail` in types.\n for (const key of PERSISTENT_TAIL_KEYS) {\n if (!(key in p) && key in prev) {\n merged[key] = prev[key];\n }\n }\n return merged;\n}\n\n/**\n * Tail-JSON keys that represent persistent floor-plan / saved-map /\n * cleaning-progress configuration — re-emitted by the device only on\n * full snapshots, not every P-frame. `mergeTails` falls these back\n * from prev when the P-frame's tail doesn't carry them, so the\n * running merged state keeps the user's geometry between full\n * snapshots.\n */\nexport const PERSISTENT_TAIL_KEYS = [\n 'vw',\n 'vws',\n 'sneak_areas',\n 'sneak_areas_end',\n 'walls_info',\n 'rism',\n 'decmap',\n] as const;\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 RawSegInf,\n} from './types.js';\nimport { ANGLE_ABSENT, HEADER_SIZE, looksLikeBase64Zlib, 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 * the raw fetched OSS bytes (a `Buffer` that is STILL the base64 envelope —\n * `OssFetcher.fetchBlob` returns the object bytes verbatim, NOT inflated), or\n * already-inflated frame bytes (`Buffer`). Pure — no IO.\n */\nexport function decodeVacuumMap(\n input: Buffer | string,\n opts: VacuumMapDecodeOptions = {},\n): VacuumMap {\n // A `Buffer` may be either an already-inflated frame (tests / the P-frame merge\n // primitive) OR the raw fetched OSS bytes, which are STILL the base64+zlib\n // envelope (the live `getMap` path). Detect the latter — a base64 wrapper around\n // a zlib stream — and run it through `unwrapEnvelope` (base64 → zlib, peeling any\n // extra wrap layers); an already-inflated binary frame fails the check and is\n // used verbatim.\n const inflated =\n typeof input === 'string'\n ? unwrapEnvelope(input, opts)\n : looksLikeBase64Zlib(input)\n ? unwrapEnvelope(input.toString('latin1'), opts)\n : 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 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. The live frame's\n // `seg_inf` also typically OMITS the user room NAMES — those live in the\n // saved map. Recurse once to surface geometry AND names; if the inner blob\n // fails to decode (corrupt / unexpected shape / missing AES IV) leave the\n // outer values as-is and swallow — must never break the outer frame.\n // Recurses one level only — the inner saved-map blob carries no `rism`.\n let segTail = tail;\n const outerHasNames = Object.values(tail.seg_inf ?? {}).some(\n (m) => typeof m?.name === 'string' && m.name.length > 0,\n );\n if (\n (!isGeometryComplete(geometry) || !outerHasNames) &&\n typeof tail.rism === 'string' &&\n tail.rism.length > 0\n ) {\n try {\n const innerInflated = unwrapEnvelope(tail.rism);\n const { tail: innerTail } = parseFrame(innerInflated);\n if (!isGeometryComplete(geometry)) {\n geometry = coalesceGeometry(geometry, parseTailGeometry(innerTail));\n }\n if (!outerHasNames && innerTail.seg_inf) {\n segTail = mergeSegInfNames(tail, innerTail.seg_inf);\n }\n } catch {\n // intentional — outer frame remains valid even if rism is unreadable\n }\n }\n const segments = canDecodePixels ? collectSegments(layers, dimensions, segTail) : [];\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\n/**\n * Merge saved-map (`rism`) `seg_inf` NAMES into the outer tail so\n * {@link collectSegments} surfaces real room names. Outer fields win for every\n * key EXCEPT `name`, which is taken from the inner saved map when the outer\n * entry has no (non-empty) name. Returns a new tail (no mutation).\n */\nfunction mergeSegInfNames(tail: MapTail, innerSegInf: Record<string, RawSegInf>): MapTail {\n const outer = tail.seg_inf ?? {};\n const ids = new Set([...Object.keys(outer), ...Object.keys(innerSegInf)]);\n const merged: Record<string, RawSegInf> = {};\n for (const id of ids) {\n const o = outer[id] ?? {};\n const inner = innerSegInf[id] ?? {};\n const name = typeof o.name === 'string' && o.name.length > 0 ? o.name : inner.name;\n merged[id] = { ...inner, ...o, ...(name !== undefined ? { name } : {}) };\n }\n return { ...tail, seg_inf: 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 * the run-length `layers` (floor / segments / carpet / walls) one pixel per grid\n * cell (× an optional integer upscale `scale`), then composite the world-frame\n * overlays on top: restricted areas (no-go / no-mop), virtual walls, the cleaning\n * path polylines, AI obstacles, the charger, and the robot (body + heading). A\n * `colorScheme` picks the palette and every overlay is independently toggleable,\n * mirroring how the Home Assistant integration exposes map customization.\n *\n * Coordinate transform: every overlay coordinate is mm in the world frame.\n * `dimensions.left` / `.top` are the world-mm of pixel (0,0) and `.gridSize` is\n * mm-per-pixel, so `xPx = (worldX - left) / gridSize` (then × `scale`).\n *\n * Y IS FLIPPED. Dreame's grid has world-Y increasing DOWNWARD relative to how\n * the robot/dock/app present the floor, so rendering rows top-down produced a\n * VERTICALLY MIRRORED map vs the Dreamehome app / Home Assistant (live-confirmed\n * on r2538z). We reflect in cell space — `yPx = ((height - 1) - cellY) × scale` —\n * applied identically to the run-length base layers ({@link paintRun}) and the\n * world-frame overlays ({@link worldToPx}) so they stay pixel-aligned, and the\n * robot heading's Y component is negated ({@link drawRobot}). Glyph labels are\n * drawn upright (only their anchor is flipped).\n *\n * Pure: only `pngjs` + arithmetic. No casts.\n */\n\nimport { PNG } from 'pngjs';\nimport type {\n VacuumMap,\n MapLayer,\n MapRun,\n MapPoint,\n MapPose,\n MapDimensions,\n MapCleanedAreaOverlay,\n} from './types.js';\n\n/** A named map palette — node-dreame's own distinct visual styles. */\nexport type MapColorScheme = 'flat' | 'dark-neon' | 'materico';\n\nexport interface RenderVacuumPngOptions {\n /** Integer upscale factor (nearest-neighbour). Default 1. */\n scale?: number;\n /** Palette. Default `'flat'`. */\n colorScheme?: MapColorScheme;\n /** Draw the cleaning/mopping path polylines. Default true. */\n showPath?: boolean;\n /** Draw the robot marker (body + heading). Default true. */\n showRobot?: boolean;\n /** Draw the charger / dock marker. Default true. */\n showCharger?: boolean;\n /** Draw no-go / no-mop restricted areas. Default true. */\n showNoGo?: boolean;\n /** Draw user-defined virtual walls. Default true. */\n showVirtualWalls?: boolean;\n /** Draw AI-detected obstacle markers. Default true. */\n showObstacles?: boolean;\n /** Draw the segment id as a tiny label at each room centroid. Default false. */\n showSegmentLabels?: boolean;\n /** Draw each room's NAME (falls back to `Room <id>`) at its centroid. Default\n * false. Mutually useful with `showSegmentLabels` (names take the centroid). */\n showSegmentNames?: boolean;\n /** Tint the cleaned-area overlay (`cleanedArea.cleaned`) when present. Default false. */\n showCleanedArea?: boolean;\n /** Outline low-clearance \"sneak under furniture\" zones. Default true. */\n showFurniture?: boolean;\n /** Colour AI obstacle markers by their `type` (else a single accent). Default true. */\n colorObstaclesByType?: boolean;\n}\n\ntype Rgba = readonly [number, number, number, number];\n\n/** Per-scheme colour set. Segment fills derive from `segHsv` (golden-angle hue). */\ninterface Palette {\n floor: Rgba;\n wall: Rgba;\n carpet: Rgba;\n /** Segment fill saturation / value (hue is per-id golden angle). */\n segSat: number;\n segVal: number;\n path: Rgba;\n robotBody: Rgba;\n robotHeading: Rgba;\n charger: Rgba;\n noGoFill: Rgba;\n noGoBorder: Rgba;\n noMopFill: Rgba;\n noMopBorder: Rgba;\n virtualWall: Rgba;\n obstacle: Rgba;\n label: Rgba;\n}\n\nconst SCHEMES: Record<MapColorScheme, Palette> = {\n // Flat-design: soft neutral floor, bright low-saturation segment fills, thin\n // mid-grey walls, a single vivid accent for the path.\n flat: {\n floor: [236, 239, 241, 255],\n wall: [120, 132, 145, 255],\n carpet: [205, 185, 160, 200],\n segSat: 0.28,\n segVal: 0.99,\n path: [255, 138, 101, 240],\n robotBody: [38, 50, 56, 255],\n robotHeading: [255, 255, 255, 255],\n charger: [38, 166, 154, 255],\n noGoFill: [239, 83, 80, 60],\n noGoBorder: [229, 57, 53, 210],\n noMopFill: [66, 165, 245, 55],\n noMopBorder: [30, 136, 229, 200],\n virtualWall: [229, 57, 53, 225],\n obstacle: [255, 167, 38, 255],\n label: [55, 71, 79, 255],\n },\n // Dark-neon: near-black floor with high-saturation glowing segment fills, a\n // bright cyan path and magenta robot.\n 'dark-neon': {\n floor: [16, 18, 27, 255],\n wall: [5, 6, 10, 255],\n carpet: [60, 50, 80, 200],\n segSat: 0.85,\n segVal: 0.95,\n path: [0, 230, 255, 240],\n robotBody: [255, 45, 170, 255],\n robotHeading: [255, 255, 255, 255],\n charger: [0, 255, 150, 255],\n noGoFill: [255, 40, 90, 80],\n noGoBorder: [255, 40, 90, 235],\n noMopFill: [40, 130, 255, 75],\n noMopBorder: [40, 160, 255, 225],\n virtualWall: [255, 40, 90, 235],\n obstacle: [255, 220, 0, 255],\n label: [230, 240, 255, 255],\n },\n // Materico (Material-design): light grey surface, Material-palette segment\n // fills, indigo/teal accents.\n materico: {\n floor: [245, 245, 245, 255],\n wall: [97, 97, 97, 255],\n carpet: [188, 170, 144, 205],\n segSat: 0.55,\n segVal: 0.93,\n path: [63, 81, 181, 235],\n robotBody: [48, 63, 159, 255],\n robotHeading: [255, 255, 255, 255],\n charger: [0, 150, 136, 255],\n noGoFill: [244, 67, 54, 65],\n noGoBorder: [211, 47, 47, 215],\n noMopFill: [33, 150, 243, 60],\n noMopBorder: [25, 118, 210, 205],\n virtualWall: [211, 47, 47, 225],\n obstacle: [255, 152, 0, 255],\n label: [33, 33, 33, 255],\n },\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) under a scheme. */\nfunction segmentColor(id: number, pal: Palette): Rgba {\n const hue = (id * 137.508) % 360;\n return hsvToRgba(hue, pal.segSat, pal.segVal);\n}\n\nexport function renderVacuumPng(map: VacuumMap, opts: RenderVacuumPngOptions = {}): Buffer {\n const scale = Math.max(1, Math.trunc(opts.scale ?? 1));\n // Default + resilient fallback to 'flat' for any unknown/legacy scheme value.\n const pal = SCHEMES[opts.colorScheme ?? 'flat'] ?? SCHEMES.flat;\n const w = Math.max(1, map.dimensions.width * scale);\n const h = Math.max(1, map.dimensions.height * scale);\n const png = new PNG({ width: w, height: h });\n png.data.fill(0); // transparent background\n\n const dim = map.dimensions;\n\n // 1) Spatial layers (floor / segments / carpet / walls), painted in order.\n for (const layer of map.layers) {\n paintLayer(png, layer, dim.height, scale, pal);\n }\n\n // 1b) Cleaned-area overlay — a translucent tint over already-cleaned cells,\n // reprojected from the inner overlay grid onto the parent grid. Drawn over the\n // base layers but under every marker/zone. Opt-in (default off).\n if (opts.showCleanedArea === true && map.cleanedArea !== null) {\n paintCleanedArea(png, map.cleanedArea, dim, scale, pal.path);\n }\n\n // 2) Restricted areas — translucent fill + opaque border.\n if (opts.showNoGo !== false) {\n for (const area of map.restrictedAreas) {\n const fill = area.kind === 'noMop' ? pal.noMopFill : pal.noGoFill;\n const border = area.kind === 'noMop' ? pal.noMopBorder : pal.noGoBorder;\n const a = worldToPx(area.bbox.xMin, area.bbox.yMin, dim, scale);\n const b = worldToPx(area.bbox.xMax, area.bbox.yMax, dim, scale);\n fillRect(png, a.x, a.y, b.x, b.y, fill);\n strokeRect(png, a.x, a.y, b.x, b.y, border);\n }\n }\n\n // 3) Virtual walls — opaque line segments.\n if (opts.showVirtualWalls !== false) {\n for (const vw of map.virtualWalls) {\n const a = worldToPx(vw.from.x, vw.from.y, dim, scale);\n const b = worldToPx(vw.to.x, vw.to.y, dim, scale);\n drawLine(png, a.x, a.y, b.x, b.y, pal.virtualWall, Math.max(1, scale));\n }\n }\n\n // 4) Cleaning path polylines (\"linee di lavaggio\").\n if (opts.showPath !== false) {\n for (const path of map.paths) {\n drawPolyline(png, path.points, dim, scale, pal.path, Math.max(1, scale));\n }\n }\n\n // 4b) Low-clearance \"sneak under furniture\" zones — outlined translucent\n // polygons. Default on.\n if (opts.showFurniture !== false) {\n for (const zone of map.lowLyingAreas) {\n paintFurnitureZone(png, zone.points, dim, scale);\n }\n }\n\n // 5) AI obstacles — coloured AND shaped by type by default so distinct hazards\n // read differently (the full type→icon table lives browser-side; here we vary\n // colour + a small shape set as a lightweight raster stand-in).\n if (opts.showObstacles !== false) {\n const byType = opts.colorObstaclesByType !== false;\n for (const ob of map.obstacles) {\n const p = worldToPx(ob.x, ob.y, dim, scale);\n const r = Math.max(2, scale * 2);\n if (byType) {\n drawObstacleShape(png, p.x, p.y, r, ob.type, obstacleColor(ob.type));\n } else {\n drawDiamond(png, p.x, p.y, r, pal.obstacle);\n }\n }\n }\n\n // 6) Charger / dock.\n if (opts.showCharger !== false && map.dock !== null) {\n drawCharger(png, map.dock, dim, scale, pal);\n }\n\n // 7) Robot (body + heading wedge) — drawn last so it sits on top.\n if (opts.showRobot !== false && map.robot !== null) {\n drawRobot(png, map.robot, dim, scale, pal);\n }\n\n // 8) Room labels at centroids — the NAME when requested (falls back to\n // `ROOM <id>` for an unnamed room), else just the numeric id. Names win the\n // centroid when both flags are set.\n if (opts.showSegmentNames === true || opts.showSegmentLabels === true) {\n for (const seg of map.segments) {\n const p = worldToPx(seg.centroid.x, seg.centroid.y, dim, scale);\n const text = opts.showSegmentNames === true ? labelText(seg.name, seg.id) : String(seg.id);\n drawLabel(png, p.x, p.y, text, pal.label, Math.max(1, scale));\n }\n }\n\n return PNG.sync.write(png);\n}\n\n// ── Coordinate transform ────────────────────────────────────────────────────\n\ninterface Px {\n x: number;\n y: number;\n}\n\n/** World-mm → scaled pixel coordinate. Y is reflected in cell space so the map\n * matches the Dreamehome app / HA orientation (see file header). */\nfunction worldToPx(worldX: number, worldY: number, dim: MapDimensions, scale: number): Px {\n const cellY = (worldY - dim.top) / dim.gridSize;\n return {\n x: Math.round(((worldX - dim.left) / dim.gridSize) * scale),\n y: Math.round((dim.height - 1 - cellY) * scale),\n };\n}\n\n// ── Layer painting (run-length) ─────────────────────────────────────────────\n\nfunction paintLayer(\n png: PNG,\n layer: MapLayer,\n dimHeight: number,\n scale: number,\n pal: Palette,\n): void {\n const color =\n layer.type === 'segment'\n ? segmentColor(layer.segmentId ?? 0, pal)\n : layer.type === 'wall'\n ? pal.wall\n : layer.type === 'carpet'\n ? pal.carpet\n : pal.floor;\n for (const run of layer.runs) {\n paintRun(png, run, dimHeight, color, scale);\n }\n}\n\n/** Fill the scale×scale block of every pixel in a `[xPx, yPx, len]` run. The row\n * is reflected (`(height-1)-yPx`) to match {@link worldToPx}'s Y flip. */\nfunction paintRun(png: PNG, run: MapRun, dimHeight: number, color: Rgba, scale: number): void {\n const [xPx, yPx, len] = run;\n const baseY = (dimHeight - 1 - yPx) * scale;\n for (let i = 0; i < len; i += 1) {\n const baseX = (xPx + i) * scale;\n for (let dy = 0; dy < scale; dy += 1) {\n for (let dx = 0; dx < scale; dx += 1) {\n setPixel(png, baseX + dx, baseY + dy, color);\n }\n }\n }\n}\n\n// ── Pixel primitives ────────────────────────────────────────────────────────\n\n/** Opaque write (or alpha-composite when the colour is translucent). */\nfunction setPixel(png: PNG, x: number, y: number, color: Rgba): void {\n if (x < 0 || y < 0 || x >= png.width || y >= png.height) return;\n const off = (y * png.width + x) * 4;\n const a = color[3];\n if (a >= 255) {\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] = 255;\n return;\n }\n // Source-over alpha composite onto the existing pixel.\n const sa = a / 255;\n const da = (png.data[off + 3] ?? 0) / 255;\n const outA = sa + da * (1 - sa);\n if (outA <= 0) return;\n for (let k = 0; k < 3; k += 1) {\n const src = color[k] ?? 0;\n const dst = png.data[off + k] ?? 0;\n png.data[off + k] = Math.round((src * sa + dst * da * (1 - sa)) / outA);\n }\n png.data[off + 3] = Math.round(outA * 255);\n}\n\n/** Translucent (or opaque) axis-aligned filled rectangle, endpoints inclusive. */\nfunction fillRect(png: PNG, x0: number, y0: number, x1: number, y1: number, color: Rgba): void {\n const xa = Math.min(x0, x1);\n const xb = Math.max(x0, x1);\n const ya = Math.min(y0, y1);\n const yb = Math.max(y0, y1);\n for (let y = ya; y <= yb; y += 1) {\n for (let x = xa; x <= xb; x += 1) {\n setPixel(png, x, y, color);\n }\n }\n}\n\n/** Rectangle border (1px). */\nfunction strokeRect(png: PNG, x0: number, y0: number, x1: number, y1: number, color: Rgba): void {\n const xa = Math.min(x0, x1);\n const xb = Math.max(x0, x1);\n const ya = Math.min(y0, y1);\n const yb = Math.max(y0, y1);\n for (let x = xa; x <= xb; x += 1) {\n setPixel(png, x, ya, color);\n setPixel(png, x, yb, color);\n }\n for (let y = ya; y <= yb; y += 1) {\n setPixel(png, xa, y, color);\n setPixel(png, xb, y, color);\n }\n}\n\n/** Bresenham line with a square pen of side `thickness`. */\nfunction drawLine(\n png: PNG,\n x0: number,\n y0: number,\n x1: number,\n y1: number,\n color: Rgba,\n thickness: number,\n): void {\n let x = x0;\n let y = y0;\n const dx = Math.abs(x1 - x0);\n const dy = -Math.abs(y1 - y0);\n const sx = x0 < x1 ? 1 : -1;\n const sy = y0 < y1 ? 1 : -1;\n let err = dx + dy;\n const r = Math.max(0, Math.trunc((thickness - 1) / 2));\n for (;;) {\n for (let oy = -r; oy <= r; oy += 1) {\n for (let ox = -r; ox <= r; ox += 1) {\n setPixel(png, x + ox, y + oy, color);\n }\n }\n if (x === x1 && y === y1) break;\n const e2 = 2 * err;\n if (e2 >= dy) {\n err += dy;\n x += sx;\n }\n if (e2 <= dx) {\n err += dx;\n y += sy;\n }\n }\n}\n\n/** Connect consecutive world-frame points with line segments. */\nfunction drawPolyline(\n png: PNG,\n points: readonly MapPoint[],\n dim: MapDimensions,\n scale: number,\n color: Rgba,\n thickness: number,\n): void {\n for (let i = 1; i < points.length; i += 1) {\n const p0 = points[i - 1];\n const p1 = points[i];\n if (p0 === undefined || p1 === undefined) continue;\n const a = worldToPx(p0.x, p0.y, dim, scale);\n const b = worldToPx(p1.x, p1.y, dim, scale);\n drawLine(png, a.x, a.y, b.x, b.y, color, thickness);\n }\n}\n\n/** Filled disk (square-distance) of radius `r` centred at `cx,cy`. */\nfunction drawDisk(png: PNG, cx: number, cy: number, r: number, color: Rgba): void {\n const rr = r * r;\n for (let dy = -r; dy <= r; dy += 1) {\n for (let dx = -r; dx <= r; dx += 1) {\n if (dx * dx + dy * dy <= rr) setPixel(png, cx + dx, cy + dy, color);\n }\n }\n}\n\n// ── Markers ─────────────────────────────────────────────────────────────────\n\nfunction drawRobot(\n png: PNG,\n robot: MapPose,\n dim: MapDimensions,\n scale: number,\n pal: Palette,\n): void {\n const p = worldToPx(robot.x, robot.y, dim, scale);\n const r = Math.max(3, scale * 3);\n drawDisk(png, p.x, p.y, r, pal.robotBody);\n // Heading wedge: a short line from the centre toward `angle` (degrees, CCW).\n // The Y axis is flipped (see worldToPx), so the screen-Y component is negated\n // to keep the arrow pointing the real direction.\n const rad = (robot.angle * Math.PI) / 180;\n const hx = Math.round(p.x + Math.cos(rad) * r);\n const hy = Math.round(p.y - Math.sin(rad) * r);\n drawLine(png, p.x, p.y, hx, hy, pal.robotHeading, Math.max(1, Math.trunc(scale / 2) + 1));\n}\n\nfunction drawCharger(\n png: PNG,\n dock: MapPose,\n dim: MapDimensions,\n scale: number,\n pal: Palette,\n): void {\n const p = worldToPx(dock.x, dock.y, dim, scale);\n const r = Math.max(2, scale * 2);\n // A small filled square reads as a dock base.\n fillRect(png, p.x - r, p.y - r, p.x + r, p.y + r, pal.charger);\n}\n\n/** Filled diamond (Manhattan disk) of radius `r` — used for AI obstacle markers\n * so they read distinctly from the round robot body. */\nfunction drawDiamond(png: PNG, cx: number, cy: number, r: number, color: Rgba): void {\n for (let dy = -r; dy <= r; dy += 1) {\n const w = r - Math.abs(dy);\n for (let dx = -w; dx <= w; dx += 1) {\n setPixel(png, cx + dx, cy + dy, color);\n }\n }\n}\n\n/** Deterministic per-type obstacle colour (golden-ish hue spread). The integer\n * `ObstacleType` → human label table lives browser-side; here we only need a\n * stable, visually distinct hue per type. */\nfunction obstacleColor(type: number): Rgba {\n const hue = (Math.abs(Math.trunc(type)) * 47) % 360;\n return hsvToRgba(hue, 0.85, 0.95);\n}\n\n/** Filled axis-aligned square. */\nfunction drawSquare(png: PNG, cx: number, cy: number, r: number, color: Rgba): void {\n fillRect(png, cx - r, cy - r, cx + r, cy + r, color);\n}\n\n/** Filled upward triangle (apex up), height/half-width `r`. */\nfunction drawTriangle(png: PNG, cx: number, cy: number, r: number, color: Rgba): void {\n for (let dy = -r; dy <= r; dy += 1) {\n // Width grows from apex (dy=-r) to base (dy=+r).\n const w = Math.round(((dy + r) / (2 * r)) * r);\n for (let dx = -w; dx <= w; dx += 1) {\n setPixel(png, cx + dx, cy + dy, color);\n }\n }\n}\n\n/** Draw an AI-obstacle marker whose SHAPE varies by type (diamond / square /\n * triangle / disk), a lightweight raster stand-in for the app's per-type icons. */\nfunction drawObstacleShape(\n png: PNG,\n cx: number,\n cy: number,\n r: number,\n type: number,\n color: Rgba,\n): void {\n switch (Math.abs(Math.trunc(type)) % 4) {\n case 0:\n drawDiamond(png, cx, cy, r, color);\n return;\n case 1:\n drawSquare(png, cx, cy, r, color);\n return;\n case 2:\n drawTriangle(png, cx, cy, r, color);\n return;\n default:\n drawDisk(png, cx, cy, r, color);\n }\n}\n\n/** Outline colour for low-clearance furniture zones — a translucent violet that\n * reads on both light and dark schemes. */\nconst FURNITURE_LINE: Rgba = [150, 110, 200, 235];\n\n/** Outline a low-clearance furniture polygon (closes the loop). */\nfunction paintFurnitureZone(\n png: PNG,\n points: readonly MapPoint[],\n dim: MapDimensions,\n scale: number,\n): void {\n if (points.length < 2) return;\n for (let i = 0; i < points.length; i += 1) {\n const a = points[i];\n const b = points[(i + 1) % points.length];\n if (a === undefined || b === undefined) continue;\n const pa = worldToPx(a.x, a.y, dim, scale);\n const pb = worldToPx(b.x, b.y, dim, scale);\n drawLine(png, pa.x, pa.y, pb.x, pb.y, FURNITURE_LINE, Math.max(1, scale));\n }\n}\n\n/** Tint already-cleaned cells, reprojecting the inner overlay grid (its OWN\n * dimensions) onto the parent pixel grid. The tint derives from the scheme's\n * path colour at low alpha. */\nfunction paintCleanedArea(\n png: PNG,\n overlay: MapCleanedAreaOverlay,\n parentDim: MapDimensions,\n scale: number,\n baseColor: Rgba,\n): void {\n const tint: Rgba = [baseColor[0], baseColor[1], baseColor[2], 55];\n const inner = overlay.dimensions;\n const block = Math.max(1, Math.round((inner.gridSize / parentDim.gridSize) * scale));\n for (const [xPx, yPx, len] of overlay.cleaned) {\n for (let i = 0; i < len; i += 1) {\n const worldX = inner.left + (xPx + i) * inner.gridSize;\n const worldY = inner.top + yPx * inner.gridSize;\n const p = worldToPx(worldX, worldY, parentDim, scale);\n for (let dy = 0; dy < block; dy += 1) {\n for (let dx = 0; dx < block; dx += 1) {\n setPixel(png, p.x + dx, p.y + dy, tint);\n }\n }\n }\n }\n}\n\n/** Build a renderable room label: uppercase the name (accents stripped, only\n * glyph-defined chars kept, capped length), falling back to `ROOM <id>` when a\n * room is unnamed or reduces to nothing renderable. */\nfunction labelText(name: string | null, id: number): string {\n const raw = (name ?? '').trim();\n const base = raw.length > 0 ? raw : `ROOM ${id}`;\n const upper = base\n .toUpperCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '');\n let out = '';\n for (const ch of upper) {\n if (GLYPHS[ch] !== undefined) out += ch;\n }\n const trimmed = out.trim().slice(0, 14);\n return trimmed.length > 0 ? trimmed : `ROOM ${id}`;\n}\n\n// ── Tiny 3×5 digit/sign font for segment labels ─────────────────────────────\n\n// Each glyph is 5 rows of a 3-bit mask (bit 2 = left column). Only the\n// characters that can appear in `String(segmentId)` are defined; an unknown\n// char is skipped so an unexpected id never throws.\nconst GLYPHS: Record<string, readonly number[]> = {\n '0': [0b111, 0b101, 0b101, 0b101, 0b111],\n '1': [0b010, 0b110, 0b010, 0b010, 0b111],\n '2': [0b111, 0b001, 0b111, 0b100, 0b111],\n '3': [0b111, 0b001, 0b111, 0b001, 0b111],\n '4': [0b101, 0b101, 0b111, 0b001, 0b001],\n '5': [0b111, 0b100, 0b111, 0b001, 0b111],\n '6': [0b111, 0b100, 0b111, 0b101, 0b111],\n '7': [0b111, 0b001, 0b010, 0b010, 0b010],\n '8': [0b111, 0b101, 0b111, 0b101, 0b111],\n '9': [0b111, 0b101, 0b111, 0b001, 0b111],\n '-': [0b000, 0b000, 0b111, 0b000, 0b000],\n ' ': [0b000, 0b000, 0b000, 0b000, 0b000],\n A: [0b010, 0b101, 0b111, 0b101, 0b101],\n B: [0b110, 0b101, 0b110, 0b101, 0b110],\n C: [0b011, 0b100, 0b100, 0b100, 0b011],\n D: [0b110, 0b101, 0b101, 0b101, 0b110],\n E: [0b111, 0b100, 0b110, 0b100, 0b111],\n F: [0b111, 0b100, 0b110, 0b100, 0b100],\n G: [0b011, 0b100, 0b101, 0b101, 0b011],\n H: [0b101, 0b101, 0b111, 0b101, 0b101],\n I: [0b111, 0b010, 0b010, 0b010, 0b111],\n J: [0b001, 0b001, 0b001, 0b101, 0b010],\n K: [0b101, 0b101, 0b110, 0b101, 0b101],\n L: [0b100, 0b100, 0b100, 0b100, 0b111],\n M: [0b101, 0b111, 0b111, 0b101, 0b101],\n N: [0b101, 0b111, 0b111, 0b111, 0b101],\n O: [0b010, 0b101, 0b101, 0b101, 0b010],\n P: [0b110, 0b101, 0b110, 0b100, 0b100],\n Q: [0b010, 0b101, 0b101, 0b110, 0b011],\n R: [0b110, 0b101, 0b110, 0b101, 0b101],\n S: [0b011, 0b100, 0b010, 0b001, 0b110],\n T: [0b111, 0b010, 0b010, 0b010, 0b010],\n U: [0b101, 0b101, 0b101, 0b101, 0b111],\n V: [0b101, 0b101, 0b101, 0b101, 0b010],\n W: [0b101, 0b101, 0b111, 0b111, 0b101],\n X: [0b101, 0b101, 0b010, 0b101, 0b101],\n Y: [0b101, 0b101, 0b010, 0b010, 0b010],\n Z: [0b111, 0b001, 0b010, 0b100, 0b111],\n};\n\n/** Draw `text` centred on `cx,cy` using the 3×5 bitmap font (× `px` per cell). */\nfunction drawLabel(png: PNG, cx: number, cy: number, text: string, color: Rgba, px: number): void {\n const glyphW = 3 * px;\n const glyphH = 5 * px;\n const gap = px;\n const totalW = text.length * glyphW + Math.max(0, text.length - 1) * gap;\n let originX = Math.round(cx - totalW / 2);\n const originY = Math.round(cy - glyphH / 2);\n for (const ch of text) {\n const glyph = GLYPHS[ch];\n if (glyph !== undefined) {\n for (let row = 0; row < 5; row += 1) {\n const bits = glyph[row];\n if (bits === undefined) continue;\n for (let col = 0; col < 3; col += 1) {\n if ((bits & (1 << (2 - col))) === 0) continue;\n for (let sy = 0; sy < px; sy += 1) {\n for (let sx = 0; sx < px; sx += 1) {\n setPixel(png, originX + col * px + sx, originY + row * px + sy, color);\n }\n }\n }\n }\n }\n originX += glyphW + gap;\n }\n}\n","/**\n * WiFi-signal map decode + render.\n *\n * The robot builds a Wi-Fi coverage map (map service SIID 6: action `wifiMap`\n * aiid 4 requests the last stored one, prop `PropWifiMap` piid 15 advertises the\n * OSS object). The blob is the SAME envelope/format as a normal map — a 27-byte\n * header + a width*height pixel grid — but each pixel's low nibble encodes the\n * Wi-Fi signal level instead of a floor/wall class (reversed from the app's\n * `getWifiMapInfo`, `mapInfo[i] & 15`):\n *\n * 10 = unreached (measured, no usable signal)\n * 11 = 1 bar (weakest) 12 = 2 bars 13 = 3 bars 14 = 4 bars (strongest)\n *\n * `left/top/gridSize` (world origin + mm-per-cell) and the robot/charger poses\n * come from the header, so a single blob is enough to sample the signal at any\n * world point — including the robot's own position.\n */\nimport { PNG } from 'pngjs';\nimport type { MapDimensions, MapPose } from './types.js';\nimport { unwrapEnvelope, looksLikeBase64Zlib, HEADER_SIZE, ANGLE_ABSENT } from './envelope.js';\nimport { parseMapHeader } from './header.js';\nimport type { VacuumMapDecodeOptions } from './types.js';\n\n/** Bars (1–4) keyed by the raw pixel nibble; 10 = unreached (0 bars). */\nconst NIBBLE_TO_BARS: Record<number, number> = { 10: 0, 11: 1, 12: 2, 13: 3, 14: 4 };\nconst isWifiNibble = (n: number): boolean => n >= 10 && n <= 14;\n\n/** Heatmap colours, matching the app (weakest→strongest), plus unreached + map outline. */\nconst NIBBLE_RGBA: Record<number, [number, number, number, number]> = {\n 2: [0xb0, 0xb6, 0xbe, 0xff], // map border (context)\n 3: [0xf2, 0xf4, 0xf7, 0xff], // mapped floor (context)\n 10: [0xe5, 0xea, 0xee, 0xff], // unreached\n 11: [0xd9, 0xe2, 0xef, 0xff], // 1 bar\n 12: [0xcd, 0xda, 0xef, 0xff], // 2 bars\n 13: [0xa1, 0xbd, 0xf2, 0xff], // 3 bars\n 14: [0x81, 0xa8, 0xf5, 0xff], // 4 bars\n};\n\nexport interface WifiSignalMap {\n readonly dimensions: MapDimensions;\n /** Robot pose from the frame header, or null when absent. */\n readonly robot: MapPose | null;\n /** Charging-dock pose from the frame header, or null when absent. */\n readonly dock: MapPose | null;\n /** Raw per-cell nibble (`pixel & 15`), row-major (width*height). */\n readonly cells: Uint8Array;\n /** Signal bars (1–4) at a world point, 0 = unreached, or null = no Wi-Fi data / off-map. */\n signalAt(worldX: number, worldY: number): number | null;\n /** Signal bars at the robot's own position (or null when unknown). */\n readonly currentSignal: number | null;\n}\n\nconst poseOrNull = (x: number, y: number, a: number): MapPose | null =>\n x === 0 && y === 0 ? null : { x, y, angle: a === ANGLE_ABSENT ? 0 : a };\n\n/** Decode a Wi-Fi map blob (raw string/Buffer or already-inflated) into signal cells + geometry. */\nexport function decodeWifiSignalMap(\n input: string | Buffer,\n opts: VacuumMapDecodeOptions = {},\n): WifiSignalMap {\n const inflated =\n typeof input === 'string'\n ? unwrapEnvelope(input, opts)\n : looksLikeBase64Zlib(input)\n ? unwrapEnvelope(input.toString('latin1'), opts)\n : input;\n\n const header = parseMapHeader(inflated);\n const { width, height, gridSize, left, top } = header;\n const pixelEnd = HEADER_SIZE + width * height;\n const grid = inflated.subarray(HEADER_SIZE, pixelEnd);\n\n const cells = new Uint8Array(width * height);\n for (let i = 0; i < cells.length; i++) cells[i] = (grid[i] ?? 0) & 0x0f;\n\n const dimensions: MapDimensions = { left, top, width, height, gridSize };\n const robot = poseOrNull(header.robotX, header.robotY, header.robotA);\n const dock = poseOrNull(header.chargerX, header.chargerY, header.chargerA);\n\n const signalAt = (worldX: number, worldY: number): number | null => {\n if (gridSize <= 0) return null;\n const col = Math.floor((worldX - left) / gridSize);\n const row = Math.floor((worldY - top) / gridSize);\n if (col < 0 || col >= width || row < 0 || row >= height) return null;\n const nibble = cells[row * width + col] ?? 0;\n return isWifiNibble(nibble) ? NIBBLE_TO_BARS[nibble]! : null;\n };\n\n return {\n dimensions,\n robot,\n dock,\n cells,\n signalAt,\n currentSignal: robot ? signalAt(robot.x, robot.y) : null,\n };\n}\n\nexport interface RenderWifiSignalPngOptions {\n /** Upscale factor (each map cell → scale×scale px). Default 4. */\n scale?: number;\n /** Draw a marker at the robot position. Default true. */\n markRobot?: boolean;\n}\n\n/**\n * Render a simple Wi-Fi coverage heatmap PNG (weakest→strongest blue, unreached\n * grey, no-data transparent), with an optional robot marker — a ready-to-show\n * image for consumers.\n */\nexport function renderWifiSignalPng(map: WifiSignalMap, opts: RenderWifiSignalPngOptions = {}): Buffer {\n const scale = Math.max(1, Math.floor(opts.scale ?? 4));\n const { width, height, left, top, gridSize } = map.dimensions;\n const png = new PNG({ width: width * scale, height: height * scale, colorType: 6 });\n\n for (let row = 0; row < height; row++) {\n for (let col = 0; col < width; col++) {\n const nibble = map.cells[row * width + col] ?? 0;\n const rgba = NIBBLE_RGBA[nibble];\n if (!rgba) continue; // no Wi-Fi data here → leave transparent\n for (let dy = 0; dy < scale; dy++) {\n for (let dx = 0; dx < scale; dx++) {\n const px = (col * scale + dx) + (row * scale + dy) * width * scale;\n const o = px << 2;\n png.data[o] = rgba[0];\n png.data[o + 1] = rgba[1];\n png.data[o + 2] = rgba[2];\n png.data[o + 3] = rgba[3];\n }\n }\n }\n }\n\n if ((opts.markRobot ?? true) && map.robot && gridSize > 0) {\n const cx = Math.floor((map.robot.x - left) / gridSize) * scale + Math.floor(scale / 2);\n const cy = Math.floor((map.robot.y - top) / gridSize) * scale + Math.floor(scale / 2);\n const r = Math.max(2, scale);\n for (let dy = -r; dy <= r; dy++) {\n for (let dx = -r; dx <= r; dx++) {\n if (dx * dx + dy * dy > r * r) continue;\n const x = cx + dx;\n const y = cy + dy;\n if (x < 0 || y < 0 || x >= width * scale || y >= height * scale) continue;\n const o = (x + y * width * scale) << 2;\n png.data[o] = 0xff;\n png.data[o + 1] = 0x45;\n png.data[o + 2] = 0x00;\n png.data[o + 3] = 0xff;\n }\n }\n }\n\n return PNG.sync.write(png);\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 {\n decodeAiFeature,\n encodeAiFeatureWrite,\n type AiDetectionRaw,\n type DreameAiFeature,\n} from './ai-detection.js';\nimport {\n decodeAutoSwitch,\n encodeAutoSwitchWrite,\n supportedAutoSwitchKeys,\n type AutoSwitchKey,\n type AutoSwitchRaw,\n} from './auto-switch.js';\nimport {\n VACUUM_CONSUMABLES,\n consumableSpec,\n type ConsumableReading,\n type DreameConsumableKey,\n} from './consumables.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 { DreameVideoSession } from '../../video/aliyun/session.js';\nimport { explainNoCameraChannel } from '../../video/camera-availability.js';\nimport { getDeviceVideoProfile } from '../../video/client.js';\nimport {\n DreameCameraController,\n type RelayMinter,\n} from '../../video/monitor/controller.js';\nimport {\n decodeVacuumMap,\n applyVacuumPFrame,\n MapDecodeError,\n OssFetcher,\n decodeWifiSignalMap,\n renderWifiSignalPng,\n type VacuumMap,\n type VacuumMapDecodeOptions,\n type OssFetchInput,\n type OssFetcherLike,\n type WifiSignalMap,\n type RenderWifiSignalPngOptions,\n} from './map/index.js';\nimport { OutOfOrderFrameError } from './map/merge.js';\nimport { looksLikeBase64Zlib, unwrapEnvelope } from './map/envelope.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\n/** Input to {@link VacuumDevice.getWifiSignalMap} / {@link VacuumDevice.getCurrentSignal}. */\nexport interface WifiSignalMapInput {\n /** Request the last stored map first (default true). `false` = read what's advertised. */\n requestFresh?: boolean;\n /** Poll interval (ms) while waiting for the OSS object to be advertised. Default 2500. */\n pollMs?: number;\n /** Max wait (ms) for the map to become available. Default 20000. */\n maxWaitMs?: number;\n /** Inject the signed-blob fetcher (tests pass a fake). */\n fetcher?: OssFetcherLike;\n /** Optional AES key/iv for an encrypted blob (hex). */\n key?: string;\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 // -- consumables --------------------------------------------------------\n /**\n * Remaining-life % (0..100) for one consumable, or `null` when the model does\n * not report it (presence == support). Call {@link refreshConsumables} first\n * on a docked robot so the cloud-shadow life values are seeded.\n */\n consumableLeftPct(key: DreameConsumableKey): number | null {\n const spec = consumableSpec(key);\n if (spec === undefined) return null;\n return this.#num(spec.life.siid, spec.life.piid);\n }\n\n /**\n * The consumables this model EXPOSES — discovered by presence (a reported\n * life property), mirroring how the HA integration derives support. Each\n * reading carries the current remaining-life % and whether a reset action\n * exists. Empty until the life properties have been observed (push) or seeded\n * via {@link refreshConsumables}.\n */\n get supportedConsumables(): readonly ConsumableReading[] {\n return VACUUM_CONSUMABLES.flatMap((spec) => {\n const leftPct = this.#num(spec.life.siid, spec.life.piid);\n return leftPct === null\n ? []\n : [{ key: spec.key, label: spec.label, leftPct, resettable: spec.reset !== null }];\n });\n }\n\n /**\n * Seed EVERY consumable life property from the cloud shadow (no robot wake) so\n * {@link supportedConsumables} reflects the full set on a docked robot. The\n * consumable life values are static settings the robot rarely re-pushes over\n * MQTT, so a fresh connect surfaces only the actively-changing ones without this.\n */\n async refreshConsumables(): Promise<void> {\n await this.refreshCachedProperties(VACUUM_CONSUMABLES.map((c) => c.life));\n }\n\n /**\n * Reset (mark replaced → life back to 100%) one consumable via its MIoT reset\n * action. Rejects when the model exposes no reset action for the key (e.g.\n * `dust-bag`) or the key is unknown.\n */\n async resetConsumable(key: DreameConsumableKey): Promise<unknown> {\n const spec = consumableSpec(key);\n if (spec === undefined || spec.reset === null) {\n throw new DreameError(`resetConsumable: no reset action for consumable \"${key}\"`);\n }\n return this.callAction(spec.reset.siid, spec.reset.aiid, []);\n }\n\n // -- AI obstacle-detection ----------------------------------------------\n /**\n * The raw `AI_DETECTION` value (siid 4 piid 22) — an int bitmask or a JSON\n * string, depending on firmware — or `null` when not yet observed. Prefer the\n * decoded {@link aiFeature} / {@link supportedAiFeatures} surface.\n */\n get aiDetectionRaw(): AiDetectionRaw {\n const v = this.getProperty(VACUUM_PROP.AI_DETECTION.siid, VACUUM_PROP.AI_DETECTION.piid)?.value;\n return typeof v === 'number' || typeof v === 'string' ? v : null;\n }\n\n /** The AI-obstacle toggles this model exposes (from its capability record). */\n get supportedAiFeatures(): readonly DreameAiFeature[] {\n return this.#caps.supportedAiFeatures;\n }\n\n /**\n * Read ONE AI-obstacle toggle's on/off state, decoded from the packed\n * `AI_DETECTION` value (transparent across the int / JSON encodings). `null`\n * when the value is unobserved or the feature is not representable.\n */\n aiFeature(feature: DreameAiFeature): boolean | null {\n return decodeAiFeature(this.aiDetectionRaw, feature);\n }\n\n /**\n * Seed {@link aiDetectionRaw} from the CLOUD SHADOW (the last value the robot\n * pushed for `AI_DETECTION`, siid 4 piid 22) WITHOUT waking it. The AI-obstacle\n * toggles are STATIC settings the robot rarely re-pushes over MQTT, so a fresh\n * connect to an idle robot has no value cached — call this on activate to\n * surface the current toggles. Emits the usual `propertyChanged`/`stateChanged`\n * (so a watching consumer re-decodes) and returns the resolved raw value\n * (`null` when the model has no AI detection or the shadow carries none yet).\n */\n async refreshAiDetection(): Promise<AiDetectionRaw> {\n if (!this.#caps.hasAiObstacleDetection) return null;\n await this.refreshCachedProperties([VACUUM_PROP.AI_DETECTION]);\n return this.aiDetectionRaw;\n }\n\n /**\n * Toggle ONE AI-obstacle feature, preserving the others via a read-modify-write\n * of the packed `AI_DETECTION` property (mirrors Tasshack `set_ai_detection`).\n * Capability-gated on `hasAiObstacleDetection`; rejects when the current value\n * is unknown (a blind write would clobber the other toggles).\n */\n async setAiFeature(feature: DreameAiFeature, value: boolean): Promise<unknown> {\n this.#requireCap(this.#caps.hasAiObstacleDetection, 'setAiFeature', 'AI obstacle detection');\n const next = encodeAiFeatureWrite(this.aiDetectionRaw, feature, value);\n return this.setProperty({ ...VACUUM_PROP.AI_DETECTION, value: next });\n }\n\n // -- auto-switch settings -----------------------------------------------\n /**\n * The raw `AUTO_SWITCH_SETTINGS` value (siid 4 piid 50) — a JSON string packing\n * every secondary toggle/setting — or `null` when not yet observed. Prefer the\n * decoded {@link autoSwitchProperty} / {@link supportedAutoSwitchKeys} surface.\n */\n get autoSwitchRaw(): AutoSwitchRaw {\n const v = this.getProperty(\n VACUUM_PROP.AUTO_SWITCH_SETTINGS.siid,\n VACUUM_PROP.AUTO_SWITCH_SETTINGS.piid,\n )?.value;\n return typeof v === 'string' ? v : null;\n }\n\n /** Whether this model reports the packed auto-switch bundle at all. */\n get hasAutoSwitchSettings(): boolean {\n return this.autoSwitchRaw !== null;\n }\n\n /**\n * The auto-switch settings this model reports, decoded from the packed value.\n * Presence-driven (a key appears iff the payload carries it) — mirrors\n * Tasshack `auto_switch_data`.\n */\n get supportedAutoSwitchKeys(): readonly AutoSwitchKey[] {\n return supportedAutoSwitchKeys(this.autoSwitchRaw);\n }\n\n /**\n * Read ONE auto-switch setting's int value, decoded from the packed\n * `AUTO_SWITCH_SETTINGS` payload. `null` when the value is unobserved or the\n * key is not present. The value is an opaque int (boolean 0/1, a small enum, or\n * a `-1`/`-n` \"not applicable\" sentinel) — the caller interprets it.\n */\n autoSwitchProperty(key: AutoSwitchKey): number | null {\n return decodeAutoSwitch(this.autoSwitchRaw, key);\n }\n\n /**\n * Seed {@link autoSwitchRaw} from the CLOUD SHADOW (the last value the robot\n * pushed for `AUTO_SWITCH_SETTINGS`, siid 4 piid 50) WITHOUT waking it. Like the\n * AI bundle these are STATIC settings the robot rarely re-pushes over MQTT, so a\n * fresh connect to an idle robot has no value cached — call this on activate to\n * surface the current settings. Returns the resolved raw value (`null` when the\n * shadow carries none yet).\n */\n async refreshAutoSwitch(): Promise<AutoSwitchRaw> {\n await this.refreshCachedProperties([VACUUM_PROP.AUTO_SWITCH_SETTINGS]);\n return this.autoSwitchRaw;\n }\n\n /**\n * Set ONE auto-switch setting. Mirrors Tasshack `set_auto_switch_property`: a\n * single-key `{\"k\":<key>,\"v\":<int>}` write the device merges server-side (the\n * other settings are preserved without a read-modify-write). Rejects when the\n * model does not report the bundle.\n */\n async setAutoSwitchProperty(key: AutoSwitchKey, value: number): Promise<unknown> {\n this.#requireCap(this.hasAutoSwitchSettings, 'setAutoSwitchProperty', 'auto-switch settings');\n const next = encodeAutoSwitchWrite(key, value);\n return this.setProperty({ ...VACUUM_PROP.AUTO_SWITCH_SETTINGS, value: next });\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 OSS object name of the latest map frame the robot advertised via its\n * PATH push (siid 6 piid 3), or `null` when none has been observed yet (the\n * robot has not uploaded a map since connect). This is the `filename` to pass\n * to {@link getMap} — or just call {@link fetchLatestMap}.\n */\n get mapFilename(): string | null {\n const v = this.getProperty(VACUUM_PROP.MAP_PATH.siid, VACUUM_PROP.MAP_PATH.piid)?.value;\n return typeof v === 'string' && v.length > 0 ? v : null;\n }\n\n /**\n * Convenience: fetch + decode the LATEST map frame the robot advertised,\n * reading {@link mapFilename} and delegating to {@link getMap}. Returns `null`\n * when no map filename has been observed yet (vs throwing) so a consumer can\n * poll it safely; once a filename is present it behaves exactly like\n * {@link getMap} (capability-gated on `canMap`, caches {@link lastMap}, emits\n * `'map'`). The `fetcher`/`key`/`iv`/`host`/`timeoutMs`/`signal` overrides are\n * forwarded verbatim.\n */\n async fetchLatestMap(opts: Omit<VacuumGetMapInput, 'filename'> = {}): Promise<VacuumMap | null> {\n const filename = this.mapFilename;\n if (filename === null) return null;\n return this.getMap({ filename, ...opts });\n }\n\n /**\n * Seed {@link mapFilename} from the CLOUD SHADOW (the last value the robot\n * pushed for siid 6 piid 3) WITHOUT waking it — so a docked/idle robot can\n * surface its LAST cleaning map without a fresh clean. Emits the usual\n * `propertyChanged`/`stateChanged` (so a map-watching consumer re-renders) and\n * returns the resolved {@link mapFilename} (null when the model has no map or\n * the shadow carries no map path yet).\n *\n * NOTE: the shadow holds the last LIVE-PATH filename; its OSS blob may have\n * expired, in which case a follow-up {@link fetchLatestMap} rejects — treat\n * that as \"no saved map available\".\n */\n async refreshSavedMapFilename(): Promise<string | null> {\n if (!this.#caps.canMap) return null;\n await this.refreshCachedProperties([VACUUM_PROP.MAP_PATH]);\n return this.mapFilename;\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 blob = await this.#fetchMapBlob(input);\n const map = decodeVacuumMap(blob, this.#decodeOpts(input));\n this.#lastMap = map;\n this.emit('map', map);\n return map;\n }\n\n /** Fetch the raw OSS blob (still the base64+zlib envelope) for a map frame. */\n async #fetchMapBlob(input: VacuumGetMapInput): Promise<Buffer> {\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 return fetcher.fetchBlob(fetchInput);\n }\n\n /** The AES key/iv decode options, threaded from a {@link VacuumGetMapInput}. */\n #decodeOpts(input: Pick<VacuumGetMapInput, 'key' | 'iv'>): VacuumMapDecodeOptions {\n return {\n ...(input.key !== undefined ? { key: input.key } : {}),\n ...(input.iv !== undefined ? { iv: input.iv } : {}),\n };\n }\n\n /**\n * Fetch the robot's Wi-Fi signal map — the last stored coverage heatmap, with\n * NO robot movement. Requests it (map service `wifiMap` action, siid 6 aiid 4),\n * polls `PropWifiMap` (6/15) for the OSS object it advertises, fetches + decodes\n * it into a per-cell signal grid + geometry + robot/dock poses. The signal at\n * any world point (incl. the robot's own position) is a lookup — see\n * {@link WifiSignalMap.signalAt} / {@link WifiSignalMap.currentSignal}.\n *\n * Pass `requestFresh: false` to skip the request and read whatever is already\n * advertised. Throws if no Wi-Fi map exists yet (never run on this device).\n */\n async getWifiSignalMap(input: WifiSignalMapInput = {}): Promise<WifiSignalMap> {\n if (input.requestFresh !== false) {\n try {\n await this.callAction(6, 4, []);\n } catch {\n /* best-effort: the object may already be advertised */\n }\n }\n const pollMs = input.pollMs ?? 2500;\n const deadline = Date.now() + (input.maxWaitMs ?? 20_000);\n let objectName = '';\n for (;;) {\n const res = await this.refreshProperties([{ siid: 6, piid: 15 }]);\n const val = res[0]?.value;\n if (typeof val === 'string' && val.length > 0) {\n objectName = val;\n break;\n }\n if (Date.now() + pollMs >= deadline) {\n throw new DreameError('wifi signal map not available (none stored yet, or request timed out)');\n }\n await new Promise((r) => setTimeout(r, pollMs));\n }\n // The property is `\"<n>,<oss-object-name>\"`; the OSS fetcher wants the object name.\n const filename = objectName.replace(/^\\d+,/, '');\n const blob = await this.#fetchMapBlob({\n filename,\n ...(input.fetcher ? { fetcher: input.fetcher } : {}),\n ...(input.host ? { host: input.host } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n ...(input.signal ? { signal: input.signal } : {}),\n });\n return decodeWifiSignalMap(blob, this.#decodeOpts(input));\n }\n\n /**\n * Render the Wi-Fi signal map as a simple PNG heatmap image (weakest→strongest,\n * unreached grey, robot marker) — ready to display. Convenience over\n * {@link getWifiSignalMap} + {@link renderWifiSignalPng}.\n */\n async getWifiSignalImage(\n input: WifiSignalMapInput & RenderWifiSignalPngOptions = {},\n ): Promise<Buffer> {\n const map = await this.getWifiSignalMap(input);\n return renderWifiSignalPng(map, input);\n }\n\n /**\n * The one-call \"current Wi-Fi signal\": fetches the signal map and returns the\n * bars (1–4, 0 = unreached) at the robot's own position, or null when unknown.\n */\n async getCurrentSignal(input: WifiSignalMapInput = {}): Promise<number | null> {\n const map = await this.getWifiSignalMap(input);\n return map.currentSignal;\n }\n\n /**\n * The merge base for live P-frame streaming — always an INFLATED frame buffer.\n * An I-frame (re)seeds it; each P-frame merge replaces it with the merged\n * inflated buffer (so further P-frames stack). `null` until the first I-frame\n * (or after an out-of-order / map-id reset).\n */\n #mapStreamBase: Buffer | null = null;\n\n /** Drop the P-frame merge base so the next I-frame re-seeds the stream. */\n resetMapStream(): void {\n this.#mapStreamBase = null;\n }\n\n /** Inflate an OSS blob to a raw frame buffer (live envelope → zlib; verbatim if already inflated). */\n #inflateFrame(blob: Buffer, opts: VacuumMapDecodeOptions): Buffer {\n return looksLikeBase64Zlib(blob) ? unwrapEnvelope(blob.toString('latin1'), opts) : blob;\n }\n\n /**\n * Fetch the latest advertised map frame and fold it into a continuously\n * updating map. An I-frame (re)seeds the merge base and renders standalone; a\n * P-frame is merged onto the base via {@link applyVacuumPFrame} so the live\n * grid stays COMPLETE (a P-frame decoded standalone is only byte-deltas). On an\n * out-of-order P-frame or a map-id change the base is dropped and `null`\n * returned — the next I-frame re-seeds. Returns `null` when no frame is\n * advertised yet, or a P-frame arrives before any I-frame.\n *\n * Caches {@link lastMap} and emits `'map'` exactly like {@link getMap}, so a\n * map-watching consumer (e.g. the camstack map child) gets a fresh complete\n * frame on every push during a live clean.\n */\n async fetchLatestMapStreaming(\n opts: Omit<VacuumGetMapInput, 'filename'> = {},\n ): Promise<VacuumMap | null> {\n this.#requireCap(this.#caps.canMap, 'fetchLatestMapStreaming', 'map decoding');\n const filename = this.mapFilename;\n if (filename === null) return null;\n const input: VacuumGetMapInput = { filename, ...opts };\n const decodeOpts = this.#decodeOpts(input);\n const blob = await this.#fetchMapBlob(input);\n const inflated = this.#inflateFrame(blob, decodeOpts);\n const frame = decodeVacuumMap(inflated, decodeOpts);\n\n if (frame.frameType === 'I') {\n this.#mapStreamBase = inflated;\n this.#lastMap = frame;\n this.emit('map', frame);\n return frame;\n }\n\n if (frame.frameType === 'P') {\n if (this.#mapStreamBase === null) return null; // need an I-frame first\n try {\n // Both base and incoming frame are inflated buffers → mergePFrame path.\n const { buffer, data } = applyVacuumPFrame(this.#mapStreamBase, inflated);\n this.#mapStreamBase = buffer;\n this.#lastMap = data;\n this.emit('map', data);\n return data;\n } catch (err) {\n // Out-of-order or a fresh map (id mismatch / grid change): drop the base\n // and wait for the next I-frame to re-seed rather than render garbage.\n if (err instanceof OutOfOrderFrameError || err instanceof MapDecodeError) {\n this.#mapStreamBase = null;\n return null;\n }\n throw err;\n }\n }\n\n // 'W' (saved-map) or any other type: render standalone, leave the base as-is.\n this.#lastMap = frame;\n this.emit('map', frame);\n return frame;\n }\n\n /**\n * Build a {@link DreameCameraController} for this device's camera, wired to\n * both planes it needs: the MIoT action transport (this device handle) and a\n * fresh Aliyun {@link DreameVideoSession} for the relay. Call\n * `controller.open()` to autonomously cold-start the stream and get a pullable\n * RTMP URL, then `controller.close()` to release it.\n *\n * The camera's per-session privacy gate means a coded camera needs its PIN:\n * pass `accessCode` (the code set at pairing). `iotId` is resolved from the\n * device's cloud video profile when not supplied.\n */\n async createCameraController(opts?: {\n /** LinkVisual channel id; resolved from the cloud profile when omitted. */\n iotId?: string;\n /** Privacy PIN set at pairing (required to wake a coded camera). */\n accessCode?: string;\n /** Video vendor; defaults to `ali` (the only vendor that mints an RTMP relay). */\n vendor?: 'ali' | 'tx';\n /** Aliyun region code reported as `area`; defaults to `'4'`. */\n area?: string;\n /** Keep-alive cadence in ms (default 10s). */\n keepAliveIntervalMs?: number;\n /** Max time to wait for the relay while the device wakes. */\n wakeTimeoutMs?: number;\n /** Reported when a keep-alive tick fails. */\n onKeepAliveError?: (err: unknown) => void;\n /** Optional nodelink-style line logger. */\n log?: (line: string) => void;\n /** Reuse an existing relay minter (e.g. a shared {@link DreameVideoSession}). */\n relay?: RelayMinter;\n }): Promise<DreameCameraController> {\n const session = this.currentSession();\n const region = this.region;\n const relay =\n opts?.relay ?? new DreameVideoSession({ session, region });\n // The profile is KEPT, not reduced to its `iotId` and discarded. When the\n // channel is missing it is the only thing that can say why — see\n // `explainNoCameraChannel`. The old sentence named the missing field and\n // covered four different causes with it, and telling them apart meant\n // reading this library's source.\n let iotId = opts?.iotId ?? null;\n if (!iotId) {\n const profile = await getDeviceVideoProfile({ session, region, did: this.deviceId });\n iotId = profile.iotId;\n if (!iotId) {\n throw new DreameError(explainNoCameraChannel(profile) ?? 'device has no video channel');\n }\n }\n return new DreameCameraController({\n device: this,\n relay,\n iotId,\n accountId: session.uid,\n ...(opts?.vendor ? { vendor: opts.vendor } : {}),\n ...(opts?.accessCode !== undefined ? { accessCode: opts.accessCode } : {}),\n ...(opts?.area ? { area: opts.area } : {}),\n ...(opts?.keepAliveIntervalMs !== undefined\n ? { keepAliveIntervalMs: opts.keepAliveIntervalMs }\n : {}),\n ...(opts?.wakeTimeoutMs !== undefined ? { wakeTimeoutMs: opts.wakeTimeoutMs } : {}),\n ...(opts?.onKeepAliveError ? { onKeepAliveError: opts.onKeepAliveError } : {}),\n ...(opts?.log ? { log: opts.log } : {}),\n });\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 VACUUM_PROP.AI_DETECTION,\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 — 20-byte heartbeat (sentinel 0xCE at [0]/[19]); byte 11\n * battery, byte 12 main-state, byte 13 task sub-state. See parseHeartbeat. */\n HEARTBEAT: { siid: 1, piid: 1 } as const,\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 SET_CURRENT_MAP: 200,\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/** Switch active map: o:200, idx=mapIndex. device.py _build_set_current_map_payload. */\nexport function buildSetCurrentMapPayload(mapIndex: number): {\n m: string;\n p: number;\n o: number;\n d: { idx: number };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.SET_CURRENT_MAP, d: { idx: mapIndex } };\n}\n\n/**\n * CMS consumable getter payload `{m:'g', t:'CMS'}` — read the blade/brush/robot\n * minute counters. Sent as the single in-param of the SCHEDULING_TASK (2:50)\n * action. device.py `_build_get_consumable_payload`.\n */\nexport function buildGetConsumablePayload(): { m: string; t: string } {\n return { m: 'g', t: 'CMS' };\n}\n\n/**\n * CMS consumable setter payload `{m:'s', t:'CMS', d:{value:[...]}}` — write the\n * three counters (used to reset one to 0). device.py\n * `_build_set_consumable_payload`. Throws on a non-3 / negative counter list.\n */\nexport function buildSetConsumablePayload(values: number[]): {\n m: string;\n t: string;\n d: { value: number[] };\n} {\n const normalized = values.map((v) => Math.trunc(v));\n if (normalized.length !== 3) {\n throw new RangeError(`CMS values must contain exactly 3 counters; got ${normalized.length}`);\n }\n if (normalized.some((v) => v < 0)) {\n throw new RangeError(`CMS values cannot be negative; got ${JSON.stringify(normalized)}`);\n }\n return { m: 's', t: 'CMS', d: { value: normalized } };\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/** Severity class of a mower DEVICE_CODE (2:2 / {@link MowerFault}). */\nexport type MowerFaultSeverity = 'info' | 'warning' | 'error';\n\n/**\n * Classify a mower device-code by severity, matching the INFO/WARNING/ERROR\n * annotations on {@link MowerFault}. Most codes are status/INFO (e.g. 50\n * TaskStart); only a subset are actionable. Unknown codes default to `info`.\n */\nexport function mowerFaultSeverity(code: number | null): MowerFaultSeverity {\n if (code === null) return 'info';\n if ((code >= 1 && code <= 30) || code === 37 || code === 73) return 'error';\n if ((code >= 31 && code <= 36) || (code >= 38 && code <= 45)) return 'warning';\n return 'info';\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// -- CMS consumables (custom action on SCHEDULING_TASK 2:50) -----------------\n\n/** Canonical CMS consumable counter keys (blade, brush, robot maintenance). */\nexport type MowerConsumableKey = 'blade' | 'brush' | 'maintenance';\n\n/** One CMS consumable counter: minutes used + total + remaining %. */\nexport interface MowerConsumableReading {\n readonly key: MowerConsumableKey;\n /** Minutes of run-time accrued on this counter (counts UP). */\n readonly usedMinutes: number;\n /** Counter's full-life duration in minutes. */\n readonly totalMinutes: number;\n /** Remaining life percentage (0..100, one decimal). */\n readonly remainingPercent: number;\n}\n\n/**\n * CMS counter layout, ported from antondaubert/dreame-mower `device.py`\n * (CONSUMABLE_COUNTER_TOTAL_MINUTES + CONSUMABLE_COUNTER_INDEX). The getter\n * returns `[blade_min, brush_min, robot_min]`; index/total are fixed per model.\n */\nconst MOWER_CONSUMABLES: ReadonlyArray<{\n readonly key: MowerConsumableKey;\n readonly index: number;\n readonly totalMinutes: number;\n}> = [\n { key: 'blade', index: 0, totalMinutes: 6000 },\n { key: 'brush', index: 1, totalMinutes: 30000 },\n { key: 'maintenance', index: 2, totalMinutes: 3600 },\n];\n\n/** Map a consumable key (incl. aliases) to its CMS counter index. */\nexport function mowerConsumableIndex(item: string): number | null {\n switch (item.trim().toLowerCase()) {\n case 'blade':\n case 'blades':\n return 0;\n case 'brush':\n case 'cleaning_brush':\n return 1;\n case 'robot':\n case 'maintenance':\n case 'robot_maintenance':\n return 2;\n default:\n return null;\n }\n}\n\nfunction toInt(v: unknown): number | null {\n if (typeof v === 'number' && Number.isFinite(v)) {\n return Math.trunc(v);\n }\n if (typeof v === 'string') {\n const n = Number(v);\n return Number.isFinite(n) ? Math.trunc(n) : null;\n }\n return null;\n}\n\n/**\n * Pull the first successful data payload out of a custom-action result. Mirrors\n * the donor `_extract_custom_action_data`: the result itself when it carries a\n * `value` list, else its `d` object, else the first non-error `out[].d`.\n */\nfunction extractCustomActionData(result: unknown): Record<string, unknown> | null {\n if (!isRecord(result)) {\n return null;\n }\n if (Array.isArray(result['value'])) {\n return result;\n }\n if (isRecord(result['d'])) {\n return result['d'];\n }\n const out = result['out'];\n if (!Array.isArray(out)) {\n return null;\n }\n for (const entry of out) {\n if (!isRecord(entry)) {\n continue;\n }\n const r = entry['r'];\n const code = entry['code'];\n const rError = r !== undefined && r !== null && r !== 0;\n const codeError = code !== undefined && code !== null && code !== 0;\n if (rError && codeError) {\n continue;\n }\n if (isRecord(entry['d'])) {\n return entry['d'];\n }\n }\n return null;\n}\n\n/**\n * Extract the raw `[blade, brush, robot]` minute counters from a CMS getter\n * result, or null if the response is malformed. Cast-free, no throw.\n */\nexport function extractMowerConsumableValues(result: unknown): number[] | null {\n const data = extractCustomActionData(result);\n if (data === null) {\n return null;\n }\n const values = data['value'];\n if (!Array.isArray(values) || values.length < 3) {\n return null;\n }\n const out: number[] = [];\n for (const v of values.slice(0, 3)) {\n const n = toInt(v);\n if (n === null) {\n return null;\n }\n out.push(n);\n }\n return out;\n}\n\n/**\n * Parse a CMS getter result into typed consumable readings (blade/brush/\n * maintenance) with remaining %. Returns null on a malformed response.\n */\nexport function parseMowerConsumables(result: unknown): readonly MowerConsumableReading[] | null {\n const values = extractMowerConsumableValues(result);\n if (values === null) {\n return null;\n }\n return MOWER_CONSUMABLES.map((c) => {\n const used = values[c.index] ?? 0;\n const remaining = c.totalMinutes - used;\n const pct = Math.max(0, Math.min(100, Math.round((remaining / c.totalMinutes) * 1000) / 10));\n return {\n key: c.key,\n usedMinutes: used,\n totalMinutes: c.totalMinutes,\n remainingPercent: pct,\n };\n });\n}\n\n// -- heartbeat task sub-state (HEARTBEAT 1:1) --------------------------------\n\n/** Decoded mowing task sub-state (heartbeat byte 13), or null when no mowing\n * session is in progress. Ported from antondaubert/dreame-mower\n * `property_misc.py` Property11Handler. */\nexport type MowerTaskSubState =\n | 'idle'\n | 'starting'\n | 'mowing'\n | 'paused'\n | 'finished'\n | 'failed'\n | 'exit'\n | 'returning-to-dock';\n\n/** Ordered sub-state table; the heartbeat encodes `subState = base + index`. */\nexport const MOWER_TASK_SUBSTATES: readonly MowerTaskSubState[] = [\n 'idle',\n 'starting',\n 'mowing',\n 'paused',\n 'finished',\n 'failed',\n 'exit',\n 'returning-to-dock',\n];\n\nconst HEARTBEAT_SENTINEL = 0xce; // 206 — byte[0] and byte[last]\nconst HEARTBEAT_SUBSTATE_BASE = 33;\nconst HEARTBEAT_MAIN_STATE_MOWING = 4;\n\n/** Typed heartbeat decode: battery byte, main-state, and (when mowing) the task\n * sub-state. */\nexport interface MowerHeartbeat {\n /** Raw battery byte (byte 11) — percent with a charging flag in the high bits. */\n readonly rawBattery: number;\n /** `(byte12 & 0x0F) - 1`; 4 = mowing. */\n readonly mainState: number;\n /** Raw sub-state byte (byte 13). */\n readonly subStateRaw: number;\n /** Decoded sub-state when a mowing session is active, else null. */\n readonly taskSubState: MowerTaskSubState | null;\n}\n\n/**\n * Decode the 20-byte HEARTBEAT (1:1) blob. Returns null when the payload is not a\n * well-formed heartbeat (wrong length / missing 0xCE sentinels). The task\n * sub-state is only meaningful while `mainState === 4` (mowing); in any other\n * main state there is no active mowing task (returns `taskSubState: null`).\n */\nexport function parseMowerHeartbeat(value: unknown): MowerHeartbeat | null {\n if (!Array.isArray(value) || value.length < 14) {\n return null;\n }\n const bytes: number[] = [];\n for (const b of value) {\n if (typeof b !== 'number') {\n return null;\n }\n bytes.push(b);\n }\n if (bytes[0] !== HEARTBEAT_SENTINEL || bytes[bytes.length - 1] !== HEARTBEAT_SENTINEL) {\n return null;\n }\n const rawBattery = bytes[11] ?? 0;\n const mainState = ((bytes[12] ?? 0) & 0x0f) - 1;\n const subStateRaw = bytes[13] ?? 0;\n const taskSubState =\n mainState === HEARTBEAT_MAIN_STATE_MOWING\n ? (MOWER_TASK_SUBSTATES[subStateRaw - HEARTBEAT_SUBSTATE_BASE] ?? null)\n : null;\n return { rawBattery, mainState, subStateRaw, taskSubState };\n}\n\n// -- mowing progress (POSE_COVERAGE 1:4 task block) --------------------------\n\nconst POSE_SENTINEL = 0xce; // 206 — frames a sentinel-wrapped pose-coverage blob\n\n/** Decoded mowing-progress signal from the POSE_COVERAGE (1:4) `task` block. */\nexport interface MowerMowingProgress {\n /** 0..100 — overall mowing completion. */\n readonly progressPercent: number;\n /** Already-finished area in m². */\n readonly currentAreaSqm: number;\n /** Total target area in m². */\n readonly totalAreaSqm: number;\n}\n\n/** Coerce a POSE_COVERAGE (1:4) raw value to a flat byte array, or null. */\nfunction poseBytes(value: unknown): number[] | null {\n if (!Array.isArray(value)) return null;\n const bytes: number[] = [];\n for (const b of value) {\n if (typeof b !== 'number') return null;\n bytes.push(b);\n }\n return bytes;\n}\n\n/**\n * Locate the 10-byte `task` block inside a POSE_COVERAGE (1:4) frame and decode\n * the mowing progress. Frame shapes (ported from antondaubert/dreame-mower\n * `pose_coverage.py`):\n * - sentinel-framed `[CE] pose(6) trace(15) task(10) [CE]` (33B) / `…task(10) trace2(11) [CE]` (44B)\n * → task at offset 22;\n * - alt `task(10) [CE] [+trace]` (no leading sentinel, ≥11B) → task at offset 0.\n * The 22B `[CE] pose trace` and 13B `[CE] pose extra` shapes carry NO task → null.\n *\n * `task` layout: `[2:4] percent u16LE (value×100) [4:7] total u24LE (centi-m²)\n * [7:10] finished u24LE (centi-m²)`.\n */\nexport function parseMowingProgress(value: unknown): MowerMowingProgress | null {\n const bytes = poseBytes(value);\n if (bytes === null || bytes.length < 11) return null;\n const last = bytes[bytes.length - 1];\n const head = bytes[0];\n\n let offset: number | null = null;\n if (head !== POSE_SENTINEL && last === POSE_SENTINEL) {\n offset = 0; // alt format: task block leads\n } else if (head === POSE_SENTINEL && last === POSE_SENTINEL && bytes.length >= 33) {\n offset = 22; // standard sentinel-framed frame with a task block\n }\n if (offset === null || offset + 10 > bytes.length) return null;\n\n const rawPercent = (bytes[offset + 2] ?? 0) | ((bytes[offset + 3] ?? 0) << 8);\n const total =\n (bytes[offset + 4] ?? 0) | ((bytes[offset + 5] ?? 0) << 8) | ((bytes[offset + 6] ?? 0) << 16);\n const finish =\n (bytes[offset + 7] ?? 0) | ((bytes[offset + 8] ?? 0) << 8) | ((bytes[offset + 9] ?? 0) << 16);\n\n return {\n progressPercent: rawPercent ? Math.min(100, rawPercent / 100) : 0,\n currentAreaSqm: finish / 100,\n totalAreaSqm: total / 100,\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 = '#32cd32';\nconst NAV_PATH = '#b4b4b4';\nconst OBSTACLE_STROKE = '#ff4d00';\nconst OBSTACLE_FILL = '#ff4d0065';\nconst TEXT_COLOR = '#000000';\nconst ZONE_LABEL_COLOR = '#3c3c3c';\n\n/** Escape the five XML metacharacters so a room name can't break the SVG. */\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\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\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 — per-zone translucent pastel (always, matching the app).\n map.zones.forEach((zone, i) => {\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 (zone-tinted).\n map.zones.forEach((zone, i) => {\n const outline = ZONE_COLORS[i % ZONE_COLORS.length]?.[1] ?? 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 // 5. Zone NAME labels at each zone's centroid (escaped; on top of fills).\n for (const zone of map.zones) {\n if (zone.name.length === 0 || zone.path.length === 0) {\n continue;\n }\n let sumX = 0;\n let sumY = 0;\n for (const p of zone.path) {\n sumX += p.x;\n sumY += p.y;\n }\n const centroid: MowerPoint = { x: sumX / zone.path.length, y: sumY / zone.path.length };\n const { px, py } = coordToPixel(centroid, bounds, width, height, padding);\n lines.push(\n `<text x=\"${px}\" y=\"${py}\" font-family=\"Arial, sans-serif\" font-size=\"20\" ` +\n `font-weight=\"bold\" fill=\"${ZONE_LABEL_COLOR}\" text-anchor=\"middle\" ` +\n `dominant-baseline=\"middle\">${escapeXml(zone.name)}</text>`,\n );\n }\n\n lines.push('</svg>');\n return lines.join('\\n');\n}\n","import { BaseDevice, type BaseDeviceInput } from '../../device/base-device.js';\nimport { getBatchDeviceDatas as defaultGetBatchDeviceDatas } from '../../cloud/commands.js';\nimport {\n MOWER_ACTION,\n MOWER_PROP,\n buildAllAreaPayload,\n buildEdgePayload,\n buildGetConsumablePayload,\n buildResumePayload,\n buildSetConsumablePayload,\n buildSetCurrentMapPayload,\n buildSpotPayload,\n buildZonePayload,\n} from './properties.js';\nimport {\n MowerChargingStatus,\n MowerControlAction,\n MowerFault,\n mowerFaultSeverity,\n MowerStatus,\n MowerTaskStatus,\n type MowerFaultSeverity,\n} from './enums.js';\nimport {\n asNum,\n enumLookup,\n extractMowerConsumableValues,\n mowerConsumableIndex,\n parseControlStatus,\n parseMowerConsumables,\n parseMowerHeartbeat,\n parseMowingProgress,\n parseTaskDescriptor,\n type MowerConsumableKey,\n type MowerConsumableReading,\n type MowerControlState,\n type MowerMowingProgress,\n type MowerTaskDescriptor,\n type MowerTaskSubState,\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 // Default to the live cloud batch-fetch (now that the endpoint is recovered),\n // building the account-auth CommonInput from the protected map-fetch\n // accessors. Tests/callers may still inject a fake via `getBatchDeviceDatas`.\n this.#fetchBatch =\n input.getBatchDeviceDatas ??\n ((did, props) =>\n defaultGetBatchDeviceDatas(\n { session: this.currentSession(), region: this.region, did },\n props,\n ));\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 /**\n * Live mowing task sub-state decoded from the HEARTBEAT (1:1) blob —\n * idle/starting/mowing/paused/finished/failed/exit/returning-to-dock — or null\n * when no mowing session is in progress (or the heartbeat is absent). This is\n * the donor's \"current task\" sensor; the heartbeat is push-only (the mower does\n * not answer live reads), so it tracks the device while it is actively mowing.\n */\n get taskSubState(): MowerTaskSubState | null {\n const hb = parseMowerHeartbeat(\n this.getProperty(MOWER_PROP.HEARTBEAT.siid, MOWER_PROP.HEARTBEAT.piid)?.value,\n );\n return hb?.taskSubState ?? null;\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 /**\n * Live mowing PROGRESS decoded from the POSE_COVERAGE (1:4) task block —\n * `{progressPercent, currentAreaSqm, totalAreaSqm}` — or null when 1:4 carries\n * no task block (idle / pose-only frame). This is the byte-accurate progress\n * the Dreamehome app shows (vs {@link coverageTargetPct}, the task TARGET).\n */\n get mowingProgress(): MowerMowingProgress | null {\n return parseMowingProgress(\n this.getProperty(MOWER_PROP.POSE_COVERAGE.siid, MOWER_PROP.POSE_COVERAGE.piid)?.value,\n );\n }\n /** Mowing completion percent (0..100) from {@link mowingProgress}, or null. */\n get mowingProgressPct(): number | null {\n return this.mowingProgress?.progressPercent ?? null;\n }\n /** Severity of the current DEVICE_CODE (2:2) — info / warning / error. */\n get faultSeverity(): MowerFaultSeverity {\n return mowerFaultSeverity(this.faultRaw);\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 * Switch the mower's ACTIVE map (2:50 o:200). Takes a map *id* (as carried by\n * {@link MowerMap.availableMaps}/`currentMapId`) and resolves it to the\n * firmware's map *index* via the last-fetched map; falls back to treating the\n * id as the index when no map has been fetched yet. Mirrors the donor\n * `set_current_map`. Capability-gated on `canMap`.\n */\n async setCurrentMap(mapId: number): Promise<unknown> {\n this.#requireCap(this.#caps.canMap, 'setCurrentMap', 'map switching');\n const id = Math.trunc(mapId);\n const entry = this.#lastMap?.availableMaps.find((m) => m.mapId === id);\n const index = entry?.mapIndex ?? id;\n return this.#sendTask(buildSetCurrentMapPayload(index));\n }\n\n // -- CMS consumables ----------------------------------------------------\n /**\n * Read the raw CMS consumable counters `[blade, brush, robot]` (minutes used),\n * via the SCHEDULING_TASK (2:50) custom-action getter `{m:'g',t:'CMS'}`. This\n * is a LIVE device action (it wakes the mower); rejects with\n * {@link DreameDeviceOfflineError} when the mower is unreachable. Returns null\n * if the response is malformed.\n */\n async getConsumableValues(): Promise<readonly number[] | null> {\n const result = await this.callAction(\n MOWER_PROP.SCHEDULING_TASK.siid,\n MOWER_PROP.SCHEDULING_TASK.piid,\n [buildGetConsumablePayload()],\n );\n return extractMowerConsumableValues(result);\n }\n\n /**\n * Read the CMS consumables as typed readings (blade/brush/maintenance) with\n * remaining %. LIVE action — see {@link getConsumableValues}. Returns null on\n * a malformed response.\n */\n async getConsumables(): Promise<readonly MowerConsumableReading[] | null> {\n const result = await this.callAction(\n MOWER_PROP.SCHEDULING_TASK.siid,\n MOWER_PROP.SCHEDULING_TASK.piid,\n [buildGetConsumablePayload()],\n );\n return parseMowerConsumables(result);\n }\n\n /**\n * Reset one CMS consumable counter to zero. Reads the current counters, zeroes\n * the selected one (leaving the others), and writes them back via the\n * `{m:'s',t:'CMS',d:{value:[…]}}` setter. LIVE action. Throws on an unknown\n * item or when the current counters cannot be read.\n */\n async resetConsumable(item: MowerConsumableKey): Promise<void> {\n const index = mowerConsumableIndex(item);\n if (index === null) {\n throw new DreameError(`resetConsumable: unknown consumable item \"${item}\"`);\n }\n const current = await this.getConsumableValues();\n if (current === null) {\n throw new DreameError('resetConsumable: failed to read current CMS counters');\n }\n const next = [...current];\n next[index] = 0;\n await this.callAction(MOWER_PROP.SCHEDULING_TASK.siid, MOWER_PROP.SCHEDULING_TASK.piid, [\n buildSetConsumablePayload(next),\n ]);\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.HEARTBEAT,\n MOWER_PROP.POSE_COVERAGE,\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 * Force-refresh callback handed down to the device's MQTT push so it can\n * self-heal a broker CONNACK auth-refusal (stale token). Bound by the facade\n * to {@link Nodreame.reauthenticate}.\n */\n onAuthFailure?: () => Promise<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 ...(args.onAuthFailure !== undefined ? { onAuthFailure: args.onAuthFailure } : {}),\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 /** Background timer that proactively refreshes before the token expires. */\n #refreshTimer: ReturnType<typeof setTimeout> | null = null;\n /** Guard against overlapping proactive-refresh runs. */\n #refreshInFlight = false;\n /**\n * The single in-flight refresh, shared by concurrent callers so the proactive\n * timer and a manual `ensureSession()`/`reauthenticate()` never double-refresh.\n */\n #inFlightRefresh: Promise<DreameSession> | null = null;\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 // Arm the proactive refresh against this session's expiry.\n this.#scheduleProactiveRefresh();\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 return this.#refreshNow(current);\n }\n\n /**\n * Force a session refresh REGARDLESS of the current expiry, then propagate the\n * new token to every live device push. Wired as each push's `onAuthFailure`:\n * when a broker refuses a CONNECT because it considers the token stale (even\n * though {@link ensureSession} still thinks it valid — e.g. server-side early\n * revocation or clock skew), this mints a genuinely fresh token instead of\n * replaying the rejected one.\n */\n async reauthenticate(): Promise<DreameSession> {\n if (this.#closed) {\n throw new DreameAuthError('client is closed');\n }\n const current = this.#session;\n if (!current) {\n return this.login();\n }\n return this.#refreshNow(current);\n }\n\n /**\n * Refresh via the refresh-token (with a full re-login fallback) and propagate\n * the new token to every device push. Shared by {@link ensureSession} (within\n * the leeway window) and {@link reauthenticate} (unconditional).\n */\n async #refreshNow(current: DreameSession): Promise<DreameSession> {\n // Coalesce concurrent refreshes (background timer + manual call) into one.\n const existing = this.#inFlightRefresh;\n if (existing) {\n return existing;\n }\n const run = this.#doRefresh(current);\n this.#inFlightRefresh = run;\n try {\n return await run;\n } finally {\n this.#inFlightRefresh = null;\n }\n }\n\n async #doRefresh(current: DreameSession): Promise<DreameSession> {\n if (current.refreshToken) {\n // Narrow scope: the full-re-login fallback must cover ONLY a failed\n // refresh-token GRANT — never the subsequent propagation. If we let the\n // catch swallow a propagation failure it would trigger a spurious full\n // re-login (and, in the fleet, tear down healthy siblings).\n let next: DreameSession | null = null;\n try {\n 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 } catch {\n next = null; // fall through to a full re-login\n }\n if (next) {\n await this.#adoptSession(next);\n return next;\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 onAuthFailure: () => this.reauthenticate(),\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 // Guard the forward-emit: reactive re-auth / cap-exhaustion failures now\n // funnel through here. An unhandled 'error' on the emitter throws, which\n // would crash a consumer that never attached an 'error' listener.\n h.on('error', (err) => {\n if (this.listenerCount('error') > 0) {\n this.emit('error', err);\n }\n });\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 // Re-arm the proactive refresh against the session used for this discovery.\n this.#scheduleProactiveRefresh();\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 this.#clearRefreshTimer();\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 this.#scheduleProactiveRefresh();\n }\n\n #clearRefreshTimer(): void {\n if (this.#refreshTimer) {\n clearTimeout(this.#refreshTimer);\n this.#refreshTimer = null;\n }\n }\n\n /** Arm the proactive-refresh timer `delayMs` from now (clamped to ≥0). */\n #armRefreshTimer(delayMs: number): void {\n this.#clearRefreshTimer();\n if (this.#closed) {\n return;\n }\n this.#refreshTimer = setTimeout(\n () => {\n this.#refreshTimer = null;\n void this.#runProactiveRefresh();\n },\n Math.max(0, delayMs),\n );\n }\n\n /** (Re)arm the proactive-refresh timer against the current session's expiry. */\n #scheduleProactiveRefresh(): void {\n const session = this.#session;\n if (!session) {\n this.#clearRefreshTimer();\n return;\n }\n this.#armRefreshTimer(session.expiresAt - this.#leewayMs - Date.now());\n }\n\n /**\n * Timer body: refresh the session before it expires, then reschedule against\n * the new expiry. Because {@link ensureSession} propagates the refreshed token\n * to every device push, each MQTT connection is re-keyed BEFORE the broker\n * would reject the old one — the reactive re-auth loop never even starts.\n */\n async #runProactiveRefresh(): Promise<void> {\n if (this.#closed || this.#refreshInFlight) {\n return;\n }\n this.#refreshInFlight = true;\n try {\n await this.ensureSession();\n this.#refreshInFlight = false;\n // Track the newest expiry (ensureSession may have returned the current\n // session unchanged if the clock had not yet crossed the boundary).\n this.#scheduleProactiveRefresh();\n } catch (err) {\n this.#refreshInFlight = false;\n // Surface only when someone is listening — an unhandled 'error' on the\n // emitter would throw. The failure is transient; the retry below recovers.\n if (this.listenerCount('error') > 0) {\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n }\n // Retry after a bounded backoff rather than hot-looping on a still-expiring\n // session (whose boundary is already in the past ⇒ a 0ms reschedule).\n if (!this.#closed) {\n this.#armRefreshTimer(this.#leewayMs);\n }\n }\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 // allSettled — NOT all: one device's applySession failure must NEVER reject\n // the whole propagation. A rejected Promise.all here would (1) orphan the\n // healthy siblings that already reconnected, (2) bubble up into #doRefresh\n // and trigger a spurious full re-login, and (3) reach the originating push's\n // reauth catch and tear down its live client. Each device self-heals its own\n // reconnect (see DreamePush.refreshSession), so per-device failures are\n // isolated here.\n await Promise.allSettled(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 // `deviceid` is listed explicitly: the bare `did` fragment does NOT match\n // `deviceid` (the substring `did` is absent from `d-e-v-i-c-e-i-d`), yet\n // base-device sets the real `did` as `deviceId` on raw payloads (FIX 1).\n 'deviceid',\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 // room/zone/map names are user-set PII (FIX 3).\n 'zone_name',\n 'zonename',\n 'map_name',\n 'mapname',\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 like `START`). ACCEPTED\n // TRADE-OFF: a bare `{ name: \"...\" }` in an event argument is NOT scrubbed by\n // key. This is mitigated in practice by (a) the value-sanitizer below\n // (sanitizeStringValue, applied to EVERY string scalar — it catches OSS\n // paths / URLs / tokens regardless of key), and (b) the specific\n // customName/deviceName/nickName/zoneName/mapName fragments which cover the\n // realistic PII-bearing name cases.\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/**\n * Risky-VALUE patterns. A string scalar can carry a secret under a NON-sensitive\n * (e.g. numeric `\"siid.piid\"`) key — an OSS object path `ali_dreame/<uid>/<did>/<n>`,\n * a signed URL with a `token=` query param, or a long opaque secret run. These\n * never reach a key-name match, so they slip past {@link isSensitiveKey}.\n *\n * Kept DELIBERATELY conservative so the actual diagnostic signal (enum names like\n * `START`/`SweepAndMop`, numeric-ish strings, comma fault lists like `\"18,107\"`,\n * small JSON) is preserved. Only the patterns below trigger a redact.\n */\nconst RISKY_VALUE_PATTERNS: readonly RegExp[] = [\n /:\\/\\//, // any URL scheme (https://, mqtts://, …) — signed URLs carry tokens\n /ali_dreame\\//i, // OSS object path embedding uid/did\n /[A-Za-z0-9_-]{32,}/, // a long opaque token run (base64/hex secrets)\n /\\b(did|uid|token)\\s*=/i, // query-param secrets (?did=…&token=…)\n /access/i, // accessKey / access-token / x-access-key style markers\n];\n\n/**\n * Replace a string VALUE with {@link REDACTED} when it matches a risky pattern,\n * otherwise return it unchanged. Applied to EVERY string scalar inside\n * {@link redact}, so it covers property values, event arguments, AND raw frames.\n */\nexport function sanitizeStringValue(s: string): string {\n return RISKY_VALUE_PATTERNS.some((re) => re.test(s)) ? REDACTED : s;\n}\n\n/**\n * Recursively scrub a value. Sensitive KEYS short-circuit to {@link REDACTED};\n * every surviving string scalar is additionally passed through\n * {@link sanitizeStringValue} (value-level secret detection).\n */\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 if (typeof value === 'string') {\n return sanitizeStringValue(value);\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","/**\n * Parsers for the robot's on-device AI detections, pushed as camera-service\n * properties (subscribe to `propertyChanged` for SIID 10001):\n * - piid 110 (`personFollow` / PERSON_DATA): a tracked-person bounding box\n * - piid 112 (`videoObstacleData`): a list of obstacle boxes\n *\n * The overlay for obstacle boxes is gated on `set_properties siid 28 piid 13 = 1`.\n * Payloads are JSON strings; these parsers are lenient (return `null` on garbage)\n * and normalize timestamps (the device reports nanoseconds).\n */\n\n/** A normalized detection box. Coordinates are passed through as-reported. */\nexport interface DetectionBox {\n readonly x: number;\n readonly y: number;\n readonly w: number;\n readonly h: number;\n /** Optional label/class if the payload carries one. */\n readonly label?: string;\n /** Optional confidence if the payload carries one. */\n readonly score?: number;\n}\n\nexport interface PersonFollowDetection {\n /** Detection time in epoch milliseconds (converted from the device's ns). */\n readonly timestampMs: number;\n readonly box: DetectionBox | null;\n}\n\nexport interface ObstacleDetection {\n readonly timestampMs: number;\n readonly boxes: readonly DetectionBox[];\n}\n\nconst asRecord = (value: unknown): Record<string, unknown> | null => {\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value);\n return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n }\n return value && typeof value === 'object' ? (value as Record<string, unknown>) : null;\n};\n\nconst num = (v: unknown): number | undefined => (typeof v === 'number' && Number.isFinite(v) ? v : undefined);\n\n/** Device timestamps are nanoseconds; scale to ms (leave already-ms values alone). */\nconst toMs = (raw: unknown): number => {\n const n = num(raw);\n if (n === undefined) return Date.now();\n // Heuristic: > 1e15 is almost certainly nanoseconds.\n return n > 1e15 ? Math.round(n / 1e6) : n;\n};\n\n/** Read a box from either an array `[x,y,w,h]` or an object `{x,y,w,h,...}`. */\nconst toBox = (raw: unknown): DetectionBox | null => {\n if (Array.isArray(raw) && raw.length >= 4) {\n const [x, y, w, h] = raw;\n if ([x, y, w, h].every((v) => num(v) !== undefined)) {\n return { x: x as number, y: y as number, w: w as number, h: h as number };\n }\n return null;\n }\n const r = asRecord(raw);\n if (!r) return null;\n const x = num(r['x']);\n const y = num(r['y']);\n const w = num(r['w']) ?? num(r['width']);\n const h = num(r['h']) ?? num(r['height']);\n if (x === undefined || y === undefined || w === undefined || h === undefined) return null;\n const label = typeof r['label'] === 'string' ? (r['label'] as string) : undefined;\n const score = num(r['score']) ?? num(r['confidence']);\n return { x, y, w, h, ...(label !== undefined ? { label } : {}), ...(score !== undefined ? { score } : {}) };\n};\n\n/** Parse a `personFollow` (piid 110) payload into a normalized detection. */\nexport function parsePersonFollow(value: unknown): PersonFollowDetection | null {\n const r = asRecord(value);\n if (!r) return null;\n return {\n timestampMs: toMs(r['timestamp'] ?? r['time']),\n box: toBox(r['bbox'] ?? r['box']),\n };\n}\n\n/** Parse a `videoObstacleData` (piid 112) payload into normalized boxes. */\nexport function parseObstacleData(value: unknown): ObstacleDetection | null {\n const r = asRecord(value);\n if (!r) return null;\n const list = r['boxlist'] ?? r['boxes'] ?? r['boxList'];\n const boxes: DetectionBox[] = [];\n if (Array.isArray(list)) {\n for (const item of list) {\n const box = toBox(item);\n if (box) boxes.push(box);\n }\n }\n return { timestampMs: toMs(r['timestamp'] ?? r['time']), boxes };\n}\n","/**\n * Minimal Node RTMP client that replicates the Aliyun LinkVisual viewer (\"private mode\" librtmp)\n * exactly as observed on the wire (findings/dreame.pcap, findings/dr3.pcap — client->relay bytes,\n * VERIFIED) and in liblvmedia.so / liblvrtmp.so (VERIFIED by disassembly).\n *\n * Wire sequence (all four written back-to-back, BEFORE any server byte is read — like the app):\n * C0 = 0x04, C1 = [uptime_ms u32][0 0 0 0][1528 random] (no C2 is ever sent)\n * connect (txn 1, csid 3, msid 0, fmt 0) {app, tcUrl, fpad:false, capabilities:15,\n * audioCodecs:3191, videoCodecs:252, videoFunction:1} — no flashVer, no objectEncoding\n * play (txn 2, csid 8, msid 1, fmt 0) null, <playpath>, -1000 — NO createStream (msid fixed = 1)\n * onStatus (txn 3, csid 3, fmt 1) null, {level:\"status\", code:\"NetStream.RequestAudioType\",\n * description:\"desc\", audio_type:\"1\"}\n * Then the server answers S0(0x04)+S1(1536) followed directly by RTMP messages (no S2), chunked with an\n * implicit 60000-byte chunk size. After the first server message is processed the app sends\n * onStatus (txn 4, csid 3, fmt 1) null, {level:\"status\", code:\"NetStream.Ping.Start\", description:\"desc\"}\n * and repeats it every 30 s (CStreamUnit::ProcessRtmpStream). The client never replies to server onStatus.\n *\n * What is ASSUMED (not observable in the pcaps, which lack the server->client direction): that the\n * server sends onStatus NetStream.Play.Start (librtmp requires it before accepting media), and that this\n * exact sequence — rather than one specific element of it — is what makes the robot publish VIDEO.\n */\nimport net from 'node:net';\nimport { EventEmitter } from 'node:events';\nimport { randomBytes } from 'node:crypto';\nimport { amfBoolean, amfNull, amfNumber, amfObject, amfString, decodeAmf0, type AmfValue } from './amf0.js';\nimport { ChunkReader, MSG, PRIVATE_MODE_CHUNK_SIZE, encodeMessage, type RtmpMessage } from './chunk.js';\nimport {\n aacSampleRate, aacToAdts, avccToAnnexB, parseAacConfig, parseAudioTag, parseAvcConfig, parseVideoTag,\n type AacConfig, type AvcConfig,\n} from './flv.js';\n\nexport const LV_STATUS = {\n REQUEST_AUDIO_TYPE: 'NetStream.RequestAudioType',\n FORCE_IFRAME_START: 'NetStream.ForceIFrame.Start',\n PING_START: 'NetStream.Ping.Start',\n PING_RESPONSE: 'NetStream.Ping.Response',\n PRE_CONNECT: 'NetStream.PreConnect',\n KEEPALIVE: 'NetStream.Keepalive',\n SERVER_INFO: 'NetStream.ServerInfo',\n TRANSFER_DATA: 'NetStream.TransferData',\n PLAY_START: 'NetStream.Play.Start',\n /** Server signals the talk (intercom uplink) channel is ready. */\n TALK_READY: 'NetStream.TalkReady',\n} as const;\n\n/**\n * FLV audio tag header byte for the intercom uplink: G.711 A-law, 8 kHz, 16-bit,\n * mono — `(7<<4)|(0<<2)|(1<<1)|0` = 0x72. Verified against liblvmedia SetTalkInfo.\n */\nexport const TALK_AUDIO_HEADER_G711A = 0x72;\n\n/** librtmp constants observed in the app's connect (RTMP_Connect1/SendConnectPacket). */\nconst CONNECT_CAPABILITIES = 15;\nconst CONNECT_AUDIO_CODECS = 3191;\nconst CONNECT_VIDEO_CODECS = 252;\nconst CONNECT_VIDEO_FUNCTION = 1;\nconst PLAY_START_LIVE = -1000; // RTMP_LF_LIVE -> start = -1000\nconst HANDSHAKE_C0_PRIVATE = 0x04;\nconst HANDSHAKE_S0S1_LEN = 1537;\nconst DEFAULT_PING_INTERVAL_MS = 30_000; // ProcessRtmpStream: 30000\nconst DEFAULT_CACHE_DURATION_MS = 3000; // app: \"&cacheDuration=3000\"\nconst ACK_THRESHOLD_BYTES = 250_000; // librtmp: m_nClientBW(2500000)/10\nconst CSID_CONTROL = 2;\nconst CSID_COMMAND = 3;\nconst CSID_TALK = 4; // intercom uplink audio (liblvmedia IOT_RTMP_SendAudio)\nconst CSID_PLAY = 8;\nconst PLAY_STREAM_ID = 1; // RTMP_InitPrivateConfig: m_stream_id = 1\n\nexport interface StatusEvent {\n code: string;\n level: string;\n description: string;\n fields: { [key: string]: AmfValue };\n transactionId: number;\n}\n\nexport interface VideoAccessUnitEvent {\n data: Buffer; // Annex-B access unit (SPS/PPS prepended on keyframes)\n isKeyframe: boolean;\n videoType: 'H264';\n microseconds: number;\n time?: number;\n}\n\nexport interface AudioInfoEvent {\n codec: 'aac' | 'g711a' | 'g711u' | 'unknown';\n sampleRate: number;\n channels: number;\n}\n\nexport interface LvRtmpClientOptions {\n /** relayUrl from stream/query, e.g. rtmp://host:8000/live?token=..&session=../<key>_0 */\n url: string;\n /** Appended to app/tcUrl as \"&cacheDuration=N\" when not already present (the app always sends 3000). */\n cacheDurationMs?: number;\n /** Send the onStatus NetStream.RequestAudioType {audio_type:\"1\"} right after play (app does). */\n requestAudioType?: boolean;\n /** Send NetStream.Ping.Start after the first server message and then periodically (app does). */\n ping?: boolean;\n pingIntervalMs?: number;\n /** Also send NetStream.ForceIFrame.Start once playing (the app sends it when it starts an MP4 record). */\n forceIFrameOnStart?: boolean;\n connectTimeoutMs?: number;\n log?: (line: string) => void;\n}\n\ninterface Endpoint {\n host: string;\n port: number;\n app: string;\n tcUrl: string;\n playpath: string;\n}\n\n/**\n * Split like librtmp/ffmpeg do: playpath = last path segment, app = everything between host and it\n * (query string included). Verified against the app's connect/play in both pcaps.\n */\nexport function parseRelayUrl(url: string, cacheDurationMs: number): Endpoint {\n const m = /^rtmp:\\/\\/([^/:]+)(?::(\\d+))?\\/(.*)$/.exec(url);\n if (!m) throw new Error(`not an rtmp:// url: ${url}`);\n const host = m[1]!;\n const port = m[2] ? Number(m[2]) : 1935;\n const path = m[3]!;\n const slash = path.lastIndexOf('/');\n if (slash < 0) throw new Error(`relay url has no playpath: ${url}`);\n let app = path.slice(0, slash);\n const playpath = path.slice(slash + 1);\n if (!/[?&]cacheDuration=/.test(app)) app += `${app.includes('?') ? '&' : '?'}cacheDuration=${cacheDurationMs}`;\n return { host, port, app, playpath, tcUrl: `rtmp://${host}:${port}/${app}` };\n}\n\ntype Events = {\n connected: [];\n status: [StatusEvent];\n command: [string, AmfValue[]];\n metadata: [{ [key: string]: AmfValue }];\n audio: [{ timestamp: number; body: Buffer }];\n video: [{ timestamp: number; body: Buffer }];\n videoAccessUnit: [VideoAccessUnitEvent];\n audioFrame: [Buffer];\n audioInfo: [AudioInfoEvent];\n error: [Error];\n close: [];\n};\n\nexport class LvRtmpClient extends EventEmitter<Events> {\n private readonly opts: Required<Omit<LvRtmpClientOptions, 'log'>> & { log: (line: string) => void };\n private readonly endpoint: Endpoint;\n private socket: net.Socket | null = null;\n private handshakeBuf: Buffer = Buffer.alloc(0);\n private handshakeDone = false;\n private c1Time = 0;\n private reader = new ChunkReader(PRIVATE_MODE_CHUNK_SIZE);\n private transactionId = 0;\n private bytesIn = 0;\n private bytesAcked = 0;\n private ackWindow = ACK_THRESHOLD_BYTES;\n private pingTimer: NodeJS.Timeout | null = null;\n private firstMessageSeen = false;\n private avcConfig: AvcConfig | null = null;\n private aacConfig: AacConfig | null = null;\n private closed = false;\n\n constructor(options: LvRtmpClientOptions) {\n super();\n this.opts = {\n url: options.url,\n cacheDurationMs: options.cacheDurationMs ?? DEFAULT_CACHE_DURATION_MS,\n requestAudioType: options.requestAudioType ?? true,\n ping: options.ping ?? true,\n pingIntervalMs: options.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,\n forceIFrameOnStart: options.forceIFrameOnStart ?? false,\n connectTimeoutMs: options.connectTimeoutMs ?? 10_000,\n log: options.log ?? (() => {}),\n };\n this.endpoint = parseRelayUrl(this.opts.url, this.opts.cacheDurationMs);\n }\n\n /** Open TCP, write C0+C1 and the pipelined connect/play/RequestAudioType. Resolves once written. */\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const sock = net.createConnection({ host: this.endpoint.host, port: this.endpoint.port });\n this.socket = sock;\n sock.setNoDelay(true);\n sock.setTimeout(this.opts.connectTimeoutMs, () => { if (!this.handshakeDone) this.fail(new Error('handshake timeout')); });\n sock.once('error', (err) => { this.emit('error', err); reject(err); });\n sock.on('data', (d) => this.onData(d));\n sock.on('close', () => this.onClose());\n sock.once('connect', () => {\n this.opts.log(`tcp connected ${this.endpoint.host}:${this.endpoint.port}`);\n sock.write(Buffer.concat([this.buildC0C1(), ...this.buildOpeningSequence()]));\n this.emit('connected');\n resolve();\n });\n });\n }\n\n close(): void {\n this.closed = true;\n if (this.pingTimer) clearInterval(this.pingTimer);\n this.pingTimer = null;\n this.socket?.destroy();\n }\n\n /** onStatus encoder — byte-exact replica of liblvmedia IOT_RTMP_SendMessage (csid 3, fmt 1, ts 0). */\n buildStatus(code: string, extra?: readonly [string, string]): Buffer {\n const pairs: Array<readonly [string, Buffer]> = [\n ['level', amfString('status')],\n ['code', amfString(code)],\n ['description', amfString('desc')],\n ];\n if (extra) pairs.push([extra[0], amfString(extra[1])]);\n const body = Buffer.concat([amfString('onStatus'), amfNumber(++this.transactionId), amfNull(), amfObject(pairs)]);\n return encodeMessage({ fmt: 1, csid: CSID_COMMAND, type: MSG.COMMAND_AMF0, body });\n }\n\n sendStatus(code: string, extra?: readonly [string, string]): void {\n this.write(this.buildStatus(code, extra));\n this.opts.log(`>> onStatus ${code}${extra ? ` ${extra[0]}=${extra[1]}` : ''} (txn ${this.transactionId})`);\n }\n\n forceIFrame(): void {\n this.sendStatus(LV_STATUS.FORCE_IFRAME_START);\n }\n\n // ---------------------------------------------------------------- outgoing\n\n private buildC0C1(): Buffer {\n this.c1Time = Math.floor(performance.now()) >>> 0;\n const c1 = Buffer.alloc(1536);\n c1.writeUInt32BE(this.c1Time, 0);\n c1.writeUInt32BE(0, 4);\n randomBytes(1528).copy(c1, 8);\n return Buffer.concat([Buffer.from([HANDSHAKE_C0_PRIVATE]), c1]);\n }\n\n private buildConnect(): Buffer {\n const obj = amfObject([\n ['app', amfString(this.endpoint.app)],\n ['tcUrl', amfString(this.endpoint.tcUrl)],\n ['fpad', amfBoolean(false)],\n ['capabilities', amfNumber(CONNECT_CAPABILITIES)],\n ['audioCodecs', amfNumber(CONNECT_AUDIO_CODECS)],\n ['videoCodecs', amfNumber(CONNECT_VIDEO_CODECS)],\n ['videoFunction', amfNumber(CONNECT_VIDEO_FUNCTION)],\n ]);\n const body = Buffer.concat([amfString('connect'), amfNumber(++this.transactionId), obj]);\n return encodeMessage({ fmt: 0, csid: CSID_COMMAND, msid: 0, type: MSG.COMMAND_AMF0, body });\n }\n\n private buildPlay(): Buffer {\n const body = Buffer.concat([\n amfString('play'), amfNumber(++this.transactionId), amfNull(), amfString(this.endpoint.playpath), amfNumber(PLAY_START_LIVE),\n ]);\n return encodeMessage({ fmt: 0, csid: CSID_PLAY, msid: PLAY_STREAM_ID, type: MSG.COMMAND_AMF0, body });\n }\n\n /** Exposed for the byte-exact self-test against the pcap. */\n buildOpeningSequence(): Buffer[] {\n const seq = [this.buildConnect(), this.buildPlay()];\n if (this.opts.requestAudioType) seq.push(this.buildStatus(LV_STATUS.REQUEST_AUDIO_TYPE, ['audio_type', '1']));\n return seq;\n }\n\n /**\n * Push one intercom (talk-back) audio frame UPSTREAM to the device — an RTMP\n * type-8 audio message on the talk chunk stream (csid 4, msid 1), exactly as\n * liblvmedia's `IOT_RTMP_SendAudio` does. `payload` is the raw codec audio\n * (default framing = G.711 A-law); `tsMs` is a running millisecond timestamp.\n * Only meaningful after the server sends {@link LV_STATUS.TALK_READY}.\n */\n sendAudioFrame(payload: Buffer, tsMs: number, header: number = TALK_AUDIO_HEADER_G711A): void {\n const body = Buffer.concat([Buffer.from([header]), payload]);\n this.write(\n encodeMessage({ fmt: 0, csid: CSID_TALK, msid: PLAY_STREAM_ID, timestamp: tsMs >>> 0, type: MSG.AUDIO, body }),\n );\n }\n\n private write(buf: Buffer): void {\n if (!this.socket || this.socket.destroyed) return;\n this.socket.write(buf);\n }\n\n private sendAck(): void {\n const b = Buffer.alloc(4);\n b.writeUInt32BE(this.bytesIn >>> 0, 0);\n this.write(encodeMessage({ fmt: 0, csid: CSID_CONTROL, msid: 0, type: MSG.ACK, body: b }));\n this.bytesAcked = this.bytesIn;\n }\n\n private sendPingResponse(serverTs: Buffer): void {\n const body = Buffer.concat([Buffer.from([0, 7]), serverTs]);\n this.write(encodeMessage({ fmt: 0, csid: CSID_CONTROL, msid: 0, type: MSG.USER_CONTROL, body }));\n }\n\n private startPinging(): void {\n if (!this.opts.ping || this.pingTimer) return;\n this.sendStatus(LV_STATUS.PING_START);\n this.pingTimer = setInterval(() => this.sendStatus(LV_STATUS.PING_START), this.opts.pingIntervalMs);\n }\n\n // ---------------------------------------------------------------- incoming\n\n private onData(data: Buffer): void {\n this.bytesIn += data.length;\n let payload = data;\n if (!this.handshakeDone) {\n this.handshakeBuf = Buffer.concat([this.handshakeBuf, data]);\n if (this.handshakeBuf.length < HANDSHAKE_S0S1_LEN) return;\n this.onHandshake(this.handshakeBuf.subarray(0, HANDSHAKE_S0S1_LEN));\n payload = this.handshakeBuf.subarray(HANDSHAKE_S0S1_LEN);\n this.handshakeBuf = Buffer.alloc(0);\n this.handshakeDone = true;\n if (payload.length === 0) return;\n }\n let messages: RtmpMessage[];\n try {\n messages = this.reader.feed(payload);\n } catch (err) {\n this.fail(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n for (const m of messages) this.dispatch(m);\n if (this.bytesIn - this.bytesAcked >= this.ackWindow) this.sendAck();\n }\n\n private onHandshake(s0s1: Buffer): void {\n const s0 = s0s1[0]!;\n const serverTime = s0s1.readUInt32BE(1);\n const zero = s0s1.readUInt32BE(5);\n this.opts.log(`<< S0=0x${s0.toString(16)} S1.time=${serverTime} S1.zero=0x${zero.toString(16)} (client sent 0x04; app logs a mismatch but continues)`);\n // Robustness: a standards-mode server might append S2 (echo of our C1). liblvrtmp never reads one;\n // detect it anyway so we do not desync if the relay behaves differently for us.\n // (Handled lazily in dispatch: if the first \"chunk\" looks like our C1 echo we skip 1536 bytes.)\n }\n\n private dispatch(m: RtmpMessage): void {\n if (!this.firstMessageSeen) {\n this.firstMessageSeen = true;\n this.startPinging(); // ProcessRtmpStream sends Ping.Start right after processing the first packet\n }\n switch (m.type) {\n case MSG.SET_CHUNK_SIZE: {\n const size = m.body.readUInt32BE(0) & 0x7fffffff;\n this.opts.log(`<< SetChunkSize ${size}`);\n this.reader.chunkSize = size;\n return;\n }\n case MSG.WINDOW_ACK_SIZE:\n this.ackWindow = Math.min(ACK_THRESHOLD_BYTES, m.body.readUInt32BE(0));\n this.opts.log(`<< WindowAckSize ${m.body.readUInt32BE(0)}`);\n return;\n case MSG.SET_PEER_BW:\n this.opts.log(`<< SetPeerBandwidth ${m.body.readUInt32BE(0)} limit=${m.body[4]}`);\n return;\n case MSG.ACK:\n case MSG.ABORT:\n return;\n case MSG.USER_CONTROL:\n return this.onUserControl(m.body);\n case MSG.COMMAND_AMF0:\n return this.onCommand(m);\n case MSG.DATA_AMF0:\n return this.onDataMessage(m.body);\n case MSG.AUDIO:\n return this.onAudio(m);\n case MSG.VIDEO:\n return this.onVideo(m);\n default:\n this.opts.log(`<< unknown message type ${m.type} len=${m.body.length}`);\n }\n }\n\n private onUserControl(body: Buffer): void {\n const event = body.readUInt16BE(0);\n const names: { [k: number]: string } = { 0: 'StreamBegin', 1: 'StreamEOF', 2: 'StreamDry', 4: 'StreamIsRecorded', 6: 'PingRequest', 7: 'PingResponse' };\n this.opts.log(`<< UserControl ${names[event] ?? event} ${body.subarray(2).toString('hex')}`);\n if (event === 6) this.sendPingResponse(body.subarray(2, 6));\n }\n\n private onCommand(m: RtmpMessage): void {\n let values: AmfValue[];\n try {\n values = decodeAmf0(m.body);\n } catch (err) {\n this.opts.log(`<< undecodable command: ${m.body.toString('hex')}`);\n return;\n }\n const [name, txn, , info] = values;\n this.opts.log(`<< ${String(name)} ${JSON.stringify(values.slice(1))}`);\n this.emit('command', String(name), values);\n if (name === 'onStatus' && info && typeof info === 'object' && !Array.isArray(info)) {\n const fields = info as { [key: string]: AmfValue };\n this.emit('status', {\n code: String(fields.code ?? ''),\n level: String(fields.level ?? ''),\n description: String(fields.description ?? ''),\n fields,\n transactionId: typeof txn === 'number' ? txn : 0,\n });\n if (fields.code === LV_STATUS.PLAY_START && this.opts.forceIFrameOnStart) this.forceIFrame();\n }\n }\n\n private onDataMessage(body: Buffer): void {\n try {\n const values = decodeAmf0(body);\n this.opts.log(`<< data ${JSON.stringify(values).slice(0, 300)}`);\n const obj = values.find((v) => v && typeof v === 'object' && !Array.isArray(v));\n if (obj) this.emit('metadata', obj as { [key: string]: AmfValue });\n } catch {\n this.opts.log(`<< undecodable data message len=${body.length}`);\n }\n }\n\n private onAudio(m: RtmpMessage): void {\n this.emit('audio', { timestamp: m.timestamp, body: m.body });\n const tag = parseAudioTag(m.body);\n if (!tag) return;\n if (tag.soundFormat === 10) {\n if (tag.aacPacketType === 0) {\n this.aacConfig = parseAacConfig(tag.data);\n this.emit('audioInfo', { codec: 'aac', sampleRate: aacSampleRate(this.aacConfig), channels: this.aacConfig.channelConfiguration });\n return;\n }\n if (this.aacConfig) this.emit('audioFrame', aacToAdts(tag.data, this.aacConfig));\n return;\n }\n if (tag.soundFormat === 7 || tag.soundFormat === 8) {\n if (!this.aacConfig) this.emit('audioInfo', { codec: tag.soundFormat === 7 ? 'g711a' : 'g711u', sampleRate: 8000, channels: 1 });\n this.emit('audioFrame', tag.data);\n return;\n }\n this.emit('audioInfo', { codec: 'unknown', sampleRate: 0, channels: tag.channels });\n }\n\n private onVideo(m: RtmpMessage): void {\n this.emit('video', { timestamp: m.timestamp, body: m.body });\n const tag = parseVideoTag(m.body);\n if (!tag || tag.codecId !== 7) return;\n if (tag.avcPacketType === 0) {\n this.avcConfig = parseAvcConfig(tag.data);\n return;\n }\n if (tag.avcPacketType !== 1 || !this.avcConfig) return;\n const isKeyframe = tag.frameType === 1;\n this.emit('videoAccessUnit', {\n data: avccToAnnexB(tag.data, this.avcConfig, isKeyframe),\n isKeyframe,\n videoType: 'H264',\n microseconds: m.timestamp * 1000,\n time: m.timestamp + tag.compositionTime,\n });\n }\n\n private fail(err: Error): void {\n this.opts.log(`!! ${err.message}`);\n this.emit('error', err);\n this.close();\n }\n\n private onClose(): void {\n if (this.pingTimer) clearInterval(this.pingTimer);\n this.pingTimer = null;\n if (!this.closed) this.opts.log('socket closed by peer');\n this.closed = true;\n this.emit('close');\n }\n}\n","/**\n * Minimal AMF0 encoder/decoder — only the types LinkVisual's librtmp uses\n * (number, boolean, string, object, null, ecma-array, strict-array, long string).\n * Erasable-syntax TypeScript: runs directly under Node >= 22.6 (`node file.ts`).\n */\n\nexport const AMF0 = {\n NUMBER: 0x00,\n BOOLEAN: 0x01,\n STRING: 0x02,\n OBJECT: 0x03,\n NULL: 0x05,\n UNDEFINED: 0x06,\n ECMA_ARRAY: 0x08,\n OBJECT_END: 0x09,\n STRICT_ARRAY: 0x0a,\n LONG_STRING: 0x0c,\n} as const;\n\nexport type AmfValue = number | boolean | string | null | undefined | AmfValue[] | { [key: string]: AmfValue };\n\nexport function amfNumber(n: number): Buffer {\n const b = Buffer.alloc(9);\n b[0] = AMF0.NUMBER;\n b.writeDoubleBE(n, 1);\n return b;\n}\n\nexport function amfBoolean(v: boolean): Buffer {\n return Buffer.from([AMF0.BOOLEAN, v ? 1 : 0]);\n}\n\nexport function amfString(s: string): Buffer {\n const str = Buffer.from(s, 'utf8');\n const b = Buffer.alloc(3 + str.length);\n b[0] = AMF0.STRING;\n b.writeUInt16BE(str.length, 1);\n str.copy(b, 3);\n return b;\n}\n\nexport function amfNull(): Buffer {\n return Buffer.from([AMF0.NULL]);\n}\n\n/** Named property: u16 key length + key + encoded value. Order is preserved (matters for byte-exactness). */\nfunction amfNamed(key: string, value: Buffer): Buffer {\n const k = Buffer.from(key, 'utf8');\n const b = Buffer.alloc(2 + k.length);\n b.writeUInt16BE(k.length, 0);\n k.copy(b, 2);\n return Buffer.concat([b, value]);\n}\n\n/** AMF0 object from an ordered list of [key, encodedValue] pairs. */\nexport function amfObject(pairs: ReadonlyArray<readonly [string, Buffer]>): Buffer {\n const parts: Buffer[] = [Buffer.from([AMF0.OBJECT])];\n for (const [k, v] of pairs) parts.push(amfNamed(k, v));\n parts.push(Buffer.from([0x00, 0x00, AMF0.OBJECT_END]));\n return Buffer.concat(parts);\n}\n\ninterface DecodeResult {\n value: AmfValue;\n next: number;\n}\n\nfunction decodeOne(buf: Buffer, pos: number): DecodeResult {\n const type = buf[pos];\n let p = pos + 1;\n switch (type) {\n case AMF0.NUMBER:\n return { value: buf.readDoubleBE(p), next: p + 8 };\n case AMF0.BOOLEAN:\n return { value: buf[p] !== 0, next: p + 1 };\n case AMF0.STRING: {\n const len = buf.readUInt16BE(p);\n return { value: buf.toString('utf8', p + 2, p + 2 + len), next: p + 2 + len };\n }\n case AMF0.LONG_STRING: {\n const len = buf.readUInt32BE(p);\n return { value: buf.toString('utf8', p + 4, p + 4 + len), next: p + 4 + len };\n }\n case AMF0.NULL:\n return { value: null, next: p };\n case AMF0.UNDEFINED:\n return { value: undefined, next: p };\n case AMF0.ECMA_ARRAY:\n case AMF0.OBJECT: {\n if (type === AMF0.ECMA_ARRAY) p += 4; // associative count, then same as object\n const obj: { [key: string]: AmfValue } = {};\n for (;;) {\n const klen = buf.readUInt16BE(p);\n if (klen === 0 && buf[p + 2] === AMF0.OBJECT_END) return { value: obj, next: p + 3 };\n const key = buf.toString('utf8', p + 2, p + 2 + klen);\n const r = decodeOne(buf, p + 2 + klen);\n obj[key] = r.value;\n p = r.next;\n }\n }\n case AMF0.STRICT_ARRAY: {\n const count = buf.readUInt32BE(p);\n p += 4;\n const arr: AmfValue[] = [];\n for (let i = 0; i < count; i++) {\n const r = decodeOne(buf, p);\n arr.push(r.value);\n p = r.next;\n }\n return { value: arr, next: p };\n }\n default:\n throw new Error(`AMF0: unsupported type 0x${(type ?? -1).toString(16)} at ${pos}`);\n }\n}\n\n/** Decode a whole message body into its top-level values (command name, txid, args...). */\nexport function decodeAmf0(buf: Buffer): AmfValue[] {\n const out: AmfValue[] = [];\n let pos = 0;\n while (pos < buf.length) {\n const r = decodeOne(buf, pos);\n out.push(r.value);\n pos = r.next;\n }\n return out;\n}\n","/**\n * RTMP chunk-stream framing (encoder + incremental reader).\n *\n * LinkVisual \"private mode\" facts (liblvrtmp.so RTMP_InitPrivateConfig, VERIFIED by disassembly):\n * m_inChunkSize = m_outChunkSize = 60000, m_stream_id = 1 — and NO SetChunkSize is exchanged.\n * Hence: we send every message as a single chunk (all control messages are < 1 KB) and we must\n * parse inbound chunks with a 60000-byte chunk size from the start (a 128 default would desync).\n */\n\nexport const MSG = {\n SET_CHUNK_SIZE: 1,\n ABORT: 2,\n ACK: 3,\n USER_CONTROL: 4,\n WINDOW_ACK_SIZE: 5,\n SET_PEER_BW: 6,\n AUDIO: 8,\n VIDEO: 9,\n DATA_AMF0: 18,\n COMMAND_AMF0: 20,\n} as const;\n\nexport const PRIVATE_MODE_CHUNK_SIZE = 60000;\n\nexport interface RtmpMessage {\n csid: number;\n msid: number;\n timestamp: number;\n type: number;\n body: Buffer;\n}\n\nexport interface EncodeOptions {\n /** 0 = 12-byte header (LARGE), 1 = 8-byte header (MEDIUM, msid inherited). Mirrors librtmp m_headerType. */\n fmt: 0 | 1;\n csid: number;\n msid?: number;\n timestamp?: number;\n type: number;\n body: Buffer;\n}\n\nfunction basicHeader(fmt: number, csid: number): Buffer {\n if (csid < 64) return Buffer.from([(fmt << 6) | csid]);\n if (csid < 320) return Buffer.from([fmt << 6, csid - 64]);\n return Buffer.from([(fmt << 6) | 1, (csid - 64) & 0xff, (csid - 64) >> 8]);\n}\n\n/** Encode one message as ONE chunk (no splitting) — exactly what the LinkVisual SDK does on the wire. */\nexport function encodeMessage(o: EncodeOptions): Buffer {\n if (o.body.length > PRIVATE_MODE_CHUNK_SIZE) {\n throw new Error(`message body ${o.body.length} exceeds private-mode chunk size ${PRIVATE_MODE_CHUNK_SIZE}`);\n }\n const ts = o.timestamp ?? 0;\n const hdrLen = o.fmt === 0 ? 11 : 7;\n const hdr = Buffer.alloc(hdrLen);\n hdr.writeUIntBE(ts >= 0xffffff ? 0xffffff : ts, 0, 3);\n hdr.writeUIntBE(o.body.length, 3, 3);\n hdr[6] = o.type;\n if (o.fmt === 0) hdr.writeUInt32LE(o.msid ?? 0, 7);\n const ext = ts >= 0xffffff ? (() => { const e = Buffer.alloc(4); e.writeUInt32BE(ts, 0); return e; })() : Buffer.alloc(0);\n return Buffer.concat([basicHeader(o.fmt, o.csid), hdr, ext, o.body]);\n}\n\ninterface CsidState {\n timestamp: number;\n delta: number;\n length: number;\n type: number;\n msid: number;\n extTs: boolean;\n partial: Buffer[];\n received: number;\n}\n\nfunction freshState(): CsidState {\n return { timestamp: 0, delta: 0, length: 0, type: 0, msid: 0, extTs: false, partial: [], received: 0 };\n}\n\n/**\n * Incremental chunk reader. Feed raw TCP bytes; get complete messages back.\n * Handles fmt 0..3, 1/2/3-byte basic headers, extended timestamps (also on fmt-3\n * continuations, per spec/librtmp), and dynamic chunk-size changes.\n */\nexport class ChunkReader {\n chunkSize: number;\n private pending: Buffer = Buffer.alloc(0);\n private readonly streams = new Map<number, CsidState>();\n\n constructor(chunkSize: number = PRIVATE_MODE_CHUNK_SIZE) {\n this.chunkSize = chunkSize;\n }\n\n feed(data: Buffer): RtmpMessage[] {\n this.pending = this.pending.length ? Buffer.concat([this.pending, data]) : data;\n const out: RtmpMessage[] = [];\n for (;;) {\n const consumed = this.tryReadChunk(out);\n if (consumed === 0) break;\n this.pending = this.pending.subarray(consumed);\n }\n return out;\n }\n\n /** Returns bytes consumed for one chunk, or 0 if more data is needed. */\n private tryReadChunk(out: RtmpMessage[]): number {\n const buf = this.pending;\n if (buf.length < 1) return 0;\n const fmt = buf[0]! >> 6;\n let csid = buf[0]! & 0x3f;\n let p = 1;\n if (csid === 0) { if (buf.length < 2) return 0; csid = 64 + buf[1]!; p = 2; }\n else if (csid === 1) { if (buf.length < 3) return 0; csid = 64 + buf[1]! + buf[2]! * 256; p = 3; }\n\n const st = this.streams.get(csid) ?? freshState();\n const hdrLen = fmt === 0 ? 11 : fmt === 1 ? 7 : fmt === 2 ? 3 : 0;\n if (buf.length < p + hdrLen) return 0;\n\n let tsField = 0;\n if (fmt <= 2) {\n tsField = buf.readUIntBE(p, 3);\n if (fmt <= 1) { st.length = buf.readUIntBE(p + 3, 3); st.type = buf[p + 6]!; }\n if (fmt === 0) st.msid = buf.readUInt32LE(p + 7);\n st.extTs = tsField === 0xffffff;\n }\n p += hdrLen;\n if (st.extTs) {\n if (buf.length < p + 4) return 0;\n tsField = buf.readUInt32BE(p);\n p += 4;\n }\n const startOfMessage = st.received === 0;\n if (startOfMessage) {\n if (fmt === 0) { st.timestamp = tsField; st.delta = 0; }\n else if (fmt <= 2) { st.delta = tsField; st.timestamp += tsField; }\n else st.timestamp += st.delta; // fmt 3 starting a new message: reuse last delta\n }\n\n const take = Math.min(this.chunkSize, st.length - st.received);\n if (buf.length < p + take) return 0;\n st.partial.push(Buffer.from(buf.subarray(p, p + take)));\n st.received += take;\n if (st.received >= st.length) {\n out.push({ csid, msid: st.msid, timestamp: st.timestamp, type: st.type, body: Buffer.concat(st.partial) });\n st.partial = [];\n st.received = 0;\n }\n this.streams.set(csid, st);\n return p + take;\n }\n}\n","/**\n * FLV tag payload helpers: AVC (H.264) AVCC -> Annex-B, AAC raw -> ADTS, and an FLV file writer\n * for offline verification (`ffprobe out.flv`).\n */\n\nexport interface VideoTag {\n frameType: number; // 1 = keyframe, 2 = inter\n codecId: number; // 7 = AVC\n avcPacketType: number; // 0 = sequence header, 1 = NALU, 2 = end of sequence\n compositionTime: number;\n data: Buffer;\n}\n\nexport interface AudioTag {\n soundFormat: number; // 10 = AAC, 7 = G.711 A-law, 8 = G.711 mu-law\n sampleRateIndex: number; // FLV: 0=5.5k 1=11k 2=22k 3=44k (informational for AAC)\n channels: number; // 1 or 2\n aacPacketType: number | null; // 0 = AudioSpecificConfig, 1 = raw frame (AAC only)\n data: Buffer;\n}\n\nexport interface AvcConfig {\n sps: Buffer[];\n pps: Buffer[];\n nalLengthSize: number;\n}\n\nexport interface AacConfig {\n objectType: number;\n samplingFrequencyIndex: number;\n channelConfiguration: number;\n}\n\nconst ANNEXB_START = Buffer.from([0, 0, 0, 1]);\n\nexport function parseVideoTag(body: Buffer): VideoTag | null {\n if (body.length < 5) return null;\n const b0 = body[0]!;\n return {\n frameType: b0 >> 4,\n codecId: b0 & 0x0f,\n avcPacketType: body[1]!,\n compositionTime: body.readIntBE(2, 3),\n data: body.subarray(5),\n };\n}\n\nexport function parseAudioTag(body: Buffer): AudioTag | null {\n if (body.length < 1) return null;\n const b0 = body[0]!;\n const soundFormat = b0 >> 4;\n const isAac = soundFormat === 10;\n if (isAac && body.length < 2) return null;\n return {\n soundFormat,\n sampleRateIndex: (b0 >> 2) & 3,\n channels: (b0 & 1) + 1,\n aacPacketType: isAac ? body[1]! : null,\n data: body.subarray(isAac ? 2 : 1),\n };\n}\n\n/** Parse an AVCDecoderConfigurationRecord (FLV AVC sequence header payload). */\nexport function parseAvcConfig(rec: Buffer): AvcConfig {\n const nalLengthSize = (rec[4]! & 3) + 1;\n let p = 5;\n const sps: Buffer[] = [];\n const pps: Buffer[] = [];\n const numSps = rec[p++]! & 0x1f;\n for (let i = 0; i < numSps; i++) { const n = rec.readUInt16BE(p); sps.push(Buffer.from(rec.subarray(p + 2, p + 2 + n))); p += 2 + n; }\n const numPps = rec[p++]!;\n for (let i = 0; i < numPps; i++) { const n = rec.readUInt16BE(p); pps.push(Buffer.from(rec.subarray(p + 2, p + 2 + n))); p += 2 + n; }\n return { sps, pps, nalLengthSize };\n}\n\n/** Length-prefixed NALUs -> Annex-B. Prepends SPS/PPS when `prependParams` is set (for keyframes). */\nexport function avccToAnnexB(data: Buffer, cfg: AvcConfig, prependParams: boolean): Buffer {\n const parts: Buffer[] = [];\n if (prependParams) for (const nal of [...cfg.sps, ...cfg.pps]) parts.push(ANNEXB_START, nal);\n let p = 0;\n while (p + cfg.nalLengthSize <= data.length) {\n const len = data.readUIntBE(p, cfg.nalLengthSize);\n p += cfg.nalLengthSize;\n if (len === 0 || p + len > data.length) break;\n parts.push(ANNEXB_START, data.subarray(p, p + len));\n p += len;\n }\n return Buffer.concat(parts);\n}\n\nexport function parseAacConfig(asc: Buffer): AacConfig {\n const a0 = asc[0]!;\n const a1 = asc[1]!;\n return {\n objectType: a0 >> 3,\n samplingFrequencyIndex: ((a0 & 7) << 1) | (a1 >> 7),\n channelConfiguration: (a1 >> 3) & 0x0f,\n };\n}\n\nconst AAC_SAMPLE_RATES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];\n\nexport function aacSampleRate(cfg: AacConfig): number {\n return AAC_SAMPLE_RATES[cfg.samplingFrequencyIndex] ?? 0;\n}\n\n/** Wrap one raw AAC frame in a 7-byte ADTS header. */\nexport function aacToAdts(raw: Buffer, cfg: AacConfig): Buffer {\n const frameLen = raw.length + 7;\n const h = Buffer.alloc(7);\n h[0] = 0xff;\n h[1] = 0xf1; // MPEG-4, layer 0, no CRC\n h[2] = ((cfg.objectType - 1) << 6) | (cfg.samplingFrequencyIndex << 2) | (cfg.channelConfiguration >> 2);\n h[3] = ((cfg.channelConfiguration & 3) << 6) | ((frameLen >> 11) & 3);\n h[4] = (frameLen >> 3) & 0xff;\n h[5] = ((frameLen & 7) << 5) | 0x1f;\n h[6] = 0xfc;\n return Buffer.concat([h, raw]);\n}\n\n/** Minimal FLV file writer (header + tags) for offline inspection. */\nexport class FlvWriter {\n private readonly chunks: Buffer[] = [];\n\n constructor() {\n const hdr = Buffer.from([0x46, 0x4c, 0x56, 0x01, 0x05, 0, 0, 0, 9, 0, 0, 0, 0]);\n this.chunks.push(hdr);\n }\n\n tag(type: number, timestamp: number, body: Buffer): void {\n const h = Buffer.alloc(11);\n h[0] = type;\n h.writeUIntBE(body.length, 1, 3);\n h.writeUIntBE(timestamp & 0xffffff, 4, 3);\n h[7] = (timestamp >> 24) & 0xff;\n const prev = Buffer.alloc(4);\n prev.writeUInt32BE(11 + body.length, 0);\n this.chunks.push(h, body, prev);\n }\n\n toBuffer(): Buffer {\n return Buffer.concat(this.chunks);\n }\n}\n","/**\n * G.711 A-law codec (ITU-T G.711) — the intercom uplink format for Dreame\n * cameras (8 kHz, mono, 16-bit PCM in). Pure functions, no state.\n */\n\nconst ALAW_SEG_END = [0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff];\n\n/** Encode one 16-bit signed PCM sample to an A-law byte. */\nexport function pcm16ToALaw(sample: number): number {\n let s = sample;\n let sign = (~s >> 8) & 0x80; // 0x80 for positive, 0 for negative\n if (sign === 0) s = -s;\n if (s > 32635) s = 32635;\n\n let aval: number;\n if (s >= 256) {\n let seg = 7;\n for (let i = 0; i < 8; i++) {\n if (s <= ALAW_SEG_END[i]!) {\n seg = i;\n break;\n }\n }\n const mantissa = (s >> (seg + 3)) & 0x0f;\n aval = (seg << 4) | mantissa;\n } else {\n aval = s >> 4;\n }\n return (aval ^ sign ^ 0x55) & 0xff;\n}\n\n/**\n * Encode a buffer of little-endian 16-bit PCM samples to A-law bytes\n * (one output byte per input sample).\n */\nexport function pcm16leToALaw(pcm: Buffer): Buffer {\n const count = pcm.length >> 1;\n const out = Buffer.allocUnsafe(count);\n for (let i = 0; i < count; i++) {\n out[i] = pcm16ToALaw(pcm.readInt16LE(i * 2));\n }\n return out;\n}\n","/**\n * End-to-end camera media pipeline: autonomously cold-start the LinkVisual\n * monitor, connect to the resulting RTMP relay, and re-emit demuxed elementary\n * frames — H.264 access units (Annex-B, SPS/PPS prepended on keyframes) and AAC\n * (ADTS) — plus lifecycle events.\n *\n * This is the reusable frame feed for any external consumer (scrypted's RFC4571\n * muxer, camstack, a file writer). It owns the {@link DreameCameraController}\n * lifecycle: {@link start} opens the monitor and connects; {@link stop} closes\n * the relay socket and releases the monitor on the device.\n */\nimport { TypedEmitter } from '../../transport/typed-emitter.js';\nimport { DreameError } from '../../transport/errors.js';\nimport type { DreameCameraController } from '../monitor/controller.js';\nimport {\n LvRtmpClient,\n LV_STATUS,\n type AudioInfoEvent,\n type StatusEvent,\n type VideoAccessUnitEvent,\n} from './lv-rtmp-client.js';\nimport { pcm16leToALaw } from './g711.js';\n\n/** The subset of {@link LvRtmpClient} the pipeline drives — injectable for tests. */\nexport interface FrameSource {\n connect(): Promise<void>;\n close(): void;\n on(event: 'videoAccessUnit', cb: (e: VideoAccessUnitEvent) => void): void;\n on(event: 'audioFrame', cb: (buf: Buffer) => void): void;\n on(event: 'audioInfo', cb: (info: AudioInfoEvent) => void): void;\n on(event: 'status', cb: (e: StatusEvent) => void): void;\n on(event: 'error', cb: (err: Error) => void): void;\n on(event: 'close', cb: () => void): void;\n /** Push an intercom (talk-back) audio frame upstream. */\n sendAudioFrame(payload: Buffer, tsMs: number, header?: number): void;\n}\n\n/** Factory for the RTMP frame source; defaults to a real {@link LvRtmpClient}. */\nexport type FrameSourceFactory = (url: string) => FrameSource;\n\nexport interface DreameCameraStreamEvents {\n /** Emitted once the relay is connected; carries the (single-use) RTMP URL. */\n started: [{ rtmpUrl: string }];\n /** One H.264 access unit (Annex-B). */\n videoAccessUnit: [VideoAccessUnitEvent];\n /** One audio frame (AAC in ADTS, or raw G.711). */\n audioFrame: [Buffer];\n /** Audio format, emitted once known. */\n audioInfo: [AudioInfoEvent];\n /** Emitted after the pipeline is fully torn down. */\n stopped: [];\n error: [Error];\n [event: string]: unknown[];\n}\n\nexport interface DreameCameraStreamInput {\n /** A controller for the target camera (not yet opened). */\n controller: DreameCameraController;\n /** Override the RTMP client (tests inject a fake). */\n clientFactory?: FrameSourceFactory;\n /** Forwarded to {@link LvRtmpClient}: send NetStream.RequestAudioType after play. */\n requestAudioType?: boolean;\n /** Forwarded to {@link LvRtmpClient}: send periodic NetStream.Ping. */\n ping?: boolean;\n /** Optional line logger for the RTMP client. */\n log?: (line: string) => void;\n}\n\nexport class DreameCameraStream extends TypedEmitter<DreameCameraStreamEvents> {\n readonly #controller: DreameCameraController;\n readonly #clientFactory: FrameSourceFactory;\n readonly #log: (line: string) => void;\n #client: FrameSource | null = null;\n #state: 'idle' | 'starting' | 'running' | 'stopping' = 'idle';\n #videoFrames = 0;\n #audioFrames = 0;\n #talkReady = false;\n #talkWaiters: Array<() => void> = [];\n #talkTs = 0;\n\n constructor(input: DreameCameraStreamInput) {\n super();\n this.#controller = input.controller;\n this.#log = input.log ?? ((): void => {});\n this.#clientFactory =\n input.clientFactory ??\n ((url: string): FrameSource =>\n new LvRtmpClient({\n url,\n ...(input.requestAudioType !== undefined ? { requestAudioType: input.requestAudioType } : {}),\n ...(input.ping !== undefined ? { ping: input.ping } : {}),\n ...(input.log ? { log: input.log } : {}),\n }));\n }\n\n /** True while frames are flowing. */\n get isRunning(): boolean {\n return this.#state === 'running';\n }\n\n /**\n * Cold-start the monitor, connect to the relay, and begin emitting frames.\n * Resolves once connected. Throws (and cleans up) if the monitor or relay\n * connection fails.\n */\n async start(): Promise<{ rtmpUrl: string }> {\n if (this.#state !== 'idle') {\n throw new DreameError(`camera stream cannot start from state '${this.#state}'`);\n }\n this.#state = 'starting';\n try {\n this.#log('[nodedreame:stream] starting');\n const handle = await this.#controller.open();\n const client = this.#clientFactory(handle.rtmpUrl);\n this.#client = client;\n client.on('videoAccessUnit', (e) => {\n this.#videoFrames += 1;\n if (this.#videoFrames === 1) this.#log('[nodedreame:stream] first video access unit');\n this.emit('videoAccessUnit', e);\n });\n client.on('audioFrame', (buf) => {\n this.#audioFrames += 1;\n if (this.#audioFrames === 1) this.#log('[nodedreame:stream] first audio frame');\n this.emit('audioFrame', buf);\n });\n client.on('audioInfo', (info) => {\n this.#log(`[nodedreame:stream] audio ${info.codec} ${info.sampleRate}Hz x${info.channels}`);\n this.emit('audioInfo', info);\n });\n client.on('status', (e) => {\n if (e.code === LV_STATUS.TALK_READY) {\n this.#log('[nodedreame:stream] talk channel ready');\n this.#talkReady = true;\n const waiters = this.#talkWaiters;\n this.#talkWaiters = [];\n for (const w of waiters) w();\n }\n });\n client.on('error', (err) => {\n this.#log(`[nodedreame:stream] rtmp error: ${err.message}`);\n this.emit('error', err);\n });\n client.on('close', () => {\n this.#log('[nodedreame:stream] upstream relay closed');\n // Upstream relay closed on us — tear down and surface as stopped.\n if (this.#state === 'running') void this.stop();\n });\n await client.connect();\n this.#state = 'running';\n this.#log('[nodedreame:stream] connected to relay');\n this.emit('started', { rtmpUrl: handle.rtmpUrl });\n return { rtmpUrl: handle.rtmpUrl };\n } catch (err) {\n this.#state = 'idle';\n await this.#safeTeardown();\n throw err;\n }\n }\n\n /** Close the relay connection and release the monitor. Idempotent. */\n async stop(): Promise<void> {\n if (this.#state === 'idle' || this.#state === 'stopping') return;\n this.#state = 'stopping';\n this.#log(`[nodedreame:stream] stopping (video=${this.#videoFrames} audio=${this.#audioFrames})`);\n await this.#safeTeardown();\n this.#state = 'idle';\n this.emit('stopped');\n }\n\n /**\n * Open the two-way intercom: send the MIoT start, then resolve once the relay\n * signals the talk channel is ready (so it's safe to {@link sendTalkPcm}).\n * Rejects if not ready within `timeoutMs`.\n */\n async startTalk(opts?: { needRecordSound?: boolean; videoCall?: boolean; timeoutMs?: number }): Promise<void> {\n if (this.#state !== 'running') throw new DreameError('stream is not running; start() first');\n this.#log('[nodedreame:stream] intercom start');\n await this.#controller.startIntercom(\n opts?.needRecordSound !== undefined || opts?.videoCall !== undefined\n ? { ...(opts.needRecordSound !== undefined ? { needRecordSound: opts.needRecordSound } : {}), ...(opts.videoCall ? { videoCall: true } : {}) }\n : undefined,\n );\n this.#talkTs = 0;\n await this.whenTalkReady(opts?.timeoutMs ?? 5000);\n }\n\n /** Resolve when the talk channel is ready (or reject on timeout). */\n whenTalkReady(timeoutMs = 5000): Promise<void> {\n if (this.#talkReady) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => reject(new DreameError('timed out waiting for TalkReady')), timeoutMs);\n timer.unref?.();\n this.#talkWaiters.push(() => {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n /**\n * Encode 16-bit LE mono PCM (8 kHz) to G.711 A-law and push it upstream.\n * `tsMs` defaults to a running clock advanced by the frame duration.\n */\n sendTalkPcm(pcm16le: Buffer, tsMs?: number): void {\n const alaw = pcm16leToALaw(pcm16le);\n this.sendTalkAlaw(alaw, tsMs);\n }\n\n /** Push raw A-law audio upstream (advancing the running talk clock). */\n sendTalkAlaw(alaw: Buffer, tsMs?: number): void {\n if (!this.#client) throw new DreameError('stream is not running');\n const ts = tsMs ?? this.#talkTs;\n this.#client.sendAudioFrame(alaw, ts);\n // A-law @ 8 kHz => 1 byte per sample => ms = samples/8.\n this.#talkTs = ts + Math.round(alaw.length / 8);\n }\n\n /** Close the two-way intercom (MIoT stop). */\n async stopTalk(): Promise<void> {\n this.#talkReady = false;\n this.#log('[nodedreame:stream] intercom stop');\n await this.#controller.stopIntercom();\n }\n\n async #safeTeardown(): Promise<void> {\n this.#talkReady = false;\n this.#talkWaiters = [];\n try {\n this.#client?.close();\n } catch {\n /* ignore */\n }\n this.#client = null;\n try {\n await this.#controller.close();\n } catch (err) {\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n }\n }\n}\n"],"mappings":"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","names":["MODEL_CAPABILITIES","undiciFetch","import_zod","mqtt","key","MiotState","ChargingStatus","SuctionLevel","WaterVolume","CleaningMode","MiotError","TaskStatus","key","key","key","import_zod","import_node_crypto","import_zod","import_node_crypto","import_zod","import_node_crypto","import_node_crypto","key","key","import_pngjs","key","key","z","MowerStatus","MowerChargingStatus","MowerControlAction","MowerTaskStatus","MowerFault","isRecord","FALLBACK","deepFreeze","MODEL_CAPABILITIES","tokensFor","isRecord","asString","key","CHARGING","z","key","import_zod","key","x","y","w","h","import_node_events","import_node_crypto","str","key","net"]}
|
|
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/connectivity.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/ai-detection.ts","../src/models/vacuum/auto-switch.ts","../src/models/vacuum/consumables.ts","../src/models/vacuum/capabilities.ts","../src/video/schemas.ts","../src/video/profile.ts","../src/video/client.ts","../src/video/aliyun/constants.ts","../src/video/aliyun/identity.ts","../src/video/aliyun/signing.ts","../src/video/aliyun/vision.ts","../src/video/aliyun/session.ts","../src/video/camera-availability.ts","../src/video/monitor/constants.ts","../src/video/monitor/protocol.ts","../src/video/monitor/vendor.ts","../src/video/monitor/controller.ts","../src/models/vacuum/map/envelope.ts","../src/models/vacuum/map/header.ts","../src/models/vacuum/map/tail.ts","../src/models/vacuum/map/segment-types.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/merge.ts","../src/models/vacuum/map/decode.ts","../src/models/vacuum/map/render.ts","../src/models/vacuum/map/wifi.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","../src/video/monitor/detections.ts","../src/video/media/lv-rtmp-client.ts","../src/video/media/amf0.ts","../src/video/media/chunk.ts","../src/video/media/flv.ts","../src/video/media/g711.ts","../src/video/media/camera-stream.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 { isAuthRefusedError } from './transport/mqtt-push.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, WifiSignalMapInput } 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';\nexport {\n AI_FEATURE_BIT,\n AI_FEATURE_JSON_KEY,\n decodeAiFeature,\n encodeAiFeatureWrite,\n} from './models/vacuum/ai-detection.js';\nexport type { DreameAiFeature, AiDetectionRaw } from './models/vacuum/ai-detection.js';\nexport {\n AUTO_SWITCH_JSON_KEY,\n decodeAutoSwitch,\n decodeAutoSwitchAll,\n supportedAutoSwitchKeys,\n encodeAutoSwitchWrite,\n} from './models/vacuum/auto-switch.js';\nexport type { AutoSwitchKey, AutoSwitchRaw } from './models/vacuum/auto-switch.js';\nexport {\n VACUUM_CONSUMABLES,\n consumableSpec,\n isDreameConsumableKey,\n} from './models/vacuum/consumables.js';\nexport type {\n ConsumableSpec,\n ConsumableReading,\n DreameConsumableKey,\n} from './models/vacuum/consumables.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 mowerFaultSeverity,\n} from './models/mower/enums.js';\nexport type { MowerFaultSeverity } 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// CMS consumables (blade/brush/maintenance) — typed readings + the parser, so\n// camstack can surface them on the `consumables` cap.\nexport {\n parseMowerConsumables,\n extractMowerConsumableValues,\n mowerConsumableIndex,\n parseMowerHeartbeat,\n parseMowingProgress,\n MOWER_TASK_SUBSTATES,\n} from './models/mower/decode.js';\nexport type { MowerMowingProgress } from './models/mower/decode.js';\nexport type {\n MowerConsumableKey,\n MowerConsumableReading,\n MowerHeartbeat,\n MowerTaskSubState,\n} 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, MapColorScheme } 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\n// --- Video control-plane (X40/X50 camera devices) ------------------------\nexport {\n getVideoAccessToken,\n getAliyunAuthCode,\n getVideoFamilyId,\n getDeviceVideoProfile,\n getTencentIdentity,\n getTencentP2PInfo,\n explainNoCameraChannel,\n toVideoProfile,\n} from './video/index.js';\nexport type {\n VideoRequestInput,\n VideoVendor,\n VideoAccessToken,\n DeviceVideoProfile,\n} from './video/index.js';\n\n// --- Aliyun LinkVisual media plane -----------------------------------------\nexport {\n DreameVideoSession,\n streamQuery,\n loginByOauth,\n createIotSession,\n signApiGatewayRequest,\n ALIYUN_REGION_ID,\n} from './video/index.js';\nexport type {\n DreameVideoSessionInput,\n StreamInfo,\n StreamQueryInput,\n OaSession,\n IotSession,\n} from './video/index.js';\n\n// --- Camera monitor control-plane + autonomous cold-start ------------------\nexport {\n DreameCameraController,\n MONITOR_SIID,\n MONITOR_AIID,\n MONITOR_PIID,\n MONITOR_VENDOR_TOKEN,\n VACUUM_SIID,\n VACUUM_MOVE,\n PET_SOUNDS,\n VACUUM_ACTIONS,\n makeMonitorSession,\n hashAccessCode,\n parsePersonFollow,\n parseObstacleData,\n remoteDriveValue,\n DRIVE_DIRECTIONS,\n} from './video/index.js';\nexport type {\n DreameCameraControllerInput,\n CameraStreamHandle,\n MonitorActionCaller,\n RelayMinter,\n DetectionBox,\n PersonFollowDetection,\n ObstacleDetection,\n DriveDirection,\n PetSound,\n VacuumActionKey,\n} from './video/index.js';\n\n// --- Camera media pipeline (RTMP relay -> H.264/AAC frames) -----------------\nexport {\n DreameCameraStream,\n LvRtmpClient,\n parseRelayUrl,\n LV_STATUS,\n TALK_AUDIO_HEADER_G711A,\n pcm16ToALaw,\n pcm16leToALaw,\n avccToAnnexB,\n aacToAdts,\n parseAvcConfig,\n parseAacConfig,\n aacSampleRate,\n} from './video/index.js';\nexport type {\n DreameCameraStreamInput,\n DreameCameraStreamEvents,\n FrameSource,\n FrameSourceFactory,\n LvRtmpClientOptions,\n VideoAccessUnitEvent,\n AudioInfoEvent,\n StatusEvent,\n AvcConfig,\n AacConfig,\n} from './video/index.js';\n\n// --- Raw device-list access (connectivity/diagnostics) --------------------\nexport { listDevices } from './cloud/devices.js';\nexport type { ListDevicesInput } from './cloud/devices.js';\nexport { parseConnectivity } from './cloud/connectivity.js';\nexport type { DeviceConnectivity, BrokerEndpoint } from './cloud/connectivity.js';\n\n// --- WiFi signal map (coverage heatmap + current-signal at robot pose) -----\nexport { decodeWifiSignalMap, renderWifiSignalPng } from './models/vacuum/map/index.js';\nexport type { WifiSignalMap, RenderWifiSignalPngOptions } from './models/vacuum/map/index.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.12.4';\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 * Upper bound for the sendCommand `id`. The Dreame cloud's device-side\n * correlation id is a NARROW integer field: verified live on\n * `dreame.mower.p2255` that ids up to 2^24 (16_777_216) round-trip fine but ids\n * ≳ 1e8 make the cloud return code 80001 \"device offline / 指令发送超时\" (the\n * oversized id never reaches/echoes from the device, so the cloud reports it\n * unreachable). A 31-bit random id therefore broke EVERY mower command. Keep ids\n * comfortably under that ceiling.\n */\nconst MAX_REQUEST_ID = 0xffffff;\n\n/** Monotonic counter, seeded with a small random base (mirrors the Dreame app's\n * `random(1,100)` start). Module-scoped so concurrent fan-out never collides. */\nlet nextRequestId = Math.floor(Math.random() * 100) + 1;\n\n/**\n * Next `id` for a sendCommand envelope — a small, monotonically increasing\n * integer that wraps under {@link MAX_REQUEST_ID}. MUST stay small: see\n * {@link MAX_REQUEST_ID} for why a large id makes the cloud 80001.\n */\nexport function randomRequestId(): number {\n nextRequestId = nextRequestId >= MAX_REQUEST_ID ? 1 : nextRequestId + 1;\n return nextRequestId;\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().nullish(),\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().nullish(),\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().nullish(),\n data: z.array(CachedPropEntrySchema).optional(),\n })\n .passthrough();\nexport type CachedPropsResponse = z.infer<typeof CachedPropsResponseSchema>;\n\n/**\n * Batched device-data read response (`dreame-user-iot/iotuserdata/getDeviceData`).\n * `data` is a flat dict of hierarchical keys (`MAP.0`, `MAP.info`, `M_PATH.0`,\n * `SETTINGS.*`, `SCHEDULE.*`, …) → string/number chunk values. Permissive: the\n * exact key set is device/firmware-specific; on an error code `data` may be\n * absent entirely.\n */\nexport const BatchDeviceDataResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z.record(z.string(), z.unknown()).optional(),\n })\n .passthrough();\nexport type BatchDeviceDataResponse = z.infer<typeof BatchDeviceDataResponseSchema>;\n\nexport const SendCommandResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\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","/**\n * Connectivity / diagnostic info distilled from a device-list record.\n *\n * Scope note: the Dreame cloud device record exposes reachability + identity +\n * the assigned MQTT broker, but NOT Wi-Fi RSSI / SSID / local IP — those are\n * native-firmware (miIO-style) details the cloud API does not return for these\n * vacuums. So `signalStrength`/`ssid`/`ip` are intentionally absent here; this\n * surfaces everything the cloud actually provides.\n */\n\n/** The device's assigned MQTT broker endpoint (from `bindDomain`). */\nexport interface BrokerEndpoint {\n readonly host: string;\n readonly port: number | null;\n}\n\nexport interface DeviceConnectivity {\n /** Whether the cloud considers the device online (LWT/online flag). */\n readonly online: boolean;\n /** Physical link type reported by the cloud (e.g. `\"WIFI\"`), or null. */\n readonly connectionType: string | null;\n /** Device MAC address, or null. */\n readonly mac: string | null;\n /** Assigned MQTT broker (`bindDomain` split into host/port), or null. */\n readonly broker: BrokerEndpoint | null;\n /** Cloud region the device is bound to (e.g. `\"eu\"`), or null. */\n readonly region: string | null;\n /** Cloud media/IoT vendor (e.g. `\"ali\"`), or null. */\n readonly cloudVendor: string | null;\n /** Firmware version (`ver`), or null. */\n readonly firmwareVersion: string | null;\n /** Serial number (`sn`), or null. */\n readonly serialNumber: string | null;\n /** Sub-model code, or null. */\n readonly subModel: string | null;\n /** Battery percentage 0–100 from the cloud snapshot, or null. */\n readonly battery: number | null;\n /** Latest MIoT status int from the cloud snapshot, or null. */\n readonly statusCode: number | null;\n}\n\nconst str = (v: unknown): string | null => (typeof v === 'string' && v.length > 0 ? v : null);\nconst numOrNull = (v: unknown): number | null => (typeof v === 'number' && Number.isFinite(v) ? v : null);\n\n/** Split a `bindDomain` like `\"10000.mt.eu.iot.dreame.tech:19973\"` into host/port. */\nfunction parseBroker(bindDomain: unknown): BrokerEndpoint | null {\n const s = str(bindDomain);\n if (!s) return null;\n const idx = s.lastIndexOf(':');\n if (idx < 0) return { host: s, port: null };\n const port = Number(s.slice(idx + 1));\n return { host: s.slice(0, idx), port: Number.isFinite(port) ? port : null };\n}\n\n/** Parse the connectivity view from a raw device-list record. */\nexport function parseConnectivity(raw: Record<string, unknown>): DeviceConnectivity {\n const deviceInfo = (raw['deviceInfo'] ?? {}) as Record<string, unknown>;\n return {\n online: raw['online'] === true,\n connectionType: str(deviceInfo['scType']),\n mac: str(raw['mac']),\n broker: parseBroker(raw['bindDomain']),\n region: str(raw['region']),\n cloudVendor: str(raw['vendor']),\n firmwareVersion: str(raw['ver']),\n serialNumber: str(raw['sn']),\n subModel: str(raw['subModel']),\n battery: numOrNull(raw['battery']),\n statusCode: numOrNull(raw['latestStatus']),\n };\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';\nimport { parseConnectivity } from './connectivity.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 connectivity: parseConnectivity(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 BatchDeviceDataResponseSchema,\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.*` chunk\n * keys, plus `SETTINGS.*` / `SCHEDULE.*` / `OTA_INFO.*` groups) for a set of\n * property groups.\n *\n * Hits `dreame-user-iot/iotuserdata/getDeviceData` — the SAME account-auth host\n * as {@link getCachedProperties} (NOT the `device/sendCommand` envelope), so it\n * resolves the cloud-stored data for a standby/sleeping robot. Body is\n * `{ did, model: props }` where `model` (the firmware's spelling) carries the\n * requested key groups (e.g. `['MAP','M_PATH']`); an empty list returns every\n * group. The response `data` is a flat dict of `KEY.idx` chunk values that\n * {@link import('../models/mower/map/parser.js').parseBatchMapData} reassembles.\n *\n * Endpoint recovered from the Tasshack `dreame-vacuum` protocol table\n * (`_strings[23]/[26]/[44]` = `dreame-user-iot/iotuserdata/getDeviceData`) and\n * the donor `antondaubert/dreame-mower` device-data analysis tool.\n */\nexport async function getBatchDeviceDatas(\n base: CommonInput,\n props: string[],\n opts: CallOptions = {},\n): Promise<Record<string, unknown>> {\n const ctx = base.ctx ?? RequestContext.from({ ...base, host: base.apiHost });\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/iotuserdata/getDeviceData',\n accessToken: base.session.accessToken,\n body: { did: base.did, model: props },\n context: 'batch device data',\n ...(signal !== undefined ? { signal } : {}),\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n });\n\n const parsed = BatchDeviceDataResponseSchema.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 `batch device data rejected: code=${parsed.code} msg=${parsed.msg ?? '?'}`,\n 200,\n parsed,\n );\n }\n return parsed.data ?? {};\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 /** MQTT-level keep-alive in seconds: the client PINGREQs the broker this often\n * so an idle connection is held open (and a dead one detected) at the protocol\n * layer — the connection's keep-alive belongs here, in the library, not in a\n * consumer-side property poll. */\n keepalive: number;\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 /** MQTT keep-alive (PINGREQ cadence) in seconds. Default 60. `0` disables. */\n keepaliveSeconds?: 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 * Called when the broker refuses a CONNECT with an auth error — a CONNACK\n * whose return code is 5 (\"Not authorized\") or 4 (\"Bad username or password\")\n * on MQTT 3.1.1 (135/134 on MQTT 5). This happens when the access token the\n * push connects with has gone stale server-side. The callback MUST resolve to\n * a freshly-minted session; the push adopts its token and reconnects with it\n * instead of hopelessly replaying the stale one. Wired by the facade to\n * `Nodreame.reauthenticate()`. When omitted, an auth-refused reconnect falls\n * back to the plain backoff loop (legacy behaviour).\n */\n onAuthFailure?: () => Promise<DreameSession>;\n /**\n * Maximum number of CONSECUTIVE auth-refused re-authentications before the\n * push stops re-logging-in and falls back to a plain backoff reconnect. The\n * counter resets to 0 on the next successful connect. Guards against a tight\n * re-login loop when the credentials are genuinely bad. Default 3.\n *\n * NOTE: this cap is PER-PUSH, so a fleet of N devices could in principle make\n * up to `maxAuthRetries * N` re-auth calls on an account-wide invalidation. In\n * practice the facade bounds this: `Nodreame.reauthenticate()` coalesces\n * concurrent refreshes into ONE grant and propagates the fresh token to every\n * device, so the first device's re-auth heals the whole fleet before the\n * others exhaust their own budgets.\n */\n maxAuthRetries?: number;\n}\n\n/**\n * True when `err` (or an error in its `cause` chain) is an MQTT CONNACK\n * auth-refusal. mqtt.js raises an `ErrorWithReasonCode` carrying a numeric\n * `code`: MQTT 3.1.1 uses 4 (bad user/pass) / 5 (not authorized); MQTT 5 uses\n * 134 / 135. We also match the human-readable message as a belt-and-braces\n * fallback in case the code is not surfaced through a wrapper.\n */\nexport function isAuthRefusedError(err: unknown): boolean {\n let cur: unknown = err;\n for (let depth = 0; cur != null && depth < 6; depth += 1) {\n if (typeof cur !== 'object') {\n break;\n }\n const code = (cur as { code?: unknown }).code;\n if (code === 4 || code === 5 || code === 134 || code === 135) {\n return true;\n }\n const message = (cur as { message?: unknown }).message;\n if (\n typeof message === 'string' &&\n /not authorized|bad user ?name|bad username or password|bad password/i.test(message)\n ) {\n return true;\n }\n cur = (cur as { cause?: unknown }).cause;\n }\n return false;\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 #keepaliveSeconds: number;\n readonly #rejectUnauthorized: boolean;\n readonly #onAuthFailure: (() => Promise<DreameSession>) | null;\n readonly #maxAuthRetries: number;\n #topic: string;\n #client: MqttLikeClient | null = null;\n #closed = false;\n #tearingDown = false;\n #reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n /** Consecutive auth-refused re-auths since the last successful connect. */\n #authFailureCount = 0;\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.#keepaliveSeconds = input.keepaliveSeconds ?? 60;\n this.#rejectUnauthorized = input.rejectUnauthorized ?? false;\n this.#onAuthFailure = input.onAuthFailure ?? null;\n this.#maxAuthRetries = input.maxAuthRetries ?? 3;\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 try {\n await this.#connectAndSubscribe();\n } catch {\n // The fresh token failed to connect transiently (broker has not yet\n // indexed the rotated token, a network blip, etc.). Arm a bounded\n // reconnect instead of leaving the device dark until the next rotation.\n // `#connectAndSubscribe` already ended + nulled its client, so this\n // schedules a clean retry. Resolve rather than reject so a propagation\n // (allSettled) caller records this device as handled — the reconnect\n // timer is now driving recovery.\n this.#scheduleReconnect();\n }\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 keepalive: this.#keepaliveSeconds,\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 try {\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 // A clean connect+subscribe proves the current token is good again:\n // reset the auth-failure budget so a LATER staleness can self-heal.\n this.#authFailureCount = 0;\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 } catch (err) {\n // The connect/subscribe failed AFTER the TCP socket may already be\n // ESTABLISHED (auth or subscribe rejected post-handshake — e.g. a stale\n // token after a re-login). Force-close it now so the socket + client\n // object are freed. Without this, the caller (`open`/`refreshSession`/the\n // reconnect loop) would orphan the client — `#scheduleReconnect` nulls\n // `#client` without `end()`, leaking one ESTABLISHED connection (and its\n // buffers) per failed attempt, which snowballs once the broker starts\n // rejecting the pile-up. Detach our close handler first so the forced\n // end() does not re-arm a reconnect.\n client.removeListener('close', this.#onClose);\n try {\n client.end(true, {}, () => undefined);\n } catch {\n // best-effort — a half-open client may throw on end(); ignore.\n }\n if (this.#client === client) {\n this.#client = null;\n }\n throw err;\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.#attemptReconnect();\n }, this.#backoffMs);\n }\n\n /**\n * One reconnect attempt with reactive auth self-heal. If the broker refuses\n * the CONNECT with an auth error (a stale access token) and an\n * `onAuthFailure` provider is wired and the re-auth budget is not exhausted,\n * mint a fresh session and retry with the new token instead of replaying the\n * old one. Any other failure (or an exhausted budget) falls back to the plain\n * backoff reconnect so bad credentials cannot hot-loop re-login.\n */\n async #attemptReconnect(): Promise<void> {\n try {\n await this.#connectAndSubscribe();\n } catch (err) {\n if (this.#closed) {\n return;\n }\n if (\n this.#onAuthFailure &&\n isAuthRefusedError(err) &&\n this.#authFailureCount < this.#maxAuthRetries\n ) {\n await this.#reauthenticateAndReconnect();\n return;\n }\n // Non-auth error, or the re-auth cap is exhausted: surface it and fall\n // back to a plain backoff reconnect (no re-login storm).\n this.emit('error', err instanceof Error ? err : new DreameTransportError(String(err)));\n this.#scheduleReconnect();\n }\n }\n\n /** Mint a fresh session via `onAuthFailure`, adopt its token, and reconnect. */\n async #reauthenticateAndReconnect(): Promise<void> {\n this.#authFailureCount += 1;\n let session: DreameSession;\n try {\n session = await this.#onAuthFailure!();\n } catch (err) {\n // Re-authentication itself failed: surface it and retry on a backoff.\n this.emit('error', err instanceof Error ? err : new DreameTransportError(String(err)));\n // NEVER orphan a live client: `onAuthFailure` propagates the new session\n // through `refreshSession()`, which may have ALREADY reconnected this push\n // before the (later) rejection surfaced. `#scheduleReconnect()` would null\n // that healthy client without ending it — a leaked socket + a duplicate\n // connection. Only re-arm when we have no live client to preserve.\n if (!this.#client) {\n this.#scheduleReconnect();\n }\n return;\n }\n if (this.#closed) {\n return;\n }\n // Adopt the fresh token + rebuild the topic (uid could differ). NOTE:\n // `onAuthFailure` typically propagates the new session back through\n // `refreshSession()`, which ALREADY tears down and reconnects this push —\n // in that case `#client` is live and we must NOT open a second connection.\n this.#session = session;\n this.#topic = buildStatusTopic(this.#device, this.#session.uid, this.#region);\n if (this.#client) {\n return;\n }\n // No propagated reconnect happened — reconnect ourselves on the new token.\n void this.#attemptReconnect();\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 /** Number of listeners registered for `event`. */\n listenerCount<K extends keyof Events & string>(event: K): number {\n return this.#emitter.listenerCount(event);\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(\n device: DreameDevice,\n session: DreameSession,\n region: DreameRegion,\n onAuthFailure?: () => Promise<DreameSession>,\n ): 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, onAuthFailure) =>\n new DreamePush({\n device,\n session,\n region,\n ...(onAuthFailure !== undefined ? { onAuthFailure } : {}),\n }),\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 /**\n * Force-refresh callback handed to the underlying push so it can self-heal a\n * broker CONNACK auth-refusal (stale token) by minting a fresh session.\n * Wired by the facade to `Nodreame.reauthenticate()`.\n */\n onAuthFailure?: () => Promise<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 #onAuthFailure: (() => Promise<DreameSession>) | undefined;\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.#onAuthFailure = input.onAuthFailure;\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(\n this.#device,\n this.#sessionRef(),\n this.#region,\n this.#onAuthFailure,\n );\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 /**\n * AI obstacle-detection toggle bundle (Tasshack types.py:1609 — `AI_DETECTION`,\n * siid 4 piid 22). The value packs ALL AI-obstacle switches into ONE property,\n * encoded as EITHER an int bitmask (`DreameVacuumAIProperty`) OR a JSON string\n * (`DreameVacuumStrAIProperty`) depending on firmware. Decode/encode per-feature\n * via `ai-detection.ts` ({@link decodeAiFeature} / {@link encodeAiFeatureWrite}).\n */\n AI_DETECTION: { siid: 4, piid: 22 } as const,\n /**\n * Auto-switch settings bundle (Tasshack types.py — `AUTO_SWITCH_SETTINGS`,\n * siid 4 piid 50). A JSON string packing every secondary toggle/setting as\n * `[{\"k\":<key>,\"v\":<int>}, …]`. Decode/encode per-setting via `auto-switch.ts`.\n */\n AUTO_SWITCH_SETTINGS: { siid: 4, piid: 50 } 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 /**\n * Map \"PATH\" push — the OSS object name of the latest map frame the robot\n * uploaded (the value passed to {@link VacuumDevice.getMap}). Arrives via the\n * MQTT push during/after cleaning; NOT in `DEFAULT_PROPS` because it is not\n * polled (it is pushed). Read it through {@link VacuumDevice.mapFilename}.\n */\n MAP_PATH: { siid: 6, piid: 3 } 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 * AI obstacle-detection codec. The robot packs ALL AI-obstacle toggles into a\n * SINGLE MIoT property — `AI_DETECTION` (siid 4 piid 22) — whose value is EITHER\n * an `int` bitmask OR a `str` JSON object, depending on firmware generation.\n * Ported from the Tasshack `dreame-vacuum` HA integration (v2.0.0b25):\n * - INT ←→ `DreameVacuumAIProperty` (bitwise flags)\n * - JSON ←→ `DreameVacuumStrAIProperty` ({ \"<key>\": bool })\n *\n * This module exposes per-feature decode + a read-modify-write encode that\n * preserves the other features, so a consumer can present each AI toggle as an\n * independent boolean without knowing which on-wire encoding the model uses.\n */\n\n/** Canonical AI-obstacle feature keys. A given model exposes a subset. */\nexport type DreameAiFeature =\n | 'furnitureDetection'\n | 'obstacleDetection'\n | 'obstaclePicture'\n | 'fluidDetection'\n | 'petDetection'\n | 'obstacleImageUpload'\n | 'petAvoidance'\n | 'fuzzyObstacleDetection'\n | 'petPicture'\n | 'petFocusedDetection'\n | 'largeParticlesBoost'\n | 'humanDetection';\n\n/**\n * Bit value in the INT-encoded `AI_DETECTION` (Tasshack `DreameVacuumAIProperty`).\n * A feature absent here has no int representation (JSON-only).\n */\nexport const AI_FEATURE_BIT: Readonly<Partial<Record<DreameAiFeature, number>>> = {\n furnitureDetection: 1,\n obstacleDetection: 2,\n obstaclePicture: 4,\n fluidDetection: 8,\n petDetection: 16,\n obstacleImageUpload: 32,\n // AI_IMAGE = 64 is an internal flag, not a user toggle — intentionally omitted.\n petAvoidance: 128,\n fuzzyObstacleDetection: 256,\n petPicture: 512,\n petFocusedDetection: 1024,\n largeParticlesBoost: 2048,\n};\n\n/**\n * Key in the JSON-string-encoded `AI_DETECTION` (Tasshack\n * `DreameVacuumStrAIProperty`). A feature absent here has no JSON representation.\n */\nexport const AI_FEATURE_JSON_KEY: Readonly<Partial<Record<DreameAiFeature, string>>> = {\n obstacleDetection: 'obstacle_detect_switch',\n obstacleImageUpload: 'obstacle_app_display_switch',\n petDetection: 'whether_have_pet',\n humanDetection: 'human_detect_switch',\n furnitureDetection: 'furniture_detect_switch',\n fluidDetection: 'fluid_detect_switch',\n};\n\n/** The raw on-wire `AI_DETECTION` value: an int bitmask, a JSON string, or absent. */\nexport type AiDetectionRaw = number | string | null;\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** Parse a JSON-string AI payload into an object, or null when not an object. */\nfunction parseJsonPayload(raw: string): Record<string, unknown> | null {\n try {\n const parsed: unknown = JSON.parse(raw);\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n/** Coerce a JSON value (bool | 1/0 | \"1\"/\"0\") to a boolean, or null when ambiguous. */\nfunction coerceBool(v: unknown): boolean | null {\n if (typeof v === 'boolean') return v;\n if (v === 1 || v === '1') return true;\n if (v === 0 || v === '0') return false;\n return null;\n}\n\n/**\n * Decode ONE AI feature's on/off state from the raw `AI_DETECTION` value,\n * transparently handling both the int-bitmask and JSON-string encodings.\n * Returns `null` when the value is absent, the encoding cannot represent the\n * feature, or the JSON payload omits the key.\n */\nexport function decodeAiFeature(raw: AiDetectionRaw, feature: DreameAiFeature): boolean | null {\n if (typeof raw === 'number') {\n const bit = AI_FEATURE_BIT[feature];\n if (bit === undefined) return null;\n return (raw & bit) === bit;\n }\n if (typeof raw === 'string') {\n const key = AI_FEATURE_JSON_KEY[feature];\n if (key === undefined) return null;\n const payload = parseJsonPayload(raw);\n if (payload === null) return null;\n return coerceBool(payload[key]);\n }\n return null;\n}\n\n/**\n * Compute the value to WRITE back to `AI_DETECTION` after toggling ONE feature,\n * preserving the on-wire encoding and the other features. Mirrors Tasshack\n * `set_ai_detection`: an int payload is the full new bitmask; a JSON payload\n * carries ONLY the changed key (the device merges it server-side).\n *\n * Throws when the current value/encoding is unknown (a blind read-modify-write\n * could clobber the other features) or the feature has no representation in the\n * active encoding.\n */\nexport function encodeAiFeatureWrite(\n raw: AiDetectionRaw,\n feature: DreameAiFeature,\n value: boolean,\n): number | string {\n if (typeof raw === 'number') {\n const bit = AI_FEATURE_BIT[feature];\n if (bit === undefined) {\n throw new Error(\n `encodeAiFeatureWrite: feature \"${feature}\" has no int-bitmask representation`,\n );\n }\n return value ? raw | bit : raw & ~bit;\n }\n if (typeof raw === 'string') {\n const key = AI_FEATURE_JSON_KEY[feature];\n if (key === undefined) {\n throw new Error(`encodeAiFeatureWrite: feature \"${feature}\" has no JSON-key representation`);\n }\n return JSON.stringify({ [key]: value });\n }\n throw new Error(\n `encodeAiFeatureWrite: AI_DETECTION value not yet known — cannot safely toggle \"${feature}\"`,\n );\n}\n","/**\n * Auto-switch settings codec. The robot packs a large bundle of secondary\n * cleaning toggles + multi-value settings into a SINGLE MIoT property —\n * `AUTO_SWITCH_SETTINGS` (siid 4 piid 50) — whose value is a JSON string.\n * Ported from the Tasshack `dreame-vacuum` HA integration (v2.0.0b25,\n * `DreameVacuumAutoSwitchProperty` + `set_auto_switch_property`):\n *\n * - READ : a JSON LIST `[{\"k\":\"LessColl\",\"v\":1}, …]` (or a single\n * `{\"k\":…,\"v\":…}` object) — every supported key/value pair.\n * - WRITE : a SINGLE `{\"k\":\"LessColl\",\"v\":0}` object carrying ONLY the\n * changed key; the device merges it server-side.\n *\n * Unlike `AI_DETECTION` this family is JSON-ONLY (no int-bitmask variant) and the\n * values are arbitrary ints (booleans 0/1, small enums, and `-1`/`-n` \"not\n * applicable\" sentinels) — the codec returns the raw int and leaves the\n * boolean-vs-enum interpretation to the caller. Presence is data-driven: a key is\n * \"supported\" iff it appears in the decoded payload (exactly how Tasshack derives\n * `auto_switch_data`).\n */\n\n/** Canonical auto-switch keys. A given model reports a subset. */\nexport type AutoSwitchKey =\n | 'collisionAvoidance'\n | 'fillLight'\n | 'autoDrying'\n | 'stainAvoidance'\n | 'moppingType'\n | 'cleanGenius'\n | 'widerCornerCoverage'\n | 'floorDirectionCleaning'\n | 'petFocusedCleaning'\n | 'autoRecleaning'\n | 'autoRewashing'\n | 'mopPadSwing'\n | 'autoCharging'\n | 'humanFollow'\n | 'maxSuctionPower'\n | 'smartDrying'\n | 'drainageConfirmResult'\n | 'drainageTestResult'\n | 'hotWashing'\n | 'uvSterilization'\n | 'cleaningRoute'\n | 'customMoppingMode'\n | 'moppingMode'\n | 'selfCleanFrequency'\n | 'intensiveCarpetCleaning'\n | 'gapCleaningExtension'\n | 'moppingUnderFurnitures'\n | 'ultraCleanMode'\n | 'streamingVoicePrompt'\n | 'mopExtend'\n | 'mopExtendFrequency'\n | 'sideReach'\n | 'intelligentStainCleaning';\n\n/**\n * Short on-wire key for each setting inside the `AUTO_SWITCH_SETTINGS` JSON\n * (Tasshack `DreameVacuumAutoSwitchProperty`). These opaque strings are the\n * device's own identifiers and must match byte-for-byte.\n */\nexport const AUTO_SWITCH_JSON_KEY: Readonly<Record<AutoSwitchKey, string>> = {\n collisionAvoidance: 'LessColl',\n fillLight: 'FillinLight',\n autoDrying: 'AutoDry',\n stainAvoidance: 'StainIdentify',\n moppingType: 'CleanType',\n cleanGenius: 'SmartHost',\n widerCornerCoverage: 'MeticulousTwist',\n floorDirectionCleaning: 'MaterialDirectionClean',\n petFocusedCleaning: 'PetPartClean',\n autoRecleaning: 'SmartAutoMop',\n autoRewashing: 'SmartAutoWash',\n mopPadSwing: 'MopScalable',\n autoCharging: 'SmartCharge',\n humanFollow: 'MonitorHumanFollow',\n maxSuctionPower: 'SuctionMax',\n smartDrying: 'SmartDrying',\n drainageConfirmResult: 'FluctuationConfirmResult',\n drainageTestResult: 'FluctuationTestResult',\n hotWashing: 'HotWash',\n uvSterilization: 'UVLight',\n cleaningRoute: 'CleanRoute',\n customMoppingMode: 'MopEffectSwitch',\n moppingMode: 'MopEffectState',\n selfCleanFrequency: 'BackWashType',\n intensiveCarpetCleaning: 'CarpetFineClean',\n gapCleaningExtension: 'LacuneMopScalable',\n moppingUnderFurnitures: 'MopScalable2',\n ultraCleanMode: 'SuperWash',\n streamingVoicePrompt: 'MonitorPromptLevel',\n mopExtend: 'MopExtrSwitch',\n mopExtendFrequency: 'ExtrFreq',\n sideReach: 'SbrushExtrSwitch',\n intelligentStainCleaning: 'HeavyStainSmart',\n};\n\n/** Reverse map: on-wire key → canonical key (built once). */\nconst JSON_KEY_TO_CANONICAL: Readonly<Record<string, AutoSwitchKey>> = Object.fromEntries(\n (Object.entries(AUTO_SWITCH_JSON_KEY) as [AutoSwitchKey, string][]).map(([k, v]) => [v, k]),\n);\n\n/** The raw on-wire `AUTO_SWITCH_SETTINGS` value: a JSON string, or absent. */\nexport type AutoSwitchRaw = string | null;\n\n/** Coerce a JSON value (number | numeric string) to a finite int, or null. */\nfunction coerceInt(v: unknown): number | null {\n if (typeof v === 'number') return Number.isFinite(v) ? v : null;\n if (typeof v === 'string' && v.trim() !== '') {\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n\n/**\n * Parse the raw `AUTO_SWITCH_SETTINGS` value into a `{ on-wire key → int }` map.\n * Accepts the list form (`[{\"k\":…,\"v\":…}, …]`) and the single-object form\n * (`{\"k\":…,\"v\":…}`). Returns null when the value is absent or unparseable.\n */\nfunction parseAutoSwitchMap(raw: AutoSwitchRaw): Map<string, number> | null {\n if (typeof raw !== 'string' || raw.length <= 2) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n const out = new Map<string, number>();\n const add = (entry: unknown): void => {\n if (entry === null || typeof entry !== 'object') return;\n const rec = entry as Record<string, unknown>;\n if (typeof rec['k'] !== 'string') return;\n const value = coerceInt(rec['v']);\n if (value !== null) out.set(rec['k'], value);\n };\n if (Array.isArray(parsed)) {\n for (const entry of parsed) add(entry);\n } else {\n add(parsed);\n }\n return out.size > 0 ? out : null;\n}\n\n/**\n * Decode ONE auto-switch setting's int value from the packed\n * `AUTO_SWITCH_SETTINGS` payload. Returns `null` when the value is absent or the\n * key is not present in the payload (i.e. unsupported by this model/firmware).\n */\nexport function decodeAutoSwitch(raw: AutoSwitchRaw, key: AutoSwitchKey): number | null {\n const map = parseAutoSwitchMap(raw);\n if (map === null) return null;\n const jsonKey = AUTO_SWITCH_JSON_KEY[key];\n return map.has(jsonKey) ? (map.get(jsonKey) ?? null) : null;\n}\n\n/**\n * The auto-switch keys the payload actually reports (presence-driven — mirrors\n * Tasshack `auto_switch_data`). On-wire keys with no canonical mapping (newer\n * firmware extras like `MopFullyScalable`) are skipped. Order follows\n * {@link AUTO_SWITCH_JSON_KEY} for determinism.\n */\nexport function supportedAutoSwitchKeys(raw: AutoSwitchRaw): readonly AutoSwitchKey[] {\n const map = parseAutoSwitchMap(raw);\n if (map === null) return [];\n const present: AutoSwitchKey[] = [];\n for (const key of Object.keys(AUTO_SWITCH_JSON_KEY) as AutoSwitchKey[]) {\n if (map.has(AUTO_SWITCH_JSON_KEY[key])) present.push(key);\n }\n return present;\n}\n\n/** All `{ canonical key → int }` pairs the payload reports (canonical keys only). */\nexport function decodeAutoSwitchAll(\n raw: AutoSwitchRaw,\n): Readonly<Partial<Record<AutoSwitchKey, number>>> {\n const map = parseAutoSwitchMap(raw);\n if (map === null) return {};\n const out: Partial<Record<AutoSwitchKey, number>> = {};\n for (const [jsonKey, value] of map) {\n const canonical = JSON_KEY_TO_CANONICAL[jsonKey];\n if (canonical !== undefined) out[canonical] = value;\n }\n return out;\n}\n\n/**\n * Compute the value to WRITE to `AUTO_SWITCH_SETTINGS` for ONE setting. Mirrors\n * Tasshack `set_auto_switch_property`: a SINGLE `{\"k\":<key>,\"v\":<int>}` object\n * (NOT the full list) — the device merges it server-side, so the other settings\n * are preserved without a read-modify-write.\n */\nexport function encodeAutoSwitchWrite(key: AutoSwitchKey, value: number): string {\n return JSON.stringify({ k: AUTO_SWITCH_JSON_KEY[key], v: Math.trunc(value) });\n}\n","/**\n * Vacuum consumable/maintenance map (ported from Tasshack `dreame-vacuum`\n * v2.0.0b25 — property + action mappings). Each consumable lives on its own MIoT\n * service `siid`: a remaining-life `%` property (`siid/piid`) and a \"reset to\n * 100%\" action (`siid/aiid 1`). A model exposes only a SUBSET — which is\n * discovered by PRESENCE (the device reports the life property) rather than a\n * hardcoded per-model list, mirroring how the HA integration derives support.\n */\n\n/** Stable consumable keys (kebab-case so a consumer can use them verbatim). */\nexport type DreameConsumableKey =\n | 'main-brush'\n | 'side-brush'\n | 'filter'\n | 'sensor'\n | 'tank-filter'\n | 'mop-pad'\n | 'silver-ion'\n | 'detergent'\n | 'squeegee'\n | 'deodorizer'\n | 'wheel'\n | 'scale-inhibitor'\n | 'dust-bag';\n\n/** One consumable's wire coordinates: remaining-life property + optional reset action. */\nexport interface ConsumableSpec {\n readonly key: DreameConsumableKey;\n readonly label: string;\n /** Remaining-life % property (0..100). */\n readonly life: { readonly siid: number; readonly piid: number };\n /** \"Mark replaced / reset life\" action, or null when the model exposes none. */\n readonly reset: { readonly siid: number; readonly aiid: number } | null;\n}\n\n/**\n * Every consumable nodedreame knows. The reset action shares the consumable's\n * service `siid` with `aiid 1` (Tasshack action map). `dust-bag` has a life\n * property but no reset action.\n */\nexport const VACUUM_CONSUMABLES: readonly ConsumableSpec[] = [\n {\n key: 'main-brush',\n label: 'Main Brush',\n life: { siid: 9, piid: 2 },\n reset: { siid: 9, aiid: 1 },\n },\n {\n key: 'side-brush',\n label: 'Side Brush',\n life: { siid: 10, piid: 2 },\n reset: { siid: 10, aiid: 1 },\n },\n { key: 'filter', label: 'Filter', life: { siid: 11, piid: 1 }, reset: { siid: 11, aiid: 1 } },\n { key: 'sensor', label: 'Sensor', life: { siid: 16, piid: 1 }, reset: { siid: 16, aiid: 1 } },\n {\n key: 'tank-filter',\n label: 'Tank Filter',\n life: { siid: 17, piid: 1 },\n reset: { siid: 17, aiid: 1 },\n },\n { key: 'mop-pad', label: 'Mop Pad', life: { siid: 18, piid: 1 }, reset: { siid: 18, aiid: 1 } },\n {\n key: 'silver-ion',\n label: 'Silver-ion',\n life: { siid: 19, piid: 2 },\n reset: { siid: 19, aiid: 1 },\n },\n {\n key: 'detergent',\n label: 'Detergent',\n life: { siid: 20, piid: 1 },\n reset: { siid: 20, aiid: 1 },\n },\n { key: 'squeegee', label: 'Squeegee', life: { siid: 24, piid: 1 }, reset: { siid: 24, aiid: 1 } },\n {\n key: 'deodorizer',\n label: 'Deodorizer',\n life: { siid: 29, piid: 2 },\n reset: { siid: 29, aiid: 1 },\n },\n { key: 'wheel', label: 'Wheel', life: { siid: 30, piid: 2 }, reset: { siid: 30, aiid: 1 } },\n {\n key: 'scale-inhibitor',\n label: 'Scale Inhibitor',\n life: { siid: 31, piid: 2 },\n reset: { siid: 31, aiid: 1 },\n },\n { key: 'dust-bag', label: 'Dust Bag', life: { siid: 27, piid: 17 }, reset: null },\n] as const;\n\n/** Lookup a consumable spec by key (undefined for an unknown key). */\nexport function consumableSpec(key: DreameConsumableKey): ConsumableSpec | undefined {\n return VACUUM_CONSUMABLES.find((c) => c.key === key);\n}\n\n/** Narrow an arbitrary string to a known {@link DreameConsumableKey} — lets a\n * string-keyed consumer (e.g. a generic consumables cap) validate before\n * calling the typed reset path with no cast. */\nexport function isDreameConsumableKey(key: string): key is DreameConsumableKey {\n return VACUUM_CONSUMABLES.some((c) => c.key === key);\n}\n\n/** A resolved consumable reading: which consumable, its remaining life %, and\n * whether a reset action exists. Only the consumables the model REPORTS appear. */\nexport interface ConsumableReading {\n readonly key: DreameConsumableKey;\n readonly label: string;\n readonly leftPct: number;\n readonly resettable: boolean;\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 { DreameAiFeature } from './ai-detection.js';\nimport type { CapabilityResolver, DeviceCapabilities } from '../../device/capability.js';\n\n/**\n * AI-obstacle features the X50 family exposes (live-verified on `dreame.vacuum.r2538z`\n * via the Dreamehome/HA superset 2026-06-29). `petPicture`/`petFocusedDetection` read\n * `unavailable` on this model, so they are intentionally excluded.\n */\nconst X50_AI_FEATURES: readonly DreameAiFeature[] = [\n 'obstacleDetection',\n 'petDetection',\n 'furnitureDetection',\n 'fluidDetection',\n 'obstaclePicture',\n 'obstacleImageUpload',\n 'fuzzyObstacleDetection',\n];\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 * The AI-obstacle toggles this model exposes (subset of {@link DreameAiFeature}),\n * each surfaced as an independent boolean from the packed `AI_DETECTION` property.\n * Empty when `hasAiObstacleDetection` is false.\n */\n supportedAiFeatures: readonly DreameAiFeature[];\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 // An unknown model advertises no AI toggles until its feature set is verified.\n supportedAiFeatures: [],\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 supportedAiFeatures: X50_AI_FEATURES,\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","import { z } from 'zod';\n\n/**\n * The third-video endpoints wrap their payload in a double `data` envelope:\n * `{ code, success, msg, data: { requestId, data: <payload> } }`. `code === 0`\n * means success (already enforced by the HTTP layer); these schemas narrow the\n * payload. All are permissive (`.passthrough()`) — the cloud adds fields freely.\n */\n\n/** `tx/user/accesstoken` → `data.data = { token, userId, expireAt }` (expireAt: epoch seconds). */\nexport const VideoAccessTokenResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z\n .object({\n data: z\n .object({\n token: z.string(),\n userId: z.union([z.string(), z.number()]).optional(),\n expireAt: z.number().optional(),\n })\n .passthrough(),\n })\n .passthrough(),\n })\n .passthrough();\nexport type VideoAccessTokenResponse = z.infer<typeof VideoAccessTokenResponseSchema>;\n\n/** `aliIot/getAuthCodeV3` → `data` is a hex string (the Aliyun authCode blob). */\nexport const AuthCodeResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z.string(),\n })\n .passthrough();\nexport type AuthCodeResponse = z.infer<typeof AuthCodeResponseSchema>;\n\n/** `tx/mgr/family/getFamilyId` → `data.data.familyId`. */\nexport const FamilyIdResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z\n .object({ data: z.object({ familyId: z.string() }).passthrough() })\n .passthrough(),\n })\n .passthrough();\nexport type FamilyIdResponse = z.infer<typeof FamilyIdResponseSchema>;\n\n/**\n * `tx/mgr/dev/getIdentity` → the device's TENCENT IoT triple.\n *\n * Absent until the device is actually provisioned on `tx`: on 2026-09-05 this\n * endpoint answered `设备三元组不存在` (\"the triple does not exist\") for an X50\n * that was then on `ali`, and on 2026-09-16 the same device — moved to `tx` —\n * returned a full triple. So a failure here is a vendor statement, not a bug.\n */\nexport const TencentIdentityResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z\n .object({\n data: z\n .object({\n productId: z.string(),\n deviceName: z.string(),\n deviceId: z.string().optional(),\n secretId: z.string().optional(),\n secretKey: z.string().optional(),\n })\n .passthrough(),\n })\n .passthrough(),\n })\n .passthrough();\nexport type TencentIdentityResponse = z.infer<typeof TencentIdentityResponseSchema>;\n\n/**\n * `tx/dev/getP2PInfo` → the xp2p session descriptor.\n *\n * An OPAQUE string — measured at 35 characters on an X50, and NOT JSON: it is\n * the handle Tencent's xp2p SDK consumes, not a relay URL. Nothing here parses\n * it, because there is nothing in it we can read.\n */\nexport const TencentP2PInfoResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: z\n .object({ data: z.object({ p2pInfo: z.string() }).passthrough() })\n .passthrough(),\n })\n .passthrough();\nexport type TencentP2PInfoResponse = z.infer<typeof TencentP2PInfoResponseSchema>;\n\n/** Nested `deviceInfo` block within a device record, carrying the video vendor hints. */\nexport const DeviceVideoInfoSchema = z\n .object({\n model: z.string().optional(),\n displayName: z.string().optional(),\n permit: z.string().optional(),\n videoDynamicVendor: z.boolean().optional(),\n defaultVendors: z.array(z.string()).optional(),\n productId: z.union([z.string(), z.number()]).optional(),\n })\n .passthrough();\n\n/** A device record as returned by `device/info` (`data` = single record). */\nexport const DeviceInfoRecordSchema = z\n .object({\n did: z.union([z.string(), z.number()]).optional(),\n model: z.string().optional(),\n customName: z.string().optional(),\n vendor: z.string().optional(),\n online: z.boolean().optional(),\n lwt: z.number().optional(),\n property: z.string().optional(),\n bindDomain: z.string().optional(),\n deviceInfo: DeviceVideoInfoSchema.optional(),\n })\n .passthrough();\nexport type DeviceInfoRecord = z.infer<typeof DeviceInfoRecordSchema>;\n\nexport const DeviceInfoResponseSchema = z\n .object({\n code: z.number().optional(),\n msg: z.string().nullish(),\n data: DeviceInfoRecordSchema,\n })\n .passthrough();\nexport type DeviceInfoResponse = z.infer<typeof DeviceInfoResponseSchema>;\n","import type { DeviceInfoRecord } from './schemas.js';\nimport type { DeviceVideoProfile, VideoVendor } from './types.js';\n\nconst KNOWN_VENDORS: readonly string[] = ['tx', 'ali'];\n\nfunction asVendor(v: string | undefined | null): VideoVendor | null {\n return v === 'tx' || v === 'ali' ? v : null;\n}\n\n/** Parse the stringified `property` blob for the fields we care about (iotId, lwt). */\nfunction parseProperty(property: string | undefined): { iotId: string | null; lwt: number | null } {\n if (!property) {\n return { iotId: null, lwt: null };\n }\n try {\n const p = JSON.parse(property) as { iotId?: unknown; lwt?: unknown };\n return {\n iotId: typeof p.iotId === 'string' && p.iotId.length > 0 ? p.iotId : null,\n lwt: typeof p.lwt === 'number' ? p.lwt : null,\n };\n } catch {\n return { iotId: null, lwt: null };\n }\n}\n\n/**\n * Derive a {@link DeviceVideoProfile} from a raw device record. Pure — no I/O —\n * so it is unit-testable against captured records. `videoCapable` is true when\n * the cloud marks the device `permit: \"video\"` or lists any supported vendor.\n */\nexport function toVideoProfile(record: DeviceInfoRecord): DeviceVideoProfile {\n const info = record.deviceInfo ?? {};\n const supportedVendors = (info.defaultVendors ?? []).filter((v): v is VideoVendor =>\n KNOWN_VENDORS.includes(v),\n );\n const prop = parseProperty(record.property);\n const online = record.online === true || record.lwt === 1 || prop.lwt === 1;\n return {\n did: String(record.did ?? ''),\n model: String(record.model ?? info.model ?? ''),\n displayName: info.displayName ?? null,\n videoCapable: info.permit === 'video' || supportedVendors.length > 0,\n currentVendor: asVendor(record.vendor),\n supportedVendors,\n dynamicVendor: info.videoDynamicVendor === true,\n iotId: prop.iotId,\n online,\n };\n}\n","import type { DreameRegion } from '../auth/config.js';\nimport type { DreameSession } from '../cloud/types.js';\nimport {\n httpPostJsonBody,\n RequestContext,\n type BaseResponse,\n type FetchImpl,\n} from '../transport/http.js';\nimport {\n AuthCodeResponseSchema,\n DeviceInfoResponseSchema,\n FamilyIdResponseSchema,\n TencentIdentityResponseSchema,\n TencentP2PInfoResponseSchema,\n VideoAccessTokenResponseSchema,\n} from './schemas.js';\nimport { toVideoProfile } from './profile.js';\nimport type {\n DeviceVideoProfile,\n TencentDeviceIdentity,\n TencentP2PDescriptor,\n VideoAccessToken,\n} from './types.js';\n\n/** Path prefixes for the two video control-plane services on the region host. */\nconst P_THIRD_VIDEO = '/dreame-third-video';\nconst P_SMARTHOME = '/dreame-smarthome';\nconst P_USER_IOT = '/dreame-user-iot';\n\n/** `os` form value the app sends; the cloud accepts any of android/iOS/1/2. */\nconst DEFAULT_OS = 'android';\n\n/** Shared inputs for every video control-plane call. Mirrors `listDevices`. */\nexport interface VideoRequestInput {\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\nfunction resolveCtx(input: VideoRequestInput): RequestContext {\n return input.ctx ?? RequestContext.from({ ...input, host: input.apiHost });\n}\n\nfunction passthrough(input: VideoRequestInput): {\n accessToken: string;\n signal?: AbortSignal;\n timeoutMs?: number;\n} {\n return {\n accessToken: input.session.accessToken,\n ...(input.signal !== undefined ? { signal: input.signal } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n };\n}\n\n/**\n * Mint the short-lived third-video access token. Required as a body field by the\n * device-scoped video calls (getIdentity, getP2PInfo, …).\n */\nexport async function getVideoAccessToken(input: VideoRequestInput): Promise<VideoAccessToken> {\n const ctx = resolveCtx(input);\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_THIRD_VIDEO}/tx/user/accesstoken`,\n body: { os: DEFAULT_OS },\n context: 'video accesstoken',\n ...passthrough(input),\n });\n const parsed = VideoAccessTokenResponseSchema.parse(raw);\n const d = parsed.data.data;\n return {\n token: d.token,\n userId: d.userId !== undefined ? String(d.userId) : null,\n // Cloud reports expiry in epoch-seconds; expose epoch-ms like DreameSession.\n expiresAt: d.expireAt !== undefined ? d.expireAt * 1000 : 0,\n };\n}\n\n/**\n * Fetch the Aliyun authCode blob (`getAuthCodeV3`). This opaque hex string is the\n * material exchanged with Aliyun IoT to obtain a LinkVisual identity — the first\n * step of the Aliyun media path.\n */\nexport async function getAliyunAuthCode(input: VideoRequestInput): Promise<string> {\n const ctx = resolveCtx(input);\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_SMARTHOME}/aliIot/getAuthCodeV3`,\n body: { os: DEFAULT_OS },\n context: 'aliyun authCode',\n ...passthrough(input),\n });\n return AuthCodeResponseSchema.parse(raw).data;\n}\n\n/**\n * Fetch the account's video `familyId` (used to scope some vendor calls). This\n * endpoint requires the third-video access token in the body — pass one via\n * `videoToken`, otherwise a fresh one is minted with {@link getVideoAccessToken}.\n */\nexport async function getVideoFamilyId(\n input: VideoRequestInput & { videoToken?: string },\n): Promise<string> {\n const ctx = resolveCtx(input);\n const videoToken = input.videoToken ?? (await getVideoAccessToken({ ...input, ctx })).token;\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_THIRD_VIDEO}/tx/mgr/family/getFamilyId`,\n body: { accesstoken: videoToken, os: DEFAULT_OS },\n context: 'video familyId',\n ...passthrough(input),\n });\n return FamilyIdResponseSchema.parse(raw).data.data.familyId;\n}\n\n/**\n * The device's TENCENT IoT triple.\n *\n * Only meaningful while the device sits on the `tx` vendor: the cloud answers\n * `设备三元组不存在` (\"the triple does not exist\") otherwise, which this\n * surfaces as a thrown {@link DreameError} like any other refusal — a device on\n * `ali` has no Tencent identity, and that is a statement about the device, not\n * a failure of the call.\n */\nexport async function getTencentIdentity(\n input: VideoRequestInput & { did: string; videoToken?: string },\n): Promise<TencentDeviceIdentity> {\n const ctx = resolveCtx(input);\n const videoToken = input.videoToken ?? (await getVideoAccessToken({ ...input, ctx })).token;\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_THIRD_VIDEO}/tx/mgr/dev/getIdentity`,\n body: { accesstoken: videoToken, os: DEFAULT_OS, did: input.did },\n context: 'tencent identity',\n ...passthrough(input),\n });\n const d = TencentIdentityResponseSchema.parse(raw).data.data;\n return {\n productId: d.productId,\n deviceName: d.deviceName,\n deviceId: d.deviceId ?? null,\n secretId: d.secretId ?? null,\n secretKey: d.secretKey ?? null,\n };\n}\n\n/**\n * The xp2p session descriptor for a device on the `tx` vendor.\n *\n * ## This may WAKE THE CAMERA\n *\n * It is the Tencent equivalent of asking for a stream, so it is never called\n * as part of a status read.\n *\n * The result is opaque (see {@link TencentP2PDescriptor}) and is useless\n * without an xp2p implementation: measured on an X50 on 2026-09-16, the\n * sibling `tx/dev/getRtcInfo` — the TRTC path, which WOULD be portable — answers\n * 404 for this model. So the only media plane Tencent offers this device is the\n * proprietary UDP P2P one.\n */\nexport async function getTencentP2PInfo(\n input: VideoRequestInput & { did: string; videoToken?: string },\n): Promise<TencentP2PDescriptor> {\n const ctx = resolveCtx(input);\n const videoToken = input.videoToken ?? (await getVideoAccessToken({ ...input, ctx })).token;\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_THIRD_VIDEO}/tx/dev/getP2PInfo`,\n body: { accesstoken: videoToken, os: DEFAULT_OS, did: input.did },\n context: 'tencent p2p info',\n ...passthrough(input),\n });\n return { p2pInfo: TencentP2PInfoResponseSchema.parse(raw).data.data.p2pInfo };\n}\n\n/**\n * Resolve a device's video profile (vendor, supported vendors, Aliyun iotId,\n * capability) from its cloud record. `did` is the Dreame device id.\n */\nexport async function getDeviceVideoProfile(\n input: VideoRequestInput & { did: string },\n): Promise<DeviceVideoProfile> {\n const ctx = resolveCtx(input);\n const raw = await httpPostJsonBody<BaseResponse>({\n ctx,\n path: `${P_USER_IOT}/iotuserbind/device/info`,\n body: { did: input.did },\n context: 'device video info',\n ...passthrough(input),\n });\n return toVideoProfile(DeviceInfoResponseSchema.parse(raw).data);\n}\n","import type { DreameRegion } from '../../auth/config.js';\n\n/**\n * Aliyun IoT App SDK credentials for the Dreamehome app. These are app-global\n * constants baked into the shipped Dreamehome binary — NOT a user secret. The\n * appKey is public and the appSecret only signs the app's own API-Gateway\n * requests (identically for every install); they rotate only with a new app\n * release. Verified against the live x-ca signature (see signing.test.ts).\n * (SAST note: shipped public client credentials, not a leaked user secret.)\n */\nexport const ALIYUN_APP_KEY = '33974273';\nexport const ALIYUN_APP_SECRET = '65a62f03ae5eda97af962fecc2e3efa6';\n\n/** Aliyun IoT region id per Dreame region. Only `eu-central-1` is verified live. */\nexport const ALIYUN_REGION_ID: Record<DreameRegion, string> = {\n eu: 'eu-central-1',\n de: 'eu-central-1',\n us: 'us-east-1',\n cn: 'cn-shanghai',\n ru: 'eu-central-1',\n sg: 'ap-southeast-1',\n in: 'ap-southeast-1',\n tw: 'cn-shanghai',\n};\n\n/** IoT App identity/session host (createSessionByAuthCode, connect). */\nexport function livingAccountHost(regionId: string): string {\n return `living-account.${regionId}.aliyuncs.com`;\n}\n\n/** IoT API gateway host (vision/customer/*). */\nexport function apiIotHost(regionId: string): string {\n return `${regionId}.api-iot.aliyuncs.com`;\n}\n\nexport const VISION_STREAM_QUERY = '/vision/customer/stream/query';\n","import { randomUUID } from 'node:crypto';\nimport { z } from 'zod';\nimport { defaultFetch, type FetchImpl } from '../../transport/fetch.js';\nimport { DreameApiError } from '../../transport/errors.js';\nimport { ALIYUN_APP_KEY, ALIYUN_APP_SECRET, apiIotHost, livingAccountHost } from './constants.js';\nimport { signApiGatewayRequest } from './signing.js';\n\n/** Aliyun OpenAccount session from loginbyoauth. */\nexport interface OaSession {\n sid: string;\n refreshToken: string;\n openId: string;\n}\n\n/** Aliyun IoT session (the iotToken used by every LinkVisual call). */\nexport interface IotSession {\n iotToken: string;\n refreshToken: string;\n identityId: string;\n /** Epoch-ms at which iotToken expires. */\n expiresAt: number;\n}\n\nconst LoginByOauthSchema = z\n .object({\n data: z\n .object({\n data: z\n .object({\n loginSuccessResult: z\n .object({\n sid: z.string(),\n refreshToken: z.string().optional(),\n token: z.string().optional(),\n openAccount: z.object({ openId: z.string().optional() }).passthrough().optional(),\n })\n .passthrough(),\n })\n .passthrough(),\n })\n .passthrough(),\n })\n .passthrough();\n\nconst CreateSessionSchema = z\n .object({\n code: z.number().optional(),\n message: z.string().nullish(),\n data: z\n .object({\n iotToken: z.string(),\n refreshToken: z.string().optional(),\n identityId: z.string().optional(),\n iotTokenExpire: z.number().optional(),\n })\n .passthrough(),\n })\n .passthrough();\n\nasync function postJson(url: string, headers: Record<string, string>, body: string, fetchImpl: FetchImpl, ctx: string): Promise<unknown> {\n const res = await fetchImpl(url, { method: 'POST', headers, body });\n const text = await res.text();\n try {\n return JSON.parse(text);\n } catch {\n throw new DreameApiError(`${ctx} returned non-JSON (status ${res.status})`, res.status);\n }\n}\n\nexport interface LoginByOauthInput {\n authCode: string;\n regionId: string;\n deviceId?: string;\n utdid?: string;\n fetchImpl?: FetchImpl;\n}\n\n/** Exchange the Dreame authCode for an Aliyun OpenAccount session (step 2). */\nexport async function loginByOauth(input: LoginByOauthInput): Promise<OaSession> {\n const host = livingAccountHost(input.regionId);\n const riskControlInfo = {\n createIfNotExist: 'true', sdkVersion: '3.4.0.27', openaccountVersion: '3.4.0.27',\n platformName: 'iOS', netType: 'wifi', brand: 'Apple', USE_H5_NC: 'true',\n deviceId: input.deviceId ?? randomUUID(), locale: 'en-US', platformVersion: '26.5.2',\n platform: 'iPhone', appKey: ALIYUN_APP_KEY, USE_OA_PWD_ENCRYPT: 'true',\n model: 'iPhone16,2', utdid: input.utdid ?? randomUUID().replace(/-/g, '').slice(0, 20),\n appVersion: '1895',\n };\n const value = JSON.stringify({\n riskControlInfo, oauthPlateform: 23, oauthAppKey: ALIYUN_APP_KEY, authCode: input.authCode,\n });\n const signed = signApiGatewayRequest({\n method: 'POST', host, path: '/api/prd/loginbyoauth.json',\n appKey: ALIYUN_APP_KEY, appSecret: ALIYUN_APP_SECRET,\n form: { loginByOauthRequest: value },\n });\n const json = await postJson(`https://${host}/api/prd/loginbyoauth.json`, signed.headers, signed.body,\n input.fetchImpl ?? defaultFetch, 'loginbyoauth');\n const r = LoginByOauthSchema.parse(json).data.data.loginSuccessResult;\n return {\n sid: r.sid,\n refreshToken: r.refreshToken ?? '',\n openId: r.openAccount?.openId ?? '',\n };\n}\n\nexport interface CreateIotSessionInput {\n /** The OpenAccount `sid` from {@link loginByOauth}. */\n sid: string;\n regionId: string;\n fetchImpl?: FetchImpl;\n}\n\n/** Exchange the OA `sid` for an Aliyun IoT session / iotToken (step 3). */\nexport async function createIotSession(input: CreateIotSessionInput): Promise<IotSession> {\n const host = apiIotHost(input.regionId);\n const path = '/account/createSessionByAuthCode';\n const payload = {\n id: randomUUID().toUpperCase(),\n params: { request: { appKey: ALIYUN_APP_KEY, authCode: input.sid, accountType: 'OA_SESSION' } },\n request: { apiVer: '1.0.4', language: 'en-US', appKey: ALIYUN_APP_KEY },\n version: '1.0.0',\n };\n const signed = signApiGatewayRequest({\n method: 'POST', host, path, appKey: ALIYUN_APP_KEY, appSecret: ALIYUN_APP_SECRET,\n json: payload, extraCaHeaders: { 'X-Ca-Stage': 'RELEASE' },\n });\n const json = await postJson(`https://${host}${path}`, signed.headers, signed.body,\n input.fetchImpl ?? defaultFetch, 'createSessionByAuthCode');\n const parsed = CreateSessionSchema.parse(json);\n if (parsed.code !== 200 && parsed.code !== 0) {\n throw new DreameApiError(`createSession rejected: code=${parsed.code} msg=${parsed.message ?? '?'}`, 200, parsed);\n }\n const d = parsed.data;\n return {\n iotToken: d.iotToken,\n refreshToken: d.refreshToken ?? '',\n identityId: d.identityId ?? '',\n expiresAt: Date.now() + (d.iotTokenExpire ?? 72000) * 1000,\n };\n}\n","import { createHash, createHmac } from 'node:crypto';\n\n/**\n * Aliyun API Gateway (\"x-ca\") request signing, HmacSHA1. Used by the LinkVisual\n * video endpoints the Dreamehome app talks to (living-account + api-iot hosts).\n *\n * The string to sign is:\n * METHOD\\n Accept\\n Content-MD5\\n Content-Type\\n Date\\n <signed-headers-block> <resource>\n * where the signed-headers block is `Name:Value\\n` for each header named in\n * `X-Ca-Signature-Headers`, sorted case-insensitively, and `resource` is the\n * path plus, for form bodies, the `?`-joined sorted form params.\n *\n * Verified byte-for-byte against a captured connect.json request (see tests).\n */\n\nexport interface SignedRequest {\n headers: Record<string, string>;\n body: string;\n}\n\nexport interface SignInput {\n method: 'POST' | 'GET';\n host: string;\n path: string;\n appKey: string;\n appSecret: string;\n /** JSON payload → sent as the raw body with a Content-MD5. */\n json?: unknown;\n /** Form fields → sent as application/x-www-form-urlencoded and folded into the resource. */\n form?: Record<string, string>;\n /** Extra x-ca headers to sign (e.g. `{ 'X-Ca-Stage': 'RELEASE' }`). */\n extraCaHeaders?: Record<string, string>;\n /** Overrides for deterministic tests. */\n nonce?: string;\n timestamp?: number;\n date?: string;\n}\n\nconst CT_JSON = 'application/json; charset=UTF-8';\nconst CT_FORM = 'application/x-www-form-urlencoded; charset=UTF-8';\n\nfunction md5Base64(s: string): string {\n return createHash('md5').update(s, 'utf8').digest('base64');\n}\n\n/** Build the x-ca headers + body for a request. */\nexport function signApiGatewayRequest(input: SignInput): SignedRequest {\n const accept = CT_JSON;\n const contentType = input.json !== undefined ? CT_JSON : CT_FORM;\n const date = input.date ?? new Date().toUTCString();\n\n let body: string;\n let contentMd5 = '';\n let formParams: Record<string, string> | undefined;\n if (input.json !== undefined) {\n body = JSON.stringify(input.json);\n contentMd5 = md5Base64(body);\n } else {\n const form = input.form ?? {};\n body = Object.entries(form)\n .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)\n .join('&');\n formParams = form;\n }\n\n const caHeaders: Record<string, string> = {\n 'X-Ca-Key': input.appKey,\n 'X-Ca-Nonce': input.nonce ?? crypto.randomUUID().toUpperCase(),\n 'X-Ca-Timestamp': String(input.timestamp ?? Date.now()),\n 'X-Ca-Version': '1',\n ...(input.extraCaHeaders ?? {}),\n };\n\n const signedHeaderNames = Object.keys(caHeaders).sort((a, b) =>\n a.toLowerCase() < b.toLowerCase() ? -1 : a.toLowerCase() > b.toLowerCase() ? 1 : 0,\n );\n const headerBlock = signedHeaderNames.map((k) => `${k}:${caHeaders[k]}\\n`).join('');\n\n let resource = input.path;\n if (formParams && Object.keys(formParams).length > 0) {\n const sorted = Object.keys(formParams).sort();\n resource += '?' + sorted.map((k) => `${k}=${formParams[k]}`).join('&');\n }\n\n const stringToSign = `${input.method}\\n${accept}\\n${contentMd5}\\n${contentType}\\n${date}\\n${headerBlock}${resource}`;\n const signature = createHmac('sha1', input.appSecret).update(stringToSign, 'utf8').digest('base64');\n\n const headers: Record<string, string> = {\n 'Content-Type': contentType,\n Accept: accept,\n Date: date,\n 'X-Ca-Signature': signature,\n 'X-Ca-Signature-Method': 'HmacSHA1',\n 'X-Ca-Signature-Headers': signedHeaderNames.join(','),\n ...caHeaders,\n };\n if (contentMd5) {\n headers['Content-MD5'] = contentMd5;\n }\n return { headers, body };\n}\n","import { z } from 'zod';\nimport { defaultFetch, type FetchImpl } from '../../transport/fetch.js';\nimport { DreameApiError, DreameDeviceOfflineError } from '../../transport/errors.js';\n\n/** LinkVisual code meaning the device's video agent isn't connected yet (waking). */\nexport const VISION_DEVICE_OFFLINE = 9201;\nimport {\n ALIYUN_APP_KEY,\n ALIYUN_APP_SECRET,\n apiIotHost,\n VISION_STREAM_QUERY,\n} from './constants.js';\nimport { signApiGatewayRequest } from './signing.js';\n\n/** LinkVisual live-stream descriptor returned by /vision/customer/stream/query. */\nexport interface StreamInfo {\n /** Connection strategies the cloud offers, e.g. [\"Direct\",\"Relay\",\"NAT\"]. */\n typeList: string[];\n /** RTMP relay URL. Plain (playable directly) when requested with relayEncrypted:false. */\n relayUrl: string | null;\n /** AES key/iv for the relay payload — present (non-empty) only when relayEncrypted:true. */\n relayDecryptKey: { iv: string; key: string } | null;\n /** P2P (Direct/NAT) parameters: STUN/TURN + the WebSocket signaling URL. */\n p2pInfo: {\n stunUrl?: string;\n alterStunUrl?: string;\n signalUrl?: string;\n portDetectStunUrlList?: string[];\n } | null;\n supportVisionPtz?: boolean;\n}\n\nconst StreamQueryResponseSchema = z\n .object({\n code: z.number().optional(),\n message: z.string().nullish(),\n data: z\n .object({\n typeList: z.array(z.string()).optional(),\n relayUrl: z.string().optional(),\n relayDecryptKey: z\n .object({ iv: z.string().optional(), key: z.string().optional() })\n .passthrough()\n .optional(),\n p2pInfo: z\n .object({\n stunUrl: z.string().optional(),\n alterStunUrl: z.string().optional(),\n signalUrl: z.string().optional(),\n portDetectStunUrlList: z.array(z.string()).optional(),\n })\n .passthrough()\n .optional(),\n supportVisionPtz: z.boolean().optional(),\n })\n .passthrough()\n .optional(),\n })\n .passthrough();\n\nexport interface StreamQueryInput {\n /** Aliyun IoT region id, e.g. `eu-central-1`. */\n regionId: string;\n /** Short-lived IoT identity token (from createSessionByAuthCode). */\n iotToken: string;\n /** LinkVisual device id. */\n iotId: string;\n /**\n * When false (default) the relay RTMP carries a PLAIN (unencrypted) FLV, so it\n * can be handed straight to ffmpeg. When true the payload is AES-encrypted and\n * `relayDecryptKey` is returned.\n */\n relayEncrypted?: boolean;\n /** 0 = HD main stream (default). */\n streamType?: number;\n appKey?: string;\n appSecret?: string;\n fetchImpl?: FetchImpl;\n signal?: AbortSignal;\n}\n\n/**\n * Query the LinkVisual live stream for a device. Returns the relay RTMP URL and\n * P2P parameters. Note: each returned relay token is single-use — request a\n * fresh one per consumer.\n */\nexport async function streamQuery(input: StreamQueryInput): Promise<StreamInfo> {\n const appKey = input.appKey ?? ALIYUN_APP_KEY;\n const appSecret = input.appSecret ?? ALIYUN_APP_SECRET;\n const host = apiIotHost(input.regionId);\n const payload = {\n id: crypto.randomUUID().toUpperCase(),\n params: {\n streamType: input.streamType ?? 0,\n needDomainName: true,\n relayEncryptType: 0,\n clientType: 'iOS',\n iotId: input.iotId,\n enablePortPredict: true,\n enableWebSocket: true,\n forceIFrame: true,\n cacheDuration: 3000,\n relayEncrypted: input.relayEncrypted ?? false,\n },\n request: { language: 'en-US', appKey, iotToken: input.iotToken, apiVer: '2.2.0' },\n version: '1.0.0',\n };\n const signed = signApiGatewayRequest({\n method: 'POST',\n host,\n path: VISION_STREAM_QUERY,\n appKey,\n appSecret,\n json: payload,\n extraCaHeaders: { 'X-Ca-Stage': 'RELEASE' },\n });\n\n const fetchImpl = input.fetchImpl ?? defaultFetch;\n const res = await fetchImpl(`https://${host}${VISION_STREAM_QUERY}`, {\n method: 'POST',\n headers: signed.headers,\n body: signed.body,\n ...(input.signal ? { signal: input.signal } : {}),\n });\n const text = await res.text();\n let json: unknown;\n try {\n json = JSON.parse(text);\n } catch {\n throw new DreameApiError(`stream/query returned non-JSON (status ${res.status})`, res.status);\n }\n const parsed = StreamQueryResponseSchema.parse(json);\n if (parsed.code === VISION_DEVICE_OFFLINE) {\n // The device's video agent is not connected yet — querying wakes it via the\n // LinkVisual cloud, so this is retryable (see DreameVideoSession.getStreamInfo).\n throw new DreameDeviceOfflineError(\n `stream/query: device video agent offline (code ${VISION_DEVICE_OFFLINE}) — retry to wake`,\n res.status,\n parsed,\n );\n }\n if (parsed.code !== 200 && parsed.code !== 0) {\n throw new DreameApiError(\n `stream/query rejected: code=${parsed.code} msg=${parsed.message ?? '?'}`,\n res.status,\n parsed,\n );\n }\n const d = parsed.data ?? {};\n const dk = d.relayDecryptKey;\n const hasKey = dk && dk.iv && dk.key;\n const p = d.p2pInfo;\n const p2pInfo: StreamInfo['p2pInfo'] = p\n ? {\n ...(p.stunUrl !== undefined ? { stunUrl: p.stunUrl } : {}),\n ...(p.alterStunUrl !== undefined ? { alterStunUrl: p.alterStunUrl } : {}),\n ...(p.signalUrl !== undefined ? { signalUrl: p.signalUrl } : {}),\n ...(p.portDetectStunUrlList !== undefined\n ? { portDetectStunUrlList: p.portDetectStunUrlList }\n : {}),\n }\n : null;\n return {\n typeList: d.typeList ?? [],\n relayUrl: d.relayUrl ?? null,\n relayDecryptKey: hasKey ? { iv: dk.iv as string, key: dk.key as string } : null,\n p2pInfo,\n ...(d.supportVisionPtz !== undefined ? { supportVisionPtz: d.supportVisionPtz } : {}),\n };\n}\n","import type { DreameRegion } from '../../auth/config.js';\nimport type { DreameSession } from '../../cloud/types.js';\nimport type { FetchImpl } from '../../transport/http.js';\nimport { getAliyunAuthCode } from '../client.js';\nimport { ALIYUN_REGION_ID } from './constants.js';\nimport { DreameDeviceOfflineError } from '../../transport/errors.js';\nimport { createIotSession, loginByOauth, type IotSession } from './identity.js';\nimport { streamQuery, type StreamInfo } from './vision.js';\n\n/** Default budget to wake a cold device via retried stream/query. */\nconst DEFAULT_WAKE_TIMEOUT_MS = 30_000;\nconst DEFAULT_WAKE_POLL_MS = 2_000;\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n/** Refresh the IoT session this many ms before it actually expires. */\nconst REFRESH_LEEWAY_MS = 60_000;\n\nexport interface DreameVideoSessionInput {\n /** An authenticated Dreame account session (from `Nodreame`). */\n session: DreameSession;\n region: DreameRegion;\n fetchImpl?: FetchImpl;\n}\n\n/**\n * High-level video session for a Dreame account. Establishes and caches the\n * Aliyun IoT identity (iotToken), and mints fresh LinkVisual RTMP stream URLs\n * on demand. Each returned relay URL is single-use — call {@link getStreamInfo}\n * (or {@link getRtmpUrl}) once per consumer.\n *\n * The plain (unencrypted) relay URL is a standard RTMP stream: hand it straight\n * to ffmpeg / scrypted / camstack. No local muxing or RTSP server required here.\n */\nexport class DreameVideoSession {\n readonly #session: DreameSession;\n readonly #region: DreameRegion;\n readonly #regionId: string;\n readonly #fetchImpl: FetchImpl | undefined;\n #iot: IotSession | null = null;\n #inFlight: Promise<IotSession> | null = null;\n\n constructor(input: DreameVideoSessionInput) {\n this.#session = input.session;\n this.#region = input.region;\n this.#regionId = ALIYUN_REGION_ID[input.region];\n this.#fetchImpl = input.fetchImpl;\n }\n\n /** The resolved Aliyun IoT region id (e.g. `eu-central-1`). */\n get regionId(): string {\n return this.#regionId;\n }\n\n /** Mint (or reuse) a valid Aliyun IoT session, refreshing before expiry. */\n async ensureIotSession(): Promise<IotSession> {\n const cur = this.#iot;\n if (cur && Date.now() < cur.expiresAt - REFRESH_LEEWAY_MS) {\n return cur;\n }\n if (this.#inFlight) {\n return this.#inFlight;\n }\n this.#inFlight = this.#mint();\n try {\n this.#iot = await this.#inFlight;\n return this.#iot;\n } finally {\n this.#inFlight = null;\n }\n }\n\n async #mint(): Promise<IotSession> {\n const base = {\n session: this.#session,\n region: this.#region,\n ...(this.#fetchImpl ? { fetchImpl: this.#fetchImpl } : {}),\n };\n const authCode = await getAliyunAuthCode(base);\n const oa = await loginByOauth({\n authCode,\n regionId: this.#regionId,\n ...(this.#fetchImpl ? { fetchImpl: this.#fetchImpl } : {}),\n });\n return createIotSession({\n sid: oa.sid,\n regionId: this.#regionId,\n ...(this.#fetchImpl ? { fetchImpl: this.#fetchImpl } : {}),\n });\n }\n\n /**\n * Mint a fresh LinkVisual stream descriptor for a device. `encrypted:false`\n * (default) yields a plain RTMP relay URL directly playable by ffmpeg.\n *\n * A camera that has been idle reports \"device offline\" until the query wakes\n * its video agent via the LinkVisual cloud. This transparently retries on that\n * (up to `wakeTimeoutMs`, default 30s) so a single call returns a live stream;\n * pass `wakeTimeoutMs: 0` to fail fast instead.\n */\n async getStreamInfo(\n iotId: string,\n opts?: { encrypted?: boolean; wakeTimeoutMs?: number; pollMs?: number },\n ): Promise<StreamInfo> {\n const deadline = Date.now() + (opts?.wakeTimeoutMs ?? DEFAULT_WAKE_TIMEOUT_MS);\n const pollMs = opts?.pollMs ?? DEFAULT_WAKE_POLL_MS;\n for (;;) {\n const iot = await this.ensureIotSession();\n try {\n return await streamQuery({\n regionId: this.#regionId,\n iotToken: iot.iotToken,\n iotId,\n relayEncrypted: opts?.encrypted ?? false,\n ...(this.#fetchImpl ? { fetchImpl: this.#fetchImpl } : {}),\n });\n } catch (err) {\n if (err instanceof DreameDeviceOfflineError && Date.now() + pollMs < deadline) {\n await sleep(pollMs);\n continue;\n }\n throw err;\n }\n }\n }\n\n /**\n * Convenience: mint a fresh plain RTMP relay URL for a device, or `null` when\n * the device is not currently streaming (no relay available).\n */\n async getRtmpUrl(iotId: string): Promise<string | null> {\n const info = await this.getStreamInfo(iotId, { encrypted: false });\n return info.relayUrl;\n }\n}\n","import type { DeviceVideoProfile } from './types.js';\n\n/**\n * Why this device cannot open a camera channel — or `null` when it can.\n *\n * The camera path needs one thing from the cloud: the `iotId`, which is the\n * CHANNEL id `startMonitor` addresses (for both vendors — see\n * `startMonitorParams`). When it is absent the call cannot proceed, and for a\n * long time that was reported as a single sentence covering four different\n * situations:\n *\n * \"device has no LinkVisual iotId; not a camera device or not provisioned\"\n *\n * A CamStack operator met it on a robot whose camera they had just enabled in\n * the Dreame app (2026-09-16). Working out which of the four they were in\n * meant reading this library's source — the sentence names a MISSING FIELD,\n * not a cause, and the profile sitting right next to it already knows whether\n * the device is a camera at all, whether it is online, and which vendors it is\n * on and supports.\n *\n * The four states want four different actions:\n *\n * - not a camera → wrong device; nothing done here will help\n * - offline → wait; the binding is published BY the device\n * - camera, online, no channel → open it once in the vendor app, which is\n * what creates the binding\n * - channel present → not this function's business\n *\n * Pure, so it is testable against captured profiles without a cloud.\n */\nexport function explainNoCameraChannel(profile: DeviceVideoProfile): string | null {\n if (profile.iotId) {\n return null;\n }\n const who = `${profile.model || 'unknown model'} (${profile.did || 'no did'})`;\n if (!profile.videoCapable) {\n return (\n `${who} is not a camera: the cloud lists no video permit and no supported ` +\n `video vendor for it, so there is no channel to open.`\n );\n }\n if (!profile.online) {\n return (\n `${who} is a camera but is OFFLINE. The video binding is published by the ` +\n `device itself, so there is nothing to read until it connects — this is ` +\n `not a provisioning fault.`\n );\n }\n const supports = profile.supportedVendors.length > 0 ? profile.supportedVendors.join(', ') : 'none';\n // ON A VENDOR WE DO NOT SPEAK.\n //\n // `iotId` is read from the ALIYUN bind endpoint (`iotuserbind/device/info`),\n // so a device provisioned on Tencent has none BY CONSTRUCTION — it is not\n // missing, it does not live there. Measured on a live X50 on 2026-09-16\n // while its owner was watching the stream in the Dreame app: `vendor: 'tx'`,\n // `supports: ['tx','ali']`, `iotId: null`.\n //\n // Telling that owner to \"open it once in the app to create the binding\" is\n // advice that cannot work, and it was given — while the app was streaming.\n // A diagnostic that sends someone to do a useless thing is worse than one\n // that says nothing, so this case says what is actually true.\n if (profile.currentVendor === 'tx') {\n return (\n `${who} is a camera and online, but is provisioned on the TENCENT video ` +\n `vendor (vendor: tx; supports: ${supports}), and this library implements ` +\n `only the Aliyun LinkVisual path — the channel id it needs lives on ` +\n `Aliyun and a tx device has none. Re-pairing or re-opening the camera in ` +\n `the app will not change this.`\n );\n }\n // A camera, online, on no vendor at all: it has never bound to the video\n // service. Streaming it once in the vendor app is what creates the binding —\n // and here that advice is right.\n const on = profile.currentVendor ?? 'none';\n return (\n `${who} is a camera and online, but has no video channel yet ` +\n `(vendor: ${on}; supports: ${supports}). The device has not bound to the ` +\n `video service — open its camera once in the Dreame app to create the ` +\n `binding, then retry.`\n );\n}\n","/**\n * MIoT surface of the Dreame camera service (SIID 10001), reversed byte-exact\n * from the r2538z (X50) React-Native plugin `Monitor` model. Every camera\n * control the app issues is a MIoT `action` on this service; the values below\n * are the verbatim aiid/piid maps from the bundle.\n */\n\n/** The camera/monitor service id. Same across the X40/X50 vacuum class. */\nexport const MONITOR_SIID = 10001;\n\n/** Action instance ids on {@link MONITOR_SIID}. */\nexport const MONITOR_AIID = {\n /** Stream lifecycle + keep-alive (monitor start/end, keep_alive). */\n CAMERA_OPERATE: 1,\n /** Two-way intercom start/end. */\n VOICE_OPERATE: 2,\n /** Generic property-scoped operations. */\n PROPERTY_OPERATE: 3,\n /** Privacy access-code gate (launch/verify/set/reset). */\n ACCESS_CODE_OPERATE: 4,\n /** Video-vendor SDK init / switch. */\n VIDEO_VENDOR: 7,\n} as const;\n\n/** Property instance ids on {@link MONITOR_SIID}. */\nexport const MONITOR_PIID = {\n MONITOR_STATUS: 1,\n MONITOR_AUDIO_STATUS: 2,\n MONITOR_RECORD_STATUS: 4,\n TAKE_PHOTO: 5,\n KEEP_ALIVE: 6,\n MONITOR_FAULTS: 7,\n FILL_LIGHT_SET: 9,\n CAMERA_LIGHT_SWITCH: 10,\n VIDEO_VENDOR_STATUS: 11,\n GET_PROPERTY: 99,\n PERSON_DATA: 110,\n VIDEO_OBSTACLE_SWITCH: 111,\n VIDEO_OBSTACLE_DATA: 112,\n MONITOR_TASK_STATUS: 103,\n UPLOAD_STATUS: 1003,\n GET_ACCESS_CODE: 1100,\n SET_ACCESS_CODE: 1101,\n VERIFY_ACCESS_CODE: 1102,\n MONITOR_REST_MODE: 1103,\n SET_ACCESS_HOT_STATUS: 1104,\n MONITOR_SPACE: 2003,\n} as const;\n\n/**\n * The `token` field the `startMonitor` action expects, keyed by vendor. Aliyun\n * LinkVisual uses the literal `\"alify\"`; Tencent uses `\"tx\"`. (Agora passes an\n * RTC access token instead and is not modelled here.)\n */\nexport const MONITOR_VENDOR_TOKEN: Record<'ali' | 'tx', string> = {\n ali: 'alify',\n tx: 'tx',\n};\n\n/** Fill-light: values 40..100 are manual %, below 40 (or 101) select auto. */\nexport const FILL_LIGHT_MIN = 40;\n\n/**\n * The vacuum work service (SIID 4). The camera has no gimbal — \"PTZ\" and the\n * camera-adjacent controls (remote drive, go-to-point, person-follow) drive the\n * robot itself through this service.\n */\nexport const VACUUM_SIID = 4;\nexport const VACUUM_MOVE = {\n /** Remote-drive state property (set_properties), sent ~1 Hz while held. */\n REMOTE_STATE_PIID: 15,\n /** Work-mode action instance (go-to-point, cruise, person-follow start). */\n WORK_AIID: 1,\n /** Work-mode stop action instance (person-follow stop). */\n STOP_AIID: 2,\n /** piid carrying the work-mode selector value in the WORK action. */\n MODE_PIID: 1,\n /** piid carrying the point-info JSON in the WORK action. */\n POINT_PIID: 10,\n /** Work-mode selector values. */\n MODE_SPOT: 23, // go to point (MonitorSpotMode)\n MODE_CRUISE: 22, // cruise / patrol / find-pet\n MODE_PERSON_FOLLOW: 26,\n MODE_SPOT_CLEAN: 20, // spot-clean the current position (SpotClean)\n /** Auto-switch setter property (fill-light auto/full toggle lives here). */\n AUTO_SWITCH_PIID: 50,\n} as const;\n\n/** Return-to-dock / charge action (battery service). */\nexport const VACUUM_CHARGE = { siid: 3, aiid: 1 } as const;\n\n/**\n * Voice/audio service (SIID 7). aiid 1 = position/locate (robot beeps), aiid 2 =\n * playSound (play a sound clip by id, carried in piid 4).\n */\nexport const VOICE_SIID = 7;\nexport const VOICE_LOCATE_AIID = 1;\nexport const VOICE_PLAY_SOUND_AIID = 2;\nexport const VOICE_SOUND_PIID = 4;\n/** Locate action — the robot beeps so you can find it. */\nexport const VACUUM_LOCATE = { siid: VOICE_SIID, aiid: VOICE_LOCATE_AIID } as const;\n\n/** Pet-teasing sound clips playable via {@link VOICE_PLAY_SOUND_AIID}. */\nexport const PET_SOUNDS = {\n meow: 684,\n bark: 685,\n footsteps: 686,\n purring: 687,\n tickTock: 688,\n} as const;\nexport type PetSound = keyof typeof PET_SOUNDS;\n\n/** Common whole-robot actions (usable as camera presets). */\nexport const VACUUM_ACTIONS = {\n startClean: { siid: 2, aiid: 1 },\n pauseClean: { siid: 2, aiid: 2 },\n stopClean: { siid: 4, aiid: 2 },\n dockWash: { siid: 4, aiid: 4 },\n autoEmpty: { siid: 15, aiid: 1 },\n} as const;\nexport type VacuumActionKey = keyof typeof VACUUM_ACTIONS;\n","/**\n * Pure protocol helpers for the camera monitor service — no I/O, so every wire\n * shape here is unit-testable in isolation. The device wraps each action value\n * as `JSON.stringify({ ...params, session })`, where `session` is a per-session\n * correlation token; these builders reproduce that envelope exactly.\n */\nimport { createHash } from 'node:crypto';\nimport { MONITOR_PIID, MONITOR_VENDOR_TOKEN } from './constants.js';\n\n/** One `in[]` entry of a MIoT action: a piid plus a JSON-encoded value. */\nexport interface MonitorActionInput {\n readonly piid: number;\n readonly value: string;\n}\n\n/** The parsed shape of a device action reply (`callAction` resolves to this). */\nexport interface MonitorActionResult {\n readonly code?: number;\n readonly out?: ReadonlyArray<{ piid?: number; value?: unknown }>;\n readonly result?: { out?: ReadonlyArray<{ piid?: number; value?: unknown }> };\n}\n\n/**\n * Per-session correlation token, exactly as the app's `Monitor.updateSession()`:\n * `MD5(accountId + \"_\" + epochMillis)`. Pass an explicit `now` for deterministic\n * tests. `accountId` is the Dreame account uid.\n */\nexport function makeMonitorSession(accountId: string, now: number = Date.now()): string {\n return createHash('md5').update(`${accountId}_${now}`).digest('hex');\n}\n\n/** SHA-256 hex of a privacy access code, as `Monitor.verifyAccessCode` sends it. */\nexport function hashAccessCode(code: string): string {\n return createHash('sha256').update(code).digest('hex');\n}\n\n/**\n * Build a single action `in[]` entry: merge `params` with the `session` (session\n * wins on key clash, matching the plugin) and JSON-encode. `params` of `null`\n * yields just `{ session }`.\n */\nexport function buildActionInput(\n piid: number,\n params: Record<string, unknown> | null,\n session: string,\n): MonitorActionInput {\n const value = JSON.stringify({ ...(params ?? {}), session });\n return { piid, value };\n}\n\n/** `startMonitor` value params for a given vendor + channel (iotId). */\nexport function startMonitorParams(\n iotId: string,\n vendor: 'ali' | 'tx',\n area: string,\n): Record<string, unknown> {\n return {\n token: MONITOR_VENDOR_TOKEN[vendor],\n channelId: iotId,\n area,\n operType: 'monitor',\n operation: 'start',\n };\n}\n\n/** `stopMonitor` value params. */\nexport function stopMonitorParams(): Record<string, unknown> {\n return { operType: 'monitor', operation: 'end' };\n}\n\n/** `keep_alive` value params. `videoStatus` is `preparing` until the relay is up, then `opened`. */\nexport function keepAliveParams(videoStatus: 'preparing' | 'opened'): Record<string, unknown> {\n return { operType: 'keep_alive', videoStatus };\n}\n\n/** `getAccessCodeLaunch` params — primes the camera pipeline and reports PIN state. */\nexport function accessCodeLaunchParams(): Record<string, unknown> {\n return { open: true };\n}\n\n/** `verifyAccessCode` params from a plaintext code. `lazymode` mirrors the app default. */\nexport function verifyAccessCodeParams(code: string, lazymode = 0): Record<string, unknown> {\n return { oldcode: hashAccessCode(code), lazymode };\n}\n\n/** `intercom` start params. `phone:1` marks a video-call intercom. */\nexport function intercomStartParams(opts?: {\n needRecordSound?: boolean;\n videoCall?: boolean;\n}): Record<string, unknown> {\n return {\n operType: 'intercom',\n operation: 'start',\n ...(opts?.needRecordSound !== undefined ? { need_record_sound: opts.needRecordSound } : {}),\n ...(opts?.videoCall ? { phone: 1 } : {}),\n };\n}\n\n/** `intercom` stop params. */\nexport function intercomStopParams(): Record<string, unknown> {\n return { operType: 'intercom', operation: 'end' };\n}\n\n/** Fill-light brightness params. Value is sent as a STRING, per the plugin. */\nexport function fillLightParams(value: number): Record<string, unknown> {\n return { value: String(value) };\n}\n\n/** Device-side snapshot (`takephoto`) params. */\nexport function takePhotoParams(): Record<string, unknown> {\n return { operType: 'takephoto', operation: 'start' };\n}\n\n/**\n * Remote-drive value (siid 4 / piid 15), sent ~1 Hz while a direction is held.\n * `spdv` = forward speed (200 fwd, 0 stop), `spdw` = turn (45 left, -45 right,\n * 180 turn-around, 0 straight). Pass an explicit `now` for deterministic tests.\n */\nexport function remoteDriveValue(\n spdv: number,\n spdw: number,\n now: number = Date.now(),\n): string {\n return JSON.stringify({\n spdv,\n spdw,\n audio: 'false',\n random: Math.floor(Math.random() * 1000),\n timestamp: now,\n });\n}\n\n/** Named remote-drive directions, mapping to (spdv, spdw). */\nexport const DRIVE_DIRECTIONS = {\n forward: [200, 0],\n left: [0, 45],\n right: [0, -45],\n turnAround: [0, 180],\n stop: [0, 0],\n} as const;\nexport type DriveDirection = keyof typeof DRIVE_DIRECTIONS;\n\n/** The device business `code` from an action reply (0 = accepted); `null` if absent. */\nexport function actionCode(res: unknown): number | null {\n const r = res as MonitorActionResult | null | undefined;\n return typeof r?.code === 'number' ? r.code : null;\n}\n\n/** First `out[0].value` from an action reply (handles both `out` and `result.out`). */\nexport function firstOutValue(res: unknown): unknown {\n const r = res as MonitorActionResult | null | undefined;\n const out = r?.out ?? r?.result?.out;\n return Array.isArray(out) && out.length > 0 ? out[0]?.value : undefined;\n}\n","import type { VideoVendor } from '../types.js';\n\n/**\n * Switching a device between its video backends.\n *\n * ## Why this exists\n *\n * A Dreame robot can be provisioned on Aliyun LinkVisual (`ali`) or Tencent\n * IoT-Video (`tx`), and a dual-vendor device (`videoDynamicVendor: true`,\n * `defaultVendors: ['tx','ali']`) moves between them. Which one it sits on is\n * not cosmetic: the two have entirely different media planes. Measured on an\n * X50 on 2026-09-16, while it sat on `tx`:\n *\n * tx/dev/getRtcInfo → 404 (the TRTC path does not exist for it)\n * tx/dev/getP2PInfo → an opaque 35-char xp2p handle\n *\n * so a `tx` device can only be streamed through Tencent's proprietary UDP P2P\n * stack, while the SAME device on `ali` streams through a plain RTMP relay this\n * library already implements end to end.\n *\n * The app switches with the very action this module builds — its own internal\n * debug page calls `changeVideoVendor('tx'|'ali')` — and then polls the status\n * until the device reports the new SDK initialised.\n *\n * ## The action\n *\n * `MONITOR_AIID.VIDEO_VENDOR` with `{vendor}` SWITCHES; the same action with\n * `null` merely initialises whatever vendor the device is already on, which is\n * what `#tryPrime` has always done. One value apart, two different operations.\n */\n\n/** Params for the vendor-switch action. `null` params init the current vendor\n * instead, which is a different operation — see the module docblock. */\nexport function videoVendorSwitchParams(vendor: VideoVendor): Record<string, unknown> {\n return { vendor };\n}\n\n/** What the device reports about its video SDK. */\nexport interface VideoVendorStatus {\n readonly vendor: VideoVendor | null;\n /** `1` once the SDK for {@link vendor} is up. A switch is not done until this. */\n readonly initStatus: number | null;\n}\n\n/**\n * Parse `videoVendorStatus` (property 11) — a JSON STRING inside the property\n * value, as every status on this service is.\n *\n * Never throws: an unreadable status is \"we do not know\", and a caller that\n * polls must not die on one malformed tick.\n */\nexport function parseVideoVendorStatus(raw: unknown): VideoVendorStatus {\n const text =\n typeof raw === 'string' ? raw : typeof raw === 'object' && raw !== null ? null : null;\n const source: unknown = text === null ? raw : safeJson(text);\n if (typeof source !== 'object' || source === null) {\n return { vendor: null, initStatus: null };\n }\n const v: unknown = Reflect.get(source, 'vendor');\n const s: unknown = Reflect.get(source, 'initStatus');\n return {\n vendor: v === 'ali' || v === 'tx' ? v : null,\n initStatus: typeof s === 'number' ? s : null,\n };\n}\n\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\n/** True when the device reports the requested vendor, initialised. */\nexport function vendorSwitchSettled(status: VideoVendorStatus, wanted: VideoVendor): boolean {\n return status.vendor === wanted && status.initStatus === 1;\n}\n","/**\n * Autonomous cold-start of a Dreame vacuum camera stream.\n *\n * Reproduces the exact sequence the r2538z (X50) app runs when you open the\n * camera — verified end-to-end against a deep-cold device:\n *\n * 1. getAccessCodeLaunch (aiid 4 / piid 1100 {open:true}) — primes the pipeline\n * 2. initCameraSdk (aiid 7 / piid 11 {}) — boots the on-device video agent\n * 3. startMonitor (aiid 1 / piid 1 …start) — on code:-1 with a PIN set,\n * verifyAccessCode (aiid 4 / piid 1102 sha256(PIN)) — satisfy the privacy gate,\n * startMonitor (retry) — now accepted (code:0 + encryptionKey)\n * 4. keep_alive loop (aiid 1 / piid 6) — every ~10s, or the device tears down\n * 5. stream/query (Aliyun LinkVisual) — mint the plain RTMP relay URL\n *\n * The resulting relay URL is a standard RTMP stream (H.264 + AAC) any consumer\n * (scrypted / camstack / ffmpeg) can pull directly. Call {@link close} to stop\n * the keep-alive loop and release the monitor on the device.\n */\nimport { DreameError } from '../../transport/errors.js';\nimport {\n MONITOR_AIID,\n MONITOR_PIID,\n MONITOR_SIID,\n VACUUM_SIID,\n VACUUM_MOVE,\n VACUUM_CHARGE,\n VACUUM_LOCATE,\n VACUUM_ACTIONS,\n VOICE_SIID,\n VOICE_PLAY_SOUND_AIID,\n VOICE_SOUND_PIID,\n PET_SOUNDS,\n type PetSound,\n type VacuumActionKey,\n} from './constants.js';\nimport {\n accessCodeLaunchParams,\n actionCode,\n buildActionInput,\n fillLightParams,\n firstOutValue,\n intercomStartParams,\n intercomStopParams,\n keepAliveParams,\n makeMonitorSession,\n remoteDriveValue,\n startMonitorParams,\n stopMonitorParams,\n takePhotoParams,\n verifyAccessCodeParams,\n DRIVE_DIRECTIONS,\n type DriveDirection,\n} from './protocol.js';\n\n/**\n * MIoT device transport the controller drives — satisfied by a nodedreame device\n * handle (VacuumDevice). `setProperty` is optional so callAction-only fakes still\n * satisfy the interface; the robot-drive helpers require it.\n */\nexport interface MonitorActionCaller {\n callAction(siid: number, aiid: number, input: unknown[]): Promise<unknown>;\n setProperty?(write: { siid: number; piid: number; value: unknown }): Promise<unknown>;\n}\n\n/** Minimal relay minter — satisfied by {@link DreameVideoSession}. */\nexport interface RelayMinter {\n getStreamInfo(\n iotId: string,\n opts?: { encrypted?: boolean; wakeTimeoutMs?: number; pollMs?: number },\n ): Promise<{ relayUrl: string | null }>;\n}\n\nexport interface DreameCameraControllerInput {\n /** MIoT action transport (the device handle). */\n readonly device: MonitorActionCaller;\n /** Aliyun relay minter (a {@link DreameVideoSession}). */\n readonly relay: RelayMinter;\n /** LinkVisual channel id for this device (`iotId`). */\n readonly iotId: string;\n /** Dreame account uid — seeds the per-session correlation token. */\n readonly accountId: string;\n /** Video vendor. Only `ali` mints an RTMP relay; defaults to `ali`. */\n readonly vendor?: 'ali' | 'tx';\n /** Privacy access code (PIN) set at pairing, if any. Required to wake a coded camera. */\n readonly accessCode?: string;\n /** Aliyun region code the device reports as `area`; defaults to `'4'`. */\n readonly area?: string;\n /** Keep-alive cadence in ms (app default 10s). */\n readonly keepAliveIntervalMs?: number;\n /** Max time to wait for the relay to come up while the device wakes. */\n readonly wakeTimeoutMs?: number;\n /** Reported when a keep-alive tick fails (the stream is likely dying). */\n readonly onKeepAliveError?: (err: unknown) => void;\n /** Optional line logger (nodelink-style). Defaults to no-op. */\n readonly log?: (line: string) => void;\n}\n\n/** A live camera stream: the pullable relay URL plus the device's per-stream key. */\nexport interface CameraStreamHandle {\n /** Plain RTMP relay URL (H.264 + AAC). Single stream; re-open for another consumer. */\n readonly rtmpUrl: string;\n /** Device-generated per-session encryption key echoed by `startMonitor`. */\n readonly encryptionKey: string | null;\n}\n\nconst DEFAULT_KEEP_ALIVE_MS = 10_000;\nconst DEFAULT_AREA = '4';\n\nexport class DreameCameraController {\n readonly #device: MonitorActionCaller;\n readonly #relay: RelayMinter;\n readonly #iotId: string;\n readonly #accountId: string;\n readonly #vendor: 'ali' | 'tx';\n readonly #accessCode: string | undefined;\n readonly #area: string;\n readonly #keepAliveIntervalMs: number;\n readonly #wakeTimeoutMs: number | undefined;\n readonly #onKeepAliveError: ((err: unknown) => void) | undefined;\n readonly #log: (line: string) => void;\n\n #session: string | null = null;\n #keepAliveTimer: ReturnType<typeof setInterval> | null = null;\n #opened = false;\n\n constructor(input: DreameCameraControllerInput) {\n this.#device = input.device;\n this.#relay = input.relay;\n this.#iotId = input.iotId;\n this.#accountId = input.accountId;\n this.#vendor = input.vendor ?? 'ali';\n this.#accessCode = input.accessCode;\n this.#area = input.area ?? DEFAULT_AREA;\n this.#keepAliveIntervalMs = input.keepAliveIntervalMs ?? DEFAULT_KEEP_ALIVE_MS;\n this.#wakeTimeoutMs = input.wakeTimeoutMs;\n this.#onKeepAliveError = input.onKeepAliveError;\n this.#log = input.log ?? ((): void => {});\n }\n\n #logLine(msg: string): void {\n this.#log(`[nodedreame:monitor] ${msg}`);\n }\n\n /** True while a monitor session is active (between {@link open} and {@link close}). */\n get isOpen(): boolean {\n return this.#opened;\n }\n\n /** Send one camera action, resolving to the raw device reply. */\n async #action(aiid: number, piid: number, params: Record<string, unknown> | null): Promise<unknown> {\n if (!this.#session) {\n throw new DreameError('camera controller has no session; call open() first');\n }\n const input = buildActionInput(piid, params, this.#session);\n return this.#device.callAction(MONITOR_SIID, aiid, [input]);\n }\n\n /**\n * Run the full cold-start sequence and return a live relay URL. Starts the\n * keep-alive loop; the caller MUST {@link close} when done to release the\n * device. Throws if the monitor cannot be started or no relay comes up.\n */\n async open(): Promise<CameraStreamHandle> {\n if (this.#opened) {\n throw new DreameError('camera stream already open');\n }\n this.#session = makeMonitorSession(this.#accountId);\n this.#logLine(`open: vendor=${this.#vendor} iotId=${this.#iotId} area=${this.#area}`);\n\n // 1) Prime the pipeline + boot the on-device video agent (best-effort:\n // failures here are non-fatal, startMonitor is the real gate).\n await this.#tryPrime();\n\n // 2) startMonitor — retry once behind the privacy gate if refused.\n const startRes = await this.#startMonitorWithGate();\n const encryptionKey = asString(firstOutValue(startRes));\n this.#logLine(`startMonitor accepted (encryptionKey=${encryptionKey ? 'yes' : 'no'})`);\n\n // 3) keep the monitor alive from now on (device tears down without it).\n this.#opened = true;\n this.#startKeepAlive();\n\n // 4) mint the relay (transparently retries while the device wakes).\n let relayUrl: string | null;\n try {\n const info = await this.#relay.getStreamInfo(this.#iotId, {\n encrypted: false,\n ...(this.#wakeTimeoutMs !== undefined ? { wakeTimeoutMs: this.#wakeTimeoutMs } : {}),\n });\n relayUrl = info.relayUrl;\n } catch (err) {\n this.#logLine(`relay mint failed: ${err instanceof Error ? err.message : String(err)}`);\n await this.close();\n throw err;\n }\n if (!relayUrl) {\n await this.close();\n throw new DreameError('monitor started but no relay URL was returned');\n }\n this.#logLine('relay up');\n\n return { rtmpUrl: relayUrl, encryptionKey };\n }\n\n async #tryPrime(): Promise<void> {\n try {\n await this.#action(MONITOR_AIID.ACCESS_CODE_OPERATE, MONITOR_PIID.GET_ACCESS_CODE, accessCodeLaunchParams());\n } catch {\n /* non-fatal */\n }\n try {\n await this.#action(MONITOR_AIID.VIDEO_VENDOR, MONITOR_PIID.VIDEO_VENDOR_STATUS, null);\n } catch {\n /* non-fatal */\n }\n }\n\n async #startMonitorWithGate(): Promise<unknown> {\n const params = startMonitorParams(this.#iotId, this.#vendor, this.#area);\n let res = await this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.MONITOR_STATUS, params);\n if (actionCode(res) === 0) {\n return res;\n }\n this.#logLine(`startMonitor refused (code ${actionCode(res) ?? '?'}); verifying access code`);\n // Refused: the camera's per-session privacy gate needs the PIN verified.\n if (this.#accessCode) {\n await this.#action(\n MONITOR_AIID.ACCESS_CODE_OPERATE,\n MONITOR_PIID.VERIFY_ACCESS_CODE,\n verifyAccessCodeParams(this.#accessCode),\n );\n res = await this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.MONITOR_STATUS, params);\n }\n if (actionCode(res) !== 0) {\n const code = actionCode(res);\n throw new DreameError(\n `startMonitor refused (code ${code ?? '?'})` +\n (this.#accessCode ? '' : '; device likely requires an accessCode'),\n );\n }\n return res;\n }\n\n #startKeepAlive(): void {\n this.#stopKeepAlive();\n this.#keepAliveTimer = setInterval(() => {\n void this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.KEEP_ALIVE, keepAliveParams('opened')).catch(\n (err: unknown) => this.#onKeepAliveError?.(err),\n );\n }, this.#keepAliveIntervalMs);\n // Don't hold the event loop open on the keep-alive timer alone.\n this.#keepAliveTimer.unref?.();\n }\n\n #stopKeepAlive(): void {\n if (this.#keepAliveTimer) {\n clearInterval(this.#keepAliveTimer);\n this.#keepAliveTimer = null;\n }\n }\n\n /**\n * Mint a FRESH single-use RTMP relay URL for the already-open monitor. Each\n * consumer needs its own relay URL, so call this once per ffmpeg/consumer.\n * Throws if the stream is not open.\n */\n async mintRelayUrl(): Promise<string> {\n if (!this.#opened) {\n throw new DreameError('camera stream is not open; call open() first');\n }\n const info = await this.#relay.getStreamInfo(this.#iotId, {\n encrypted: false,\n ...(this.#wakeTimeoutMs !== undefined ? { wakeTimeoutMs: this.#wakeTimeoutMs } : {}),\n });\n if (!info.relayUrl) {\n throw new DreameError('no relay URL available for the open monitor');\n }\n return info.relayUrl;\n }\n\n /** Start the two-way intercom session (control plane only; see module docs on mic uplink). */\n async startIntercom(opts?: { needRecordSound?: boolean; videoCall?: boolean }): Promise<unknown> {\n return this.#action(MONITOR_AIID.VOICE_OPERATE, MONITOR_PIID.MONITOR_AUDIO_STATUS, intercomStartParams(opts));\n }\n\n /** Stop the two-way intercom session. */\n async stopIntercom(): Promise<unknown> {\n return this.#action(MONITOR_AIID.VOICE_OPERATE, MONITOR_PIID.MONITOR_AUDIO_STATUS, intercomStopParams());\n }\n\n /** Set the fill-light brightness (40–100 = manual %, below 40 / 101 = auto). */\n async setFillLight(value: number): Promise<unknown> {\n return this.#action(MONITOR_AIID.PROPERTY_OPERATE, MONITOR_PIID.FILL_LIGHT_SET, fillLightParams(value));\n }\n\n /**\n * Device-side snapshot: the robot captures a still and uploads it to cloud\n * storage (read the result back from the `uploadStatus` property). Prefer a\n * frame-grab from the live stream for an instant image.\n */\n async takePhoto(): Promise<unknown> {\n return this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.TAKE_PHOTO, takePhotoParams());\n }\n\n /**\n * Remote-drive the robot (\"PTZ\" substitute — the camera is fixed, the robot\n * moves). `spdv` = forward speed, `spdw` = turn; send repeatedly (~1 Hz) while\n * a direction is held, then `stop`. Only valid while the stream is open.\n */\n async drive(spdv: number, spdw: number): Promise<unknown> {\n this.#requireSession();\n if (!this.#device.setProperty) {\n throw new DreameError('device does not support setProperty; cannot remote-drive');\n }\n return this.#device.setProperty({\n siid: VACUUM_SIID,\n piid: VACUUM_MOVE.REMOTE_STATE_PIID,\n value: remoteDriveValue(spdv, spdw),\n });\n }\n\n /** Remote-drive by named direction (forward/left/right/turnAround/stop). */\n async driveDirection(direction: DriveDirection): Promise<unknown> {\n const [spdv, spdw] = DRIVE_DIRECTIONS[direction];\n return this.drive(spdv, spdw);\n }\n\n /** Start person-follow mode (robot tracks a detected person). */\n async startPersonFollow(): Promise<unknown> {\n this.#requireSession();\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.WORK_AIID, [\n { piid: VACUUM_MOVE.MODE_PIID, value: VACUUM_MOVE.MODE_PERSON_FOLLOW },\n ]);\n }\n\n /**\n * Universal stop for the current work mode — stops person-follow, spot-clean,\n * go-to-point, cruise, etc. (siid 4 / aiid 2).\n */\n async stopWork(): Promise<unknown> {\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.STOP_AIID, []);\n }\n\n /** Stop person-follow mode (alias for {@link stopWork}). */\n async stopPersonFollow(): Promise<unknown> {\n return this.stopWork();\n }\n\n /** Spot-clean the robot's current position (WorkMode SpotClean). */\n async spotClean(): Promise<unknown> {\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.WORK_AIID, [\n { piid: VACUUM_MOVE.MODE_PIID, value: VACUUM_MOVE.MODE_SPOT_CLEAN },\n ]);\n }\n\n /** Play one of the robot's sound clips (e.g. pet sounds) by id. */\n async playSound(soundId: number): Promise<unknown> {\n return this.#device.callAction(VOICE_SIID, VOICE_PLAY_SOUND_AIID, [\n { piid: VOICE_SOUND_PIID, value: soundId },\n ]);\n }\n\n /** Play a named pet-teasing sound (meow/bark/footsteps/purring/tickTock). */\n async playPetSound(sound: PetSound): Promise<unknown> {\n return this.playSound(PET_SOUNDS[sound]);\n }\n\n /** Run a common whole-robot action (startClean/pauseClean/stopClean/dockWash/autoEmpty). */\n async runVacuumAction(action: VacuumActionKey): Promise<unknown> {\n const a = VACUUM_ACTIONS[action];\n return this.#device.callAction(a.siid, a.aiid, []);\n }\n\n /** Find-pet: cruise the home looking for the pet. */\n async findPet(): Promise<unknown> {\n this.#requireSession();\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.WORK_AIID, [\n { piid: VACUUM_MOVE.MODE_PIID, value: VACUUM_MOVE.MODE_CRUISE },\n { piid: VACUUM_MOVE.POINT_PIID, value: JSON.stringify({ findpet: '' }) },\n ]);\n }\n\n /** Send the robot to a map point (`spoint` current, `tpoint` targets). */\n async goToPoint(spoint: number[][], tpoint: number[][]): Promise<unknown> {\n this.#requireSession();\n return this.#device.callAction(VACUUM_SIID, VACUUM_MOVE.WORK_AIID, [\n { piid: VACUUM_MOVE.MODE_PIID, value: VACUUM_MOVE.MODE_SPOT },\n { piid: VACUUM_MOVE.POINT_PIID, value: JSON.stringify({ spoint, tpoint }) },\n ]);\n }\n\n /** Toggle the fill light between auto (`full=false`) and full-on (`full=true`). */\n async setFillLightAuto(full: boolean): Promise<unknown> {\n if (!this.#device.setProperty) {\n throw new DreameError('device does not support setProperty; cannot toggle fill light');\n }\n return this.#device.setProperty({\n siid: VACUUM_SIID,\n piid: VACUUM_MOVE.AUTO_SWITCH_PIID,\n value: JSON.stringify({ k: 'FillinLight', v: full ? 1 : 0 }),\n });\n }\n\n /**\n * Send the robot back to its dock to charge (\"go home\"). Works regardless of\n * stream state — usable as a standalone command or a PTZ \"home\" preset.\n */\n async returnToDock(): Promise<unknown> {\n return this.#device.callAction(VACUUM_CHARGE.siid, VACUUM_CHARGE.aiid, []);\n }\n\n /** Locate the robot — it beeps. Works regardless of stream state. */\n async locate(): Promise<unknown> {\n return this.#device.callAction(VACUUM_LOCATE.siid, VACUUM_LOCATE.aiid, []);\n }\n\n #requireSession(): void {\n if (!this.#opened) {\n throw new DreameError('camera stream is not open; call open() first');\n }\n }\n\n /** Stop the keep-alive loop and release the monitor on the device. Idempotent. */\n async close(): Promise<void> {\n this.#stopKeepAlive();\n if (!this.#opened) {\n this.#session = null;\n return;\n }\n this.#opened = false;\n this.#logLine('close: stopping monitor');\n try {\n await this.#action(MONITOR_AIID.CAMERA_OPERATE, MONITOR_PIID.MONITOR_STATUS, stopMonitorParams());\n } finally {\n this.#session = null;\n }\n }\n}\n\nfunction asString(v: unknown): string | null {\n return typeof v === 'string' ? v : null;\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 let inflated: Buffer;\n try {\n inflated = zlib.inflateSync(bytes);\n } catch (err) {\n throw new MapDecodeError('envelope: zlib inflate failed', { cause: err });\n }\n\n // Some models/firmwares DOUBLE-wrap live-clean frames: the first inflate yields\n // a URL-safe base64 STRING of ANOTHER zlib stream rather than the binary frame\n // (verified on dreame.vacuum.r2538z — head decodes to the `78 xx` zlib magic).\n // Peel extra base64→zlib layers until the payload is binary (bounded, so a\n // pathological input can't loop). A genuine binary frame is NOT entirely\n // base64-text (its int16 header fields + 0x00 padding fail the check), so the\n // single-wrap path is untouched.\n let guard = 0;\n while (guard < MAX_EXTRA_WRAP_LAYERS && looksLikeBase64Zlib(inflated)) {\n const text = inflated.toString('latin1').replace(/-/g, '+').replace(/_/g, '/');\n try {\n inflated = zlib.inflateSync(Buffer.from(text, 'base64'));\n } catch (err) {\n throw new MapDecodeError('envelope: nested zlib inflate failed', { cause: err });\n }\n guard += 1;\n }\n return inflated;\n}\n\n/** Upper bound on nested base64+zlib layers peeled by {@link unwrapEnvelope}. */\nconst MAX_EXTRA_WRAP_LAYERS = 4;\n\n/**\n * Heuristic: does this buffer look like a base64-text wrapper around ANOTHER\n * zlib stream (rather than a binary map frame)? True only when the WHOLE buffer\n * is URL-safe-base64 characters AND a short prefix decodes to the zlib magic\n * byte `0x78`. A binary frame contains non-base64 bytes (header int16s / `0x00`\n * padding), so it never matches.\n */\nexport function looksLikeBase64Zlib(buf: Buffer): boolean {\n if (buf.length < 8) return false;\n for (const b of buf) {\n const isB64 =\n (b >= 0x41 && b <= 0x5a) || // A-Z\n (b >= 0x61 && b <= 0x7a) || // a-z\n (b >= 0x30 && b <= 0x39) || // 0-9\n b === 0x2b ||\n b === 0x2f || // + /\n b === 0x2d ||\n b === 0x5f || // - _ (url-safe)\n b === 0x3d; // =\n if (!isB64) return false;\n }\n try {\n const prefix = buf.subarray(0, 16).toString('latin1').replace(/-/g, '+').replace(/_/g, '/');\n const decoded = Buffer.from(prefix, 'base64');\n return decoded.length >= 2 && decoded[0] === 0x78;\n } catch {\n return false;\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 * Dreame room SEGMENT-TYPE → display-name table. A robot segment carries a\n * `type` code in its `seg_inf` record; the Dreamehome app (and the HA\n * `dreame_vacuum` integration) render a localized DEFAULT name from that code\n * when the user has NOT assigned a custom name. A custom-named room is stored\n * with `type === 0` plus a base64 `name`. Ported verbatim from the donor\n * `dreame/types.py SEGMENT_TYPE_CODE_TO_NAME`.\n */\nexport const SEGMENT_TYPE_CODE_TO_NAME: Readonly<Record<number, string>> = {\n 0: 'Room',\n 1: 'Living Room',\n 2: 'Primary Bedroom',\n 3: 'Study',\n 4: 'Kitchen',\n 5: 'Dining Hall',\n 6: 'Bathroom',\n 7: 'Balcony',\n 8: 'Corridor',\n 9: 'Utility Room',\n 10: 'Closet',\n 11: 'Meeting Room',\n 12: 'Office',\n 13: 'Fitness Area',\n 14: 'Recreation Area',\n 15: 'Secondary Bedroom',\n};\n\n/** Decode a base64 segment name to UTF-8, or `null` on malformed input. */\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/**\n * Resolve a segment's display name, mirroring the donor `Segment.set_name`:\n * 1. a known room `type` (≠ 0) wins → its localized default name, suffixed\n * `\" {index+1}\"` for the 2nd+ room of the same type (e.g. `Bedroom 2`);\n * 2. otherwise a user-assigned base64 `customName` (stored with `type === 0`);\n * 3. otherwise `null` (the caller renders a `Room <id>` fallback).\n *\n * @param type `seg_inf.<id>.type` room-category code (0 = custom/none).\n * @param index `seg_inf.<id>.index` ordinal among same-type rooms.\n * @param customName `seg_inf.<id>.name` — base64-encoded user name, if any.\n */\nexport function resolveSegmentName(\n type: number | undefined,\n index: number | undefined,\n customName: string | undefined,\n): string | null {\n if (type !== undefined && type !== 0 && SEGMENT_TYPE_CODE_TO_NAME[type] !== undefined) {\n const base = SEGMENT_TYPE_CODE_TO_NAME[type];\n return index !== undefined && index > 0 ? `${base} ${index + 1}` : base;\n }\n if (typeof customName === 'string' && customName.length > 0) {\n return safeBase64ToUtf8(customName);\n }\n return null;\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';\nimport { resolveSegmentName } from './segment-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 // Mirror the donor: a room-type code (≠0) yields a localized default name\n // (e.g. \"Kitchen\"), else the user's custom base64 name, else null.\n name: resolveSegmentName(meta?.type, meta?.index, meta?.name),\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","/**\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 * P-frame merging.\n *\n * The live channel emits a base I-frame (full snapshot, OSS-fetched per\n * Phase 0 finding) followed by sequential P-frames carrying byte-add\n * deltas plus updated robot/dock pose, latest obstacle list, and an\n * incremental `tr` path fragment. To keep an up-to-date renderable map,\n * each P-frame must be merged onto the running state.\n *\n * Strategy (matches Tasshack `dreame/map.py:5018-5070`):\n * - allocate a new pixel buffer sized to the union of (prev bbox,\n * P-frame bbox) in world-frame mm\n * - copy prev pixels into the new buffer at the offset of prev's\n * world-frame origin within the union\n * - byte-add each P-frame pixel into the new buffer at its offset\n * (`out[i] = (out[i] + p[i]) & 0xFF`, intentional 8-bit wrap)\n * - re-stamp the result as `frame_type = I` (73). The pixel grid is\n * now an absolute classification, not a delta — and `decodeVacuumMap`\n * deliberately skips pixel decode on P-frames, so the merged buffer\n * must look like an I-frame to round-trip cleanly\n * - merge JSON tail: prefer P's keys (latest state), but APPEND P's\n * `tr` to prev's (the path is incremental), and fall back to prev's\n * `seg_inf` when P doesn't carry one (most P-frames don't)\n * - advance `frame_id` to P's, keep `map_id`\n *\n * The result is a synthetic inflated frame buffer that\n * `decodeVacuumMap(buffer)` consumes normally. No envelope wrap.\n *\n * For sequence enforcement: `mergePFrame` throws `OutOfOrderFrameError`\n * when `pframe.frameId !== prev.frameId + 1`. The frame-orchestration\n * follow-up uses this to queue or trigger a re-request.\n */\n\nimport { FRAME_TYPE, HEADER_SIZE, MapDecodeError, unwrapEnvelope } from './envelope.js';\nimport { parseMapHeader } from './header.js';\nimport { parseMapJsonTail, sliceTailText } from './tail.js';\nimport type { VacuumMapDecodeOptions, MapTail } from './types.js';\n\nexport class OutOfOrderFrameError extends Error {\n readonly expectedFrameId: number;\n readonly actualFrameId: number;\n constructor(expectedFrameId: number, actualFrameId: number) {\n super(`map: out-of-order P-frame (expected frame_id=${expectedFrameId}, got ${actualFrameId})`);\n this.name = 'OutOfOrderFrameError';\n this.expectedFrameId = expectedFrameId;\n this.actualFrameId = actualFrameId;\n }\n}\n\n/**\n * Merge a P-frame onto the previous (I-frame or already-merged) inflated\n * frame buffer. Returns a new inflated buffer re-stamped as `frame_type=I`.\n *\n * Both inputs must already be unwrapped (`unwrapEnvelope` applied). For\n * envelope-string inputs use `mergePFrameEnvelope` or `applyVacuumPFrame`.\n */\nexport function mergePFrame(prevInflated: Buffer, pFrameInflated: Buffer): Buffer {\n const prevHeader = parseMapHeader(prevInflated);\n const pHeader = parseMapHeader(pFrameInflated);\n\n if (pHeader.frameType !== 'P') {\n throw new MapDecodeError(`mergePFrame: expected P-frame, got frame_type=${pHeader.frameType}`);\n }\n if (pHeader.mapId !== prevHeader.mapId) {\n throw new MapDecodeError(\n `mergePFrame: map_id mismatch (prev=${prevHeader.mapId}, P=${pHeader.mapId}) — request a fresh I-frame`,\n );\n }\n if (pHeader.frameId !== prevHeader.frameId + 1) {\n throw new OutOfOrderFrameError(prevHeader.frameId + 1, pHeader.frameId);\n }\n if (prevHeader.gridSize !== pHeader.gridSize && pHeader.width > 0 && pHeader.height > 0) {\n throw new MapDecodeError(\n `mergePFrame: grid_size changed mid-stream (${prevHeader.gridSize} → ${pHeader.gridSize})`,\n );\n }\n\n const prevTail = parseMapJsonTail(sliceTailText(prevInflated, prevHeader));\n const pTail = parseMapJsonTail(sliceTailText(pFrameInflated, pHeader));\n\n // ── compute union bbox ──────────────────────────────────────────────\n // tail.origin and header.left/top agree on r2532a fsm:1 captures —\n // prefer the tail value when present, fall back to the header.\n const prevLeft = prevTail.origin?.[0] ?? prevHeader.left;\n const prevTop = prevTail.origin?.[1] ?? prevHeader.top;\n const grid = prevHeader.gridSize;\n const prevRight = prevLeft + prevHeader.width * grid;\n const prevBottom = prevTop + prevHeader.height * grid;\n\n // P-frames with width=0,height=0 represent \"no spatial change\" — robot\n // moved or obstacle list updated, but no pixel deltas. Skip the bbox\n // union and the byte-add loop in that case.\n const hasPixelDelta = pHeader.width > 0 && pHeader.height > 0;\n let unionLeft = prevLeft;\n let unionTop = prevTop;\n let unionWidth = prevHeader.width;\n let unionHeight = prevHeader.height;\n let pLeft = 0;\n let pTop = 0;\n\n if (hasPixelDelta) {\n pLeft = pTail.origin?.[0] ?? pHeader.left;\n pTop = pTail.origin?.[1] ?? pHeader.top;\n const pRight = pLeft + pHeader.width * grid;\n const pBottom = pTop + pHeader.height * grid;\n\n if ((pLeft - prevLeft) % grid !== 0 || (pTop - prevTop) % grid !== 0) {\n throw new MapDecodeError(\n `mergePFrame: P-frame origin not aligned to prev grid (offset=${pLeft - prevLeft},${pTop - prevTop} vs grid=${grid})`,\n );\n }\n\n unionLeft = Math.min(prevLeft, pLeft);\n unionTop = Math.min(prevTop, pTop);\n const unionRight = Math.max(prevRight, pRight);\n const unionBottom = Math.max(prevBottom, pBottom);\n unionWidth = (unionRight - unionLeft) / grid;\n unionHeight = (unionBottom - unionTop) / grid;\n }\n\n // ── allocate new pixel grid + copy prev pixels in ───────────────────\n const newPixels = Buffer.alloc(unionWidth * unionHeight);\n const prevPixelEnd = HEADER_SIZE + prevHeader.width * prevHeader.height;\n if (prevInflated.length < prevPixelEnd) {\n throw new MapDecodeError(\n `mergePFrame: prev buffer truncated (need ${prevPixelEnd} bytes for header+pixels, got ${prevInflated.length})`,\n );\n }\n const prevPixels = prevInflated.subarray(HEADER_SIZE, prevPixelEnd);\n const prevDxPx = (prevLeft - unionLeft) / grid;\n const prevDyPx = (prevTop - unionTop) / grid;\n for (let y = 0; y < prevHeader.height; y++) {\n const srcOff = y * prevHeader.width;\n const dstOff = (prevDyPx + y) * unionWidth + prevDxPx;\n prevPixels.copy(newPixels, dstOff, srcOff, srcOff + prevHeader.width);\n }\n\n // ── byte-add P pixels at their offset ───────────────────────────────\n if (hasPixelDelta) {\n const pPixelEnd = HEADER_SIZE + pHeader.width * pHeader.height;\n if (pFrameInflated.length < pPixelEnd) {\n throw new MapDecodeError(\n `mergePFrame: P buffer truncated (need ${pPixelEnd} bytes for header+pixels, got ${pFrameInflated.length})`,\n );\n }\n const pPixels = pFrameInflated.subarray(HEADER_SIZE, pPixelEnd);\n const pDxPx = (pLeft - unionLeft) / grid;\n const pDyPx = (pTop - unionTop) / grid;\n for (let y = 0; y < pHeader.height; y++) {\n const dstRow = (pDyPx + y) * unionWidth + pDxPx;\n const srcRow = y * pHeader.width;\n for (let x = 0; x < pHeader.width; x++) {\n newPixels[dstRow + x] = (newPixels[dstRow + x]! + pPixels[srcRow + x]!) & 0xff;\n }\n }\n }\n\n // ── build new header (re-stamped as I-frame, union bbox) ────────────\n const newHeader = Buffer.alloc(HEADER_SIZE);\n newHeader.writeInt16LE(prevHeader.mapId, 0);\n newHeader.writeInt16LE(pHeader.frameId, 2);\n newHeader[4] = FRAME_TYPE.I;\n newHeader.writeInt16LE(pHeader.robotX, 5);\n newHeader.writeInt16LE(pHeader.robotY, 7);\n newHeader.writeInt16LE(pHeader.robotA, 9);\n newHeader.writeInt16LE(pHeader.chargerX, 11);\n newHeader.writeInt16LE(pHeader.chargerY, 13);\n newHeader.writeInt16LE(pHeader.chargerA, 15);\n newHeader.writeInt16LE(grid, 17);\n newHeader.writeInt16LE(unionWidth, 19);\n newHeader.writeInt16LE(unionHeight, 21);\n newHeader.writeInt16LE(unionLeft, 23);\n newHeader.writeInt16LE(unionTop, 25);\n\n // ── merge JSON tail ─────────────────────────────────────────────────\n const mergedTail = mergeTails(prevTail, pTail, unionLeft, unionTop);\n const tailBytes = Buffer.from(JSON.stringify(mergedTail), 'utf8');\n\n return Buffer.concat([newHeader, newPixels, tailBytes]);\n}\n\n/**\n * Convenience wrapper: accept envelope strings (or raw buffers) for both\n * inputs and return the merged inflated buffer.\n */\nexport function mergePFrameEnvelope(\n prev: Buffer | string,\n pframe: Buffer | string,\n prevOpts?: VacuumMapDecodeOptions,\n pframeOpts?: VacuumMapDecodeOptions,\n): Buffer {\n const prevBuf = typeof prev === 'string' ? unwrapEnvelope(prev, prevOpts) : prev;\n const pBuf = typeof pframe === 'string' ? unwrapEnvelope(pframe, pframeOpts) : pframe;\n return mergePFrame(prevBuf, pBuf);\n}\n\n/**\n * Construct the merged JSON tail. P-frame's tail is taken as the base\n * (it carries the latest robot/dock/obstacle/etc. state); a few keys\n * need explicit treatment:\n *\n * - `origin` is overwritten with the union origin\n * - `tr` (cleaning path) is concatenated: `prev.tr + p.tr`. P-frame\n * uses lowercase `l` for line continuation; `parsePathTr` already\n * normalises that, so plain concatenation is correct\n * - `seg_inf` falls back to prev's when P doesn't supply one (typical;\n * P-frames re-send seg_inf only on segment metadata changes)\n * - `sa` (active segments) similarly falls back to prev's when absent\n */\nfunction mergeTails(\n prev: MapTail,\n p: MapTail,\n unionLeft: number,\n unionTop: number,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...p };\n merged.origin = [unionLeft, unionTop];\n\n const prevTr = typeof prev.tr === 'string' ? prev.tr : '';\n const pTr = typeof p.tr === 'string' ? p.tr : '';\n if (prevTr || pTr) {\n merged.tr = prevTr + pTr;\n } else {\n delete merged.tr;\n }\n\n if (!('seg_inf' in p) && 'seg_inf' in prev) {\n merged.seg_inf = prev.seg_inf;\n }\n if (!('sa' in p) && 'sa' in prev) {\n merged.sa = prev.sa;\n }\n // User-defined geometry + persistent overlay blocks are\n // configuration, not live state — most P-frames don't re-send any\n // of them. Fall back to prev when absent so the running state\n // retains user-defined geometry / cleaned-area / saved-map across\n // the chain. To wire a new persistent tail key (e.g. `vw.cliff`\n // when we get a fixture), add it here and to `MapTail` in types.\n for (const key of PERSISTENT_TAIL_KEYS) {\n if (!(key in p) && key in prev) {\n merged[key] = prev[key];\n }\n }\n return merged;\n}\n\n/**\n * Tail-JSON keys that represent persistent floor-plan / saved-map /\n * cleaning-progress configuration — re-emitted by the device only on\n * full snapshots, not every P-frame. `mergeTails` falls these back\n * from prev when the P-frame's tail doesn't carry them, so the\n * running merged state keeps the user's geometry between full\n * snapshots.\n */\nexport const PERSISTENT_TAIL_KEYS = [\n 'vw',\n 'vws',\n 'sneak_areas',\n 'sneak_areas_end',\n 'walls_info',\n 'rism',\n 'decmap',\n] as const;\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 RawSegInf,\n} from './types.js';\nimport { ANGLE_ABSENT, HEADER_SIZE, looksLikeBase64Zlib, 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 * the raw fetched OSS bytes (a `Buffer` that is STILL the base64 envelope —\n * `OssFetcher.fetchBlob` returns the object bytes verbatim, NOT inflated), or\n * already-inflated frame bytes (`Buffer`). Pure — no IO.\n */\nexport function decodeVacuumMap(\n input: Buffer | string,\n opts: VacuumMapDecodeOptions = {},\n): VacuumMap {\n // A `Buffer` may be either an already-inflated frame (tests / the P-frame merge\n // primitive) OR the raw fetched OSS bytes, which are STILL the base64+zlib\n // envelope (the live `getMap` path). Detect the latter — a base64 wrapper around\n // a zlib stream — and run it through `unwrapEnvelope` (base64 → zlib, peeling any\n // extra wrap layers); an already-inflated binary frame fails the check and is\n // used verbatim.\n const inflated =\n typeof input === 'string'\n ? unwrapEnvelope(input, opts)\n : looksLikeBase64Zlib(input)\n ? unwrapEnvelope(input.toString('latin1'), opts)\n : 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 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. The live frame's\n // `seg_inf` also typically OMITS the user room NAMES — those live in the\n // saved map. Recurse once to surface geometry AND names; if the inner blob\n // fails to decode (corrupt / unexpected shape / missing AES IV) leave the\n // outer values as-is and swallow — must never break the outer frame.\n // Recurses one level only — the inner saved-map blob carries no `rism`.\n let segTail = tail;\n const outerHasNames = Object.values(tail.seg_inf ?? {}).some(\n (m) => typeof m?.name === 'string' && m.name.length > 0,\n );\n if (\n (!isGeometryComplete(geometry) || !outerHasNames) &&\n typeof tail.rism === 'string' &&\n tail.rism.length > 0\n ) {\n try {\n const innerInflated = unwrapEnvelope(tail.rism);\n const { tail: innerTail } = parseFrame(innerInflated);\n if (!isGeometryComplete(geometry)) {\n geometry = coalesceGeometry(geometry, parseTailGeometry(innerTail));\n }\n if (!outerHasNames && innerTail.seg_inf) {\n segTail = mergeSegInfNames(tail, innerTail.seg_inf);\n }\n } catch {\n // intentional — outer frame remains valid even if rism is unreadable\n }\n }\n const segments = canDecodePixels ? collectSegments(layers, dimensions, segTail) : [];\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\n/**\n * Merge saved-map (`rism`) `seg_inf` NAMES into the outer tail so\n * {@link collectSegments} surfaces real room names. Outer fields win for every\n * key EXCEPT `name`, which is taken from the inner saved map when the outer\n * entry has no (non-empty) name. Returns a new tail (no mutation).\n */\nfunction mergeSegInfNames(tail: MapTail, innerSegInf: Record<string, RawSegInf>): MapTail {\n const outer = tail.seg_inf ?? {};\n const ids = new Set([...Object.keys(outer), ...Object.keys(innerSegInf)]);\n const merged: Record<string, RawSegInf> = {};\n for (const id of ids) {\n const o = outer[id] ?? {};\n const inner = innerSegInf[id] ?? {};\n const name = typeof o.name === 'string' && o.name.length > 0 ? o.name : inner.name;\n merged[id] = { ...inner, ...o, ...(name !== undefined ? { name } : {}) };\n }\n return { ...tail, seg_inf: 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 * the run-length `layers` (floor / segments / carpet / walls) one pixel per grid\n * cell (× an optional integer upscale `scale`), then composite the world-frame\n * overlays on top: restricted areas (no-go / no-mop), virtual walls, the cleaning\n * path polylines, AI obstacles, the charger, and the robot (body + heading). A\n * `colorScheme` picks the palette and every overlay is independently toggleable,\n * mirroring how the Home Assistant integration exposes map customization.\n *\n * Coordinate transform: every overlay coordinate is mm in the world frame.\n * `dimensions.left` / `.top` are the world-mm of pixel (0,0) and `.gridSize` is\n * mm-per-pixel, so `xPx = (worldX - left) / gridSize` (then × `scale`).\n *\n * Y IS FLIPPED. Dreame's grid has world-Y increasing DOWNWARD relative to how\n * the robot/dock/app present the floor, so rendering rows top-down produced a\n * VERTICALLY MIRRORED map vs the Dreamehome app / Home Assistant (live-confirmed\n * on r2538z). We reflect in cell space — `yPx = ((height - 1) - cellY) × scale` —\n * applied identically to the run-length base layers ({@link paintRun}) and the\n * world-frame overlays ({@link worldToPx}) so they stay pixel-aligned, and the\n * robot heading's Y component is negated ({@link drawRobot}). Glyph labels are\n * drawn upright (only their anchor is flipped).\n *\n * Pure: only `pngjs` + arithmetic. No casts.\n */\n\nimport { PNG } from 'pngjs';\nimport type {\n VacuumMap,\n MapLayer,\n MapRun,\n MapPoint,\n MapPose,\n MapDimensions,\n MapCleanedAreaOverlay,\n} from './types.js';\n\n/** A named map palette — node-dreame's own distinct visual styles. */\nexport type MapColorScheme = 'flat' | 'dark-neon' | 'materico';\n\nexport interface RenderVacuumPngOptions {\n /** Integer upscale factor (nearest-neighbour). Default 1. */\n scale?: number;\n /** Palette. Default `'flat'`. */\n colorScheme?: MapColorScheme;\n /** Draw the cleaning/mopping path polylines. Default true. */\n showPath?: boolean;\n /** Draw the robot marker (body + heading). Default true. */\n showRobot?: boolean;\n /** Draw the charger / dock marker. Default true. */\n showCharger?: boolean;\n /** Draw no-go / no-mop restricted areas. Default true. */\n showNoGo?: boolean;\n /** Draw user-defined virtual walls. Default true. */\n showVirtualWalls?: boolean;\n /** Draw AI-detected obstacle markers. Default true. */\n showObstacles?: boolean;\n /** Draw the segment id as a tiny label at each room centroid. Default false. */\n showSegmentLabels?: boolean;\n /** Draw each room's NAME (falls back to `Room <id>`) at its centroid. Default\n * false. Mutually useful with `showSegmentLabels` (names take the centroid). */\n showSegmentNames?: boolean;\n /** Tint the cleaned-area overlay (`cleanedArea.cleaned`) when present. Default false. */\n showCleanedArea?: boolean;\n /** Outline low-clearance \"sneak under furniture\" zones. Default true. */\n showFurniture?: boolean;\n /** Colour AI obstacle markers by their `type` (else a single accent). Default true. */\n colorObstaclesByType?: boolean;\n}\n\ntype Rgba = readonly [number, number, number, number];\n\n/** Per-scheme colour set. Segment fills derive from `segHsv` (golden-angle hue). */\ninterface Palette {\n floor: Rgba;\n wall: Rgba;\n carpet: Rgba;\n /** Segment fill saturation / value (hue is per-id golden angle). */\n segSat: number;\n segVal: number;\n path: Rgba;\n robotBody: Rgba;\n robotHeading: Rgba;\n charger: Rgba;\n noGoFill: Rgba;\n noGoBorder: Rgba;\n noMopFill: Rgba;\n noMopBorder: Rgba;\n virtualWall: Rgba;\n obstacle: Rgba;\n label: Rgba;\n}\n\nconst SCHEMES: Record<MapColorScheme, Palette> = {\n // Flat-design: soft neutral floor, bright low-saturation segment fills, thin\n // mid-grey walls, a single vivid accent for the path.\n flat: {\n floor: [236, 239, 241, 255],\n wall: [120, 132, 145, 255],\n carpet: [205, 185, 160, 200],\n segSat: 0.28,\n segVal: 0.99,\n path: [255, 138, 101, 240],\n robotBody: [38, 50, 56, 255],\n robotHeading: [255, 255, 255, 255],\n charger: [38, 166, 154, 255],\n noGoFill: [239, 83, 80, 60],\n noGoBorder: [229, 57, 53, 210],\n noMopFill: [66, 165, 245, 55],\n noMopBorder: [30, 136, 229, 200],\n virtualWall: [229, 57, 53, 225],\n obstacle: [255, 167, 38, 255],\n label: [55, 71, 79, 255],\n },\n // Dark-neon: near-black floor with high-saturation glowing segment fills, a\n // bright cyan path and magenta robot.\n 'dark-neon': {\n floor: [16, 18, 27, 255],\n wall: [5, 6, 10, 255],\n carpet: [60, 50, 80, 200],\n segSat: 0.85,\n segVal: 0.95,\n path: [0, 230, 255, 240],\n robotBody: [255, 45, 170, 255],\n robotHeading: [255, 255, 255, 255],\n charger: [0, 255, 150, 255],\n noGoFill: [255, 40, 90, 80],\n noGoBorder: [255, 40, 90, 235],\n noMopFill: [40, 130, 255, 75],\n noMopBorder: [40, 160, 255, 225],\n virtualWall: [255, 40, 90, 235],\n obstacle: [255, 220, 0, 255],\n label: [230, 240, 255, 255],\n },\n // Materico (Material-design): light grey surface, Material-palette segment\n // fills, indigo/teal accents.\n materico: {\n floor: [245, 245, 245, 255],\n wall: [97, 97, 97, 255],\n carpet: [188, 170, 144, 205],\n segSat: 0.55,\n segVal: 0.93,\n path: [63, 81, 181, 235],\n robotBody: [48, 63, 159, 255],\n robotHeading: [255, 255, 255, 255],\n charger: [0, 150, 136, 255],\n noGoFill: [244, 67, 54, 65],\n noGoBorder: [211, 47, 47, 215],\n noMopFill: [33, 150, 243, 60],\n noMopBorder: [25, 118, 210, 205],\n virtualWall: [211, 47, 47, 225],\n obstacle: [255, 152, 0, 255],\n label: [33, 33, 33, 255],\n },\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) under a scheme. */\nfunction segmentColor(id: number, pal: Palette): Rgba {\n const hue = (id * 137.508) % 360;\n return hsvToRgba(hue, pal.segSat, pal.segVal);\n}\n\nexport function renderVacuumPng(map: VacuumMap, opts: RenderVacuumPngOptions = {}): Buffer {\n const scale = Math.max(1, Math.trunc(opts.scale ?? 1));\n // Default + resilient fallback to 'flat' for any unknown/legacy scheme value.\n const pal = SCHEMES[opts.colorScheme ?? 'flat'] ?? SCHEMES.flat;\n const w = Math.max(1, map.dimensions.width * scale);\n const h = Math.max(1, map.dimensions.height * scale);\n const png = new PNG({ width: w, height: h });\n png.data.fill(0); // transparent background\n\n const dim = map.dimensions;\n\n // 1) Spatial layers (floor / segments / carpet / walls), painted in order.\n for (const layer of map.layers) {\n paintLayer(png, layer, dim.height, scale, pal);\n }\n\n // 1b) Cleaned-area overlay — a translucent tint over already-cleaned cells,\n // reprojected from the inner overlay grid onto the parent grid. Drawn over the\n // base layers but under every marker/zone. Opt-in (default off).\n if (opts.showCleanedArea === true && map.cleanedArea !== null) {\n paintCleanedArea(png, map.cleanedArea, dim, scale, pal.path);\n }\n\n // 2) Restricted areas — translucent fill + opaque border.\n if (opts.showNoGo !== false) {\n for (const area of map.restrictedAreas) {\n const fill = area.kind === 'noMop' ? pal.noMopFill : pal.noGoFill;\n const border = area.kind === 'noMop' ? pal.noMopBorder : pal.noGoBorder;\n const a = worldToPx(area.bbox.xMin, area.bbox.yMin, dim, scale);\n const b = worldToPx(area.bbox.xMax, area.bbox.yMax, dim, scale);\n fillRect(png, a.x, a.y, b.x, b.y, fill);\n strokeRect(png, a.x, a.y, b.x, b.y, border);\n }\n }\n\n // 3) Virtual walls — opaque line segments.\n if (opts.showVirtualWalls !== false) {\n for (const vw of map.virtualWalls) {\n const a = worldToPx(vw.from.x, vw.from.y, dim, scale);\n const b = worldToPx(vw.to.x, vw.to.y, dim, scale);\n drawLine(png, a.x, a.y, b.x, b.y, pal.virtualWall, Math.max(1, scale));\n }\n }\n\n // 4) Cleaning path polylines (\"linee di lavaggio\").\n if (opts.showPath !== false) {\n for (const path of map.paths) {\n drawPolyline(png, path.points, dim, scale, pal.path, Math.max(1, scale));\n }\n }\n\n // 4b) Low-clearance \"sneak under furniture\" zones — outlined translucent\n // polygons. Default on.\n if (opts.showFurniture !== false) {\n for (const zone of map.lowLyingAreas) {\n paintFurnitureZone(png, zone.points, dim, scale);\n }\n }\n\n // 5) AI obstacles — coloured AND shaped by type by default so distinct hazards\n // read differently (the full type→icon table lives browser-side; here we vary\n // colour + a small shape set as a lightweight raster stand-in).\n if (opts.showObstacles !== false) {\n const byType = opts.colorObstaclesByType !== false;\n for (const ob of map.obstacles) {\n const p = worldToPx(ob.x, ob.y, dim, scale);\n const r = Math.max(2, scale * 2);\n if (byType) {\n drawObstacleShape(png, p.x, p.y, r, ob.type, obstacleColor(ob.type));\n } else {\n drawDiamond(png, p.x, p.y, r, pal.obstacle);\n }\n }\n }\n\n // 6) Charger / dock.\n if (opts.showCharger !== false && map.dock !== null) {\n drawCharger(png, map.dock, dim, scale, pal);\n }\n\n // 7) Robot (body + heading wedge) — drawn last so it sits on top.\n if (opts.showRobot !== false && map.robot !== null) {\n drawRobot(png, map.robot, dim, scale, pal);\n }\n\n // 8) Room labels at centroids — the NAME when requested (falls back to\n // `ROOM <id>` for an unnamed room), else just the numeric id. Names win the\n // centroid when both flags are set.\n if (opts.showSegmentNames === true || opts.showSegmentLabels === true) {\n for (const seg of map.segments) {\n const p = worldToPx(seg.centroid.x, seg.centroid.y, dim, scale);\n const text = opts.showSegmentNames === true ? labelText(seg.name, seg.id) : String(seg.id);\n drawLabel(png, p.x, p.y, text, pal.label, Math.max(1, scale));\n }\n }\n\n return PNG.sync.write(png);\n}\n\n// ── Coordinate transform ────────────────────────────────────────────────────\n\ninterface Px {\n x: number;\n y: number;\n}\n\n/** World-mm → scaled pixel coordinate. Y is reflected in cell space so the map\n * matches the Dreamehome app / HA orientation (see file header). */\nfunction worldToPx(worldX: number, worldY: number, dim: MapDimensions, scale: number): Px {\n const cellY = (worldY - dim.top) / dim.gridSize;\n return {\n x: Math.round(((worldX - dim.left) / dim.gridSize) * scale),\n y: Math.round((dim.height - 1 - cellY) * scale),\n };\n}\n\n// ── Layer painting (run-length) ─────────────────────────────────────────────\n\nfunction paintLayer(\n png: PNG,\n layer: MapLayer,\n dimHeight: number,\n scale: number,\n pal: Palette,\n): void {\n const color =\n layer.type === 'segment'\n ? segmentColor(layer.segmentId ?? 0, pal)\n : layer.type === 'wall'\n ? pal.wall\n : layer.type === 'carpet'\n ? pal.carpet\n : pal.floor;\n for (const run of layer.runs) {\n paintRun(png, run, dimHeight, color, scale);\n }\n}\n\n/** Fill the scale×scale block of every pixel in a `[xPx, yPx, len]` run. The row\n * is reflected (`(height-1)-yPx`) to match {@link worldToPx}'s Y flip. */\nfunction paintRun(png: PNG, run: MapRun, dimHeight: number, color: Rgba, scale: number): void {\n const [xPx, yPx, len] = run;\n const baseY = (dimHeight - 1 - yPx) * scale;\n for (let i = 0; i < len; i += 1) {\n const baseX = (xPx + i) * scale;\n for (let dy = 0; dy < scale; dy += 1) {\n for (let dx = 0; dx < scale; dx += 1) {\n setPixel(png, baseX + dx, baseY + dy, color);\n }\n }\n }\n}\n\n// ── Pixel primitives ────────────────────────────────────────────────────────\n\n/** Opaque write (or alpha-composite when the colour is translucent). */\nfunction setPixel(png: PNG, x: number, y: number, color: Rgba): void {\n if (x < 0 || y < 0 || x >= png.width || y >= png.height) return;\n const off = (y * png.width + x) * 4;\n const a = color[3];\n if (a >= 255) {\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] = 255;\n return;\n }\n // Source-over alpha composite onto the existing pixel.\n const sa = a / 255;\n const da = (png.data[off + 3] ?? 0) / 255;\n const outA = sa + da * (1 - sa);\n if (outA <= 0) return;\n for (let k = 0; k < 3; k += 1) {\n const src = color[k] ?? 0;\n const dst = png.data[off + k] ?? 0;\n png.data[off + k] = Math.round((src * sa + dst * da * (1 - sa)) / outA);\n }\n png.data[off + 3] = Math.round(outA * 255);\n}\n\n/** Translucent (or opaque) axis-aligned filled rectangle, endpoints inclusive. */\nfunction fillRect(png: PNG, x0: number, y0: number, x1: number, y1: number, color: Rgba): void {\n const xa = Math.min(x0, x1);\n const xb = Math.max(x0, x1);\n const ya = Math.min(y0, y1);\n const yb = Math.max(y0, y1);\n for (let y = ya; y <= yb; y += 1) {\n for (let x = xa; x <= xb; x += 1) {\n setPixel(png, x, y, color);\n }\n }\n}\n\n/** Rectangle border (1px). */\nfunction strokeRect(png: PNG, x0: number, y0: number, x1: number, y1: number, color: Rgba): void {\n const xa = Math.min(x0, x1);\n const xb = Math.max(x0, x1);\n const ya = Math.min(y0, y1);\n const yb = Math.max(y0, y1);\n for (let x = xa; x <= xb; x += 1) {\n setPixel(png, x, ya, color);\n setPixel(png, x, yb, color);\n }\n for (let y = ya; y <= yb; y += 1) {\n setPixel(png, xa, y, color);\n setPixel(png, xb, y, color);\n }\n}\n\n/** Bresenham line with a square pen of side `thickness`. */\nfunction drawLine(\n png: PNG,\n x0: number,\n y0: number,\n x1: number,\n y1: number,\n color: Rgba,\n thickness: number,\n): void {\n let x = x0;\n let y = y0;\n const dx = Math.abs(x1 - x0);\n const dy = -Math.abs(y1 - y0);\n const sx = x0 < x1 ? 1 : -1;\n const sy = y0 < y1 ? 1 : -1;\n let err = dx + dy;\n const r = Math.max(0, Math.trunc((thickness - 1) / 2));\n for (;;) {\n for (let oy = -r; oy <= r; oy += 1) {\n for (let ox = -r; ox <= r; ox += 1) {\n setPixel(png, x + ox, y + oy, color);\n }\n }\n if (x === x1 && y === y1) break;\n const e2 = 2 * err;\n if (e2 >= dy) {\n err += dy;\n x += sx;\n }\n if (e2 <= dx) {\n err += dx;\n y += sy;\n }\n }\n}\n\n/** Connect consecutive world-frame points with line segments. */\nfunction drawPolyline(\n png: PNG,\n points: readonly MapPoint[],\n dim: MapDimensions,\n scale: number,\n color: Rgba,\n thickness: number,\n): void {\n for (let i = 1; i < points.length; i += 1) {\n const p0 = points[i - 1];\n const p1 = points[i];\n if (p0 === undefined || p1 === undefined) continue;\n const a = worldToPx(p0.x, p0.y, dim, scale);\n const b = worldToPx(p1.x, p1.y, dim, scale);\n drawLine(png, a.x, a.y, b.x, b.y, color, thickness);\n }\n}\n\n/** Filled disk (square-distance) of radius `r` centred at `cx,cy`. */\nfunction drawDisk(png: PNG, cx: number, cy: number, r: number, color: Rgba): void {\n const rr = r * r;\n for (let dy = -r; dy <= r; dy += 1) {\n for (let dx = -r; dx <= r; dx += 1) {\n if (dx * dx + dy * dy <= rr) setPixel(png, cx + dx, cy + dy, color);\n }\n }\n}\n\n// ── Markers ─────────────────────────────────────────────────────────────────\n\nfunction drawRobot(\n png: PNG,\n robot: MapPose,\n dim: MapDimensions,\n scale: number,\n pal: Palette,\n): void {\n const p = worldToPx(robot.x, robot.y, dim, scale);\n const r = Math.max(3, scale * 3);\n drawDisk(png, p.x, p.y, r, pal.robotBody);\n // Heading wedge: a short line from the centre toward `angle` (degrees, CCW).\n // The Y axis is flipped (see worldToPx), so the screen-Y component is negated\n // to keep the arrow pointing the real direction.\n const rad = (robot.angle * Math.PI) / 180;\n const hx = Math.round(p.x + Math.cos(rad) * r);\n const hy = Math.round(p.y - Math.sin(rad) * r);\n drawLine(png, p.x, p.y, hx, hy, pal.robotHeading, Math.max(1, Math.trunc(scale / 2) + 1));\n}\n\nfunction drawCharger(\n png: PNG,\n dock: MapPose,\n dim: MapDimensions,\n scale: number,\n pal: Palette,\n): void {\n const p = worldToPx(dock.x, dock.y, dim, scale);\n const r = Math.max(2, scale * 2);\n // A small filled square reads as a dock base.\n fillRect(png, p.x - r, p.y - r, p.x + r, p.y + r, pal.charger);\n}\n\n/** Filled diamond (Manhattan disk) of radius `r` — used for AI obstacle markers\n * so they read distinctly from the round robot body. */\nfunction drawDiamond(png: PNG, cx: number, cy: number, r: number, color: Rgba): void {\n for (let dy = -r; dy <= r; dy += 1) {\n const w = r - Math.abs(dy);\n for (let dx = -w; dx <= w; dx += 1) {\n setPixel(png, cx + dx, cy + dy, color);\n }\n }\n}\n\n/** Deterministic per-type obstacle colour (golden-ish hue spread). The integer\n * `ObstacleType` → human label table lives browser-side; here we only need a\n * stable, visually distinct hue per type. */\nfunction obstacleColor(type: number): Rgba {\n const hue = (Math.abs(Math.trunc(type)) * 47) % 360;\n return hsvToRgba(hue, 0.85, 0.95);\n}\n\n/** Filled axis-aligned square. */\nfunction drawSquare(png: PNG, cx: number, cy: number, r: number, color: Rgba): void {\n fillRect(png, cx - r, cy - r, cx + r, cy + r, color);\n}\n\n/** Filled upward triangle (apex up), height/half-width `r`. */\nfunction drawTriangle(png: PNG, cx: number, cy: number, r: number, color: Rgba): void {\n for (let dy = -r; dy <= r; dy += 1) {\n // Width grows from apex (dy=-r) to base (dy=+r).\n const w = Math.round(((dy + r) / (2 * r)) * r);\n for (let dx = -w; dx <= w; dx += 1) {\n setPixel(png, cx + dx, cy + dy, color);\n }\n }\n}\n\n/** Draw an AI-obstacle marker whose SHAPE varies by type (diamond / square /\n * triangle / disk), a lightweight raster stand-in for the app's per-type icons. */\nfunction drawObstacleShape(\n png: PNG,\n cx: number,\n cy: number,\n r: number,\n type: number,\n color: Rgba,\n): void {\n switch (Math.abs(Math.trunc(type)) % 4) {\n case 0:\n drawDiamond(png, cx, cy, r, color);\n return;\n case 1:\n drawSquare(png, cx, cy, r, color);\n return;\n case 2:\n drawTriangle(png, cx, cy, r, color);\n return;\n default:\n drawDisk(png, cx, cy, r, color);\n }\n}\n\n/** Outline colour for low-clearance furniture zones — a translucent violet that\n * reads on both light and dark schemes. */\nconst FURNITURE_LINE: Rgba = [150, 110, 200, 235];\n\n/** Outline a low-clearance furniture polygon (closes the loop). */\nfunction paintFurnitureZone(\n png: PNG,\n points: readonly MapPoint[],\n dim: MapDimensions,\n scale: number,\n): void {\n if (points.length < 2) return;\n for (let i = 0; i < points.length; i += 1) {\n const a = points[i];\n const b = points[(i + 1) % points.length];\n if (a === undefined || b === undefined) continue;\n const pa = worldToPx(a.x, a.y, dim, scale);\n const pb = worldToPx(b.x, b.y, dim, scale);\n drawLine(png, pa.x, pa.y, pb.x, pb.y, FURNITURE_LINE, Math.max(1, scale));\n }\n}\n\n/** Tint already-cleaned cells, reprojecting the inner overlay grid (its OWN\n * dimensions) onto the parent pixel grid. The tint derives from the scheme's\n * path colour at low alpha. */\nfunction paintCleanedArea(\n png: PNG,\n overlay: MapCleanedAreaOverlay,\n parentDim: MapDimensions,\n scale: number,\n baseColor: Rgba,\n): void {\n const tint: Rgba = [baseColor[0], baseColor[1], baseColor[2], 55];\n const inner = overlay.dimensions;\n const block = Math.max(1, Math.round((inner.gridSize / parentDim.gridSize) * scale));\n for (const [xPx, yPx, len] of overlay.cleaned) {\n for (let i = 0; i < len; i += 1) {\n const worldX = inner.left + (xPx + i) * inner.gridSize;\n const worldY = inner.top + yPx * inner.gridSize;\n const p = worldToPx(worldX, worldY, parentDim, scale);\n for (let dy = 0; dy < block; dy += 1) {\n for (let dx = 0; dx < block; dx += 1) {\n setPixel(png, p.x + dx, p.y + dy, tint);\n }\n }\n }\n }\n}\n\n/** Build a renderable room label: uppercase the name (accents stripped, only\n * glyph-defined chars kept, capped length), falling back to `ROOM <id>` when a\n * room is unnamed or reduces to nothing renderable. */\nfunction labelText(name: string | null, id: number): string {\n const raw = (name ?? '').trim();\n const base = raw.length > 0 ? raw : `ROOM ${id}`;\n const upper = base\n .toUpperCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '');\n let out = '';\n for (const ch of upper) {\n if (GLYPHS[ch] !== undefined) out += ch;\n }\n const trimmed = out.trim().slice(0, 14);\n return trimmed.length > 0 ? trimmed : `ROOM ${id}`;\n}\n\n// ── Tiny 3×5 digit/sign font for segment labels ─────────────────────────────\n\n// Each glyph is 5 rows of a 3-bit mask (bit 2 = left column). Only the\n// characters that can appear in `String(segmentId)` are defined; an unknown\n// char is skipped so an unexpected id never throws.\nconst GLYPHS: Record<string, readonly number[]> = {\n '0': [0b111, 0b101, 0b101, 0b101, 0b111],\n '1': [0b010, 0b110, 0b010, 0b010, 0b111],\n '2': [0b111, 0b001, 0b111, 0b100, 0b111],\n '3': [0b111, 0b001, 0b111, 0b001, 0b111],\n '4': [0b101, 0b101, 0b111, 0b001, 0b001],\n '5': [0b111, 0b100, 0b111, 0b001, 0b111],\n '6': [0b111, 0b100, 0b111, 0b101, 0b111],\n '7': [0b111, 0b001, 0b010, 0b010, 0b010],\n '8': [0b111, 0b101, 0b111, 0b101, 0b111],\n '9': [0b111, 0b101, 0b111, 0b001, 0b111],\n '-': [0b000, 0b000, 0b111, 0b000, 0b000],\n ' ': [0b000, 0b000, 0b000, 0b000, 0b000],\n A: [0b010, 0b101, 0b111, 0b101, 0b101],\n B: [0b110, 0b101, 0b110, 0b101, 0b110],\n C: [0b011, 0b100, 0b100, 0b100, 0b011],\n D: [0b110, 0b101, 0b101, 0b101, 0b110],\n E: [0b111, 0b100, 0b110, 0b100, 0b111],\n F: [0b111, 0b100, 0b110, 0b100, 0b100],\n G: [0b011, 0b100, 0b101, 0b101, 0b011],\n H: [0b101, 0b101, 0b111, 0b101, 0b101],\n I: [0b111, 0b010, 0b010, 0b010, 0b111],\n J: [0b001, 0b001, 0b001, 0b101, 0b010],\n K: [0b101, 0b101, 0b110, 0b101, 0b101],\n L: [0b100, 0b100, 0b100, 0b100, 0b111],\n M: [0b101, 0b111, 0b111, 0b101, 0b101],\n N: [0b101, 0b111, 0b111, 0b111, 0b101],\n O: [0b010, 0b101, 0b101, 0b101, 0b010],\n P: [0b110, 0b101, 0b110, 0b100, 0b100],\n Q: [0b010, 0b101, 0b101, 0b110, 0b011],\n R: [0b110, 0b101, 0b110, 0b101, 0b101],\n S: [0b011, 0b100, 0b010, 0b001, 0b110],\n T: [0b111, 0b010, 0b010, 0b010, 0b010],\n U: [0b101, 0b101, 0b101, 0b101, 0b111],\n V: [0b101, 0b101, 0b101, 0b101, 0b010],\n W: [0b101, 0b101, 0b111, 0b111, 0b101],\n X: [0b101, 0b101, 0b010, 0b101, 0b101],\n Y: [0b101, 0b101, 0b010, 0b010, 0b010],\n Z: [0b111, 0b001, 0b010, 0b100, 0b111],\n};\n\n/** Draw `text` centred on `cx,cy` using the 3×5 bitmap font (× `px` per cell). */\nfunction drawLabel(png: PNG, cx: number, cy: number, text: string, color: Rgba, px: number): void {\n const glyphW = 3 * px;\n const glyphH = 5 * px;\n const gap = px;\n const totalW = text.length * glyphW + Math.max(0, text.length - 1) * gap;\n let originX = Math.round(cx - totalW / 2);\n const originY = Math.round(cy - glyphH / 2);\n for (const ch of text) {\n const glyph = GLYPHS[ch];\n if (glyph !== undefined) {\n for (let row = 0; row < 5; row += 1) {\n const bits = glyph[row];\n if (bits === undefined) continue;\n for (let col = 0; col < 3; col += 1) {\n if ((bits & (1 << (2 - col))) === 0) continue;\n for (let sy = 0; sy < px; sy += 1) {\n for (let sx = 0; sx < px; sx += 1) {\n setPixel(png, originX + col * px + sx, originY + row * px + sy, color);\n }\n }\n }\n }\n }\n originX += glyphW + gap;\n }\n}\n","/**\n * WiFi-signal map decode + render.\n *\n * The robot builds a Wi-Fi coverage map (map service SIID 6: action `wifiMap`\n * aiid 4 requests the last stored one, prop `PropWifiMap` piid 15 advertises the\n * OSS object). The blob is the SAME envelope/format as a normal map — a 27-byte\n * header + a width*height pixel grid — but each pixel's low nibble encodes the\n * Wi-Fi signal level instead of a floor/wall class (reversed from the app's\n * `getWifiMapInfo`, `mapInfo[i] & 15`):\n *\n * 10 = unreached (measured, no usable signal)\n * 11 = 1 bar (weakest) 12 = 2 bars 13 = 3 bars 14 = 4 bars (strongest)\n *\n * `left/top/gridSize` (world origin + mm-per-cell) and the robot/charger poses\n * come from the header, so a single blob is enough to sample the signal at any\n * world point — including the robot's own position.\n */\nimport { PNG } from 'pngjs';\nimport type { MapDimensions, MapPose } from './types.js';\nimport { unwrapEnvelope, looksLikeBase64Zlib, HEADER_SIZE, ANGLE_ABSENT } from './envelope.js';\nimport { parseMapHeader } from './header.js';\nimport type { VacuumMapDecodeOptions } from './types.js';\n\n/** Bars (1–4) keyed by the raw pixel nibble; 10 = unreached (0 bars). */\nconst NIBBLE_TO_BARS: Record<number, number> = { 10: 0, 11: 1, 12: 2, 13: 3, 14: 4 };\nconst isWifiNibble = (n: number): boolean => n >= 10 && n <= 14;\n\n/** Heatmap colours, matching the app (weakest→strongest), plus unreached + map outline. */\nconst NIBBLE_RGBA: Record<number, [number, number, number, number]> = {\n 2: [0xb0, 0xb6, 0xbe, 0xff], // map border (context)\n 3: [0xf2, 0xf4, 0xf7, 0xff], // mapped floor (context)\n 10: [0xe5, 0xea, 0xee, 0xff], // unreached\n 11: [0xd9, 0xe2, 0xef, 0xff], // 1 bar\n 12: [0xcd, 0xda, 0xef, 0xff], // 2 bars\n 13: [0xa1, 0xbd, 0xf2, 0xff], // 3 bars\n 14: [0x81, 0xa8, 0xf5, 0xff], // 4 bars\n};\n\nexport interface WifiSignalMap {\n readonly dimensions: MapDimensions;\n /** Robot pose from the frame header, or null when absent. */\n readonly robot: MapPose | null;\n /** Charging-dock pose from the frame header, or null when absent. */\n readonly dock: MapPose | null;\n /** Raw per-cell nibble (`pixel & 15`), row-major (width*height). */\n readonly cells: Uint8Array;\n /** Signal bars (1–4) at a world point, 0 = unreached, or null = no Wi-Fi data / off-map. */\n signalAt(worldX: number, worldY: number): number | null;\n /** Signal bars at the robot's own position (or null when unknown). */\n readonly currentSignal: number | null;\n}\n\nconst poseOrNull = (x: number, y: number, a: number): MapPose | null =>\n x === 0 && y === 0 ? null : { x, y, angle: a === ANGLE_ABSENT ? 0 : a };\n\n/** Decode a Wi-Fi map blob (raw string/Buffer or already-inflated) into signal cells + geometry. */\nexport function decodeWifiSignalMap(\n input: string | Buffer,\n opts: VacuumMapDecodeOptions = {},\n): WifiSignalMap {\n const inflated =\n typeof input === 'string'\n ? unwrapEnvelope(input, opts)\n : looksLikeBase64Zlib(input)\n ? unwrapEnvelope(input.toString('latin1'), opts)\n : input;\n\n const header = parseMapHeader(inflated);\n const { width, height, gridSize, left, top } = header;\n const pixelEnd = HEADER_SIZE + width * height;\n const grid = inflated.subarray(HEADER_SIZE, pixelEnd);\n\n const cells = new Uint8Array(width * height);\n for (let i = 0; i < cells.length; i++) cells[i] = (grid[i] ?? 0) & 0x0f;\n\n const dimensions: MapDimensions = { left, top, width, height, gridSize };\n const robot = poseOrNull(header.robotX, header.robotY, header.robotA);\n const dock = poseOrNull(header.chargerX, header.chargerY, header.chargerA);\n\n const signalAt = (worldX: number, worldY: number): number | null => {\n if (gridSize <= 0) return null;\n const col = Math.floor((worldX - left) / gridSize);\n const row = Math.floor((worldY - top) / gridSize);\n if (col < 0 || col >= width || row < 0 || row >= height) return null;\n const nibble = cells[row * width + col] ?? 0;\n return isWifiNibble(nibble) ? NIBBLE_TO_BARS[nibble]! : null;\n };\n\n return {\n dimensions,\n robot,\n dock,\n cells,\n signalAt,\n currentSignal: robot ? signalAt(robot.x, robot.y) : null,\n };\n}\n\nexport interface RenderWifiSignalPngOptions {\n /** Upscale factor (each map cell → scale×scale px). Default 4. */\n scale?: number;\n /** Draw a marker at the robot position. Default true. */\n markRobot?: boolean;\n}\n\n/**\n * Render a simple Wi-Fi coverage heatmap PNG (weakest→strongest blue, unreached\n * grey, no-data transparent), with an optional robot marker — a ready-to-show\n * image for consumers.\n */\nexport function renderWifiSignalPng(map: WifiSignalMap, opts: RenderWifiSignalPngOptions = {}): Buffer {\n const scale = Math.max(1, Math.floor(opts.scale ?? 4));\n const { width, height, left, top, gridSize } = map.dimensions;\n const png = new PNG({ width: width * scale, height: height * scale, colorType: 6 });\n\n for (let row = 0; row < height; row++) {\n for (let col = 0; col < width; col++) {\n const nibble = map.cells[row * width + col] ?? 0;\n const rgba = NIBBLE_RGBA[nibble];\n if (!rgba) continue; // no Wi-Fi data here → leave transparent\n for (let dy = 0; dy < scale; dy++) {\n for (let dx = 0; dx < scale; dx++) {\n const px = (col * scale + dx) + (row * scale + dy) * width * scale;\n const o = px << 2;\n png.data[o] = rgba[0];\n png.data[o + 1] = rgba[1];\n png.data[o + 2] = rgba[2];\n png.data[o + 3] = rgba[3];\n }\n }\n }\n }\n\n if ((opts.markRobot ?? true) && map.robot && gridSize > 0) {\n const cx = Math.floor((map.robot.x - left) / gridSize) * scale + Math.floor(scale / 2);\n const cy = Math.floor((map.robot.y - top) / gridSize) * scale + Math.floor(scale / 2);\n const r = Math.max(2, scale);\n for (let dy = -r; dy <= r; dy++) {\n for (let dx = -r; dx <= r; dx++) {\n if (dx * dx + dy * dy > r * r) continue;\n const x = cx + dx;\n const y = cy + dy;\n if (x < 0 || y < 0 || x >= width * scale || y >= height * scale) continue;\n const o = (x + y * width * scale) << 2;\n png.data[o] = 0xff;\n png.data[o + 1] = 0x45;\n png.data[o + 2] = 0x00;\n png.data[o + 3] = 0xff;\n }\n }\n }\n\n return PNG.sync.write(png);\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 {\n decodeAiFeature,\n encodeAiFeatureWrite,\n type AiDetectionRaw,\n type DreameAiFeature,\n} from './ai-detection.js';\nimport {\n decodeAutoSwitch,\n encodeAutoSwitchWrite,\n supportedAutoSwitchKeys,\n type AutoSwitchKey,\n type AutoSwitchRaw,\n} from './auto-switch.js';\nimport {\n VACUUM_CONSUMABLES,\n consumableSpec,\n type ConsumableReading,\n type DreameConsumableKey,\n} from './consumables.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 { DreameVideoSession } from '../../video/aliyun/session.js';\nimport { explainNoCameraChannel } from '../../video/camera-availability.js';\nimport { getDeviceVideoProfile } from '../../video/client.js';\nimport { MONITOR_AIID, MONITOR_PIID, MONITOR_SIID } from '../../video/monitor/constants.js';\nimport { buildActionInput, makeMonitorSession } from '../../video/monitor/protocol.js';\nimport {\n parseVideoVendorStatus,\n vendorSwitchSettled,\n videoVendorSwitchParams,\n type VideoVendorStatus,\n} from '../../video/monitor/vendor.js';\nimport type { VideoVendor } from '../../video/types.js';\nimport {\n DreameCameraController,\n type RelayMinter,\n} from '../../video/monitor/controller.js';\nimport {\n decodeVacuumMap,\n applyVacuumPFrame,\n MapDecodeError,\n OssFetcher,\n decodeWifiSignalMap,\n renderWifiSignalPng,\n type VacuumMap,\n type VacuumMapDecodeOptions,\n type OssFetchInput,\n type OssFetcherLike,\n type WifiSignalMap,\n type RenderWifiSignalPngOptions,\n} from './map/index.js';\nimport { OutOfOrderFrameError } from './map/merge.js';\nimport { looksLikeBase64Zlib, unwrapEnvelope } from './map/envelope.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\n/** Input to {@link VacuumDevice.getWifiSignalMap} / {@link VacuumDevice.getCurrentSignal}. */\nexport interface WifiSignalMapInput {\n /** Request the last stored map first (default true). `false` = read what's advertised. */\n requestFresh?: boolean;\n /** Poll interval (ms) while waiting for the OSS object to be advertised. Default 2500. */\n pollMs?: number;\n /** Max wait (ms) for the map to become available. Default 20000. */\n maxWaitMs?: number;\n /** Inject the signed-blob fetcher (tests pass a fake). */\n fetcher?: OssFetcherLike;\n /** Optional AES key/iv for an encrypted blob (hex). */\n key?: string;\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 // -- consumables --------------------------------------------------------\n /**\n * Remaining-life % (0..100) for one consumable, or `null` when the model does\n * not report it (presence == support). Call {@link refreshConsumables} first\n * on a docked robot so the cloud-shadow life values are seeded.\n */\n consumableLeftPct(key: DreameConsumableKey): number | null {\n const spec = consumableSpec(key);\n if (spec === undefined) return null;\n return this.#num(spec.life.siid, spec.life.piid);\n }\n\n /**\n * The consumables this model EXPOSES — discovered by presence (a reported\n * life property), mirroring how the HA integration derives support. Each\n * reading carries the current remaining-life % and whether a reset action\n * exists. Empty until the life properties have been observed (push) or seeded\n * via {@link refreshConsumables}.\n */\n get supportedConsumables(): readonly ConsumableReading[] {\n return VACUUM_CONSUMABLES.flatMap((spec) => {\n const leftPct = this.#num(spec.life.siid, spec.life.piid);\n return leftPct === null\n ? []\n : [{ key: spec.key, label: spec.label, leftPct, resettable: spec.reset !== null }];\n });\n }\n\n /**\n * Seed EVERY consumable life property from the cloud shadow (no robot wake) so\n * {@link supportedConsumables} reflects the full set on a docked robot. The\n * consumable life values are static settings the robot rarely re-pushes over\n * MQTT, so a fresh connect surfaces only the actively-changing ones without this.\n */\n async refreshConsumables(): Promise<void> {\n await this.refreshCachedProperties(VACUUM_CONSUMABLES.map((c) => c.life));\n }\n\n /**\n * Reset (mark replaced → life back to 100%) one consumable via its MIoT reset\n * action. Rejects when the model exposes no reset action for the key (e.g.\n * `dust-bag`) or the key is unknown.\n */\n async resetConsumable(key: DreameConsumableKey): Promise<unknown> {\n const spec = consumableSpec(key);\n if (spec === undefined || spec.reset === null) {\n throw new DreameError(`resetConsumable: no reset action for consumable \"${key}\"`);\n }\n return this.callAction(spec.reset.siid, spec.reset.aiid, []);\n }\n\n // -- AI obstacle-detection ----------------------------------------------\n /**\n * The raw `AI_DETECTION` value (siid 4 piid 22) — an int bitmask or a JSON\n * string, depending on firmware — or `null` when not yet observed. Prefer the\n * decoded {@link aiFeature} / {@link supportedAiFeatures} surface.\n */\n get aiDetectionRaw(): AiDetectionRaw {\n const v = this.getProperty(VACUUM_PROP.AI_DETECTION.siid, VACUUM_PROP.AI_DETECTION.piid)?.value;\n return typeof v === 'number' || typeof v === 'string' ? v : null;\n }\n\n /** The AI-obstacle toggles this model exposes (from its capability record). */\n get supportedAiFeatures(): readonly DreameAiFeature[] {\n return this.#caps.supportedAiFeatures;\n }\n\n /**\n * Read ONE AI-obstacle toggle's on/off state, decoded from the packed\n * `AI_DETECTION` value (transparent across the int / JSON encodings). `null`\n * when the value is unobserved or the feature is not representable.\n */\n aiFeature(feature: DreameAiFeature): boolean | null {\n return decodeAiFeature(this.aiDetectionRaw, feature);\n }\n\n /**\n * Seed {@link aiDetectionRaw} from the CLOUD SHADOW (the last value the robot\n * pushed for `AI_DETECTION`, siid 4 piid 22) WITHOUT waking it. The AI-obstacle\n * toggles are STATIC settings the robot rarely re-pushes over MQTT, so a fresh\n * connect to an idle robot has no value cached — call this on activate to\n * surface the current toggles. Emits the usual `propertyChanged`/`stateChanged`\n * (so a watching consumer re-decodes) and returns the resolved raw value\n * (`null` when the model has no AI detection or the shadow carries none yet).\n */\n async refreshAiDetection(): Promise<AiDetectionRaw> {\n if (!this.#caps.hasAiObstacleDetection) return null;\n await this.refreshCachedProperties([VACUUM_PROP.AI_DETECTION]);\n return this.aiDetectionRaw;\n }\n\n /**\n * Toggle ONE AI-obstacle feature, preserving the others via a read-modify-write\n * of the packed `AI_DETECTION` property (mirrors Tasshack `set_ai_detection`).\n * Capability-gated on `hasAiObstacleDetection`; rejects when the current value\n * is unknown (a blind write would clobber the other toggles).\n */\n async setAiFeature(feature: DreameAiFeature, value: boolean): Promise<unknown> {\n this.#requireCap(this.#caps.hasAiObstacleDetection, 'setAiFeature', 'AI obstacle detection');\n const next = encodeAiFeatureWrite(this.aiDetectionRaw, feature, value);\n return this.setProperty({ ...VACUUM_PROP.AI_DETECTION, value: next });\n }\n\n // -- auto-switch settings -----------------------------------------------\n /**\n * The raw `AUTO_SWITCH_SETTINGS` value (siid 4 piid 50) — a JSON string packing\n * every secondary toggle/setting — or `null` when not yet observed. Prefer the\n * decoded {@link autoSwitchProperty} / {@link supportedAutoSwitchKeys} surface.\n */\n get autoSwitchRaw(): AutoSwitchRaw {\n const v = this.getProperty(\n VACUUM_PROP.AUTO_SWITCH_SETTINGS.siid,\n VACUUM_PROP.AUTO_SWITCH_SETTINGS.piid,\n )?.value;\n return typeof v === 'string' ? v : null;\n }\n\n /** Whether this model reports the packed auto-switch bundle at all. */\n get hasAutoSwitchSettings(): boolean {\n return this.autoSwitchRaw !== null;\n }\n\n /**\n * The auto-switch settings this model reports, decoded from the packed value.\n * Presence-driven (a key appears iff the payload carries it) — mirrors\n * Tasshack `auto_switch_data`.\n */\n get supportedAutoSwitchKeys(): readonly AutoSwitchKey[] {\n return supportedAutoSwitchKeys(this.autoSwitchRaw);\n }\n\n /**\n * Read ONE auto-switch setting's int value, decoded from the packed\n * `AUTO_SWITCH_SETTINGS` payload. `null` when the value is unobserved or the\n * key is not present. The value is an opaque int (boolean 0/1, a small enum, or\n * a `-1`/`-n` \"not applicable\" sentinel) — the caller interprets it.\n */\n autoSwitchProperty(key: AutoSwitchKey): number | null {\n return decodeAutoSwitch(this.autoSwitchRaw, key);\n }\n\n /**\n * Seed {@link autoSwitchRaw} from the CLOUD SHADOW (the last value the robot\n * pushed for `AUTO_SWITCH_SETTINGS`, siid 4 piid 50) WITHOUT waking it. Like the\n * AI bundle these are STATIC settings the robot rarely re-pushes over MQTT, so a\n * fresh connect to an idle robot has no value cached — call this on activate to\n * surface the current settings. Returns the resolved raw value (`null` when the\n * shadow carries none yet).\n */\n async refreshAutoSwitch(): Promise<AutoSwitchRaw> {\n await this.refreshCachedProperties([VACUUM_PROP.AUTO_SWITCH_SETTINGS]);\n return this.autoSwitchRaw;\n }\n\n /**\n * Set ONE auto-switch setting. Mirrors Tasshack `set_auto_switch_property`: a\n * single-key `{\"k\":<key>,\"v\":<int>}` write the device merges server-side (the\n * other settings are preserved without a read-modify-write). Rejects when the\n * model does not report the bundle.\n */\n async setAutoSwitchProperty(key: AutoSwitchKey, value: number): Promise<unknown> {\n this.#requireCap(this.hasAutoSwitchSettings, 'setAutoSwitchProperty', 'auto-switch settings');\n const next = encodeAutoSwitchWrite(key, value);\n return this.setProperty({ ...VACUUM_PROP.AUTO_SWITCH_SETTINGS, value: next });\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 OSS object name of the latest map frame the robot advertised via its\n * PATH push (siid 6 piid 3), or `null` when none has been observed yet (the\n * robot has not uploaded a map since connect). This is the `filename` to pass\n * to {@link getMap} — or just call {@link fetchLatestMap}.\n */\n get mapFilename(): string | null {\n const v = this.getProperty(VACUUM_PROP.MAP_PATH.siid, VACUUM_PROP.MAP_PATH.piid)?.value;\n return typeof v === 'string' && v.length > 0 ? v : null;\n }\n\n /**\n * Convenience: fetch + decode the LATEST map frame the robot advertised,\n * reading {@link mapFilename} and delegating to {@link getMap}. Returns `null`\n * when no map filename has been observed yet (vs throwing) so a consumer can\n * poll it safely; once a filename is present it behaves exactly like\n * {@link getMap} (capability-gated on `canMap`, caches {@link lastMap}, emits\n * `'map'`). The `fetcher`/`key`/`iv`/`host`/`timeoutMs`/`signal` overrides are\n * forwarded verbatim.\n */\n async fetchLatestMap(opts: Omit<VacuumGetMapInput, 'filename'> = {}): Promise<VacuumMap | null> {\n const filename = this.mapFilename;\n if (filename === null) return null;\n return this.getMap({ filename, ...opts });\n }\n\n /**\n * Seed {@link mapFilename} from the CLOUD SHADOW (the last value the robot\n * pushed for siid 6 piid 3) WITHOUT waking it — so a docked/idle robot can\n * surface its LAST cleaning map without a fresh clean. Emits the usual\n * `propertyChanged`/`stateChanged` (so a map-watching consumer re-renders) and\n * returns the resolved {@link mapFilename} (null when the model has no map or\n * the shadow carries no map path yet).\n *\n * NOTE: the shadow holds the last LIVE-PATH filename; its OSS blob may have\n * expired, in which case a follow-up {@link fetchLatestMap} rejects — treat\n * that as \"no saved map available\".\n */\n async refreshSavedMapFilename(): Promise<string | null> {\n if (!this.#caps.canMap) return null;\n await this.refreshCachedProperties([VACUUM_PROP.MAP_PATH]);\n return this.mapFilename;\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 blob = await this.#fetchMapBlob(input);\n const map = decodeVacuumMap(blob, this.#decodeOpts(input));\n this.#lastMap = map;\n this.emit('map', map);\n return map;\n }\n\n /** Fetch the raw OSS blob (still the base64+zlib envelope) for a map frame. */\n async #fetchMapBlob(input: VacuumGetMapInput): Promise<Buffer> {\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 return fetcher.fetchBlob(fetchInput);\n }\n\n /** The AES key/iv decode options, threaded from a {@link VacuumGetMapInput}. */\n #decodeOpts(input: Pick<VacuumGetMapInput, 'key' | 'iv'>): VacuumMapDecodeOptions {\n return {\n ...(input.key !== undefined ? { key: input.key } : {}),\n ...(input.iv !== undefined ? { iv: input.iv } : {}),\n };\n }\n\n /**\n * Fetch the robot's Wi-Fi signal map — the last stored coverage heatmap, with\n * NO robot movement. Requests it (map service `wifiMap` action, siid 6 aiid 4),\n * polls `PropWifiMap` (6/15) for the OSS object it advertises, fetches + decodes\n * it into a per-cell signal grid + geometry + robot/dock poses. The signal at\n * any world point (incl. the robot's own position) is a lookup — see\n * {@link WifiSignalMap.signalAt} / {@link WifiSignalMap.currentSignal}.\n *\n * Pass `requestFresh: false` to skip the request and read whatever is already\n * advertised. Throws if no Wi-Fi map exists yet (never run on this device).\n */\n async getWifiSignalMap(input: WifiSignalMapInput = {}): Promise<WifiSignalMap> {\n if (input.requestFresh !== false) {\n try {\n await this.callAction(6, 4, []);\n } catch {\n /* best-effort: the object may already be advertised */\n }\n }\n const pollMs = input.pollMs ?? 2500;\n const deadline = Date.now() + (input.maxWaitMs ?? 20_000);\n let objectName = '';\n for (;;) {\n const res = await this.refreshProperties([{ siid: 6, piid: 15 }]);\n const val = res[0]?.value;\n if (typeof val === 'string' && val.length > 0) {\n objectName = val;\n break;\n }\n if (Date.now() + pollMs >= deadline) {\n throw new DreameError('wifi signal map not available (none stored yet, or request timed out)');\n }\n await new Promise((r) => setTimeout(r, pollMs));\n }\n // The property is `\"<n>,<oss-object-name>\"`; the OSS fetcher wants the object name.\n const filename = objectName.replace(/^\\d+,/, '');\n const blob = await this.#fetchMapBlob({\n filename,\n ...(input.fetcher ? { fetcher: input.fetcher } : {}),\n ...(input.host ? { host: input.host } : {}),\n ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),\n ...(input.signal ? { signal: input.signal } : {}),\n });\n return decodeWifiSignalMap(blob, this.#decodeOpts(input));\n }\n\n /**\n * Render the Wi-Fi signal map as a simple PNG heatmap image (weakest→strongest,\n * unreached grey, robot marker) — ready to display. Convenience over\n * {@link getWifiSignalMap} + {@link renderWifiSignalPng}.\n */\n async getWifiSignalImage(\n input: WifiSignalMapInput & RenderWifiSignalPngOptions = {},\n ): Promise<Buffer> {\n const map = await this.getWifiSignalMap(input);\n return renderWifiSignalPng(map, input);\n }\n\n /**\n * The one-call \"current Wi-Fi signal\": fetches the signal map and returns the\n * bars (1–4, 0 = unreached) at the robot's own position, or null when unknown.\n */\n async getCurrentSignal(input: WifiSignalMapInput = {}): Promise<number | null> {\n const map = await this.getWifiSignalMap(input);\n return map.currentSignal;\n }\n\n /**\n * The merge base for live P-frame streaming — always an INFLATED frame buffer.\n * An I-frame (re)seeds it; each P-frame merge replaces it with the merged\n * inflated buffer (so further P-frames stack). `null` until the first I-frame\n * (or after an out-of-order / map-id reset).\n */\n #mapStreamBase: Buffer | null = null;\n\n /** Drop the P-frame merge base so the next I-frame re-seeds the stream. */\n resetMapStream(): void {\n this.#mapStreamBase = null;\n }\n\n /** Inflate an OSS blob to a raw frame buffer (live envelope → zlib; verbatim if already inflated). */\n #inflateFrame(blob: Buffer, opts: VacuumMapDecodeOptions): Buffer {\n return looksLikeBase64Zlib(blob) ? unwrapEnvelope(blob.toString('latin1'), opts) : blob;\n }\n\n /**\n * Fetch the latest advertised map frame and fold it into a continuously\n * updating map. An I-frame (re)seeds the merge base and renders standalone; a\n * P-frame is merged onto the base via {@link applyVacuumPFrame} so the live\n * grid stays COMPLETE (a P-frame decoded standalone is only byte-deltas). On an\n * out-of-order P-frame or a map-id change the base is dropped and `null`\n * returned — the next I-frame re-seeds. Returns `null` when no frame is\n * advertised yet, or a P-frame arrives before any I-frame.\n *\n * Caches {@link lastMap} and emits `'map'` exactly like {@link getMap}, so a\n * map-watching consumer (e.g. the camstack map child) gets a fresh complete\n * frame on every push during a live clean.\n */\n async fetchLatestMapStreaming(\n opts: Omit<VacuumGetMapInput, 'filename'> = {},\n ): Promise<VacuumMap | null> {\n this.#requireCap(this.#caps.canMap, 'fetchLatestMapStreaming', 'map decoding');\n const filename = this.mapFilename;\n if (filename === null) return null;\n const input: VacuumGetMapInput = { filename, ...opts };\n const decodeOpts = this.#decodeOpts(input);\n const blob = await this.#fetchMapBlob(input);\n const inflated = this.#inflateFrame(blob, decodeOpts);\n const frame = decodeVacuumMap(inflated, decodeOpts);\n\n if (frame.frameType === 'I') {\n this.#mapStreamBase = inflated;\n this.#lastMap = frame;\n this.emit('map', frame);\n return frame;\n }\n\n if (frame.frameType === 'P') {\n if (this.#mapStreamBase === null) return null; // need an I-frame first\n try {\n // Both base and incoming frame are inflated buffers → mergePFrame path.\n const { buffer, data } = applyVacuumPFrame(this.#mapStreamBase, inflated);\n this.#mapStreamBase = buffer;\n this.#lastMap = data;\n this.emit('map', data);\n return data;\n } catch (err) {\n // Out-of-order or a fresh map (id mismatch / grid change): drop the base\n // and wait for the next I-frame to re-seed rather than render garbage.\n if (err instanceof OutOfOrderFrameError || err instanceof MapDecodeError) {\n this.#mapStreamBase = null;\n return null;\n }\n throw err;\n }\n }\n\n // 'W' (saved-map) or any other type: render standalone, leave the base as-is.\n this.#lastMap = frame;\n this.emit('map', frame);\n return frame;\n }\n\n /**\n * Build a {@link DreameCameraController} for this device's camera, wired to\n * both planes it needs: the MIoT action transport (this device handle) and a\n * fresh Aliyun {@link DreameVideoSession} for the relay. Call\n * `controller.open()` to autonomously cold-start the stream and get a pullable\n * RTMP URL, then `controller.close()` to release it.\n *\n * The camera's per-session privacy gate means a coded camera needs its PIN:\n * pass `accessCode` (the code set at pairing). `iotId` is resolved from the\n * device's cloud video profile when not supplied.\n */\n async createCameraController(opts?: {\n /** LinkVisual channel id; resolved from the cloud profile when omitted. */\n iotId?: string;\n /** Privacy PIN set at pairing (required to wake a coded camera). */\n accessCode?: string;\n /** Video vendor; defaults to `ali` (the only vendor that mints an RTMP relay). */\n vendor?: 'ali' | 'tx';\n /** Aliyun region code reported as `area`; defaults to `'4'`. */\n area?: string;\n /** Keep-alive cadence in ms (default 10s). */\n keepAliveIntervalMs?: number;\n /** Max time to wait for the relay while the device wakes. */\n wakeTimeoutMs?: number;\n /** Reported when a keep-alive tick fails. */\n onKeepAliveError?: (err: unknown) => void;\n /** Optional nodelink-style line logger. */\n log?: (line: string) => void;\n /** Reuse an existing relay minter (e.g. a shared {@link DreameVideoSession}). */\n relay?: RelayMinter;\n }): Promise<DreameCameraController> {\n const session = this.currentSession();\n const region = this.region;\n const relay =\n opts?.relay ?? new DreameVideoSession({ session, region });\n // The profile is KEPT, not reduced to its `iotId` and discarded. When the\n // channel is missing it is the only thing that can say why — see\n // `explainNoCameraChannel`. The old sentence named the missing field and\n // covered four different causes with it, and telling them apart meant\n // reading this library's source.\n let iotId = opts?.iotId ?? null;\n if (!iotId) {\n const profile = await getDeviceVideoProfile({ session, region, did: this.deviceId });\n iotId = profile.iotId;\n if (!iotId) {\n throw new DreameError(explainNoCameraChannel(profile) ?? 'device has no video channel');\n }\n }\n return new DreameCameraController({\n device: this,\n relay,\n iotId,\n accountId: session.uid,\n ...(opts?.vendor ? { vendor: opts.vendor } : {}),\n ...(opts?.accessCode !== undefined ? { accessCode: opts.accessCode } : {}),\n ...(opts?.area ? { area: opts.area } : {}),\n ...(opts?.keepAliveIntervalMs !== undefined\n ? { keepAliveIntervalMs: opts.keepAliveIntervalMs }\n : {}),\n ...(opts?.wakeTimeoutMs !== undefined ? { wakeTimeoutMs: opts.wakeTimeoutMs } : {}),\n ...(opts?.onKeepAliveError ? { onKeepAliveError: opts.onKeepAliveError } : {}),\n ...(opts?.log ? { log: opts.log } : {}),\n });\n }\n\n /**\n * What video backend this robot is on, and whether that backend's SDK is up.\n *\n * A CACHED read by default: the cloud shadow answers for a robot on its dock\n * without waking it, which is the whole point of asking before deciding how\n * to stream. Pass `{ live: true }` when the answer must be current — while a\n * switch settles, for instance.\n */\n async readVideoVendorStatus(opts?: { live?: boolean }): Promise<VideoVendorStatus> {\n const props = [{ siid: MONITOR_SIID, piid: MONITOR_PIID.VIDEO_VENDOR_STATUS }];\n const results = opts?.live === true\n ? await this.refreshProperties(props)\n : await this.refreshCachedProperties(props);\n return parseVideoVendorStatus(results[0]?.value);\n }\n\n /**\n * Move this robot onto a video backend, and WAIT until it is really there.\n *\n * The two backends are not interchangeable: measured on an X50 on\n * 2026-09-16, a robot on `tx` offers only Tencent's proprietary UDP P2P\n * plane (`getRtcInfo` answers 404), while the same robot on `ali` streams\n * through a plain RTMP relay this library implements end to end. So which\n * one it sits on decides whether it can be streamed at all.\n *\n * The device reports the NEW vendor before its SDK is up, which is why the\n * app polls rather than trusting the action's return — and why this resolves\n * on `initStatus === 1` and not a moment earlier. A half-switched robot\n * answers questions about a backend it cannot yet serve.\n *\n * Only meaningful on a dual-vendor device (`videoDynamicVendor`, with the\n * target in `defaultVendors`); a device that cannot move simply never\n * settles, and this reports that as a timeout rather than a silent success.\n */\n async setVideoVendor(\n vendor: VideoVendor,\n opts?: { timeoutMs?: number; pollIntervalMs?: number },\n ): Promise<VideoVendorStatus> {\n const already = await this.readVideoVendorStatus({ live: true });\n if (vendorSwitchSettled(already, vendor)) {\n return already;\n }\n // THROUGH THE ONE BUILDER. The device wants `value` as a JSON STRING that\n // also carries a `session`; an object without one is silently ignored —\n // measured on an X50 on 2026-09-16, which stayed on `tx` with\n // `initStatus: 1` and never protested. Hand-shaping this payload is how\n // that happened, so it is shaped where every other camera action is.\n const session = makeMonitorSession(this.currentSession().uid);\n await this.callAction(MONITOR_SIID, MONITOR_AIID.VIDEO_VENDOR, [\n buildActionInput(MONITOR_PIID.VIDEO_VENDOR_STATUS, videoVendorSwitchParams(vendor), session),\n ]);\n // The app's own cadence: every 5 s, ten times.\n const every = opts?.pollIntervalMs ?? 5_000;\n const deadline = Date.now() + (opts?.timeoutMs ?? every * 10);\n let last = already;\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, every));\n last = await this.readVideoVendorStatus({ live: true });\n if (vendorSwitchSettled(last, vendor)) {\n return last;\n }\n }\n throw new DreameError(\n `video vendor did not settle on \"${vendor}\" within the timeout ` +\n `(last seen: vendor=${last.vendor ?? 'unknown'}, initStatus=${String(last.initStatus)})`,\n );\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 VACUUM_PROP.AI_DETECTION,\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 — 20-byte heartbeat (sentinel 0xCE at [0]/[19]); byte 11\n * battery, byte 12 main-state, byte 13 task sub-state. See parseHeartbeat. */\n HEARTBEAT: { siid: 1, piid: 1 } as const,\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 SET_CURRENT_MAP: 200,\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/** Switch active map: o:200, idx=mapIndex. device.py _build_set_current_map_payload. */\nexport function buildSetCurrentMapPayload(mapIndex: number): {\n m: string;\n p: number;\n o: number;\n d: { idx: number };\n} {\n return { m: 'a', p: 0, o: TASK_OPCODE.SET_CURRENT_MAP, d: { idx: mapIndex } };\n}\n\n/**\n * CMS consumable getter payload `{m:'g', t:'CMS'}` — read the blade/brush/robot\n * minute counters. Sent as the single in-param of the SCHEDULING_TASK (2:50)\n * action. device.py `_build_get_consumable_payload`.\n */\nexport function buildGetConsumablePayload(): { m: string; t: string } {\n return { m: 'g', t: 'CMS' };\n}\n\n/**\n * CMS consumable setter payload `{m:'s', t:'CMS', d:{value:[...]}}` — write the\n * three counters (used to reset one to 0). device.py\n * `_build_set_consumable_payload`. Throws on a non-3 / negative counter list.\n */\nexport function buildSetConsumablePayload(values: number[]): {\n m: string;\n t: string;\n d: { value: number[] };\n} {\n const normalized = values.map((v) => Math.trunc(v));\n if (normalized.length !== 3) {\n throw new RangeError(`CMS values must contain exactly 3 counters; got ${normalized.length}`);\n }\n if (normalized.some((v) => v < 0)) {\n throw new RangeError(`CMS values cannot be negative; got ${JSON.stringify(normalized)}`);\n }\n return { m: 's', t: 'CMS', d: { value: normalized } };\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/** Severity class of a mower DEVICE_CODE (2:2 / {@link MowerFault}). */\nexport type MowerFaultSeverity = 'info' | 'warning' | 'error';\n\n/**\n * Classify a mower device-code by severity, matching the INFO/WARNING/ERROR\n * annotations on {@link MowerFault}. Most codes are status/INFO (e.g. 50\n * TaskStart); only a subset are actionable. Unknown codes default to `info`.\n */\nexport function mowerFaultSeverity(code: number | null): MowerFaultSeverity {\n if (code === null) return 'info';\n if ((code >= 1 && code <= 30) || code === 37 || code === 73) return 'error';\n if ((code >= 31 && code <= 36) || (code >= 38 && code <= 45)) return 'warning';\n return 'info';\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// -- CMS consumables (custom action on SCHEDULING_TASK 2:50) -----------------\n\n/** Canonical CMS consumable counter keys (blade, brush, robot maintenance). */\nexport type MowerConsumableKey = 'blade' | 'brush' | 'maintenance';\n\n/** One CMS consumable counter: minutes used + total + remaining %. */\nexport interface MowerConsumableReading {\n readonly key: MowerConsumableKey;\n /** Minutes of run-time accrued on this counter (counts UP). */\n readonly usedMinutes: number;\n /** Counter's full-life duration in minutes. */\n readonly totalMinutes: number;\n /** Remaining life percentage (0..100, one decimal). */\n readonly remainingPercent: number;\n}\n\n/**\n * CMS counter layout, ported from antondaubert/dreame-mower `device.py`\n * (CONSUMABLE_COUNTER_TOTAL_MINUTES + CONSUMABLE_COUNTER_INDEX). The getter\n * returns `[blade_min, brush_min, robot_min]`; index/total are fixed per model.\n */\nconst MOWER_CONSUMABLES: ReadonlyArray<{\n readonly key: MowerConsumableKey;\n readonly index: number;\n readonly totalMinutes: number;\n}> = [\n { key: 'blade', index: 0, totalMinutes: 6000 },\n { key: 'brush', index: 1, totalMinutes: 30000 },\n { key: 'maintenance', index: 2, totalMinutes: 3600 },\n];\n\n/** Map a consumable key (incl. aliases) to its CMS counter index. */\nexport function mowerConsumableIndex(item: string): number | null {\n switch (item.trim().toLowerCase()) {\n case 'blade':\n case 'blades':\n return 0;\n case 'brush':\n case 'cleaning_brush':\n return 1;\n case 'robot':\n case 'maintenance':\n case 'robot_maintenance':\n return 2;\n default:\n return null;\n }\n}\n\nfunction toInt(v: unknown): number | null {\n if (typeof v === 'number' && Number.isFinite(v)) {\n return Math.trunc(v);\n }\n if (typeof v === 'string') {\n const n = Number(v);\n return Number.isFinite(n) ? Math.trunc(n) : null;\n }\n return null;\n}\n\n/**\n * Pull the first successful data payload out of a custom-action result. Mirrors\n * the donor `_extract_custom_action_data`: the result itself when it carries a\n * `value` list, else its `d` object, else the first non-error `out[].d`.\n */\nfunction extractCustomActionData(result: unknown): Record<string, unknown> | null {\n if (!isRecord(result)) {\n return null;\n }\n if (Array.isArray(result['value'])) {\n return result;\n }\n if (isRecord(result['d'])) {\n return result['d'];\n }\n const out = result['out'];\n if (!Array.isArray(out)) {\n return null;\n }\n for (const entry of out) {\n if (!isRecord(entry)) {\n continue;\n }\n const r = entry['r'];\n const code = entry['code'];\n const rError = r !== undefined && r !== null && r !== 0;\n const codeError = code !== undefined && code !== null && code !== 0;\n if (rError && codeError) {\n continue;\n }\n if (isRecord(entry['d'])) {\n return entry['d'];\n }\n }\n return null;\n}\n\n/**\n * Extract the raw `[blade, brush, robot]` minute counters from a CMS getter\n * result, or null if the response is malformed. Cast-free, no throw.\n */\nexport function extractMowerConsumableValues(result: unknown): number[] | null {\n const data = extractCustomActionData(result);\n if (data === null) {\n return null;\n }\n const values = data['value'];\n if (!Array.isArray(values) || values.length < 3) {\n return null;\n }\n const out: number[] = [];\n for (const v of values.slice(0, 3)) {\n const n = toInt(v);\n if (n === null) {\n return null;\n }\n out.push(n);\n }\n return out;\n}\n\n/**\n * Parse a CMS getter result into typed consumable readings (blade/brush/\n * maintenance) with remaining %. Returns null on a malformed response.\n */\nexport function parseMowerConsumables(result: unknown): readonly MowerConsumableReading[] | null {\n const values = extractMowerConsumableValues(result);\n if (values === null) {\n return null;\n }\n return MOWER_CONSUMABLES.map((c) => {\n const used = values[c.index] ?? 0;\n const remaining = c.totalMinutes - used;\n const pct = Math.max(0, Math.min(100, Math.round((remaining / c.totalMinutes) * 1000) / 10));\n return {\n key: c.key,\n usedMinutes: used,\n totalMinutes: c.totalMinutes,\n remainingPercent: pct,\n };\n });\n}\n\n// -- heartbeat task sub-state (HEARTBEAT 1:1) --------------------------------\n\n/** Decoded mowing task sub-state (heartbeat byte 13), or null when no mowing\n * session is in progress. Ported from antondaubert/dreame-mower\n * `property_misc.py` Property11Handler. */\nexport type MowerTaskSubState =\n | 'idle'\n | 'starting'\n | 'mowing'\n | 'paused'\n | 'finished'\n | 'failed'\n | 'exit'\n | 'returning-to-dock';\n\n/** Ordered sub-state table; the heartbeat encodes `subState = base + index`. */\nexport const MOWER_TASK_SUBSTATES: readonly MowerTaskSubState[] = [\n 'idle',\n 'starting',\n 'mowing',\n 'paused',\n 'finished',\n 'failed',\n 'exit',\n 'returning-to-dock',\n];\n\nconst HEARTBEAT_SENTINEL = 0xce; // 206 — byte[0] and byte[last]\nconst HEARTBEAT_SUBSTATE_BASE = 33;\nconst HEARTBEAT_MAIN_STATE_MOWING = 4;\n\n/** Typed heartbeat decode: battery byte, main-state, and (when mowing) the task\n * sub-state. */\nexport interface MowerHeartbeat {\n /** Raw battery byte (byte 11) — percent with a charging flag in the high bits. */\n readonly rawBattery: number;\n /** `(byte12 & 0x0F) - 1`; 4 = mowing. */\n readonly mainState: number;\n /** Raw sub-state byte (byte 13). */\n readonly subStateRaw: number;\n /** Decoded sub-state when a mowing session is active, else null. */\n readonly taskSubState: MowerTaskSubState | null;\n}\n\n/**\n * Decode the 20-byte HEARTBEAT (1:1) blob. Returns null when the payload is not a\n * well-formed heartbeat (wrong length / missing 0xCE sentinels). The task\n * sub-state is only meaningful while `mainState === 4` (mowing); in any other\n * main state there is no active mowing task (returns `taskSubState: null`).\n */\nexport function parseMowerHeartbeat(value: unknown): MowerHeartbeat | null {\n if (!Array.isArray(value) || value.length < 14) {\n return null;\n }\n const bytes: number[] = [];\n for (const b of value) {\n if (typeof b !== 'number') {\n return null;\n }\n bytes.push(b);\n }\n if (bytes[0] !== HEARTBEAT_SENTINEL || bytes[bytes.length - 1] !== HEARTBEAT_SENTINEL) {\n return null;\n }\n const rawBattery = bytes[11] ?? 0;\n const mainState = ((bytes[12] ?? 0) & 0x0f) - 1;\n const subStateRaw = bytes[13] ?? 0;\n const taskSubState =\n mainState === HEARTBEAT_MAIN_STATE_MOWING\n ? (MOWER_TASK_SUBSTATES[subStateRaw - HEARTBEAT_SUBSTATE_BASE] ?? null)\n : null;\n return { rawBattery, mainState, subStateRaw, taskSubState };\n}\n\n// -- mowing progress (POSE_COVERAGE 1:4 task block) --------------------------\n\nconst POSE_SENTINEL = 0xce; // 206 — frames a sentinel-wrapped pose-coverage blob\n\n/** Decoded mowing-progress signal from the POSE_COVERAGE (1:4) `task` block. */\nexport interface MowerMowingProgress {\n /** 0..100 — overall mowing completion. */\n readonly progressPercent: number;\n /** Already-finished area in m². */\n readonly currentAreaSqm: number;\n /** Total target area in m². */\n readonly totalAreaSqm: number;\n}\n\n/** Coerce a POSE_COVERAGE (1:4) raw value to a flat byte array, or null. */\nfunction poseBytes(value: unknown): number[] | null {\n if (!Array.isArray(value)) return null;\n const bytes: number[] = [];\n for (const b of value) {\n if (typeof b !== 'number') return null;\n bytes.push(b);\n }\n return bytes;\n}\n\n/**\n * Locate the 10-byte `task` block inside a POSE_COVERAGE (1:4) frame and decode\n * the mowing progress. Frame shapes (ported from antondaubert/dreame-mower\n * `pose_coverage.py`):\n * - sentinel-framed `[CE] pose(6) trace(15) task(10) [CE]` (33B) / `…task(10) trace2(11) [CE]` (44B)\n * → task at offset 22;\n * - alt `task(10) [CE] [+trace]` (no leading sentinel, ≥11B) → task at offset 0.\n * The 22B `[CE] pose trace` and 13B `[CE] pose extra` shapes carry NO task → null.\n *\n * `task` layout: `[2:4] percent u16LE (value×100) [4:7] total u24LE (centi-m²)\n * [7:10] finished u24LE (centi-m²)`.\n */\nexport function parseMowingProgress(value: unknown): MowerMowingProgress | null {\n const bytes = poseBytes(value);\n if (bytes === null || bytes.length < 11) return null;\n const last = bytes[bytes.length - 1];\n const head = bytes[0];\n\n let offset: number | null = null;\n if (head !== POSE_SENTINEL && last === POSE_SENTINEL) {\n offset = 0; // alt format: task block leads\n } else if (head === POSE_SENTINEL && last === POSE_SENTINEL && bytes.length >= 33) {\n offset = 22; // standard sentinel-framed frame with a task block\n }\n if (offset === null || offset + 10 > bytes.length) return null;\n\n const rawPercent = (bytes[offset + 2] ?? 0) | ((bytes[offset + 3] ?? 0) << 8);\n const total =\n (bytes[offset + 4] ?? 0) | ((bytes[offset + 5] ?? 0) << 8) | ((bytes[offset + 6] ?? 0) << 16);\n const finish =\n (bytes[offset + 7] ?? 0) | ((bytes[offset + 8] ?? 0) << 8) | ((bytes[offset + 9] ?? 0) << 16);\n\n return {\n progressPercent: rawPercent ? Math.min(100, rawPercent / 100) : 0,\n currentAreaSqm: finish / 100,\n totalAreaSqm: total / 100,\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 = '#32cd32';\nconst NAV_PATH = '#b4b4b4';\nconst OBSTACLE_STROKE = '#ff4d00';\nconst OBSTACLE_FILL = '#ff4d0065';\nconst TEXT_COLOR = '#000000';\nconst ZONE_LABEL_COLOR = '#3c3c3c';\n\n/** Escape the five XML metacharacters so a room name can't break the SVG. */\nfunction escapeXml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\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\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 — per-zone translucent pastel (always, matching the app).\n map.zones.forEach((zone, i) => {\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 (zone-tinted).\n map.zones.forEach((zone, i) => {\n const outline = ZONE_COLORS[i % ZONE_COLORS.length]?.[1] ?? 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 // 5. Zone NAME labels at each zone's centroid (escaped; on top of fills).\n for (const zone of map.zones) {\n if (zone.name.length === 0 || zone.path.length === 0) {\n continue;\n }\n let sumX = 0;\n let sumY = 0;\n for (const p of zone.path) {\n sumX += p.x;\n sumY += p.y;\n }\n const centroid: MowerPoint = { x: sumX / zone.path.length, y: sumY / zone.path.length };\n const { px, py } = coordToPixel(centroid, bounds, width, height, padding);\n lines.push(\n `<text x=\"${px}\" y=\"${py}\" font-family=\"Arial, sans-serif\" font-size=\"20\" ` +\n `font-weight=\"bold\" fill=\"${ZONE_LABEL_COLOR}\" text-anchor=\"middle\" ` +\n `dominant-baseline=\"middle\">${escapeXml(zone.name)}</text>`,\n );\n }\n\n lines.push('</svg>');\n return lines.join('\\n');\n}\n","import { BaseDevice, type BaseDeviceInput } from '../../device/base-device.js';\nimport { getBatchDeviceDatas as defaultGetBatchDeviceDatas } from '../../cloud/commands.js';\nimport {\n MOWER_ACTION,\n MOWER_PROP,\n buildAllAreaPayload,\n buildEdgePayload,\n buildGetConsumablePayload,\n buildResumePayload,\n buildSetConsumablePayload,\n buildSetCurrentMapPayload,\n buildSpotPayload,\n buildZonePayload,\n} from './properties.js';\nimport {\n MowerChargingStatus,\n MowerControlAction,\n MowerFault,\n mowerFaultSeverity,\n MowerStatus,\n MowerTaskStatus,\n type MowerFaultSeverity,\n} from './enums.js';\nimport {\n asNum,\n enumLookup,\n extractMowerConsumableValues,\n mowerConsumableIndex,\n parseControlStatus,\n parseMowerConsumables,\n parseMowerHeartbeat,\n parseMowingProgress,\n parseTaskDescriptor,\n type MowerConsumableKey,\n type MowerConsumableReading,\n type MowerControlState,\n type MowerMowingProgress,\n type MowerTaskDescriptor,\n type MowerTaskSubState,\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 // Default to the live cloud batch-fetch (now that the endpoint is recovered),\n // building the account-auth CommonInput from the protected map-fetch\n // accessors. Tests/callers may still inject a fake via `getBatchDeviceDatas`.\n this.#fetchBatch =\n input.getBatchDeviceDatas ??\n ((did, props) =>\n defaultGetBatchDeviceDatas(\n { session: this.currentSession(), region: this.region, did },\n props,\n ));\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 /**\n * Live mowing task sub-state decoded from the HEARTBEAT (1:1) blob —\n * idle/starting/mowing/paused/finished/failed/exit/returning-to-dock — or null\n * when no mowing session is in progress (or the heartbeat is absent). This is\n * the donor's \"current task\" sensor; the heartbeat is push-only (the mower does\n * not answer live reads), so it tracks the device while it is actively mowing.\n */\n get taskSubState(): MowerTaskSubState | null {\n const hb = parseMowerHeartbeat(\n this.getProperty(MOWER_PROP.HEARTBEAT.siid, MOWER_PROP.HEARTBEAT.piid)?.value,\n );\n return hb?.taskSubState ?? null;\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 /**\n * Live mowing PROGRESS decoded from the POSE_COVERAGE (1:4) task block —\n * `{progressPercent, currentAreaSqm, totalAreaSqm}` — or null when 1:4 carries\n * no task block (idle / pose-only frame). This is the byte-accurate progress\n * the Dreamehome app shows (vs {@link coverageTargetPct}, the task TARGET).\n */\n get mowingProgress(): MowerMowingProgress | null {\n return parseMowingProgress(\n this.getProperty(MOWER_PROP.POSE_COVERAGE.siid, MOWER_PROP.POSE_COVERAGE.piid)?.value,\n );\n }\n /** Mowing completion percent (0..100) from {@link mowingProgress}, or null. */\n get mowingProgressPct(): number | null {\n return this.mowingProgress?.progressPercent ?? null;\n }\n /** Severity of the current DEVICE_CODE (2:2) — info / warning / error. */\n get faultSeverity(): MowerFaultSeverity {\n return mowerFaultSeverity(this.faultRaw);\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 * Switch the mower's ACTIVE map (2:50 o:200). Takes a map *id* (as carried by\n * {@link MowerMap.availableMaps}/`currentMapId`) and resolves it to the\n * firmware's map *index* via the last-fetched map; falls back to treating the\n * id as the index when no map has been fetched yet. Mirrors the donor\n * `set_current_map`. Capability-gated on `canMap`.\n */\n async setCurrentMap(mapId: number): Promise<unknown> {\n this.#requireCap(this.#caps.canMap, 'setCurrentMap', 'map switching');\n const id = Math.trunc(mapId);\n const entry = this.#lastMap?.availableMaps.find((m) => m.mapId === id);\n const index = entry?.mapIndex ?? id;\n return this.#sendTask(buildSetCurrentMapPayload(index));\n }\n\n // -- CMS consumables ----------------------------------------------------\n /**\n * Read the raw CMS consumable counters `[blade, brush, robot]` (minutes used),\n * via the SCHEDULING_TASK (2:50) custom-action getter `{m:'g',t:'CMS'}`. This\n * is a LIVE device action (it wakes the mower); rejects with\n * {@link DreameDeviceOfflineError} when the mower is unreachable. Returns null\n * if the response is malformed.\n */\n async getConsumableValues(): Promise<readonly number[] | null> {\n const result = await this.callAction(\n MOWER_PROP.SCHEDULING_TASK.siid,\n MOWER_PROP.SCHEDULING_TASK.piid,\n [buildGetConsumablePayload()],\n );\n return extractMowerConsumableValues(result);\n }\n\n /**\n * Read the CMS consumables as typed readings (blade/brush/maintenance) with\n * remaining %. LIVE action — see {@link getConsumableValues}. Returns null on\n * a malformed response.\n */\n async getConsumables(): Promise<readonly MowerConsumableReading[] | null> {\n const result = await this.callAction(\n MOWER_PROP.SCHEDULING_TASK.siid,\n MOWER_PROP.SCHEDULING_TASK.piid,\n [buildGetConsumablePayload()],\n );\n return parseMowerConsumables(result);\n }\n\n /**\n * Reset one CMS consumable counter to zero. Reads the current counters, zeroes\n * the selected one (leaving the others), and writes them back via the\n * `{m:'s',t:'CMS',d:{value:[…]}}` setter. LIVE action. Throws on an unknown\n * item or when the current counters cannot be read.\n */\n async resetConsumable(item: MowerConsumableKey): Promise<void> {\n const index = mowerConsumableIndex(item);\n if (index === null) {\n throw new DreameError(`resetConsumable: unknown consumable item \"${item}\"`);\n }\n const current = await this.getConsumableValues();\n if (current === null) {\n throw new DreameError('resetConsumable: failed to read current CMS counters');\n }\n const next = [...current];\n next[index] = 0;\n await this.callAction(MOWER_PROP.SCHEDULING_TASK.siid, MOWER_PROP.SCHEDULING_TASK.piid, [\n buildSetConsumablePayload(next),\n ]);\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.HEARTBEAT,\n MOWER_PROP.POSE_COVERAGE,\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 * Force-refresh callback handed down to the device's MQTT push so it can\n * self-heal a broker CONNACK auth-refusal (stale token). Bound by the facade\n * to {@link Nodreame.reauthenticate}.\n */\n onAuthFailure?: () => Promise<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 ...(args.onAuthFailure !== undefined ? { onAuthFailure: args.onAuthFailure } : {}),\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 /** Background timer that proactively refreshes before the token expires. */\n #refreshTimer: ReturnType<typeof setTimeout> | null = null;\n /** Guard against overlapping proactive-refresh runs. */\n #refreshInFlight = false;\n /**\n * The single in-flight refresh, shared by concurrent callers so the proactive\n * timer and a manual `ensureSession()`/`reauthenticate()` never double-refresh.\n */\n #inFlightRefresh: Promise<DreameSession> | null = null;\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 // Arm the proactive refresh against this session's expiry.\n this.#scheduleProactiveRefresh();\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 return this.#refreshNow(current);\n }\n\n /**\n * Force a session refresh REGARDLESS of the current expiry, then propagate the\n * new token to every live device push. Wired as each push's `onAuthFailure`:\n * when a broker refuses a CONNECT because it considers the token stale (even\n * though {@link ensureSession} still thinks it valid — e.g. server-side early\n * revocation or clock skew), this mints a genuinely fresh token instead of\n * replaying the rejected one.\n */\n async reauthenticate(): Promise<DreameSession> {\n if (this.#closed) {\n throw new DreameAuthError('client is closed');\n }\n const current = this.#session;\n if (!current) {\n return this.login();\n }\n return this.#refreshNow(current);\n }\n\n /**\n * Refresh via the refresh-token (with a full re-login fallback) and propagate\n * the new token to every device push. Shared by {@link ensureSession} (within\n * the leeway window) and {@link reauthenticate} (unconditional).\n */\n async #refreshNow(current: DreameSession): Promise<DreameSession> {\n // Coalesce concurrent refreshes (background timer + manual call) into one.\n const existing = this.#inFlightRefresh;\n if (existing) {\n return existing;\n }\n const run = this.#doRefresh(current);\n this.#inFlightRefresh = run;\n try {\n return await run;\n } finally {\n this.#inFlightRefresh = null;\n }\n }\n\n async #doRefresh(current: DreameSession): Promise<DreameSession> {\n if (current.refreshToken) {\n // Narrow scope: the full-re-login fallback must cover ONLY a failed\n // refresh-token GRANT — never the subsequent propagation. If we let the\n // catch swallow a propagation failure it would trigger a spurious full\n // re-login (and, in the fleet, tear down healthy siblings).\n let next: DreameSession | null = null;\n try {\n 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 } catch {\n next = null; // fall through to a full re-login\n }\n if (next) {\n await this.#adoptSession(next);\n return next;\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 onAuthFailure: () => this.reauthenticate(),\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 // Guard the forward-emit: reactive re-auth / cap-exhaustion failures now\n // funnel through here. An unhandled 'error' on the emitter throws, which\n // would crash a consumer that never attached an 'error' listener.\n h.on('error', (err) => {\n if (this.listenerCount('error') > 0) {\n this.emit('error', err);\n }\n });\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 // Re-arm the proactive refresh against the session used for this discovery.\n this.#scheduleProactiveRefresh();\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 this.#clearRefreshTimer();\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 this.#scheduleProactiveRefresh();\n }\n\n #clearRefreshTimer(): void {\n if (this.#refreshTimer) {\n clearTimeout(this.#refreshTimer);\n this.#refreshTimer = null;\n }\n }\n\n /** Arm the proactive-refresh timer `delayMs` from now (clamped to ≥0). */\n #armRefreshTimer(delayMs: number): void {\n this.#clearRefreshTimer();\n if (this.#closed) {\n return;\n }\n this.#refreshTimer = setTimeout(\n () => {\n this.#refreshTimer = null;\n void this.#runProactiveRefresh();\n },\n Math.max(0, delayMs),\n );\n }\n\n /** (Re)arm the proactive-refresh timer against the current session's expiry. */\n #scheduleProactiveRefresh(): void {\n const session = this.#session;\n if (!session) {\n this.#clearRefreshTimer();\n return;\n }\n this.#armRefreshTimer(session.expiresAt - this.#leewayMs - Date.now());\n }\n\n /**\n * Timer body: refresh the session before it expires, then reschedule against\n * the new expiry. Because {@link ensureSession} propagates the refreshed token\n * to every device push, each MQTT connection is re-keyed BEFORE the broker\n * would reject the old one — the reactive re-auth loop never even starts.\n */\n async #runProactiveRefresh(): Promise<void> {\n if (this.#closed || this.#refreshInFlight) {\n return;\n }\n this.#refreshInFlight = true;\n try {\n await this.ensureSession();\n this.#refreshInFlight = false;\n // Track the newest expiry (ensureSession may have returned the current\n // session unchanged if the clock had not yet crossed the boundary).\n this.#scheduleProactiveRefresh();\n } catch (err) {\n this.#refreshInFlight = false;\n // Surface only when someone is listening — an unhandled 'error' on the\n // emitter would throw. The failure is transient; the retry below recovers.\n if (this.listenerCount('error') > 0) {\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n }\n // Retry after a bounded backoff rather than hot-looping on a still-expiring\n // session (whose boundary is already in the past ⇒ a 0ms reschedule).\n if (!this.#closed) {\n this.#armRefreshTimer(this.#leewayMs);\n }\n }\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 // allSettled — NOT all: one device's applySession failure must NEVER reject\n // the whole propagation. A rejected Promise.all here would (1) orphan the\n // healthy siblings that already reconnected, (2) bubble up into #doRefresh\n // and trigger a spurious full re-login, and (3) reach the originating push's\n // reauth catch and tear down its live client. Each device self-heals its own\n // reconnect (see DreamePush.refreshSession), so per-device failures are\n // isolated here.\n await Promise.allSettled(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 // `deviceid` is listed explicitly: the bare `did` fragment does NOT match\n // `deviceid` (the substring `did` is absent from `d-e-v-i-c-e-i-d`), yet\n // base-device sets the real `did` as `deviceId` on raw payloads (FIX 1).\n 'deviceid',\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 // room/zone/map names are user-set PII (FIX 3).\n 'zone_name',\n 'zonename',\n 'map_name',\n 'mapname',\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 like `START`). ACCEPTED\n // TRADE-OFF: a bare `{ name: \"...\" }` in an event argument is NOT scrubbed by\n // key. This is mitigated in practice by (a) the value-sanitizer below\n // (sanitizeStringValue, applied to EVERY string scalar — it catches OSS\n // paths / URLs / tokens regardless of key), and (b) the specific\n // customName/deviceName/nickName/zoneName/mapName fragments which cover the\n // realistic PII-bearing name cases.\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/**\n * Risky-VALUE patterns. A string scalar can carry a secret under a NON-sensitive\n * (e.g. numeric `\"siid.piid\"`) key — an OSS object path `ali_dreame/<uid>/<did>/<n>`,\n * a signed URL with a `token=` query param, or a long opaque secret run. These\n * never reach a key-name match, so they slip past {@link isSensitiveKey}.\n *\n * Kept DELIBERATELY conservative so the actual diagnostic signal (enum names like\n * `START`/`SweepAndMop`, numeric-ish strings, comma fault lists like `\"18,107\"`,\n * small JSON) is preserved. Only the patterns below trigger a redact.\n */\nconst RISKY_VALUE_PATTERNS: readonly RegExp[] = [\n /:\\/\\//, // any URL scheme (https://, mqtts://, …) — signed URLs carry tokens\n /ali_dreame\\//i, // OSS object path embedding uid/did\n /[A-Za-z0-9_-]{32,}/, // a long opaque token run (base64/hex secrets)\n /\\b(did|uid|token)\\s*=/i, // query-param secrets (?did=…&token=…)\n /access/i, // accessKey / access-token / x-access-key style markers\n];\n\n/**\n * Replace a string VALUE with {@link REDACTED} when it matches a risky pattern,\n * otherwise return it unchanged. Applied to EVERY string scalar inside\n * {@link redact}, so it covers property values, event arguments, AND raw frames.\n */\nexport function sanitizeStringValue(s: string): string {\n return RISKY_VALUE_PATTERNS.some((re) => re.test(s)) ? REDACTED : s;\n}\n\n/**\n * Recursively scrub a value. Sensitive KEYS short-circuit to {@link REDACTED};\n * every surviving string scalar is additionally passed through\n * {@link sanitizeStringValue} (value-level secret detection).\n */\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 if (typeof value === 'string') {\n return sanitizeStringValue(value);\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","/**\n * Parsers for the robot's on-device AI detections, pushed as camera-service\n * properties (subscribe to `propertyChanged` for SIID 10001):\n * - piid 110 (`personFollow` / PERSON_DATA): a tracked-person bounding box\n * - piid 112 (`videoObstacleData`): a list of obstacle boxes\n *\n * The overlay for obstacle boxes is gated on `set_properties siid 28 piid 13 = 1`.\n * Payloads are JSON strings; these parsers are lenient (return `null` on garbage)\n * and normalize timestamps (the device reports nanoseconds).\n */\n\n/** A normalized detection box. Coordinates are passed through as-reported. */\nexport interface DetectionBox {\n readonly x: number;\n readonly y: number;\n readonly w: number;\n readonly h: number;\n /** Optional label/class if the payload carries one. */\n readonly label?: string;\n /** Optional confidence if the payload carries one. */\n readonly score?: number;\n}\n\nexport interface PersonFollowDetection {\n /** Detection time in epoch milliseconds (converted from the device's ns). */\n readonly timestampMs: number;\n readonly box: DetectionBox | null;\n}\n\nexport interface ObstacleDetection {\n readonly timestampMs: number;\n readonly boxes: readonly DetectionBox[];\n}\n\nconst asRecord = (value: unknown): Record<string, unknown> | null => {\n if (typeof value === 'string') {\n try {\n const parsed: unknown = JSON.parse(value);\n return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n }\n return value && typeof value === 'object' ? (value as Record<string, unknown>) : null;\n};\n\nconst num = (v: unknown): number | undefined => (typeof v === 'number' && Number.isFinite(v) ? v : undefined);\n\n/** Device timestamps are nanoseconds; scale to ms (leave already-ms values alone). */\nconst toMs = (raw: unknown): number => {\n const n = num(raw);\n if (n === undefined) return Date.now();\n // Heuristic: > 1e15 is almost certainly nanoseconds.\n return n > 1e15 ? Math.round(n / 1e6) : n;\n};\n\n/** Read a box from either an array `[x,y,w,h]` or an object `{x,y,w,h,...}`. */\nconst toBox = (raw: unknown): DetectionBox | null => {\n if (Array.isArray(raw) && raw.length >= 4) {\n const [x, y, w, h] = raw;\n if ([x, y, w, h].every((v) => num(v) !== undefined)) {\n return { x: x as number, y: y as number, w: w as number, h: h as number };\n }\n return null;\n }\n const r = asRecord(raw);\n if (!r) return null;\n const x = num(r['x']);\n const y = num(r['y']);\n const w = num(r['w']) ?? num(r['width']);\n const h = num(r['h']) ?? num(r['height']);\n if (x === undefined || y === undefined || w === undefined || h === undefined) return null;\n const label = typeof r['label'] === 'string' ? (r['label'] as string) : undefined;\n const score = num(r['score']) ?? num(r['confidence']);\n return { x, y, w, h, ...(label !== undefined ? { label } : {}), ...(score !== undefined ? { score } : {}) };\n};\n\n/** Parse a `personFollow` (piid 110) payload into a normalized detection. */\nexport function parsePersonFollow(value: unknown): PersonFollowDetection | null {\n const r = asRecord(value);\n if (!r) return null;\n return {\n timestampMs: toMs(r['timestamp'] ?? r['time']),\n box: toBox(r['bbox'] ?? r['box']),\n };\n}\n\n/** Parse a `videoObstacleData` (piid 112) payload into normalized boxes. */\nexport function parseObstacleData(value: unknown): ObstacleDetection | null {\n const r = asRecord(value);\n if (!r) return null;\n const list = r['boxlist'] ?? r['boxes'] ?? r['boxList'];\n const boxes: DetectionBox[] = [];\n if (Array.isArray(list)) {\n for (const item of list) {\n const box = toBox(item);\n if (box) boxes.push(box);\n }\n }\n return { timestampMs: toMs(r['timestamp'] ?? r['time']), boxes };\n}\n","/**\n * Minimal Node RTMP client that replicates the Aliyun LinkVisual viewer (\"private mode\" librtmp)\n * exactly as observed on the wire (findings/dreame.pcap, findings/dr3.pcap — client->relay bytes,\n * VERIFIED) and in liblvmedia.so / liblvrtmp.so (VERIFIED by disassembly).\n *\n * Wire sequence (all four written back-to-back, BEFORE any server byte is read — like the app):\n * C0 = 0x04, C1 = [uptime_ms u32][0 0 0 0][1528 random] (no C2 is ever sent)\n * connect (txn 1, csid 3, msid 0, fmt 0) {app, tcUrl, fpad:false, capabilities:15,\n * audioCodecs:3191, videoCodecs:252, videoFunction:1} — no flashVer, no objectEncoding\n * play (txn 2, csid 8, msid 1, fmt 0) null, <playpath>, -1000 — NO createStream (msid fixed = 1)\n * onStatus (txn 3, csid 3, fmt 1) null, {level:\"status\", code:\"NetStream.RequestAudioType\",\n * description:\"desc\", audio_type:\"1\"}\n * Then the server answers S0(0x04)+S1(1536) followed directly by RTMP messages (no S2), chunked with an\n * implicit 60000-byte chunk size. After the first server message is processed the app sends\n * onStatus (txn 4, csid 3, fmt 1) null, {level:\"status\", code:\"NetStream.Ping.Start\", description:\"desc\"}\n * and repeats it every 30 s (CStreamUnit::ProcessRtmpStream). The client never replies to server onStatus.\n *\n * What is ASSUMED (not observable in the pcaps, which lack the server->client direction): that the\n * server sends onStatus NetStream.Play.Start (librtmp requires it before accepting media), and that this\n * exact sequence — rather than one specific element of it — is what makes the robot publish VIDEO.\n */\nimport net from 'node:net';\nimport { EventEmitter } from 'node:events';\nimport { randomBytes } from 'node:crypto';\nimport { amfBoolean, amfNull, amfNumber, amfObject, amfString, decodeAmf0, type AmfValue } from './amf0.js';\nimport { ChunkReader, MSG, PRIVATE_MODE_CHUNK_SIZE, encodeMessage, type RtmpMessage } from './chunk.js';\nimport {\n aacSampleRate, aacToAdts, avccToAnnexB, parseAacConfig, parseAudioTag, parseAvcConfig, parseVideoTag,\n type AacConfig, type AvcConfig,\n} from './flv.js';\n\nexport const LV_STATUS = {\n REQUEST_AUDIO_TYPE: 'NetStream.RequestAudioType',\n FORCE_IFRAME_START: 'NetStream.ForceIFrame.Start',\n PING_START: 'NetStream.Ping.Start',\n PING_RESPONSE: 'NetStream.Ping.Response',\n PRE_CONNECT: 'NetStream.PreConnect',\n KEEPALIVE: 'NetStream.Keepalive',\n SERVER_INFO: 'NetStream.ServerInfo',\n TRANSFER_DATA: 'NetStream.TransferData',\n PLAY_START: 'NetStream.Play.Start',\n /** Server signals the talk (intercom uplink) channel is ready. */\n TALK_READY: 'NetStream.TalkReady',\n} as const;\n\n/**\n * FLV audio tag header byte for the intercom uplink: G.711 A-law, 8 kHz, 16-bit,\n * mono — `(7<<4)|(0<<2)|(1<<1)|0` = 0x72. Verified against liblvmedia SetTalkInfo.\n */\nexport const TALK_AUDIO_HEADER_G711A = 0x72;\n\n/** librtmp constants observed in the app's connect (RTMP_Connect1/SendConnectPacket). */\nconst CONNECT_CAPABILITIES = 15;\nconst CONNECT_AUDIO_CODECS = 3191;\nconst CONNECT_VIDEO_CODECS = 252;\nconst CONNECT_VIDEO_FUNCTION = 1;\nconst PLAY_START_LIVE = -1000; // RTMP_LF_LIVE -> start = -1000\nconst HANDSHAKE_C0_PRIVATE = 0x04;\nconst HANDSHAKE_S0S1_LEN = 1537;\nconst DEFAULT_PING_INTERVAL_MS = 30_000; // ProcessRtmpStream: 30000\nconst DEFAULT_CACHE_DURATION_MS = 3000; // app: \"&cacheDuration=3000\"\nconst ACK_THRESHOLD_BYTES = 250_000; // librtmp: m_nClientBW(2500000)/10\nconst CSID_CONTROL = 2;\nconst CSID_COMMAND = 3;\nconst CSID_TALK = 4; // intercom uplink audio (liblvmedia IOT_RTMP_SendAudio)\nconst CSID_PLAY = 8;\nconst PLAY_STREAM_ID = 1; // RTMP_InitPrivateConfig: m_stream_id = 1\n\nexport interface StatusEvent {\n code: string;\n level: string;\n description: string;\n fields: { [key: string]: AmfValue };\n transactionId: number;\n}\n\nexport interface VideoAccessUnitEvent {\n data: Buffer; // Annex-B access unit (SPS/PPS prepended on keyframes)\n isKeyframe: boolean;\n videoType: 'H264';\n microseconds: number;\n time?: number;\n}\n\nexport interface AudioInfoEvent {\n codec: 'aac' | 'g711a' | 'g711u' | 'unknown';\n sampleRate: number;\n channels: number;\n}\n\nexport interface LvRtmpClientOptions {\n /** relayUrl from stream/query, e.g. rtmp://host:8000/live?token=..&session=../<key>_0 */\n url: string;\n /** Appended to app/tcUrl as \"&cacheDuration=N\" when not already present (the app always sends 3000). */\n cacheDurationMs?: number;\n /** Send the onStatus NetStream.RequestAudioType {audio_type:\"1\"} right after play (app does). */\n requestAudioType?: boolean;\n /** Send NetStream.Ping.Start after the first server message and then periodically (app does). */\n ping?: boolean;\n pingIntervalMs?: number;\n /** Also send NetStream.ForceIFrame.Start once playing (the app sends it when it starts an MP4 record). */\n forceIFrameOnStart?: boolean;\n connectTimeoutMs?: number;\n log?: (line: string) => void;\n}\n\ninterface Endpoint {\n host: string;\n port: number;\n app: string;\n tcUrl: string;\n playpath: string;\n}\n\n/**\n * Split like librtmp/ffmpeg do: playpath = last path segment, app = everything between host and it\n * (query string included). Verified against the app's connect/play in both pcaps.\n */\nexport function parseRelayUrl(url: string, cacheDurationMs: number): Endpoint {\n const m = /^rtmp:\\/\\/([^/:]+)(?::(\\d+))?\\/(.*)$/.exec(url);\n if (!m) throw new Error(`not an rtmp:// url: ${url}`);\n const host = m[1]!;\n const port = m[2] ? Number(m[2]) : 1935;\n const path = m[3]!;\n const slash = path.lastIndexOf('/');\n if (slash < 0) throw new Error(`relay url has no playpath: ${url}`);\n let app = path.slice(0, slash);\n const playpath = path.slice(slash + 1);\n if (!/[?&]cacheDuration=/.test(app)) app += `${app.includes('?') ? '&' : '?'}cacheDuration=${cacheDurationMs}`;\n return { host, port, app, playpath, tcUrl: `rtmp://${host}:${port}/${app}` };\n}\n\ntype Events = {\n connected: [];\n status: [StatusEvent];\n command: [string, AmfValue[]];\n metadata: [{ [key: string]: AmfValue }];\n audio: [{ timestamp: number; body: Buffer }];\n video: [{ timestamp: number; body: Buffer }];\n videoAccessUnit: [VideoAccessUnitEvent];\n audioFrame: [Buffer];\n audioInfo: [AudioInfoEvent];\n error: [Error];\n close: [];\n};\n\nexport class LvRtmpClient extends EventEmitter<Events> {\n private readonly opts: Required<Omit<LvRtmpClientOptions, 'log'>> & { log: (line: string) => void };\n private readonly endpoint: Endpoint;\n private socket: net.Socket | null = null;\n private handshakeBuf: Buffer = Buffer.alloc(0);\n private handshakeDone = false;\n private c1Time = 0;\n private reader = new ChunkReader(PRIVATE_MODE_CHUNK_SIZE);\n private transactionId = 0;\n private bytesIn = 0;\n private bytesAcked = 0;\n private ackWindow = ACK_THRESHOLD_BYTES;\n private pingTimer: NodeJS.Timeout | null = null;\n private firstMessageSeen = false;\n private avcConfig: AvcConfig | null = null;\n private aacConfig: AacConfig | null = null;\n private closed = false;\n\n constructor(options: LvRtmpClientOptions) {\n super();\n this.opts = {\n url: options.url,\n cacheDurationMs: options.cacheDurationMs ?? DEFAULT_CACHE_DURATION_MS,\n requestAudioType: options.requestAudioType ?? true,\n ping: options.ping ?? true,\n pingIntervalMs: options.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,\n forceIFrameOnStart: options.forceIFrameOnStart ?? false,\n connectTimeoutMs: options.connectTimeoutMs ?? 10_000,\n log: options.log ?? (() => {}),\n };\n this.endpoint = parseRelayUrl(this.opts.url, this.opts.cacheDurationMs);\n }\n\n /** Open TCP, write C0+C1 and the pipelined connect/play/RequestAudioType. Resolves once written. */\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const sock = net.createConnection({ host: this.endpoint.host, port: this.endpoint.port });\n this.socket = sock;\n sock.setNoDelay(true);\n sock.setTimeout(this.opts.connectTimeoutMs, () => { if (!this.handshakeDone) this.fail(new Error('handshake timeout')); });\n sock.once('error', (err) => { this.emit('error', err); reject(err); });\n sock.on('data', (d) => this.onData(d));\n sock.on('close', () => this.onClose());\n sock.once('connect', () => {\n this.opts.log(`tcp connected ${this.endpoint.host}:${this.endpoint.port}`);\n sock.write(Buffer.concat([this.buildC0C1(), ...this.buildOpeningSequence()]));\n this.emit('connected');\n resolve();\n });\n });\n }\n\n close(): void {\n this.closed = true;\n if (this.pingTimer) clearInterval(this.pingTimer);\n this.pingTimer = null;\n this.socket?.destroy();\n }\n\n /** onStatus encoder — byte-exact replica of liblvmedia IOT_RTMP_SendMessage (csid 3, fmt 1, ts 0). */\n buildStatus(code: string, extra?: readonly [string, string]): Buffer {\n const pairs: Array<readonly [string, Buffer]> = [\n ['level', amfString('status')],\n ['code', amfString(code)],\n ['description', amfString('desc')],\n ];\n if (extra) pairs.push([extra[0], amfString(extra[1])]);\n const body = Buffer.concat([amfString('onStatus'), amfNumber(++this.transactionId), amfNull(), amfObject(pairs)]);\n return encodeMessage({ fmt: 1, csid: CSID_COMMAND, type: MSG.COMMAND_AMF0, body });\n }\n\n sendStatus(code: string, extra?: readonly [string, string]): void {\n this.write(this.buildStatus(code, extra));\n this.opts.log(`>> onStatus ${code}${extra ? ` ${extra[0]}=${extra[1]}` : ''} (txn ${this.transactionId})`);\n }\n\n forceIFrame(): void {\n this.sendStatus(LV_STATUS.FORCE_IFRAME_START);\n }\n\n // ---------------------------------------------------------------- outgoing\n\n private buildC0C1(): Buffer {\n this.c1Time = Math.floor(performance.now()) >>> 0;\n const c1 = Buffer.alloc(1536);\n c1.writeUInt32BE(this.c1Time, 0);\n c1.writeUInt32BE(0, 4);\n randomBytes(1528).copy(c1, 8);\n return Buffer.concat([Buffer.from([HANDSHAKE_C0_PRIVATE]), c1]);\n }\n\n private buildConnect(): Buffer {\n const obj = amfObject([\n ['app', amfString(this.endpoint.app)],\n ['tcUrl', amfString(this.endpoint.tcUrl)],\n ['fpad', amfBoolean(false)],\n ['capabilities', amfNumber(CONNECT_CAPABILITIES)],\n ['audioCodecs', amfNumber(CONNECT_AUDIO_CODECS)],\n ['videoCodecs', amfNumber(CONNECT_VIDEO_CODECS)],\n ['videoFunction', amfNumber(CONNECT_VIDEO_FUNCTION)],\n ]);\n const body = Buffer.concat([amfString('connect'), amfNumber(++this.transactionId), obj]);\n return encodeMessage({ fmt: 0, csid: CSID_COMMAND, msid: 0, type: MSG.COMMAND_AMF0, body });\n }\n\n private buildPlay(): Buffer {\n const body = Buffer.concat([\n amfString('play'), amfNumber(++this.transactionId), amfNull(), amfString(this.endpoint.playpath), amfNumber(PLAY_START_LIVE),\n ]);\n return encodeMessage({ fmt: 0, csid: CSID_PLAY, msid: PLAY_STREAM_ID, type: MSG.COMMAND_AMF0, body });\n }\n\n /** Exposed for the byte-exact self-test against the pcap. */\n buildOpeningSequence(): Buffer[] {\n const seq = [this.buildConnect(), this.buildPlay()];\n if (this.opts.requestAudioType) seq.push(this.buildStatus(LV_STATUS.REQUEST_AUDIO_TYPE, ['audio_type', '1']));\n return seq;\n }\n\n /**\n * Push one intercom (talk-back) audio frame UPSTREAM to the device — an RTMP\n * type-8 audio message on the talk chunk stream (csid 4, msid 1), exactly as\n * liblvmedia's `IOT_RTMP_SendAudio` does. `payload` is the raw codec audio\n * (default framing = G.711 A-law); `tsMs` is a running millisecond timestamp.\n * Only meaningful after the server sends {@link LV_STATUS.TALK_READY}.\n */\n sendAudioFrame(payload: Buffer, tsMs: number, header: number = TALK_AUDIO_HEADER_G711A): void {\n const body = Buffer.concat([Buffer.from([header]), payload]);\n this.write(\n encodeMessage({ fmt: 0, csid: CSID_TALK, msid: PLAY_STREAM_ID, timestamp: tsMs >>> 0, type: MSG.AUDIO, body }),\n );\n }\n\n private write(buf: Buffer): void {\n if (!this.socket || this.socket.destroyed) return;\n this.socket.write(buf);\n }\n\n private sendAck(): void {\n const b = Buffer.alloc(4);\n b.writeUInt32BE(this.bytesIn >>> 0, 0);\n this.write(encodeMessage({ fmt: 0, csid: CSID_CONTROL, msid: 0, type: MSG.ACK, body: b }));\n this.bytesAcked = this.bytesIn;\n }\n\n private sendPingResponse(serverTs: Buffer): void {\n const body = Buffer.concat([Buffer.from([0, 7]), serverTs]);\n this.write(encodeMessage({ fmt: 0, csid: CSID_CONTROL, msid: 0, type: MSG.USER_CONTROL, body }));\n }\n\n private startPinging(): void {\n if (!this.opts.ping || this.pingTimer) return;\n this.sendStatus(LV_STATUS.PING_START);\n this.pingTimer = setInterval(() => this.sendStatus(LV_STATUS.PING_START), this.opts.pingIntervalMs);\n }\n\n // ---------------------------------------------------------------- incoming\n\n private onData(data: Buffer): void {\n this.bytesIn += data.length;\n let payload = data;\n if (!this.handshakeDone) {\n this.handshakeBuf = Buffer.concat([this.handshakeBuf, data]);\n if (this.handshakeBuf.length < HANDSHAKE_S0S1_LEN) return;\n this.onHandshake(this.handshakeBuf.subarray(0, HANDSHAKE_S0S1_LEN));\n payload = this.handshakeBuf.subarray(HANDSHAKE_S0S1_LEN);\n this.handshakeBuf = Buffer.alloc(0);\n this.handshakeDone = true;\n if (payload.length === 0) return;\n }\n let messages: RtmpMessage[];\n try {\n messages = this.reader.feed(payload);\n } catch (err) {\n this.fail(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n for (const m of messages) this.dispatch(m);\n if (this.bytesIn - this.bytesAcked >= this.ackWindow) this.sendAck();\n }\n\n private onHandshake(s0s1: Buffer): void {\n const s0 = s0s1[0]!;\n const serverTime = s0s1.readUInt32BE(1);\n const zero = s0s1.readUInt32BE(5);\n this.opts.log(`<< S0=0x${s0.toString(16)} S1.time=${serverTime} S1.zero=0x${zero.toString(16)} (client sent 0x04; app logs a mismatch but continues)`);\n // Robustness: a standards-mode server might append S2 (echo of our C1). liblvrtmp never reads one;\n // detect it anyway so we do not desync if the relay behaves differently for us.\n // (Handled lazily in dispatch: if the first \"chunk\" looks like our C1 echo we skip 1536 bytes.)\n }\n\n private dispatch(m: RtmpMessage): void {\n if (!this.firstMessageSeen) {\n this.firstMessageSeen = true;\n this.startPinging(); // ProcessRtmpStream sends Ping.Start right after processing the first packet\n }\n switch (m.type) {\n case MSG.SET_CHUNK_SIZE: {\n const size = m.body.readUInt32BE(0) & 0x7fffffff;\n this.opts.log(`<< SetChunkSize ${size}`);\n this.reader.chunkSize = size;\n return;\n }\n case MSG.WINDOW_ACK_SIZE:\n this.ackWindow = Math.min(ACK_THRESHOLD_BYTES, m.body.readUInt32BE(0));\n this.opts.log(`<< WindowAckSize ${m.body.readUInt32BE(0)}`);\n return;\n case MSG.SET_PEER_BW:\n this.opts.log(`<< SetPeerBandwidth ${m.body.readUInt32BE(0)} limit=${m.body[4]}`);\n return;\n case MSG.ACK:\n case MSG.ABORT:\n return;\n case MSG.USER_CONTROL:\n return this.onUserControl(m.body);\n case MSG.COMMAND_AMF0:\n return this.onCommand(m);\n case MSG.DATA_AMF0:\n return this.onDataMessage(m.body);\n case MSG.AUDIO:\n return this.onAudio(m);\n case MSG.VIDEO:\n return this.onVideo(m);\n default:\n this.opts.log(`<< unknown message type ${m.type} len=${m.body.length}`);\n }\n }\n\n private onUserControl(body: Buffer): void {\n const event = body.readUInt16BE(0);\n const names: { [k: number]: string } = { 0: 'StreamBegin', 1: 'StreamEOF', 2: 'StreamDry', 4: 'StreamIsRecorded', 6: 'PingRequest', 7: 'PingResponse' };\n this.opts.log(`<< UserControl ${names[event] ?? event} ${body.subarray(2).toString('hex')}`);\n if (event === 6) this.sendPingResponse(body.subarray(2, 6));\n }\n\n private onCommand(m: RtmpMessage): void {\n let values: AmfValue[];\n try {\n values = decodeAmf0(m.body);\n } catch (err) {\n this.opts.log(`<< undecodable command: ${m.body.toString('hex')}`);\n return;\n }\n const [name, txn, , info] = values;\n this.opts.log(`<< ${String(name)} ${JSON.stringify(values.slice(1))}`);\n this.emit('command', String(name), values);\n if (name === 'onStatus' && info && typeof info === 'object' && !Array.isArray(info)) {\n const fields = info as { [key: string]: AmfValue };\n this.emit('status', {\n code: String(fields.code ?? ''),\n level: String(fields.level ?? ''),\n description: String(fields.description ?? ''),\n fields,\n transactionId: typeof txn === 'number' ? txn : 0,\n });\n if (fields.code === LV_STATUS.PLAY_START && this.opts.forceIFrameOnStart) this.forceIFrame();\n }\n }\n\n private onDataMessage(body: Buffer): void {\n try {\n const values = decodeAmf0(body);\n this.opts.log(`<< data ${JSON.stringify(values).slice(0, 300)}`);\n const obj = values.find((v) => v && typeof v === 'object' && !Array.isArray(v));\n if (obj) this.emit('metadata', obj as { [key: string]: AmfValue });\n } catch {\n this.opts.log(`<< undecodable data message len=${body.length}`);\n }\n }\n\n private onAudio(m: RtmpMessage): void {\n this.emit('audio', { timestamp: m.timestamp, body: m.body });\n const tag = parseAudioTag(m.body);\n if (!tag) return;\n if (tag.soundFormat === 10) {\n if (tag.aacPacketType === 0) {\n this.aacConfig = parseAacConfig(tag.data);\n this.emit('audioInfo', { codec: 'aac', sampleRate: aacSampleRate(this.aacConfig), channels: this.aacConfig.channelConfiguration });\n return;\n }\n if (this.aacConfig) this.emit('audioFrame', aacToAdts(tag.data, this.aacConfig));\n return;\n }\n if (tag.soundFormat === 7 || tag.soundFormat === 8) {\n if (!this.aacConfig) this.emit('audioInfo', { codec: tag.soundFormat === 7 ? 'g711a' : 'g711u', sampleRate: 8000, channels: 1 });\n this.emit('audioFrame', tag.data);\n return;\n }\n this.emit('audioInfo', { codec: 'unknown', sampleRate: 0, channels: tag.channels });\n }\n\n private onVideo(m: RtmpMessage): void {\n this.emit('video', { timestamp: m.timestamp, body: m.body });\n const tag = parseVideoTag(m.body);\n if (!tag || tag.codecId !== 7) return;\n if (tag.avcPacketType === 0) {\n this.avcConfig = parseAvcConfig(tag.data);\n return;\n }\n if (tag.avcPacketType !== 1 || !this.avcConfig) return;\n const isKeyframe = tag.frameType === 1;\n this.emit('videoAccessUnit', {\n data: avccToAnnexB(tag.data, this.avcConfig, isKeyframe),\n isKeyframe,\n videoType: 'H264',\n microseconds: m.timestamp * 1000,\n time: m.timestamp + tag.compositionTime,\n });\n }\n\n private fail(err: Error): void {\n this.opts.log(`!! ${err.message}`);\n this.emit('error', err);\n this.close();\n }\n\n private onClose(): void {\n if (this.pingTimer) clearInterval(this.pingTimer);\n this.pingTimer = null;\n if (!this.closed) this.opts.log('socket closed by peer');\n this.closed = true;\n this.emit('close');\n }\n}\n","/**\n * Minimal AMF0 encoder/decoder — only the types LinkVisual's librtmp uses\n * (number, boolean, string, object, null, ecma-array, strict-array, long string).\n * Erasable-syntax TypeScript: runs directly under Node >= 22.6 (`node file.ts`).\n */\n\nexport const AMF0 = {\n NUMBER: 0x00,\n BOOLEAN: 0x01,\n STRING: 0x02,\n OBJECT: 0x03,\n NULL: 0x05,\n UNDEFINED: 0x06,\n ECMA_ARRAY: 0x08,\n OBJECT_END: 0x09,\n STRICT_ARRAY: 0x0a,\n LONG_STRING: 0x0c,\n} as const;\n\nexport type AmfValue = number | boolean | string | null | undefined | AmfValue[] | { [key: string]: AmfValue };\n\nexport function amfNumber(n: number): Buffer {\n const b = Buffer.alloc(9);\n b[0] = AMF0.NUMBER;\n b.writeDoubleBE(n, 1);\n return b;\n}\n\nexport function amfBoolean(v: boolean): Buffer {\n return Buffer.from([AMF0.BOOLEAN, v ? 1 : 0]);\n}\n\nexport function amfString(s: string): Buffer {\n const str = Buffer.from(s, 'utf8');\n const b = Buffer.alloc(3 + str.length);\n b[0] = AMF0.STRING;\n b.writeUInt16BE(str.length, 1);\n str.copy(b, 3);\n return b;\n}\n\nexport function amfNull(): Buffer {\n return Buffer.from([AMF0.NULL]);\n}\n\n/** Named property: u16 key length + key + encoded value. Order is preserved (matters for byte-exactness). */\nfunction amfNamed(key: string, value: Buffer): Buffer {\n const k = Buffer.from(key, 'utf8');\n const b = Buffer.alloc(2 + k.length);\n b.writeUInt16BE(k.length, 0);\n k.copy(b, 2);\n return Buffer.concat([b, value]);\n}\n\n/** AMF0 object from an ordered list of [key, encodedValue] pairs. */\nexport function amfObject(pairs: ReadonlyArray<readonly [string, Buffer]>): Buffer {\n const parts: Buffer[] = [Buffer.from([AMF0.OBJECT])];\n for (const [k, v] of pairs) parts.push(amfNamed(k, v));\n parts.push(Buffer.from([0x00, 0x00, AMF0.OBJECT_END]));\n return Buffer.concat(parts);\n}\n\ninterface DecodeResult {\n value: AmfValue;\n next: number;\n}\n\nfunction decodeOne(buf: Buffer, pos: number): DecodeResult {\n const type = buf[pos];\n let p = pos + 1;\n switch (type) {\n case AMF0.NUMBER:\n return { value: buf.readDoubleBE(p), next: p + 8 };\n case AMF0.BOOLEAN:\n return { value: buf[p] !== 0, next: p + 1 };\n case AMF0.STRING: {\n const len = buf.readUInt16BE(p);\n return { value: buf.toString('utf8', p + 2, p + 2 + len), next: p + 2 + len };\n }\n case AMF0.LONG_STRING: {\n const len = buf.readUInt32BE(p);\n return { value: buf.toString('utf8', p + 4, p + 4 + len), next: p + 4 + len };\n }\n case AMF0.NULL:\n return { value: null, next: p };\n case AMF0.UNDEFINED:\n return { value: undefined, next: p };\n case AMF0.ECMA_ARRAY:\n case AMF0.OBJECT: {\n if (type === AMF0.ECMA_ARRAY) p += 4; // associative count, then same as object\n const obj: { [key: string]: AmfValue } = {};\n for (;;) {\n const klen = buf.readUInt16BE(p);\n if (klen === 0 && buf[p + 2] === AMF0.OBJECT_END) return { value: obj, next: p + 3 };\n const key = buf.toString('utf8', p + 2, p + 2 + klen);\n const r = decodeOne(buf, p + 2 + klen);\n obj[key] = r.value;\n p = r.next;\n }\n }\n case AMF0.STRICT_ARRAY: {\n const count = buf.readUInt32BE(p);\n p += 4;\n const arr: AmfValue[] = [];\n for (let i = 0; i < count; i++) {\n const r = decodeOne(buf, p);\n arr.push(r.value);\n p = r.next;\n }\n return { value: arr, next: p };\n }\n default:\n throw new Error(`AMF0: unsupported type 0x${(type ?? -1).toString(16)} at ${pos}`);\n }\n}\n\n/** Decode a whole message body into its top-level values (command name, txid, args...). */\nexport function decodeAmf0(buf: Buffer): AmfValue[] {\n const out: AmfValue[] = [];\n let pos = 0;\n while (pos < buf.length) {\n const r = decodeOne(buf, pos);\n out.push(r.value);\n pos = r.next;\n }\n return out;\n}\n","/**\n * RTMP chunk-stream framing (encoder + incremental reader).\n *\n * LinkVisual \"private mode\" facts (liblvrtmp.so RTMP_InitPrivateConfig, VERIFIED by disassembly):\n * m_inChunkSize = m_outChunkSize = 60000, m_stream_id = 1 — and NO SetChunkSize is exchanged.\n * Hence: we send every message as a single chunk (all control messages are < 1 KB) and we must\n * parse inbound chunks with a 60000-byte chunk size from the start (a 128 default would desync).\n */\n\nexport const MSG = {\n SET_CHUNK_SIZE: 1,\n ABORT: 2,\n ACK: 3,\n USER_CONTROL: 4,\n WINDOW_ACK_SIZE: 5,\n SET_PEER_BW: 6,\n AUDIO: 8,\n VIDEO: 9,\n DATA_AMF0: 18,\n COMMAND_AMF0: 20,\n} as const;\n\nexport const PRIVATE_MODE_CHUNK_SIZE = 60000;\n\nexport interface RtmpMessage {\n csid: number;\n msid: number;\n timestamp: number;\n type: number;\n body: Buffer;\n}\n\nexport interface EncodeOptions {\n /** 0 = 12-byte header (LARGE), 1 = 8-byte header (MEDIUM, msid inherited). Mirrors librtmp m_headerType. */\n fmt: 0 | 1;\n csid: number;\n msid?: number;\n timestamp?: number;\n type: number;\n body: Buffer;\n}\n\nfunction basicHeader(fmt: number, csid: number): Buffer {\n if (csid < 64) return Buffer.from([(fmt << 6) | csid]);\n if (csid < 320) return Buffer.from([fmt << 6, csid - 64]);\n return Buffer.from([(fmt << 6) | 1, (csid - 64) & 0xff, (csid - 64) >> 8]);\n}\n\n/** Encode one message as ONE chunk (no splitting) — exactly what the LinkVisual SDK does on the wire. */\nexport function encodeMessage(o: EncodeOptions): Buffer {\n if (o.body.length > PRIVATE_MODE_CHUNK_SIZE) {\n throw new Error(`message body ${o.body.length} exceeds private-mode chunk size ${PRIVATE_MODE_CHUNK_SIZE}`);\n }\n const ts = o.timestamp ?? 0;\n const hdrLen = o.fmt === 0 ? 11 : 7;\n const hdr = Buffer.alloc(hdrLen);\n hdr.writeUIntBE(ts >= 0xffffff ? 0xffffff : ts, 0, 3);\n hdr.writeUIntBE(o.body.length, 3, 3);\n hdr[6] = o.type;\n if (o.fmt === 0) hdr.writeUInt32LE(o.msid ?? 0, 7);\n const ext = ts >= 0xffffff ? (() => { const e = Buffer.alloc(4); e.writeUInt32BE(ts, 0); return e; })() : Buffer.alloc(0);\n return Buffer.concat([basicHeader(o.fmt, o.csid), hdr, ext, o.body]);\n}\n\ninterface CsidState {\n timestamp: number;\n delta: number;\n length: number;\n type: number;\n msid: number;\n extTs: boolean;\n partial: Buffer[];\n received: number;\n}\n\nfunction freshState(): CsidState {\n return { timestamp: 0, delta: 0, length: 0, type: 0, msid: 0, extTs: false, partial: [], received: 0 };\n}\n\n/**\n * Incremental chunk reader. Feed raw TCP bytes; get complete messages back.\n * Handles fmt 0..3, 1/2/3-byte basic headers, extended timestamps (also on fmt-3\n * continuations, per spec/librtmp), and dynamic chunk-size changes.\n */\nexport class ChunkReader {\n chunkSize: number;\n private pending: Buffer = Buffer.alloc(0);\n private readonly streams = new Map<number, CsidState>();\n\n constructor(chunkSize: number = PRIVATE_MODE_CHUNK_SIZE) {\n this.chunkSize = chunkSize;\n }\n\n feed(data: Buffer): RtmpMessage[] {\n this.pending = this.pending.length ? Buffer.concat([this.pending, data]) : data;\n const out: RtmpMessage[] = [];\n for (;;) {\n const consumed = this.tryReadChunk(out);\n if (consumed === 0) break;\n this.pending = this.pending.subarray(consumed);\n }\n return out;\n }\n\n /** Returns bytes consumed for one chunk, or 0 if more data is needed. */\n private tryReadChunk(out: RtmpMessage[]): number {\n const buf = this.pending;\n if (buf.length < 1) return 0;\n const fmt = buf[0]! >> 6;\n let csid = buf[0]! & 0x3f;\n let p = 1;\n if (csid === 0) { if (buf.length < 2) return 0; csid = 64 + buf[1]!; p = 2; }\n else if (csid === 1) { if (buf.length < 3) return 0; csid = 64 + buf[1]! + buf[2]! * 256; p = 3; }\n\n const st = this.streams.get(csid) ?? freshState();\n const hdrLen = fmt === 0 ? 11 : fmt === 1 ? 7 : fmt === 2 ? 3 : 0;\n if (buf.length < p + hdrLen) return 0;\n\n let tsField = 0;\n if (fmt <= 2) {\n tsField = buf.readUIntBE(p, 3);\n if (fmt <= 1) { st.length = buf.readUIntBE(p + 3, 3); st.type = buf[p + 6]!; }\n if (fmt === 0) st.msid = buf.readUInt32LE(p + 7);\n st.extTs = tsField === 0xffffff;\n }\n p += hdrLen;\n if (st.extTs) {\n if (buf.length < p + 4) return 0;\n tsField = buf.readUInt32BE(p);\n p += 4;\n }\n const startOfMessage = st.received === 0;\n if (startOfMessage) {\n if (fmt === 0) { st.timestamp = tsField; st.delta = 0; }\n else if (fmt <= 2) { st.delta = tsField; st.timestamp += tsField; }\n else st.timestamp += st.delta; // fmt 3 starting a new message: reuse last delta\n }\n\n const take = Math.min(this.chunkSize, st.length - st.received);\n if (buf.length < p + take) return 0;\n st.partial.push(Buffer.from(buf.subarray(p, p + take)));\n st.received += take;\n if (st.received >= st.length) {\n out.push({ csid, msid: st.msid, timestamp: st.timestamp, type: st.type, body: Buffer.concat(st.partial) });\n st.partial = [];\n st.received = 0;\n }\n this.streams.set(csid, st);\n return p + take;\n }\n}\n","/**\n * FLV tag payload helpers: AVC (H.264) AVCC -> Annex-B, AAC raw -> ADTS, and an FLV file writer\n * for offline verification (`ffprobe out.flv`).\n */\n\nexport interface VideoTag {\n frameType: number; // 1 = keyframe, 2 = inter\n codecId: number; // 7 = AVC\n avcPacketType: number; // 0 = sequence header, 1 = NALU, 2 = end of sequence\n compositionTime: number;\n data: Buffer;\n}\n\nexport interface AudioTag {\n soundFormat: number; // 10 = AAC, 7 = G.711 A-law, 8 = G.711 mu-law\n sampleRateIndex: number; // FLV: 0=5.5k 1=11k 2=22k 3=44k (informational for AAC)\n channels: number; // 1 or 2\n aacPacketType: number | null; // 0 = AudioSpecificConfig, 1 = raw frame (AAC only)\n data: Buffer;\n}\n\nexport interface AvcConfig {\n sps: Buffer[];\n pps: Buffer[];\n nalLengthSize: number;\n}\n\nexport interface AacConfig {\n objectType: number;\n samplingFrequencyIndex: number;\n channelConfiguration: number;\n}\n\nconst ANNEXB_START = Buffer.from([0, 0, 0, 1]);\n\nexport function parseVideoTag(body: Buffer): VideoTag | null {\n if (body.length < 5) return null;\n const b0 = body[0]!;\n return {\n frameType: b0 >> 4,\n codecId: b0 & 0x0f,\n avcPacketType: body[1]!,\n compositionTime: body.readIntBE(2, 3),\n data: body.subarray(5),\n };\n}\n\nexport function parseAudioTag(body: Buffer): AudioTag | null {\n if (body.length < 1) return null;\n const b0 = body[0]!;\n const soundFormat = b0 >> 4;\n const isAac = soundFormat === 10;\n if (isAac && body.length < 2) return null;\n return {\n soundFormat,\n sampleRateIndex: (b0 >> 2) & 3,\n channels: (b0 & 1) + 1,\n aacPacketType: isAac ? body[1]! : null,\n data: body.subarray(isAac ? 2 : 1),\n };\n}\n\n/** Parse an AVCDecoderConfigurationRecord (FLV AVC sequence header payload). */\nexport function parseAvcConfig(rec: Buffer): AvcConfig {\n const nalLengthSize = (rec[4]! & 3) + 1;\n let p = 5;\n const sps: Buffer[] = [];\n const pps: Buffer[] = [];\n const numSps = rec[p++]! & 0x1f;\n for (let i = 0; i < numSps; i++) { const n = rec.readUInt16BE(p); sps.push(Buffer.from(rec.subarray(p + 2, p + 2 + n))); p += 2 + n; }\n const numPps = rec[p++]!;\n for (let i = 0; i < numPps; i++) { const n = rec.readUInt16BE(p); pps.push(Buffer.from(rec.subarray(p + 2, p + 2 + n))); p += 2 + n; }\n return { sps, pps, nalLengthSize };\n}\n\n/** Length-prefixed NALUs -> Annex-B. Prepends SPS/PPS when `prependParams` is set (for keyframes). */\nexport function avccToAnnexB(data: Buffer, cfg: AvcConfig, prependParams: boolean): Buffer {\n const parts: Buffer[] = [];\n if (prependParams) for (const nal of [...cfg.sps, ...cfg.pps]) parts.push(ANNEXB_START, nal);\n let p = 0;\n while (p + cfg.nalLengthSize <= data.length) {\n const len = data.readUIntBE(p, cfg.nalLengthSize);\n p += cfg.nalLengthSize;\n if (len === 0 || p + len > data.length) break;\n parts.push(ANNEXB_START, data.subarray(p, p + len));\n p += len;\n }\n return Buffer.concat(parts);\n}\n\nexport function parseAacConfig(asc: Buffer): AacConfig {\n const a0 = asc[0]!;\n const a1 = asc[1]!;\n return {\n objectType: a0 >> 3,\n samplingFrequencyIndex: ((a0 & 7) << 1) | (a1 >> 7),\n channelConfiguration: (a1 >> 3) & 0x0f,\n };\n}\n\nconst AAC_SAMPLE_RATES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];\n\nexport function aacSampleRate(cfg: AacConfig): number {\n return AAC_SAMPLE_RATES[cfg.samplingFrequencyIndex] ?? 0;\n}\n\n/** Wrap one raw AAC frame in a 7-byte ADTS header. */\nexport function aacToAdts(raw: Buffer, cfg: AacConfig): Buffer {\n const frameLen = raw.length + 7;\n const h = Buffer.alloc(7);\n h[0] = 0xff;\n h[1] = 0xf1; // MPEG-4, layer 0, no CRC\n h[2] = ((cfg.objectType - 1) << 6) | (cfg.samplingFrequencyIndex << 2) | (cfg.channelConfiguration >> 2);\n h[3] = ((cfg.channelConfiguration & 3) << 6) | ((frameLen >> 11) & 3);\n h[4] = (frameLen >> 3) & 0xff;\n h[5] = ((frameLen & 7) << 5) | 0x1f;\n h[6] = 0xfc;\n return Buffer.concat([h, raw]);\n}\n\n/** Minimal FLV file writer (header + tags) for offline inspection. */\nexport class FlvWriter {\n private readonly chunks: Buffer[] = [];\n\n constructor() {\n const hdr = Buffer.from([0x46, 0x4c, 0x56, 0x01, 0x05, 0, 0, 0, 9, 0, 0, 0, 0]);\n this.chunks.push(hdr);\n }\n\n tag(type: number, timestamp: number, body: Buffer): void {\n const h = Buffer.alloc(11);\n h[0] = type;\n h.writeUIntBE(body.length, 1, 3);\n h.writeUIntBE(timestamp & 0xffffff, 4, 3);\n h[7] = (timestamp >> 24) & 0xff;\n const prev = Buffer.alloc(4);\n prev.writeUInt32BE(11 + body.length, 0);\n this.chunks.push(h, body, prev);\n }\n\n toBuffer(): Buffer {\n return Buffer.concat(this.chunks);\n }\n}\n","/**\n * G.711 A-law codec (ITU-T G.711) — the intercom uplink format for Dreame\n * cameras (8 kHz, mono, 16-bit PCM in). Pure functions, no state.\n */\n\nconst ALAW_SEG_END = [0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff];\n\n/** Encode one 16-bit signed PCM sample to an A-law byte. */\nexport function pcm16ToALaw(sample: number): number {\n let s = sample;\n let sign = (~s >> 8) & 0x80; // 0x80 for positive, 0 for negative\n if (sign === 0) s = -s;\n if (s > 32635) s = 32635;\n\n let aval: number;\n if (s >= 256) {\n let seg = 7;\n for (let i = 0; i < 8; i++) {\n if (s <= ALAW_SEG_END[i]!) {\n seg = i;\n break;\n }\n }\n const mantissa = (s >> (seg + 3)) & 0x0f;\n aval = (seg << 4) | mantissa;\n } else {\n aval = s >> 4;\n }\n return (aval ^ sign ^ 0x55) & 0xff;\n}\n\n/**\n * Encode a buffer of little-endian 16-bit PCM samples to A-law bytes\n * (one output byte per input sample).\n */\nexport function pcm16leToALaw(pcm: Buffer): Buffer {\n const count = pcm.length >> 1;\n const out = Buffer.allocUnsafe(count);\n for (let i = 0; i < count; i++) {\n out[i] = pcm16ToALaw(pcm.readInt16LE(i * 2));\n }\n return out;\n}\n","/**\n * End-to-end camera media pipeline: autonomously cold-start the LinkVisual\n * monitor, connect to the resulting RTMP relay, and re-emit demuxed elementary\n * frames — H.264 access units (Annex-B, SPS/PPS prepended on keyframes) and AAC\n * (ADTS) — plus lifecycle events.\n *\n * This is the reusable frame feed for any external consumer (scrypted's RFC4571\n * muxer, camstack, a file writer). It owns the {@link DreameCameraController}\n * lifecycle: {@link start} opens the monitor and connects; {@link stop} closes\n * the relay socket and releases the monitor on the device.\n */\nimport { TypedEmitter } from '../../transport/typed-emitter.js';\nimport { DreameError } from '../../transport/errors.js';\nimport type { DreameCameraController } from '../monitor/controller.js';\nimport {\n LvRtmpClient,\n LV_STATUS,\n type AudioInfoEvent,\n type StatusEvent,\n type VideoAccessUnitEvent,\n} from './lv-rtmp-client.js';\nimport { pcm16leToALaw } from './g711.js';\n\n/** The subset of {@link LvRtmpClient} the pipeline drives — injectable for tests. */\nexport interface FrameSource {\n connect(): Promise<void>;\n close(): void;\n on(event: 'videoAccessUnit', cb: (e: VideoAccessUnitEvent) => void): void;\n on(event: 'audioFrame', cb: (buf: Buffer) => void): void;\n on(event: 'audioInfo', cb: (info: AudioInfoEvent) => void): void;\n on(event: 'status', cb: (e: StatusEvent) => void): void;\n on(event: 'error', cb: (err: Error) => void): void;\n on(event: 'close', cb: () => void): void;\n /** Push an intercom (talk-back) audio frame upstream. */\n sendAudioFrame(payload: Buffer, tsMs: number, header?: number): void;\n}\n\n/** Factory for the RTMP frame source; defaults to a real {@link LvRtmpClient}. */\nexport type FrameSourceFactory = (url: string) => FrameSource;\n\nexport interface DreameCameraStreamEvents {\n /** Emitted once the relay is connected; carries the (single-use) RTMP URL. */\n started: [{ rtmpUrl: string }];\n /** One H.264 access unit (Annex-B). */\n videoAccessUnit: [VideoAccessUnitEvent];\n /** One audio frame (AAC in ADTS, or raw G.711). */\n audioFrame: [Buffer];\n /** Audio format, emitted once known. */\n audioInfo: [AudioInfoEvent];\n /** Emitted after the pipeline is fully torn down. */\n stopped: [];\n error: [Error];\n [event: string]: unknown[];\n}\n\nexport interface DreameCameraStreamInput {\n /** A controller for the target camera (not yet opened). */\n controller: DreameCameraController;\n /** Override the RTMP client (tests inject a fake). */\n clientFactory?: FrameSourceFactory;\n /** Forwarded to {@link LvRtmpClient}: send NetStream.RequestAudioType after play. */\n requestAudioType?: boolean;\n /** Forwarded to {@link LvRtmpClient}: send periodic NetStream.Ping. */\n ping?: boolean;\n /** Optional line logger for the RTMP client. */\n log?: (line: string) => void;\n}\n\nexport class DreameCameraStream extends TypedEmitter<DreameCameraStreamEvents> {\n readonly #controller: DreameCameraController;\n readonly #clientFactory: FrameSourceFactory;\n readonly #log: (line: string) => void;\n #client: FrameSource | null = null;\n #state: 'idle' | 'starting' | 'running' | 'stopping' = 'idle';\n #videoFrames = 0;\n #audioFrames = 0;\n #talkReady = false;\n #talkWaiters: Array<() => void> = [];\n #talkTs = 0;\n\n constructor(input: DreameCameraStreamInput) {\n super();\n this.#controller = input.controller;\n this.#log = input.log ?? ((): void => {});\n this.#clientFactory =\n input.clientFactory ??\n ((url: string): FrameSource =>\n new LvRtmpClient({\n url,\n ...(input.requestAudioType !== undefined ? { requestAudioType: input.requestAudioType } : {}),\n ...(input.ping !== undefined ? { ping: input.ping } : {}),\n ...(input.log ? { log: input.log } : {}),\n }));\n }\n\n /** True while frames are flowing. */\n get isRunning(): boolean {\n return this.#state === 'running';\n }\n\n /**\n * Cold-start the monitor, connect to the relay, and begin emitting frames.\n * Resolves once connected. Throws (and cleans up) if the monitor or relay\n * connection fails.\n */\n async start(): Promise<{ rtmpUrl: string }> {\n if (this.#state !== 'idle') {\n throw new DreameError(`camera stream cannot start from state '${this.#state}'`);\n }\n this.#state = 'starting';\n try {\n this.#log('[nodedreame:stream] starting');\n const handle = await this.#controller.open();\n const client = this.#clientFactory(handle.rtmpUrl);\n this.#client = client;\n client.on('videoAccessUnit', (e) => {\n this.#videoFrames += 1;\n if (this.#videoFrames === 1) this.#log('[nodedreame:stream] first video access unit');\n this.emit('videoAccessUnit', e);\n });\n client.on('audioFrame', (buf) => {\n this.#audioFrames += 1;\n if (this.#audioFrames === 1) this.#log('[nodedreame:stream] first audio frame');\n this.emit('audioFrame', buf);\n });\n client.on('audioInfo', (info) => {\n this.#log(`[nodedreame:stream] audio ${info.codec} ${info.sampleRate}Hz x${info.channels}`);\n this.emit('audioInfo', info);\n });\n client.on('status', (e) => {\n if (e.code === LV_STATUS.TALK_READY) {\n this.#log('[nodedreame:stream] talk channel ready');\n this.#talkReady = true;\n const waiters = this.#talkWaiters;\n this.#talkWaiters = [];\n for (const w of waiters) w();\n }\n });\n client.on('error', (err) => {\n this.#log(`[nodedreame:stream] rtmp error: ${err.message}`);\n this.emit('error', err);\n });\n client.on('close', () => {\n this.#log('[nodedreame:stream] upstream relay closed');\n // Upstream relay closed on us — tear down and surface as stopped.\n if (this.#state === 'running') void this.stop();\n });\n await client.connect();\n this.#state = 'running';\n this.#log('[nodedreame:stream] connected to relay');\n this.emit('started', { rtmpUrl: handle.rtmpUrl });\n return { rtmpUrl: handle.rtmpUrl };\n } catch (err) {\n this.#state = 'idle';\n await this.#safeTeardown();\n throw err;\n }\n }\n\n /** Close the relay connection and release the monitor. Idempotent. */\n async stop(): Promise<void> {\n if (this.#state === 'idle' || this.#state === 'stopping') return;\n this.#state = 'stopping';\n this.#log(`[nodedreame:stream] stopping (video=${this.#videoFrames} audio=${this.#audioFrames})`);\n await this.#safeTeardown();\n this.#state = 'idle';\n this.emit('stopped');\n }\n\n /**\n * Open the two-way intercom: send the MIoT start, then resolve once the relay\n * signals the talk channel is ready (so it's safe to {@link sendTalkPcm}).\n * Rejects if not ready within `timeoutMs`.\n */\n async startTalk(opts?: { needRecordSound?: boolean; videoCall?: boolean; timeoutMs?: number }): Promise<void> {\n if (this.#state !== 'running') throw new DreameError('stream is not running; start() first');\n this.#log('[nodedreame:stream] intercom start');\n await this.#controller.startIntercom(\n opts?.needRecordSound !== undefined || opts?.videoCall !== undefined\n ? { ...(opts.needRecordSound !== undefined ? { needRecordSound: opts.needRecordSound } : {}), ...(opts.videoCall ? { videoCall: true } : {}) }\n : undefined,\n );\n this.#talkTs = 0;\n await this.whenTalkReady(opts?.timeoutMs ?? 5000);\n }\n\n /** Resolve when the talk channel is ready (or reject on timeout). */\n whenTalkReady(timeoutMs = 5000): Promise<void> {\n if (this.#talkReady) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => reject(new DreameError('timed out waiting for TalkReady')), timeoutMs);\n timer.unref?.();\n this.#talkWaiters.push(() => {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n /**\n * Encode 16-bit LE mono PCM (8 kHz) to G.711 A-law and push it upstream.\n * `tsMs` defaults to a running clock advanced by the frame duration.\n */\n sendTalkPcm(pcm16le: Buffer, tsMs?: number): void {\n const alaw = pcm16leToALaw(pcm16le);\n this.sendTalkAlaw(alaw, tsMs);\n }\n\n /** Push raw A-law audio upstream (advancing the running talk clock). */\n sendTalkAlaw(alaw: Buffer, tsMs?: number): void {\n if (!this.#client) throw new DreameError('stream is not running');\n const ts = tsMs ?? this.#talkTs;\n this.#client.sendAudioFrame(alaw, ts);\n // A-law @ 8 kHz => 1 byte per sample => ms = samples/8.\n this.#talkTs = ts + Math.round(alaw.length / 8);\n }\n\n /** Close the two-way intercom (MIoT stop). */\n async stopTalk(): Promise<void> {\n this.#talkReady = false;\n this.#log('[nodedreame:stream] intercom stop');\n await this.#controller.stopIntercom();\n }\n\n async #safeTeardown(): Promise<void> {\n this.#talkReady = false;\n this.#talkWaiters = [];\n try {\n this.#client?.close();\n } catch {\n /* ignore */\n }\n this.#client = null;\n try {\n await this.#controller.close();\n } catch (err) {\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n }\n }\n}\n"],"mappings":"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,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,KAAK,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,KAAK,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,KAAK,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,KAAK,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,KAAK,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,KAAK,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,KAAK,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,KAAK,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,KAAK,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACvC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAAO,GAAO,GAAO,GAAO,CAAK;AAAA,EACrC,GAAG,CAAC,GAA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","names":["MODEL_CAPABILITIES","undiciFetch","import_zod","mqtt","key","MiotState","ChargingStatus","SuctionLevel","WaterVolume","CleaningMode","MiotError","TaskStatus","key","key","key","import_zod","import_node_crypto","import_zod","import_node_crypto","import_zod","import_node_crypto","import_node_crypto","key","key","import_pngjs","key","key","z","MowerStatus","MowerChargingStatus","MowerControlAction","MowerTaskStatus","MowerFault","isRecord","FALLBACK","deepFreeze","MODEL_CAPABILITIES","tokensFor","isRecord","asString","key","CHARGING","z","key","import_zod","key","x","y","w","h","import_node_events","import_node_crypto","str","key","net"]}
|