@mega-yfue/eufy-sdk 0.0.5 → 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.
Files changed (143) hide show
  1. package/README.md +31 -12
  2. package/dist/client/device-registry.d.ts +343 -0
  3. package/dist/client/eufy-mega.d.ts +872 -0
  4. package/dist/client/index.d.ts +1 -6
  5. package/dist/client/map-channels.d.ts +22 -0
  6. package/dist/client/types.d.ts +383 -0
  7. package/dist/core/contracts.d.ts +898 -0
  8. package/dist/core/crypto.d.ts +98 -0
  9. package/dist/core/index.d.ts +9 -7
  10. package/dist/core/logger.d.ts +53 -0
  11. package/dist/core/lz4-block.d.ts +35 -0
  12. package/dist/core/raw-dp-hex.d.ts +32 -0
  13. package/dist/core/raw-dp-writer.d.ts +83 -0
  14. package/dist/core/store.d.ts +43 -0
  15. package/dist/core/types.d.ts +169 -0
  16. package/dist/core/util.d.ts +78 -0
  17. package/dist/index.d.ts +4 -7
  18. package/dist/index.js +25526 -14
  19. package/dist/index.js.map +7 -1
  20. package/dist/model/capabilities/access.d.ts +127 -0
  21. package/dist/model/capabilities/arming.d.ts +201 -0
  22. package/dist/model/capabilities/audio.d.ts +154 -0
  23. package/dist/model/capabilities/battery.d.ts +363 -0
  24. package/dist/model/capabilities/camera.d.ts +564 -0
  25. package/dist/model/capabilities/co.d.ts +40 -0
  26. package/dist/model/capabilities/contact.d.ts +133 -0
  27. package/dist/model/capabilities/doorbell.d.ts +345 -0
  28. package/dist/model/capabilities/dp-catalog.d.ts +38 -0
  29. package/dist/model/capabilities/index.d.ts +561 -0
  30. package/dist/model/capabilities/info.d.ts +28 -0
  31. package/dist/model/capabilities/keypad.d.ts +61 -0
  32. package/dist/model/capabilities/leak.d.ts +43 -0
  33. package/dist/model/capabilities/light.d.ts +174 -0
  34. package/dist/model/capabilities/locate.d.ts +63 -0
  35. package/dist/model/capabilities/lock.d.ts +242 -0
  36. package/dist/model/capabilities/manifest.d.ts +107 -0
  37. package/dist/model/capabilities/members.d.ts +647 -0
  38. package/dist/model/capabilities/motion.d.ts +377 -0
  39. package/dist/model/capabilities/person-detection.d.ts +8 -0
  40. package/dist/model/capabilities/ptz.d.ts +289 -0
  41. package/dist/model/capabilities/rtsp.d.ts +221 -0
  42. package/dist/model/capabilities/siren.d.ts +218 -0
  43. package/dist/model/capabilities/smart-light.d.ts +172 -0
  44. package/dist/model/capabilities/smoke.d.ts +40 -0
  45. package/dist/model/capabilities/snapshot.d.ts +6 -0
  46. package/dist/model/capabilities/storage.d.ts +11 -0
  47. package/dist/model/capabilities/suction.d.ts +104 -0
  48. package/dist/model/capabilities/types.d.ts +484 -0
  49. package/dist/model/capabilities/vacuum-clean.d.ts +1946 -0
  50. package/dist/model/capabilities/vacuum-dock.d.ts +208 -0
  51. package/dist/model/capabilities/video.d.ts +6 -0
  52. package/dist/model/classify.d.ts +78 -0
  53. package/dist/model/clean-record-detail.d.ts +65 -0
  54. package/dist/model/clean-records.d.ts +69 -0
  55. package/dist/model/device-family.d.ts +73 -0
  56. package/dist/model/device-types.d.ts +123 -0
  57. package/dist/model/device.d.ts +265 -0
  58. package/dist/model/index.d.ts +29 -4
  59. package/dist/model/infer.d.ts +23 -0
  60. package/dist/model/inspect.d.ts +61 -0
  61. package/dist/model/life-params.d.ts +21 -0
  62. package/dist/model/map-pixels.d.ts +70 -0
  63. package/dist/model/param-dictionary.d.ts +28 -0
  64. package/dist/model/param-namespace.d.ts +21 -0
  65. package/dist/model/proto-read.d.ts +53 -0
  66. package/dist/model/push-events.d.ts +147 -0
  67. package/dist/model/registry.d.ts +54 -0
  68. package/dist/model/types.d.ts +301 -0
  69. package/dist/model/vacuum-map-store.d.ts +92 -0
  70. package/dist/model/vacuum-map.d.ts +286 -0
  71. package/dist/model/vacuum-scenes.d.ts +76 -0
  72. package/dist/model/vacuum-schedules.d.ts +85 -0
  73. package/dist/transport/dp-preset.d.ts +102 -0
  74. package/dist/transport/ff09.d.ts +444 -0
  75. package/dist/transport/ffmpeg.d.ts +86 -0
  76. package/dist/transport/http/decodeImageV1.d.ts +20 -0
  77. package/dist/transport/http/decodeImageV2.d.ts +19 -0
  78. package/dist/transport/http/index.d.ts +5 -0
  79. package/dist/transport/http/light-catalog.d.ts +62 -0
  80. package/dist/transport/http/media-download.d.ts +14 -0
  81. package/dist/transport/http/mega-client.d.ts +514 -0
  82. package/dist/transport/http/phone-model.d.ts +21 -0
  83. package/dist/transport/index.d.ts +10 -7
  84. package/dist/transport/mqtt/app-client-id.d.ts +16 -0
  85. package/dist/transport/mqtt/availability.d.ts +14 -0
  86. package/dist/transport/mqtt/bare-ip-tls.d.ts +46 -0
  87. package/dist/transport/mqtt/biz-stream.d.ts +98 -0
  88. package/dist/transport/mqtt/broker-discovery.d.ts +55 -0
  89. package/dist/transport/mqtt/clean-codec.d.ts +14 -0
  90. package/dist/transport/mqtt/command-router.d.ts +285 -0
  91. package/dist/transport/mqtt/dp-codec.d.ts +58 -0
  92. package/dist/transport/mqtt/dp-color.d.ts +14 -0
  93. package/dist/transport/mqtt/engine.d.ts +16 -0
  94. package/dist/transport/mqtt/index.d.ts +5 -0
  95. package/dist/transport/mqtt/secure-mqtt.d.ts +107 -0
  96. package/dist/transport/mqtt/topics.d.ts +80 -0
  97. package/dist/transport/p2p/adts.d.ts +91 -0
  98. package/dist/transport/p2p/annexb.d.ts +124 -0
  99. package/dist/transport/p2p/codec.d.ts +166 -0
  100. package/dist/transport/p2p/command-router.d.ts +655 -0
  101. package/dist/transport/p2p/commands.d.ts +550 -0
  102. package/dist/transport/p2p/envelope.d.ts +46 -0
  103. package/dist/transport/p2p/fmp4.d.ts +89 -0
  104. package/dist/transport/p2p/fragment-recording.d.ts +33 -0
  105. package/dist/transport/p2p/index.d.ts +13 -0
  106. package/dist/transport/p2p/lan-ip.d.ts +25 -0
  107. package/dist/transport/p2p/live-stream.d.ts +223 -0
  108. package/dist/transport/p2p/live-trace.d.ts +123 -0
  109. package/dist/transport/p2p/media.d.ts +105 -0
  110. package/dist/transport/p2p/p2p-session.d.ts +620 -0
  111. package/dist/transport/p2p/readable-egress.d.ts +27 -0
  112. package/dist/transport/p2p/session-manager.d.ts +154 -0
  113. package/dist/transport/p2p/shared-live-source.d.ts +431 -0
  114. package/dist/transport/p2p/talkback.d.ts +187 -0
  115. package/dist/transport/p2p/video.d.ts +150 -0
  116. package/dist/transport/p2p/write-commands.d.ts +21 -0
  117. package/dist/transport/protobuf.d.ts +5 -0
  118. package/dist/transport/push/fcm.d.ts +23 -0
  119. package/dist/transport/push/index.d.ts +6 -0
  120. package/dist/transport/push/message-tags.d.ts +26 -0
  121. package/dist/transport/push/parser.d.ts +27 -0
  122. package/dist/transport/push/proto.d.ts +11 -0
  123. package/dist/transport/push/push-client.d.ts +64 -0
  124. package/dist/transport/push/store.d.ts +23 -0
  125. package/dist/transport/push/types.d.ts +180 -0
  126. package/dist/transport/raw-dp.d.ts +6 -0
  127. package/dist/transport/stored-image-cache.d.ts +23 -0
  128. package/dist/transport/tuya/account.d.ts +44 -0
  129. package/dist/transport/tuya/client.d.ts +89 -0
  130. package/dist/transport/tuya/command-router.d.ts +79 -0
  131. package/dist/transport/tuya/dp-codec.d.ts +56 -0
  132. package/dist/transport/tuya/index.d.ts +28 -0
  133. package/dist/transport/tuya/request.d.ts +157 -0
  134. package/dist/transport/tuya/sign.d.ts +64 -0
  135. package/package.json +12 -13
  136. package/dist/client/index.js +0 -2
  137. package/dist/client/index.js.map +0 -1
  138. package/dist/core/index.js +0 -2
  139. package/dist/core/index.js.map +0 -1
  140. package/dist/model/index.js +0 -2
  141. package/dist/model/index.js.map +0 -1
  142. package/dist/transport/index.js +0 -2
  143. package/dist/transport/index.js.map +0 -1
@@ -0,0 +1,377 @@
1
+ import { type Surface } from "./members.js";
2
+ import type { CapabilityModule, CommandContext } from "./types.js";
3
+ import type { Command } from "../../core/contracts.js";
4
+ /**
5
+ * The P2P **feature-command ids** this motion capability drives. Capability-owned wire vocabulary
6
+ * (transport forwards `cmd.param` opaquely; full id→name catalog in the generated
7
+ * `transport/p2p/commands.ts`).
8
+ */
9
+ export declare const MOTION_CMD: {
10
+ /**
11
+ * Motion / PIR detection on-off (app `CAMERA_PIR`). ✅ Wire reversed from a
12
+ * live outbound capture + replay-verified on T8425 (ch3): the direct-binary 136-byte struct
13
+ * `[u32 ch][u32 value][account_id pad128]`, signCode 8 — SAME shape as watermark 1214. 1 = on, 0 = off.
14
+ */
15
+ readonly CAMERA_PIR: 1011;
16
+ /**
17
+ * Motion sensitivity (app `SET_MOTION_DETECTION_SENSITIVITY_DOORBELL`, despite the name NOT
18
+ * doorbell-specific — see below). ✅ Wire captured live on a T8170 ( 2026-07-23,
19
+ * confirmed exchange), moving the sensitivity slider twice: `1350`
20
+ * SET_PAYLOAD, cmd 1276, mChannel `<deviceCh>`, explicit mValue3:0, `payload:{sensitivity:<int>,
21
+ * channel:<deviceCh>}`. Two real values captured: slider position 0 → `sensitivity:1`, slider
22
+ * position 6 → `sensitivity:7` — the wire is **1-indexed**, one higher than the app UI's 0-indexed
23
+ * display. Captured on a T8170; no other model is assumed to share it.
24
+ */
25
+ readonly MOTION_SENSITIVITY: 1276;
26
+ /**
27
+ * PIR sensitivity on a standalone motion **sensor** (app `APP_CMD_MOTION_SENSOR_SET_PIR_SENSITIVITY`).
28
+ * The same user-facing setting as {@link MOTION_SENSITIVITY}, under a DIFFERENT id — a camera takes
29
+ * 1276, a sensor takes this. ✅ Captured live 2026-08-03 on a T8910: the app sent 1609 as a
30
+ * direct-binary 136-byte frame on the sensor's channel, and the value reads back on the cloud param
31
+ * of the same id. Sending a camera's 1276 to a sensor is a fire-and-forget no-op that looks like a
32
+ * success, which is why the two are kept apart rather than merged.
33
+ *
34
+ * ⚠️ The value is NOT the app's slider index. Its picker offers five steps (low → high, annotated
35
+ * with a detection distance), while two sensors on one account read 8 and 37 — so the mapping from
36
+ * a step to this number is unknown, and no range can be validated beyond rejecting below 1. A caller
37
+ * setting this is passing through a raw device value, not choosing a documented level.
38
+ *
39
+ * A change applies after about a minute, or immediately if the sensor is triggered — the app says so
40
+ * on the same screen, and the same wake behaviour governs every write to a sensor.
41
+ */
42
+ readonly SENSOR_PIR_SENSITIVITY: 1609;
43
+ /**
44
+ * Enter a motion sensor's **user test mode** (app `APP_CMD_MOTION_SENSOR_ENTER_USER_TEST_MODE`).
45
+ *
46
+ * ✅ Decrypted from the app's own frame and replayed live on a T8910 (2026-08-03): a `1350`
47
+ * SET_PAYLOAD whose payload carries the sensor's channel — `{"cmd":1613,"payload":{"channel":<ch>}}`.
48
+ * The channel MUST be in the payload; passing it only as the envelope's `mChannel` with an empty
49
+ * payload is refused with `-1`. The station acknowledges with `0` and confirms by reporting
50
+ * {@link SENSOR_WORK_MODE} `1`.
51
+ */
52
+ readonly SENSOR_ENTER_TEST_MODE: 1613;
53
+ /**
54
+ * Leave a motion sensor's user test mode (app `APP_CMD_MOTION_SENSOR_EXIT_USER_TEST_MODE`).
55
+ *
56
+ * ✅ Replayed live on a T8910, confirmed by {@link SENSOR_WORK_MODE} reporting `0`. NOT the same
57
+ * shape as its {@link SENSOR_ENTER_TEST_MODE} counterpart: this is a direct-binary frame with the id
58
+ * as the outer command, and each is refused in the other's shape.
59
+ */
60
+ readonly SENSOR_EXIT_TEST_MODE: 1610;
61
+ /**
62
+ * The mode a motion sensor reports it is in (app `APP_CMD_MOTION_SENSOR_WORK_MODE`), inbound only —
63
+ * `1` while test mode is on, `0` once it is left. Never present in the cloud record; it exists only
64
+ * on the P2P path.
65
+ */
66
+ readonly SENSOR_WORK_MODE: 1612;
67
+ /**
68
+ * AI detection TYPE — which classifications trigger a detection (person / pet / vehicle / …).
69
+ * From the app's own JS parser (command_schema.json): `1350` SET_PAYLOAD, inner cmd `AI_DETECT_TYPE`
70
+ * (1298), `payload:{ai_detect_type:<bitmask>, channel:<deviceCh>}` — same envelope shape as the
71
+ * ✅-verified night-vision (1277). 1298 holds the detailed BITMASK. ✅ Bits decoded live + confirmed
72
+ * vs the app (see {@link AiDetectType} / {@link encodeAiDetectType}). ✅ WRITE HW-verified live on
73
+ * T8124 (each write landed byte-exact + read back: 0x30003 → 0x8 → 0x3000b).
74
+ */
75
+ readonly AI_DETECT_TYPE: 1298;
76
+ /**
77
+ * Notification **snooze** — temporarily silence motion/detection notifications for N seconds. ✅ WIRE
78
+ * CONFIRMED on a T8170 (2026-07-23,
79
+ * app force-relaunched to guarantee a fresh handshake): a **bare JSON frame, NOT a `1350`/`1700`
80
+ * envelope** — outer P2P cmd IS `1271` itself, plaintext exactly `{account_id,...}` (see
81
+ * {@link module:./intent setJsonRaw}). Three real writes captured on the device channel (ch2):
82
+ * `{snooze_time:21600,chime_onoff:0,homebase_onoff:1,motion_notify_onoff:1,startTime:1784794716}`,
83
+ * `{snooze_time:0}` (clearing/cancelling — the bare shape, no extra fields), and
84
+ * `{snooze_time:3600,chime_onoff:0,homebase_onoff:0,motion_notify_onoff:1,startTime:1784794730}`.
85
+ * Confirms `command_schema.json`'s field names exactly; it is a real P2P frame, not the cloud-only
86
+ * HTTP path a `paramValue = base64(JSON.stringify(payload))` shape might suggest.
87
+ * `chime_onoff`/`homebase_onoff`/`motion_notify_onoff` meanings are NOT independently verified
88
+ * (only observed alongside 2 different snooze picks) — `setSnoozeTime` ships the exact 2nd-capture
89
+ * values as fixed defaults rather than exposing them, since their semantics aren't confirmed enough
90
+ * to make configurable without risking a wrong guess on a fire-and-forget write.
91
+ */
92
+ readonly SNOOZE_TIME: 1271;
93
+ /**
94
+ * Use AI classification ONLY at night (app `APP_CMD_BAT_DOORBELL_SET_ONLY_USE_AI_AT_NIGHT`).
95
+ * `1350` SET_PAYLOAD, inner cmd 1719, `payload:{only_ai:0|1}`, mValue3 0. The envelope's mChannel is
96
+ * the DEVICE channel — which is set by NOT passing `setPayload`'s explicit-channel arg, and is
97
+ * independent of what keys the payload carries. (Contrast 1277/1298, which DO carry a `channel`
98
+ * payload key AND pass mChannel 0 explicitly; the two are separate choices, not linked.)
99
+ *
100
+ * ⚠️ Replay + readback confirmed on a HomeBase-attached T8425 (1719 `0`→`1`→`0`), NOT byte-captured.
101
+ * Provenance is `apk`, not `verified`: a divergent-but-also-accepted frame can't be ruled out.
102
+ */
103
+ readonly HUMAN_ONLY_AT_NIGHT: 1719;
104
+ /**
105
+ * Loitering detection (app `APP_CMD_DUALCAM_SET_RADAR_WD_SWITCH`) — alert on lingering, not passing.
106
+ * `1350` SET_PAYLOAD, inner cmd 2706, `payload:{radar_wd_switch:0|1}`, mValue3 0, mChannel = device
107
+ * channel. Observed on the T8214 doorbell only.
108
+ *
109
+ * **READ is object-OR-scalar.** The app reads it as
110
+ * `typeof v === "object" ? v.radar_wd_switch : v`, so the stored value may be a JSON object
111
+ * `{radar_wd_switch,…}` OR a bare scalar — a plain bool coercion reports `false` for the object
112
+ * form, so a feature that is on reads as off.
113
+ * Decoded by {@link decodeRadarWdSwitch} to match the app.
114
+ *
115
+ * ⚠️ Replay + readback confirmed on a T8214 (2706 `0`→`1`→`0`), NOT byte-captured. Provenance `apk`.
116
+ */
117
+ readonly LOITERING_DETECTION: 2706;
118
+ /**
119
+ * Detection sensitivity on the inverted seven-step camera scale (app `APP_CMD_SET_PIRSENSITIVITY`).
120
+ * Reported by nine camera families but only accepted by the one whose reported value falls on its
121
+ * seven-step ladder, which is how the scale is resolved without consulting the model.
122
+ */
123
+ readonly CAMERA_PIR_SENSITIVITY: 1210;
124
+ /**
125
+ * Detection sensitivity an indoor camera reports AND accepts (app `INDOOR_MOTION_DETECTION_SENSITIVITY`),
126
+ * as a `1700` control payload carrying an index rather than the `1350` envelope its siblings take.
127
+ */
128
+ readonly INDOOR_SENSITIVITY_INDEX: 6041;
129
+ /**
130
+ * Detection sensitivity a solo camera REPORTS (app `SET_MOTION_DETECTION_SENSITIVITY_SOLO`). It does
131
+ * not accept writes on this id — the write goes to {@link MOTION_SENSITIVITY}, which is why a scale
132
+ * carries a read id and a write id separately.
133
+ */
134
+ readonly SOLO_SENSITIVITY: 6070;
135
+ };
136
+ /**
137
+ * AI detection type bits — the `ai_detect_type` bitmask, decoded on-device and cross-checked against
138
+ * the app. `enabledBase` (0x30000) is the "AI detection on" flag, set on every camera and always OR'd
139
+ * into a value. `humanRecognition` (face) and `humanDetection` always co-occur in observed data; the app
140
+ * lists them in this order. (Some indoor cams set extra high bits — sound/crying — not modelled here.)
141
+ */
142
+ export declare const AiDetectType: {
143
+ /** "AI detection enabled" base — always present in a valid value. */
144
+ readonly enabledBase: 196608;
145
+ readonly humanRecognition: 1;
146
+ readonly humanDetection: 2;
147
+ readonly vehicle: 4;
148
+ readonly pet: 8;
149
+ };
150
+ /** Bound motion controls — the object returned by `dev.motion()`. */
151
+ export type MotionActions = Surface<typeof MOTION_MEMBERS> & {
152
+ /** How many steps this device's picker offers, or `undefined` when its scale is not known. */
153
+ sensitivitySteps(): number | undefined;
154
+ /**
155
+ * Current detection-sensitivity step, 1 being the least sensitive.
156
+ *
157
+ * Resolved through the same scale the write uses, because the id a device reports this on differs
158
+ * between scales and several devices report more than one of them — so a caller reading the raw
159
+ * param would have to know which one counts, and in which direction. `undefined` when the device has
160
+ * not reported yet, or reports a value no known ladder contains.
161
+ */
162
+ sensitivityStep(): number | undefined;
163
+ /**
164
+ * Set the detection-sensitivity step, from 1 (least sensitive) up to `sensitivitySteps`.
165
+ *
166
+ * A step, not a device value: the five device families this is captured on use four different
167
+ * command ids, three frame shapes and two opposite numeric directions, so a raw number means the
168
+ * opposite thing depending on what it is sent to. Step 1 is always the least sensitive.
169
+ *
170
+ * Rejects a step outside the device's own range, and throws for a model whose scale has not been
171
+ * captured — a wrong id is a fire-and-forget no-op that would otherwise look like it worked.
172
+ */
173
+ setSensitivityStep(step: number): Promise<void>;
174
+ };
175
+ /** Named AI detection types, for {@link encodeAiDetectType} / {@link decodeAiDetectType}. */
176
+ export interface AiDetectFlags {
177
+ humanRecognition?: boolean;
178
+ humanDetection?: boolean;
179
+ vehicle?: boolean;
180
+ pet?: boolean;
181
+ }
182
+ /** Encode named detection types → the `ai_detect_type` bitmask (always includes the enabled base). */
183
+ export declare function encodeAiDetectType(flags: AiDetectFlags): number;
184
+ /** Decode an `ai_detect_type` bitmask → which detection types are on. */
185
+ export declare function decodeAiDetectType(value: number): AiDetectFlags;
186
+ /**
187
+ * Decode a `radar_wd_switch` (2706, loitering) read the way the app does:
188
+ * `typeof v === "object" ? v.radar_wd_switch : v`. The device may store it as a JSON object
189
+ * `{radar_wd_switch,…}` OR a bare scalar, so a plain bool coercion mis-reads the object form as
190
+ * `false` (see `MOTION_CMD.LOITERING_DETECTION`). Returns `undefined` for an unreadable value
191
+ * rather than guessing.
192
+ * @internal
193
+ */
194
+ export declare function decodeRadarWdSwitch(raw: unknown): boolean | undefined;
195
+ /**
196
+ * `motion` — PIR / motion detection. `motionDetection` (1011), `motionSensitivity` (see
197
+ * `MOTION_CMD.MOTION_SENSITIVITY`), and `snoozeTime` (see `MOTION_CMD.SNOOZE_TIME`) are
198
+ * all verified: read AND write.
199
+ */
200
+ /**
201
+ * Every `motion` feature, declared once — the property schema, the typed getters, the derived setters,
202
+ * the intent routes and the descriptions all come out of this table.
203
+ *
204
+ * The four raw sensitivity params are read-only members: each family reports its ladder under a
205
+ * DIFFERENT id and two of them run inverted, so the meaningful value is the STEP, resolved across
206
+ * all four by the methods in `actions()`. They stay in the schema because the device reports them and a
207
+ * diagnosis may want the raw number.
208
+ *
209
+ * Exported but NOT published: each entry states its wire id and the evidence it was confirmed on,
210
+ * which the reference site does not carry.
211
+ * @internal
212
+ */
213
+ export declare const MOTION_MEMBERS: {
214
+ /**
215
+ * The master motion/PIR switch. `writeAs` names the setter `setDetection` rather than the
216
+ * `setDetectionEnabled` the key would derive. The write calls `requireFamily` first, because
217
+ * this frame is captured on cameras only and a P2P write is fire-and-forget — sent to a sensor it
218
+ * would be dropped silently while the call reported success, so it throws instead.
219
+ */
220
+ readonly detectionEnabled: {
221
+ readonly param: 1011;
222
+ readonly property: "motionDetection";
223
+ readonly type: "bool";
224
+ readonly kind: "boolean";
225
+ readonly provenance: "verified";
226
+ readonly description: "Motion/PIR detection enabled (verified: param 1011 = CAMERA_PIR).";
227
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
228
+ readonly writeAs: "setDetection";
229
+ };
230
+ /**
231
+ * First of the four raw sensitivity params, all `unexposed`: reported, so they stay in the schema and
232
+ * answer through `getProperty` for a diagnosis, but given no typed getter because a raw number here
233
+ * means nothing on its own — each family reports its ladder under a different id and two run
234
+ * inverted. The meaningful value is the STEP, resolved across all four by `sensitivityStep()` in
235
+ * `actions()`. This id is both the read and the write for the five-step rising scale.
236
+ */
237
+ readonly motionSensitivity: {
238
+ readonly param: 1276;
239
+ readonly type: "number";
240
+ readonly kind: "scalar";
241
+ readonly provenance: "verified";
242
+ readonly unexposed: true;
243
+ readonly description: string;
244
+ };
245
+ /**
246
+ * 1298 holds the detailed AI-type BITMASK (live-observed: `0x30000` enabled-base | type bits, e.g. a
247
+ * T8425 reads `0x3000f`). Cams also report 1299 (`hbAiDetectType`) but that is a separate, simpler
248
+ * value (1) — NOT this bitmask, so 1298 is the read/write id.
249
+ */
250
+ readonly aiDetectType: {
251
+ readonly param: 1298;
252
+ readonly type: "number";
253
+ readonly kind: "bitfield";
254
+ readonly provenance: "verified";
255
+ readonly description: string;
256
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command | undefined;
257
+ };
258
+ /**
259
+ * The raw sensitivity a solo camera REPORTS — and only reports: its scale writes to
260
+ * `MOTION_CMD.MOTION_SENSITIVITY` instead, which is why `SensitivityScale` carries a read id and
261
+ * a write id separately. `unexposed` like its three siblings; read the resolved step instead.
262
+ */
263
+ readonly soloSensitivity: {
264
+ readonly param: 6070;
265
+ readonly type: "number";
266
+ readonly kind: "scalar";
267
+ readonly provenance: "verified";
268
+ readonly unexposed: true;
269
+ readonly description: "Raw sensitivity a solo camera reports (verified: param 6070).";
270
+ };
271
+ /**
272
+ * The raw sensitivity an indoor camera reports, on the one id whose write takes a `1700` control
273
+ * payload carrying an index rather than the `1350` envelope its siblings use — a frame-shape
274
+ * difference `sensitivityCommand` resolves from the scale. `unexposed` like its three siblings.
275
+ */
276
+ readonly indoorSensitivity: {
277
+ readonly param: 6041;
278
+ readonly type: "number";
279
+ readonly kind: "scalar";
280
+ readonly provenance: "verified";
281
+ readonly unexposed: true;
282
+ readonly description: "Raw sensitivity an indoor camera reports (verified: param 6041).";
283
+ };
284
+ /**
285
+ * The raw sensitivity on the INVERTED seven-step camera scale — a HIGHER number is LESS sensitive, so
286
+ * this is the member where reading the number as a level gets the direction backwards. Nine camera
287
+ * families report 1210 and only the one whose value lands on that ladder accepts it, which is how
288
+ * `scaleFor` tells them apart without consulting the model. `unexposed`; read the step instead.
289
+ */
290
+ readonly pirSensitivityRaw: {
291
+ readonly param: 1210;
292
+ readonly type: "number";
293
+ readonly kind: "scalar";
294
+ readonly provenance: "verified";
295
+ readonly unexposed: true;
296
+ readonly description: "Raw sensitivity reported on the inverted seven-step scale (verified: param 1210).";
297
+ };
298
+ /**
299
+ * The raw sensitivity a standalone PIR sensor reports, on its own inverted five-step ladder — the
300
+ * sensor-side counterpart to the camera's 1276, kept apart because sending a camera's id to a sensor
301
+ * is a fire-and-forget no-op that looks like success. The number is not the app's picker index (see
302
+ * `MOTION_CMD.SENSOR_PIR_SENSITIVITY`), which is exactly why it is `unexposed`.
303
+ */
304
+ readonly sensorPirSensitivity: {
305
+ readonly param: 1609;
306
+ readonly type: "number";
307
+ readonly kind: "scalar";
308
+ readonly provenance: "verified";
309
+ readonly unexposed: true;
310
+ readonly description: "Raw sensitivity a standalone PIR sensor reports (verified: param 1609, inverted ladder).";
311
+ };
312
+ /**
313
+ * A standalone motion sensor's user test mode — the ONLY state in which such a sensor reports
314
+ * detections over P2P; outside it, detections arrive on the push path. `realtime` is deliberately NOT
315
+ * set: the id never appears in the cloud record, so the getter arrives only once the station has
316
+ * reported it.
317
+ *
318
+ * The write is the one asymmetric pair here — enter is a `1350` payload carrying the channel, leave is
319
+ * a direct-binary frame, and each is refused in the other's shape. Sensor-family only; a camera
320
+ * throws via `requireFamily`.
321
+ */
322
+ readonly testMode: {
323
+ readonly param: 1612;
324
+ readonly type: "bool";
325
+ readonly kind: "boolean";
326
+ readonly provenance: "verified";
327
+ readonly description: string;
328
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
329
+ };
330
+ /**
331
+ * Seconds of notification silence remaining, `0` for none — and the reason this member has both a
332
+ * `type: "string"` and a `decode`: the device reports the whole snooze CONFIG, so the stored property
333
+ * describes the blob while `decodeSnoozeSeconds` lifts the duration out of it on read. The
334
+ * `decode`'s return type is what a caller gets, so the getter answers a number.
335
+ *
336
+ * Writing a positive duration re-sends the capture's fixed companion fields rather than exposing them
337
+ * (`snoozePayload`); writing 0 sends the bare clear shape. Camera-family only.
338
+ */
339
+ readonly snoozeTime: {
340
+ readonly param: 1271;
341
+ readonly type: "string";
342
+ readonly provenance: "verified";
343
+ readonly min: 0;
344
+ readonly decode: (raw: unknown) => number | undefined;
345
+ readonly decodedKind: "seconds";
346
+ readonly description: string;
347
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command | undefined;
348
+ };
349
+ /**
350
+ * Not every model offers it, and one that does not accepts the frame without acting on it — so the
351
+ * write is offered only where the device reports the id, and the surface says it is optional.
352
+ */
353
+ readonly humanOnlyAtNight: {
354
+ readonly param: 1719;
355
+ readonly type: "bool";
356
+ readonly kind: "boolean";
357
+ readonly provenance: "apk";
358
+ readonly description: string;
359
+ readonly requires: readonly [1719];
360
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
361
+ };
362
+ /**
363
+ * The app reads it as `typeof v === "object" ? v.radar_wd_switch : v`, so the stored value may be a
364
+ * JSON object OR a bare scalar — a plain bool coercion reports `false` for the object form.
365
+ */
366
+ readonly loiteringDetection: {
367
+ readonly param: 2706;
368
+ readonly type: "bool";
369
+ readonly kind: "boolean";
370
+ readonly provenance: "apk";
371
+ readonly coerce: (raw: string | number | boolean) => boolean;
372
+ readonly description: string;
373
+ readonly requires: readonly [2706];
374
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
375
+ };
376
+ };
377
+ export declare const MOTION: CapabilityModule;
@@ -0,0 +1,8 @@
1
+ import type { CapabilityModule } from "./types.js";
2
+ /**
3
+ * `person_detection` — human/AI detection.
4
+ *
5
+ * Push events are the whole read surface: a person arrives as an event, and no owned camera reports a
6
+ * detection-enable or detected-state parameter, so there is nothing for a state table to project.
7
+ */
8
+ export declare const PERSON_DETECTION: CapabilityModule;