@mega-yfue/eufy-sdk 0.0.4 → 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -12
- package/dist/client/device-registry.d.ts +343 -0
- package/dist/client/eufy-mega.d.ts +872 -0
- package/dist/client/index.d.ts +1 -6
- package/dist/client/map-channels.d.ts +22 -0
- package/dist/client/types.d.ts +383 -0
- package/dist/core/contracts.d.ts +898 -0
- package/dist/core/crypto.d.ts +98 -0
- package/dist/core/index.d.ts +9 -7
- package/dist/core/logger.d.ts +53 -0
- package/dist/core/lz4-block.d.ts +35 -0
- package/dist/core/raw-dp-hex.d.ts +32 -0
- package/dist/core/raw-dp-writer.d.ts +83 -0
- package/dist/core/store.d.ts +43 -0
- package/dist/core/types.d.ts +169 -0
- package/dist/core/util.d.ts +78 -0
- package/dist/index.d.ts +4 -7
- package/dist/index.js +25526 -14
- package/dist/index.js.map +7 -1
- package/dist/model/capabilities/access.d.ts +127 -0
- package/dist/model/capabilities/arming.d.ts +201 -0
- package/dist/model/capabilities/audio.d.ts +154 -0
- package/dist/model/capabilities/battery.d.ts +363 -0
- package/dist/model/capabilities/camera.d.ts +564 -0
- package/dist/model/capabilities/co.d.ts +40 -0
- package/dist/model/capabilities/contact.d.ts +133 -0
- package/dist/model/capabilities/doorbell.d.ts +345 -0
- package/dist/model/capabilities/dp-catalog.d.ts +38 -0
- package/dist/model/capabilities/index.d.ts +561 -0
- package/dist/model/capabilities/info.d.ts +28 -0
- package/dist/model/capabilities/keypad.d.ts +61 -0
- package/dist/model/capabilities/leak.d.ts +43 -0
- package/dist/model/capabilities/light.d.ts +174 -0
- package/dist/model/capabilities/locate.d.ts +63 -0
- package/dist/model/capabilities/lock.d.ts +242 -0
- package/dist/model/capabilities/manifest.d.ts +107 -0
- package/dist/model/capabilities/members.d.ts +647 -0
- package/dist/model/capabilities/motion.d.ts +377 -0
- package/dist/model/capabilities/person-detection.d.ts +8 -0
- package/dist/model/capabilities/ptz.d.ts +289 -0
- package/dist/model/capabilities/rtsp.d.ts +221 -0
- package/dist/model/capabilities/siren.d.ts +218 -0
- package/dist/model/capabilities/smart-light.d.ts +172 -0
- package/dist/model/capabilities/smoke.d.ts +40 -0
- package/dist/model/capabilities/snapshot.d.ts +6 -0
- package/dist/model/capabilities/storage.d.ts +11 -0
- package/dist/model/capabilities/suction.d.ts +104 -0
- package/dist/model/capabilities/types.d.ts +484 -0
- package/dist/model/capabilities/vacuum-clean.d.ts +1946 -0
- package/dist/model/capabilities/vacuum-dock.d.ts +208 -0
- package/dist/model/capabilities/video.d.ts +6 -0
- package/dist/model/classify.d.ts +78 -0
- package/dist/model/clean-record-detail.d.ts +65 -0
- package/dist/model/clean-records.d.ts +69 -0
- package/dist/model/device-family.d.ts +73 -0
- package/dist/model/device-types.d.ts +123 -0
- package/dist/model/device.d.ts +265 -0
- package/dist/model/index.d.ts +29 -4
- package/dist/model/infer.d.ts +23 -0
- package/dist/model/inspect.d.ts +61 -0
- package/dist/model/life-params.d.ts +21 -0
- package/dist/model/map-pixels.d.ts +70 -0
- package/dist/model/param-dictionary.d.ts +28 -0
- package/dist/model/param-namespace.d.ts +21 -0
- package/dist/model/proto-read.d.ts +53 -0
- package/dist/model/push-events.d.ts +147 -0
- package/dist/model/registry.d.ts +54 -0
- package/dist/model/types.d.ts +301 -0
- package/dist/model/vacuum-map-store.d.ts +92 -0
- package/dist/model/vacuum-map.d.ts +286 -0
- package/dist/model/vacuum-scenes.d.ts +76 -0
- package/dist/model/vacuum-schedules.d.ts +85 -0
- package/dist/transport/dp-preset.d.ts +102 -0
- package/dist/transport/ff09.d.ts +444 -0
- package/dist/transport/ffmpeg.d.ts +86 -0
- package/dist/transport/http/decodeImageV1.d.ts +20 -0
- package/dist/transport/http/decodeImageV2.d.ts +19 -0
- package/dist/transport/http/index.d.ts +5 -0
- package/dist/transport/http/light-catalog.d.ts +62 -0
- package/dist/transport/http/media-download.d.ts +14 -0
- package/dist/transport/http/mega-client.d.ts +514 -0
- package/dist/transport/http/phone-model.d.ts +21 -0
- package/dist/transport/index.d.ts +10 -7
- package/dist/transport/mqtt/app-client-id.d.ts +16 -0
- package/dist/transport/mqtt/availability.d.ts +14 -0
- package/dist/transport/mqtt/bare-ip-tls.d.ts +46 -0
- package/dist/transport/mqtt/biz-stream.d.ts +98 -0
- package/dist/transport/mqtt/broker-discovery.d.ts +55 -0
- package/dist/transport/mqtt/clean-codec.d.ts +14 -0
- package/dist/transport/mqtt/command-router.d.ts +285 -0
- package/dist/transport/mqtt/dp-codec.d.ts +58 -0
- package/dist/transport/mqtt/dp-color.d.ts +14 -0
- package/dist/transport/mqtt/engine.d.ts +16 -0
- package/dist/transport/mqtt/index.d.ts +5 -0
- package/dist/transport/mqtt/secure-mqtt.d.ts +107 -0
- package/dist/transport/mqtt/topics.d.ts +80 -0
- package/dist/transport/p2p/adts.d.ts +91 -0
- package/dist/transport/p2p/annexb.d.ts +124 -0
- package/dist/transport/p2p/codec.d.ts +166 -0
- package/dist/transport/p2p/command-router.d.ts +655 -0
- package/dist/transport/p2p/commands.d.ts +550 -0
- package/dist/transport/p2p/envelope.d.ts +46 -0
- package/dist/transport/p2p/fmp4.d.ts +89 -0
- package/dist/transport/p2p/fragment-recording.d.ts +33 -0
- package/dist/transport/p2p/index.d.ts +13 -0
- package/dist/transport/p2p/lan-ip.d.ts +25 -0
- package/dist/transport/p2p/live-stream.d.ts +223 -0
- package/dist/transport/p2p/live-trace.d.ts +123 -0
- package/dist/transport/p2p/media.d.ts +105 -0
- package/dist/transport/p2p/p2p-session.d.ts +620 -0
- package/dist/transport/p2p/readable-egress.d.ts +27 -0
- package/dist/transport/p2p/session-manager.d.ts +154 -0
- package/dist/transport/p2p/shared-live-source.d.ts +431 -0
- package/dist/transport/p2p/talkback.d.ts +187 -0
- package/dist/transport/p2p/video.d.ts +150 -0
- package/dist/transport/p2p/write-commands.d.ts +21 -0
- package/dist/transport/protobuf.d.ts +5 -0
- package/dist/transport/push/fcm.d.ts +23 -0
- package/dist/transport/push/index.d.ts +6 -0
- package/dist/transport/push/message-tags.d.ts +26 -0
- package/dist/transport/push/parser.d.ts +27 -0
- package/dist/transport/push/proto.d.ts +11 -0
- package/dist/transport/push/push-client.d.ts +64 -0
- package/dist/transport/push/store.d.ts +23 -0
- package/dist/transport/push/types.d.ts +180 -0
- package/dist/transport/raw-dp.d.ts +6 -0
- package/dist/transport/stored-image-cache.d.ts +23 -0
- package/dist/transport/tuya/account.d.ts +44 -0
- package/dist/transport/tuya/client.d.ts +89 -0
- package/dist/transport/tuya/command-router.d.ts +79 -0
- package/dist/transport/tuya/dp-codec.d.ts +56 -0
- package/dist/transport/tuya/index.d.ts +28 -0
- package/dist/transport/tuya/request.d.ts +157 -0
- package/dist/transport/tuya/sign.d.ts +64 -0
- package/package.json +12 -13
- package/dist/client/index.js +0 -2
- package/dist/client/index.js.map +0 -1
- package/dist/core/index.js +0 -2
- package/dist/core/index.js.map +0 -1
- package/dist/model/index.js +0 -2
- package/dist/model/index.js.map +0 -1
- package/dist/transport/index.js +0 -2
- package/dist/transport/index.js.map +0 -1
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The robot's map, as `stream.proto` describes it — the messages that arrive on the `biz/…/res` leg.
|
|
3
|
+
*
|
|
4
|
+
* Six message types carry everything about a map: its size and where it sits in
|
|
5
|
+
* the world, which cell is floor and which is wall, which room each cell belongs to, what those rooms
|
|
6
|
+
* are called and how they are cleaned, and where the user drew a line the robot must not cross. This
|
|
7
|
+
* decodes all six. It does not draw anything: what it hands back is dimensions, coordinates, names and
|
|
8
|
+
* two pixel planes.
|
|
9
|
+
*
|
|
10
|
+
* **Units, once, because every coordinate here shares them.** Distances are centimetres — the vendor
|
|
11
|
+
* writes them as "m × 100" throughout — and `resolution` is the width of one cell in those same
|
|
12
|
+
* centimetres, so a cell at `(col, row)` covers world position `origin + (col, row) × resolution`. An
|
|
13
|
+
* angle is radians × 100. Nothing is converted on the way through: a value the device sent as an
|
|
14
|
+
* integer stays one, because dividing it here would make every caller guess whether it had been.
|
|
15
|
+
*
|
|
16
|
+
* **What is deliberately not read.** `RestrictedZone.suggestion` carries zones the robot has *proposed*
|
|
17
|
+
* and the user has not accepted. Merging them into the real lists would report restrictions the robot
|
|
18
|
+
* is not enforcing, and reporting them as fact is worse than not reporting them at all.
|
|
19
|
+
*
|
|
20
|
+
* @module model/vacuum-map
|
|
21
|
+
*/
|
|
22
|
+
import type { RawDpCodec } from "../core/contracts.js";
|
|
23
|
+
import type { CleanExtent, MopLevel, VacuumCleanType } from "./capabilities/vacuum-clean.js";
|
|
24
|
+
/** A position on the map, in centimetres from the map's own origin. */
|
|
25
|
+
export interface MapPoint {
|
|
26
|
+
readonly x: number;
|
|
27
|
+
readonly y: number;
|
|
28
|
+
}
|
|
29
|
+
/** A position with a heading. `theta` is radians × 100, as the device sends it. */
|
|
30
|
+
export interface MapPose extends MapPoint {
|
|
31
|
+
readonly theta: number;
|
|
32
|
+
}
|
|
33
|
+
/** A virtual wall: the segment between two points that the robot will not cross. */
|
|
34
|
+
export interface MapLine {
|
|
35
|
+
readonly from: MapPoint;
|
|
36
|
+
readonly to: MapPoint;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A four-cornered zone, corners in the order the device sent them.
|
|
40
|
+
*
|
|
41
|
+
* Not necessarily a rectangle — the vendor's type is `Quadrangle`, and the app lets a zone be rotated
|
|
42
|
+
* — so the corners describe a polygon and not a bounding box.
|
|
43
|
+
*/
|
|
44
|
+
export interface MapQuad {
|
|
45
|
+
readonly corners: readonly [MapPoint, MapPoint, MapPoint, MapPoint];
|
|
46
|
+
}
|
|
47
|
+
/** What a dock is: a bare charging base, or a full station with water and dust handling. */
|
|
48
|
+
export declare const DOCK_KINDS: readonly ["charger", "station"];
|
|
49
|
+
export type DockKind = (typeof DOCK_KINDS)[number];
|
|
50
|
+
/** Where a dock stands, and which kind it is. */
|
|
51
|
+
export interface MapDock {
|
|
52
|
+
readonly kind: DockKind;
|
|
53
|
+
readonly pose: MapPose;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* How complete the device considers this map.
|
|
57
|
+
*
|
|
58
|
+
* `"listFull"` is not a quality at all but a notice: the robot has nowhere left to store a new map and
|
|
59
|
+
* is telling the app to replace one or discard this. It shares the field, so it is named here.
|
|
60
|
+
*/
|
|
61
|
+
export declare const MAP_QUALITIES: readonly ["incomplete", "rough", "effective", "listFull"];
|
|
62
|
+
export type MapQuality = (typeof MAP_QUALITIES)[number];
|
|
63
|
+
/** Size, placement and quality of a map — the vendor's `MapInfo`. */
|
|
64
|
+
export interface VacuumMapGeometry {
|
|
65
|
+
/** Cells across and down. Both are non-zero: a plane without dimensions cannot be indexed. */
|
|
66
|
+
readonly width: number;
|
|
67
|
+
readonly height: number;
|
|
68
|
+
/**
|
|
69
|
+
* The width of one cell in centimetres, or `undefined` when the device omitted it.
|
|
70
|
+
*
|
|
71
|
+
* No default is invented for the absent case. The reference integration substitutes 5 without saying
|
|
72
|
+
* why, and a guessed scale silently misplaces every zone and every room label on the map.
|
|
73
|
+
*/
|
|
74
|
+
readonly resolution: number | undefined;
|
|
75
|
+
/** Where cell `(0, 0)` sits, in centimetres. Signed, and routinely negative. */
|
|
76
|
+
readonly origin: MapPoint;
|
|
77
|
+
/** Clockwise rotation the app applies when drawing, in degrees. */
|
|
78
|
+
readonly angle: number;
|
|
79
|
+
readonly quality: MapQuality;
|
|
80
|
+
/** Every dock the device knows about. Empty until the robot has seen one. */
|
|
81
|
+
readonly docks: readonly MapDock[];
|
|
82
|
+
}
|
|
83
|
+
/** Whether a map frame replaces what came before it, or amends it. */
|
|
84
|
+
export declare const MAP_FRAME_KINDS: readonly ["full", "incremental"];
|
|
85
|
+
export type MapFrameKind = (typeof MAP_FRAME_KINDS)[number];
|
|
86
|
+
/**
|
|
87
|
+
* What one cell of {@link VacuumMapPlane.cells} means, by its two-bit value.
|
|
88
|
+
*
|
|
89
|
+
* Indexed by the value itself, so `MAP_CELL_VALUES[2]` is `"free"`.
|
|
90
|
+
*/
|
|
91
|
+
export declare const MAP_CELL_VALUES: readonly ["unknown", "obstacle", "free", "carpet"];
|
|
92
|
+
export type MapCellValue = (typeof MAP_CELL_VALUES)[number];
|
|
93
|
+
/** A map frame — the vendor's `Map`, with its pixel plane decompressed. */
|
|
94
|
+
export interface VacuumMapPlane {
|
|
95
|
+
/**
|
|
96
|
+
* Whether this frame is the whole map or an amendment to the last one.
|
|
97
|
+
*
|
|
98
|
+
* **How an `"incremental"` frame is applied is not known.** Nothing states whether it replaces a
|
|
99
|
+
* region, carries its own origin, or assumes the previous frame's geometry. A stale-but-correct map
|
|
100
|
+
* beats one assembled by a guessed rule.
|
|
101
|
+
*/
|
|
102
|
+
readonly frame: MapFrameKind;
|
|
103
|
+
/** The device's id for this map. `undefined` when it sent none. */
|
|
104
|
+
readonly mapId: number | undefined;
|
|
105
|
+
/** The map's name, as the user set it. `undefined` when unnamed. */
|
|
106
|
+
readonly name: string | undefined;
|
|
107
|
+
/** The map's revision counter. It advances whenever the map is edited. */
|
|
108
|
+
readonly releases: number;
|
|
109
|
+
/** The frame's position in a sequence, as the device counts it. Zero on a lone frame. */
|
|
110
|
+
readonly index: number;
|
|
111
|
+
readonly geometry: VacuumMapGeometry;
|
|
112
|
+
/**
|
|
113
|
+
* The cell plane, decompressed: **four cells per byte, two bits each, low bits first**, in one run
|
|
114
|
+
* with no row padding. The cell at `(col, row)` is at bit `(i & 3) * 2` of byte `i >> 2`, where
|
|
115
|
+
* `i = row * width + col`, and its value indexes {@link MAP_CELL_VALUES}.
|
|
116
|
+
*
|
|
117
|
+
* Handed over packed rather than expanded. A large map is a megabyte once every cell is its own
|
|
118
|
+
* byte.
|
|
119
|
+
*/
|
|
120
|
+
readonly cells: Buffer;
|
|
121
|
+
}
|
|
122
|
+
/** Which room each cell belongs to — the vendor's `RoomOutline`, with its plane decompressed. */
|
|
123
|
+
export interface VacuumRoomOutline {
|
|
124
|
+
readonly mapId: number | undefined;
|
|
125
|
+
readonly releases: number;
|
|
126
|
+
readonly width: number;
|
|
127
|
+
readonly height: number;
|
|
128
|
+
readonly resolution: number | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* Where this plane's cell `(0, 0)` sits, in centimetres.
|
|
131
|
+
*
|
|
132
|
+
* **Its own origin, not the map's.** The two planes are not guaranteed to start at the same world
|
|
133
|
+
* position, so a room lookup under a map cell must convert through world coordinates rather than
|
|
134
|
+
* reusing the index.
|
|
135
|
+
*/
|
|
136
|
+
readonly origin: MapPoint;
|
|
137
|
+
/**
|
|
138
|
+
* The room plane: **one byte per cell**, `row * width + col`, no padding — a different packing from
|
|
139
|
+
* {@link VacuumMapPlane.cells}, which the vendor states separately for each.
|
|
140
|
+
*
|
|
141
|
+
* The byte is not the room id on its own. Its low two bits carry a sub-type and the id is the
|
|
142
|
+
* remaining bits, so the room at a cell is `cells[i] >> 2`.
|
|
143
|
+
*/
|
|
144
|
+
readonly cells: Buffer;
|
|
145
|
+
}
|
|
146
|
+
/** What kind of floor a room has, by the vendor's `Floor.Type`. */
|
|
147
|
+
export declare const FLOOR_TYPES: readonly ["unknown", "carpet", "wood", "tile"];
|
|
148
|
+
export type FloorType = (typeof FLOOR_TYPES)[number];
|
|
149
|
+
/** What kind of room this is, by the vendor's `RoomScene.Type`. */
|
|
150
|
+
export declare const ROOM_SCENES: readonly ["unknown", "study", "bedroom", "bathroom", "kitchen", "livingRoom", "diningRoom", "corridor"];
|
|
151
|
+
export type RoomScene = (typeof ROOM_SCENES)[number];
|
|
152
|
+
/**
|
|
153
|
+
* Suction, on the scale the vendor declares for a room's `Fan.suction`.
|
|
154
|
+
*
|
|
155
|
+
* **Not the `SuctionLevel` scale DP 158 uses, and the two must not be unified.** DP 158 reports suction on a six-value
|
|
156
|
+
* scale where 4 is BoostIQ and 5 is Max Pro; `clean_param.proto` declares this field's enum with five
|
|
157
|
+
* values ending at `MAX_PLUS = 4`. They agree from 0 to 3 and disagree at 4, so naming a room's
|
|
158
|
+
* `Fan.suction` through the DP scale would report "BoostIQ" for a room the user set to Max+.
|
|
159
|
+
*
|
|
160
|
+
* A live capture showed DP 158 and `clean_param.fan` moving together, which is why the clean-parameter
|
|
161
|
+
* read treats them as one scale — but that capture only covered 0 and 2, where both scales agree. The
|
|
162
|
+
* divergence above is declared by the vendor, not contradicted by anything observed, so this follows
|
|
163
|
+
* the proto the field is actually declared in.
|
|
164
|
+
*/
|
|
165
|
+
export declare const ROOM_SUCTIONS: readonly ["quiet", "standard", "turbo", "max", "maxPlus"];
|
|
166
|
+
export type RoomSuction = (typeof ROOM_SUCTIONS)[number];
|
|
167
|
+
/** The per-room clean settings, when the user has set any. */
|
|
168
|
+
export interface VacuumRoomSettings {
|
|
169
|
+
readonly cleanType: VacuumCleanType | undefined;
|
|
170
|
+
readonly suction: RoomSuction | undefined;
|
|
171
|
+
readonly mopLevel: MopLevel | undefined;
|
|
172
|
+
readonly cleanExtent: CleanExtent | undefined;
|
|
173
|
+
/** How many passes this room gets. `undefined` for the vendor's zero, which means "not set". */
|
|
174
|
+
readonly cleanTimes: number | undefined;
|
|
175
|
+
}
|
|
176
|
+
/** One room on the map. */
|
|
177
|
+
export interface VacuumRoom {
|
|
178
|
+
/** The room's id — what the byte in {@link VacuumRoomOutline.cells} resolves to, and what a room-select frame names. */
|
|
179
|
+
readonly id: number;
|
|
180
|
+
/**
|
|
181
|
+
* The name the user gave this room, or `undefined`.
|
|
182
|
+
*
|
|
183
|
+
* `undefined` is common and is not a decode failure: the vendor's own comment says an unnamed room
|
|
184
|
+
* is labelled by the app from {@link VacuumRoom.scene} and {@link VacuumRoom.sceneIndex} — "Kitchen 1",
|
|
185
|
+
* "Bedroom 2" — in the user's language. A host wanting the same label builds it the same way, which
|
|
186
|
+
* it can only do if the absence is reported rather than papered over.
|
|
187
|
+
*/
|
|
188
|
+
readonly name: string | undefined;
|
|
189
|
+
readonly scene: RoomScene;
|
|
190
|
+
/** Which room of its kind this is, counting from 1. Pairs with {@link VacuumRoom.scene} to name it. */
|
|
191
|
+
readonly sceneIndex: number;
|
|
192
|
+
readonly floor: FloorType;
|
|
193
|
+
/** Where the room sits in the order the app lists them. */
|
|
194
|
+
readonly order: number;
|
|
195
|
+
/** This room's own clean settings. Only in force while {@link VacuumRoomParams.customEnabled}. */
|
|
196
|
+
readonly settings: VacuumRoomSettings;
|
|
197
|
+
}
|
|
198
|
+
/** The room list for a map — the vendor's `RoomParams`. */
|
|
199
|
+
export interface VacuumRoomParams {
|
|
200
|
+
readonly mapId: number | undefined;
|
|
201
|
+
readonly releases: number;
|
|
202
|
+
/**
|
|
203
|
+
* Whether the per-room settings are in force.
|
|
204
|
+
*
|
|
205
|
+
* When `false` the device cleans every room with the global parameters and each room's
|
|
206
|
+
* {@link VacuumRoom.settings} is inert. The settings are still reported, because they are what the
|
|
207
|
+
* user last chose and what turning this on would restore.
|
|
208
|
+
*/
|
|
209
|
+
readonly customEnabled: boolean;
|
|
210
|
+
/** The device's "smart mode" switch, which it reports alongside the rooms. */
|
|
211
|
+
readonly smartMode: boolean;
|
|
212
|
+
readonly rooms: readonly VacuumRoom[];
|
|
213
|
+
}
|
|
214
|
+
/** Everywhere the user has told the robot not to go — the vendor's `RestrictedZone`. */
|
|
215
|
+
export interface VacuumRestrictedZones {
|
|
216
|
+
readonly mapId: number | undefined;
|
|
217
|
+
readonly releases: number;
|
|
218
|
+
/** Lines the robot will not cross. */
|
|
219
|
+
readonly virtualWalls: readonly MapLine[];
|
|
220
|
+
/** Areas the robot will not enter at all. */
|
|
221
|
+
readonly noGoZones: readonly MapQuad[];
|
|
222
|
+
/** Areas the robot may sweep but will not mop. */
|
|
223
|
+
readonly noMopZones: readonly MapQuad[];
|
|
224
|
+
}
|
|
225
|
+
/** A map's identity, without its pixels — the vendor's `MapDescription`. */
|
|
226
|
+
export interface VacuumMapDescription {
|
|
227
|
+
readonly mapId: number | undefined;
|
|
228
|
+
readonly releases: number;
|
|
229
|
+
readonly name: string | undefined;
|
|
230
|
+
/**
|
|
231
|
+
* Why the map exists, as the device's own code. Not translated to names: the vendor ships this as a
|
|
232
|
+
* bare `uint32` with no enum beside it, so any name here would be invented.
|
|
233
|
+
*/
|
|
234
|
+
readonly createCause: number;
|
|
235
|
+
/**
|
|
236
|
+
* When the map was made, and when it was last used, as the device sent them.
|
|
237
|
+
*
|
|
238
|
+
* **The unit is not stated.** The field is a `uint64` and could be seconds or milliseconds; both are
|
|
239
|
+
* used elsewhere on this line. A consumer can tell them apart by magnitude far more safely than this
|
|
240
|
+
* decoder can assume one. `undefined` for the vendor's zero, which is "never".
|
|
241
|
+
*/
|
|
242
|
+
readonly createdAt: number | undefined;
|
|
243
|
+
/** See {@link VacuumMapDescription.createdAt}. */
|
|
244
|
+
readonly lastUsedAt: number | undefined;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* A whole map in one message — the vendor's `MapBackup`, sent when a map is switched or edited.
|
|
248
|
+
*
|
|
249
|
+
* Every part is optional because the device sends what changed. A backup with only `description` set
|
|
250
|
+
* is a rename, and reading its absent `map` as an empty one would erase a map already held.
|
|
251
|
+
*/
|
|
252
|
+
export interface VacuumMapBackup {
|
|
253
|
+
readonly description: VacuumMapDescription | undefined;
|
|
254
|
+
readonly map: VacuumMapPlane | undefined;
|
|
255
|
+
readonly outline: VacuumRoomOutline | undefined;
|
|
256
|
+
readonly rooms: VacuumRoomParams | undefined;
|
|
257
|
+
readonly zones: VacuumRestrictedZones | undefined;
|
|
258
|
+
}
|
|
259
|
+
/** Decode a `MapInfo` message — size, placement and docks, with no pixels attached. */
|
|
260
|
+
export declare function decodeVacuumMapGeometry(raw: unknown, codec: RawDpCodec | undefined): VacuumMapGeometry | undefined;
|
|
261
|
+
/** Decode a `DynamicData` message — where the robot is, right now. */
|
|
262
|
+
export declare function decodeVacuumPose(raw: unknown, codec: RawDpCodec | undefined): MapPose | undefined;
|
|
263
|
+
/**
|
|
264
|
+
* Decode a `Map` message — a frame of the cell plane with the geometry that places it.
|
|
265
|
+
*
|
|
266
|
+
* `undefined` when the frame cannot be used rather than when it cannot be parsed: no geometry, no
|
|
267
|
+
* pixels, a plane that failed to decompress, or one too short for the grid it claims. Each of those
|
|
268
|
+
* yields a map that would draw, and draw wrongly.
|
|
269
|
+
*/
|
|
270
|
+
export declare function decodeVacuumMap(raw: unknown, codec: RawDpCodec | undefined): VacuumMapPlane | undefined;
|
|
271
|
+
/** Decode a `RoomOutline` message — one byte per cell saying which room it belongs to. */
|
|
272
|
+
export declare function decodeVacuumRoomOutline(raw: unknown, codec: RawDpCodec | undefined): VacuumRoomOutline | undefined;
|
|
273
|
+
/** Decode a `RoomParams` message — the room list, their names, and how each is cleaned. */
|
|
274
|
+
export declare function decodeVacuumRoomParams(raw: unknown, codec: RawDpCodec | undefined): VacuumRoomParams | undefined;
|
|
275
|
+
/** Decode a `RestrictedZone` message — virtual walls, no-go zones and no-mop zones. */
|
|
276
|
+
export declare function decodeVacuumRestrictedZones(raw: unknown, codec: RawDpCodec | undefined): VacuumRestrictedZones | undefined;
|
|
277
|
+
/** Decode a `MapDescription` message — a map's identity, without its pixels. */
|
|
278
|
+
export declare function decodeVacuumMapDescription(raw: unknown, codec: RawDpCodec | undefined): VacuumMapDescription | undefined;
|
|
279
|
+
/**
|
|
280
|
+
* Decode a `MapBackup` message — the five-part snapshot sent when a map is switched or edited.
|
|
281
|
+
*
|
|
282
|
+
* Each part is decoded from its own bytes through the same decoder that reads it standing alone, so a
|
|
283
|
+
* backup and a live frame cannot drift apart. A part the device did not send stays `undefined` rather
|
|
284
|
+
* than becoming an empty one: an absent `map` in a rename is not a map with no cells.
|
|
285
|
+
*/
|
|
286
|
+
export declare function decodeVacuumMapBackup(raw: unknown, codec: RawDpCodec | undefined): VacuumMapBackup | undefined;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cleaning scenes a robot holds — `SceneResponse` on DP 180, decoded.
|
|
3
|
+
*
|
|
4
|
+
* A scene is a saved routine: which rooms or zones, in what order, with which clean parameters. The
|
|
5
|
+
* robot reports the LIST over this DP — ids, names, the map each belongs to, whether each still works
|
|
6
|
+
* — while the tasks inside a scene are only ever sent, never reported. So this reads what the device
|
|
7
|
+
* publishes and does not pretend to the rest.
|
|
8
|
+
*
|
|
9
|
+
* **A real `mapId` is on this DP.** Multi-map management on DP 172 cannot supply one:
|
|
10
|
+
* `multi_maps.proto` states outright that a `MAP_GET_ALL` or `MAP_GET_ONE` response travels over p2p
|
|
11
|
+
* rather than the data point, leaving DP 172 carrying only the method, sequence and result of an
|
|
12
|
+
* operation. `SceneInfo.mapid` is on the DP, and so is a scheduled rooms-clean's `map_id` — two reads
|
|
13
|
+
* that carry the id the area-select frames need, with no p2p transfer and no default invented for them.
|
|
14
|
+
*
|
|
15
|
+
* @module model/vacuum-scenes
|
|
16
|
+
*/
|
|
17
|
+
import type { RawDpCodec } from "../core/contracts.js";
|
|
18
|
+
/**
|
|
19
|
+
* Why the device considers a scene unusable. `"none"` is the healthy case — the vendor's `NORMAL`,
|
|
20
|
+
* which every working scene reports.
|
|
21
|
+
*
|
|
22
|
+
* `"legacyDefault"` is the vendor's own dead value: its comment retires `DEFAULT = 5` in favour of the
|
|
23
|
+
* `type` field, so a device still sending it is naming a default scene the old way. Kept named rather
|
|
24
|
+
* than dropped, because a value that arrives and has no name reads as a decode failure.
|
|
25
|
+
*/
|
|
26
|
+
export declare const SCENE_INVALID_REASONS: readonly ["none", "mapMissing", "mapUnavailable", "mapMismatch", "other", "legacyDefault"];
|
|
27
|
+
export type SceneInvalidReason = (typeof SCENE_INVALID_REASONS)[number];
|
|
28
|
+
/** The vendor's four built-in scenes, and `"custom"` for one a user made. */
|
|
29
|
+
export declare const SCENE_TYPES: readonly ["custom", "wholeHouseDaily", "wholeHouseDeep", "afterDinner", "petArea"];
|
|
30
|
+
export type SceneType = (typeof SCENE_TYPES)[number];
|
|
31
|
+
/** One saved scene, as the robot reports it. */
|
|
32
|
+
export interface VacuumScene {
|
|
33
|
+
/** The device's own id for this scene — what `encodeSceneClean` takes to run it. */
|
|
34
|
+
readonly id: number;
|
|
35
|
+
/** The scene's name. `undefined` when the device sent none, which a built-in scene does. */
|
|
36
|
+
readonly name: string | undefined;
|
|
37
|
+
/** Whether the scene can still run. A scene whose map was deleted is kept and reported invalid. */
|
|
38
|
+
readonly valid: boolean;
|
|
39
|
+
/** Why it cannot run. `"none"` while {@link valid} — the vendor reports both fields either way. */
|
|
40
|
+
readonly invalidReason: SceneInvalidReason | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* The map this scene's rooms belong to, and one of the two real map ids the device reports — the
|
|
43
|
+
* other being a scheduled rooms-clean's.
|
|
44
|
+
*
|
|
45
|
+
* `undefined` when the device sends its no-map sentinel, which is `-2` written into a `uint32`, and
|
|
46
|
+
* `undefined` for a zero as well: proto3 omits a zero-valued field, so a scene tied to map 0 and one
|
|
47
|
+
* that said nothing about a map are the same bytes. Reporting "no map" for both is the reading that
|
|
48
|
+
* cannot send a clean at the wrong floor.
|
|
49
|
+
*/
|
|
50
|
+
readonly mapId: number | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* How long the device expects a run to take, as it reports it. `undefined` for the vendor's `0`,
|
|
53
|
+
* which its own comment marks invalid rather than instant.
|
|
54
|
+
*
|
|
55
|
+
* **The unit is not stated anywhere.** Every other duration on this line is seconds, which is a
|
|
56
|
+
* reason to expect seconds and not a reason for this SDK to claim it — so the number is reported as
|
|
57
|
+
* sent and named for what it is.
|
|
58
|
+
*/
|
|
59
|
+
readonly estimatedRuntime: number | undefined;
|
|
60
|
+
/** Where the scene sits in the list the app shows, counting from 1. */
|
|
61
|
+
readonly order: number;
|
|
62
|
+
/** Whether this is one of the vendor's built-in scenes, and which. */
|
|
63
|
+
readonly type: SceneType;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Decode a `SceneResponse` (DP 180) to the scenes it reports, or `undefined`.
|
|
67
|
+
*
|
|
68
|
+
* `undefined` means the payload could not be read — no codec, not a Raw-DP value, malformed bytes. An
|
|
69
|
+
* **empty array** is a different answer: the device reports its scenes in full on boot and after any
|
|
70
|
+
* change, so a report carrying none says this robot has no scenes saved.
|
|
71
|
+
*/
|
|
72
|
+
export declare function decodeVacuumScenes(raw: unknown, codec: RawDpCodec | undefined): readonly VacuumScene[] | undefined;
|
|
73
|
+
/** How many scenes the robot holds, or `undefined` when the payload could not be read. */
|
|
74
|
+
export declare function decodeVacuumSceneCount(raw: unknown, codec: RawDpCodec | undefined): number | undefined;
|
|
75
|
+
/** How many of the reported scenes can still run. */
|
|
76
|
+
export declare function decodeUsableVacuumSceneCount(raw: unknown, codec: RawDpCodec | undefined): number | undefined;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The schedules a robot vacuum holds — `TimerResponse` on DP 164, decoded.
|
|
3
|
+
*
|
|
4
|
+
* The DP number comes from a `get_product_data_point` dump; the product catalogue names the DP, and the
|
|
5
|
+
* vendor's `timing.proto` carries the message.
|
|
6
|
+
*
|
|
7
|
+
* The shape is deeper than anything else on this line — a repeated `TimerInfo`, each with four nested
|
|
8
|
+
* containers and a `oneof` for what the timer actually does — which is why it decodes here rather than
|
|
9
|
+
* as one more field reader inside `vacuum-clean`. It reads a payload and answers a list; it never asks
|
|
10
|
+
* the device for one, and it holds no DP number of its own.
|
|
11
|
+
*
|
|
12
|
+
* **`TimerInfo.Addition` is deliberately not decoded.** It carries the account ids of whoever created
|
|
13
|
+
* and last edited each timer — the kind of value this SDK does not surface without a reason.
|
|
14
|
+
*
|
|
15
|
+
* @module model/vacuum-schedules
|
|
16
|
+
*/
|
|
17
|
+
import type { RawDpCodec } from "../core/contracts.js";
|
|
18
|
+
/** What a timer runs when it fires, as the vendor's `Action` oneof names it. */
|
|
19
|
+
export declare const VACUUM_SCHEDULE_ACTIONS: readonly ["autoClean", "roomsClean", "cruise", "sceneClean"];
|
|
20
|
+
export type VacuumScheduleAction = (typeof VACUUM_SCHEDULE_ACTIONS)[number];
|
|
21
|
+
/**
|
|
22
|
+
* Weekdays in the vendor's own bit order — Sunday first, because `Cycle.week_bits` puts it at bit 0.
|
|
23
|
+
*
|
|
24
|
+
* Written out rather than derived from a locale so the mapping is the wire's and not the reader's.
|
|
25
|
+
*/
|
|
26
|
+
export declare const VACUUM_SCHEDULE_WEEKDAYS: readonly ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
|
|
27
|
+
export type VacuumScheduleWeekday = (typeof VACUUM_SCHEDULE_WEEKDAYS)[number];
|
|
28
|
+
/** One schedule the robot holds. */
|
|
29
|
+
export interface VacuumSchedule {
|
|
30
|
+
/** The device's own id for this timer — what a future edit or delete would name it by. */
|
|
31
|
+
readonly id: number;
|
|
32
|
+
/** Whether the timer is switched on. A valid timer that is off stays stored and does not fire. */
|
|
33
|
+
readonly enabled: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Whether the device still considers the timer usable. A timer pointing at a deleted scene or a map
|
|
36
|
+
* that no longer exists is reported `valid: false` rather than removed, so the app can show why.
|
|
37
|
+
*/
|
|
38
|
+
readonly valid: boolean;
|
|
39
|
+
/** `true` for a weekly timer, `false` for one that fires once. */
|
|
40
|
+
readonly repeats: boolean;
|
|
41
|
+
/** Hour of the day it fires, 0–23, in the user's own timezone — see {@link utcOffsetSeconds}. */
|
|
42
|
+
readonly hour: number;
|
|
43
|
+
/** Minute of the hour it fires, 0–59. */
|
|
44
|
+
readonly minute: number;
|
|
45
|
+
/**
|
|
46
|
+
* The days a repeating timer fires on. Empty for a one-shot timer, and empty for a repeating one
|
|
47
|
+
* whose `week_bits` is zero — which the device treats as a timer that never fires.
|
|
48
|
+
*/
|
|
49
|
+
readonly weekdays: readonly VacuumScheduleWeekday[];
|
|
50
|
+
/**
|
|
51
|
+
* The offset from UTC the timer's clock was set against, in seconds east.
|
|
52
|
+
*
|
|
53
|
+
* Carried per timer rather than per device: the robot stores whatever the phone that created the
|
|
54
|
+
* schedule told it, so an absolute instant follows from this offset and not from any local zone.
|
|
55
|
+
*/
|
|
56
|
+
readonly utcOffsetSeconds: number;
|
|
57
|
+
/** Whether the phone that created the timer said its region observes daylight saving. */
|
|
58
|
+
readonly daylightSaving: boolean;
|
|
59
|
+
/** What the timer runs. */
|
|
60
|
+
readonly action: VacuumScheduleAction;
|
|
61
|
+
/** The map the run targets, for a rooms-clean or a cruise. */
|
|
62
|
+
readonly mapId?: number;
|
|
63
|
+
/** The rooms a rooms-clean visits, in the order the timer lists them. */
|
|
64
|
+
readonly roomIds?: readonly number[];
|
|
65
|
+
/** The scene a scene-clean runs. */
|
|
66
|
+
readonly sceneId?: number;
|
|
67
|
+
/** The scene's name as the device last saw it — kept so a deleted scene can still be named. */
|
|
68
|
+
readonly sceneName?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Decode a `TimerResponse` (DP 164) to the schedules it reports, or `undefined`.
|
|
72
|
+
*
|
|
73
|
+
* `undefined` means the payload could not be read at all — no codec, not a Raw-DP value, malformed
|
|
74
|
+
* bytes. An **empty array** is a different and equally real answer: the device reports its timers in
|
|
75
|
+
* full every time, so a report carrying none says this robot has no schedules set.
|
|
76
|
+
*
|
|
77
|
+
* The device sends this unprompted on boot and after any change, and in reply to an `INQUIRY`. Which
|
|
78
|
+
* of those produced a given payload does not change the reading — every report is the complete list —
|
|
79
|
+
* so the request `method` and `seq` beside it are not surfaced.
|
|
80
|
+
*/
|
|
81
|
+
export declare function decodeVacuumSchedules(raw: unknown, codec: RawDpCodec | undefined): readonly VacuumSchedule[] | undefined;
|
|
82
|
+
/** How many schedules a `TimerResponse` reports, or `undefined` when the payload could not be read. */
|
|
83
|
+
export declare function decodeVacuumScheduleCount(raw: unknown, codec: RawDpCodec | undefined): number | undefined;
|
|
84
|
+
/** How many of the reported schedules are switched on and still usable. */
|
|
85
|
+
export declare function decodeActiveVacuumScheduleCount(raw: unknown, codec: RawDpCodec | undefined): number | undefined;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `0x020D` light-effect payload serializer for the `eufy_life` smart-light line: the catalog effect
|
|
3
|
+
* definition ({@link DpPresetSpec}) → the command-specific DP fields, including the per-layer
|
|
4
|
+
* header/colour/trailer encoding and the RGBCW colour approximation.
|
|
5
|
+
*
|
|
6
|
+
* Lives at the transport root, not under one transport folder, because two consumers need it and they
|
|
7
|
+
* sit in different subfolders: `mqtt/command-router.ts` serializes an effect to send it, and
|
|
8
|
+
* `http/light-catalog.ts` trial-serializes one to report whether the catalog entry is drivable at all.
|
|
9
|
+
* Same reason `ff09.ts` sits here. It builds FIELDS, not a frame — the DP framing + envelope stay in
|
|
10
|
+
* `mqtt/dp-codec.ts`, and nothing here names a capability or a transport.
|
|
11
|
+
*
|
|
12
|
+
* Header + trailer are byte-exact vs. real captures; colours run through {@link rgbcwBlock}, a
|
|
13
|
+
* physically-grounded approximation of the device's on-device 5-channel (R,G,B,WarmWhite,ColdWhite)
|
|
14
|
+
* mixing engine — EXACT on pure red and blue, ~18/255 mean on mixed colours, because the engine's
|
|
15
|
+
* nonlinear gamut expansion isn't fully reversed.
|
|
16
|
+
*/
|
|
17
|
+
import type { DpField } from "./mqtt/dp-codec.js";
|
|
18
|
+
/** One layer of a gallery light effect, as the cloud catalog `params.layer[]` describes it. */
|
|
19
|
+
export interface DpPresetLayer {
|
|
20
|
+
current_layer_type: number;
|
|
21
|
+
colors?: string;
|
|
22
|
+
layer_priority?: number;
|
|
23
|
+
layer_speed?: number;
|
|
24
|
+
layer_range?: number | [number, number];
|
|
25
|
+
interval_type?: number;
|
|
26
|
+
interval_value?: number;
|
|
27
|
+
layer_execution_parameter?: number;
|
|
28
|
+
light_effect_post_cycle_status?: number;
|
|
29
|
+
color_pick_mode?: number;
|
|
30
|
+
gradient_value?: number;
|
|
31
|
+
flow_direction?: number;
|
|
32
|
+
direction_change_mode?: number;
|
|
33
|
+
length_range?: number;
|
|
34
|
+
color_fill_mode?: number;
|
|
35
|
+
insert_block_mode?: number;
|
|
36
|
+
insert_block_range?: number | [number, number];
|
|
37
|
+
insert_black_block_mode?: number;
|
|
38
|
+
insert_black_block_range?: number | [number, number];
|
|
39
|
+
brightness_variation_type?: number;
|
|
40
|
+
brightness_range?: number | [number, number];
|
|
41
|
+
light_effect_cycle_method?: number;
|
|
42
|
+
execution_parameter?: number;
|
|
43
|
+
is_lights_move_with_people?: number;
|
|
44
|
+
brightness_value?: number;
|
|
45
|
+
display_mode?: number;
|
|
46
|
+
color_quantity_range?: number;
|
|
47
|
+
transition_mode?: number;
|
|
48
|
+
unit_transition_duration?: number;
|
|
49
|
+
color_switch_mode?: number;
|
|
50
|
+
switch_count?: number;
|
|
51
|
+
color_pick_sequence?: number;
|
|
52
|
+
blink_cycle_count?: number;
|
|
53
|
+
blink_position_mode?: number;
|
|
54
|
+
blink_interval?: number | [number, number];
|
|
55
|
+
blink_quantity?: number;
|
|
56
|
+
blink_asynchrony?: number;
|
|
57
|
+
blink_color_switch_mode?: number;
|
|
58
|
+
}
|
|
59
|
+
/** A resolved gallery effect, produced by the client (from the HTTP catalog) and serialized here. */
|
|
60
|
+
export interface DpPresetSpec {
|
|
61
|
+
lightId: number;
|
|
62
|
+
speed: number;
|
|
63
|
+
layerExecutionMode: number;
|
|
64
|
+
layers: DpPresetLayer[];
|
|
65
|
+
/** The catalog's own overall brightness (0-100), sent as a companion `0x0201` frame. */
|
|
66
|
+
brightness?: number;
|
|
67
|
+
}
|
|
68
|
+
/** "RRGGBB" → the 5-byte wire block `[R,G,B,WarmWhite,ColdWhite]` via the RGBCW approximation. */
|
|
69
|
+
export declare function rgbcwBlock(hex: string): Buffer | null;
|
|
70
|
+
/**
|
|
71
|
+
* A single scalar byte. THROWS on an array: several catalog fields are scalar in the shapes the
|
|
72
|
+
* reverse-engineering validated, but some nature/moods effects carry a `[min,max]` pair in one of
|
|
73
|
+
* these slots (e.g. `interval_value: [1,1]`), and how the app packs that into the fixed-width header
|
|
74
|
+
* is NOT reversed. Silently collapsing the pair to a byte would ship a mis-packed frame that the light
|
|
75
|
+
* ignores while the fire-and-forget write reports success — worse than failing. Refuse it, exactly as
|
|
76
|
+
* {@link layerTrailer} bails on an unknown layer type, until a capture pins the packing down.
|
|
77
|
+
*/
|
|
78
|
+
export declare const b8: (v: unknown) => number;
|
|
79
|
+
/**
|
|
80
|
+
* Whether {@link dpPresetFields} would succeed on this spec, decided WITHOUT building the frame.
|
|
81
|
+
* An effect is drivable only if it has at least one layer, every top-level scalar fits a byte, and
|
|
82
|
+
* every layer has a known `current_layer_type`, byte-sized fields, and RGB-hex colours — the same set
|
|
83
|
+
* {@link dpPresetFields} accepts, checked field-for-field.
|
|
84
|
+
*
|
|
85
|
+
* Deliberately does not call {@link layerBlob}: a trial build runs {@link rgbcwBlock}'s gamut search
|
|
86
|
+
* per colour and allocates a buffer per layer, and the catalogue runs this over every entry of a
|
|
87
|
+
* many-hundred-id scan. `layerHeader`/`layerTrailer` raise every relevant throw on plain numbers, and
|
|
88
|
+
* a colour only has to satisfy the same hex test `rgbcwBlock` gates on.
|
|
89
|
+
*/
|
|
90
|
+
export declare function specIsSerializable(spec: {
|
|
91
|
+
speed?: unknown;
|
|
92
|
+
layerExecutionMode?: unknown;
|
|
93
|
+
layers: readonly DpPresetLayer[];
|
|
94
|
+
}): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Build the `0x020D` light-effect frame's command-specific fields (`0xa3` onward — the header
|
|
97
|
+
* `a3`-`a8` plus one `0xa9+idx` layer blob each). `a1`/`a2` are prepended by {@link buildDpFrame}.
|
|
98
|
+
* `a7` is deliberately absent (an empty slot in every real capture).
|
|
99
|
+
*/
|
|
100
|
+
export declare function dpPresetFields(spec: DpPresetSpec): DpField[];
|
|
101
|
+
/** The `0xa4`-brightness `0x0201` device-info fields for the companion brightness write. */
|
|
102
|
+
export declare function dpLevelFields(level: number): DpField[];
|