@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.
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,127 @@
1
+ import type { Capability } from "../types.js";
2
+ import type { ActionSpec, AvailabilityContext, CommandContext, CapabilityStateReader } from "./types.js";
3
+ import type { Command, ScalarForm } from "../../core/contracts.js";
4
+ /**
5
+ * Capability param access — the shared surface capability modules use to WRITE a param (the
6
+ * command-intent builders) and to READ one (the typed read extractors at the bottom of this file).
7
+ *
8
+ * **Write:** a capability states *what* to change (param id + already-polarity-resolved value) and,
9
+ * when the firmware pins it, *which wire form* the data takes. It never picks an encryption level or
10
+ * assembles a P2P frame: the transport resolver ({@link module:index} `resolveScalar`) does that,
11
+ * session-first for `"auto"`. Adding or changing a capability = call these helpers; the transport
12
+ * stays untouched.
13
+ *
14
+ * **Read:** the `readNum`/`readBool`/`readStr` extractors narrow a live property to a typed value for
15
+ * the fluent read getters (`dev.battery()?.level`) — the dual of the write builders.
16
+ *
17
+ * @module model/capabilities/access
18
+ * ## `ScalarForm`
19
+ * - `"auto"` — a scalar int whose encryption level the **session** decides: L2 direct-binary when a
20
+ * level-2 key is negotiated, else L1 int-string. Correct for params that are the same bytes either
21
+ * way (camera power 1035 — verified live on standalone T8410 = L1 and HomeBase T8114 = L2).
22
+ * - `"int-string"` — force the L1 int+string frame. For params the firmware only accepts as level-1
23
+ * even on a HomeBase (status LED 1045; the Cam2C/2/3 floodlight switch 1400).
24
+ * - `"direct-binary"` — force the L2 binary "direct" frame (spotlight brightness/color-temp/enable).
25
+ *
26
+ * ({@link ScalarForm} is declared in `./types` to keep the Command union self-contained.)
27
+ */
28
+ /**
29
+ * Set a scalar (integer) param. `form` defaults to `"auto"` — let the session decide the level.
30
+ * Pass an explicit form only when the firmware pins the wire (see {@link ScalarForm}).
31
+ */
32
+ export declare function setScalar(param: number, value: number, ctx: CommandContext, form?: ScalarForm): Command;
33
+ /**
34
+ * Set a param carried as a JSON control-payload (`{commandType:param, data}` under the 1700 wrapper).
35
+ * The transport picks L1 (standalone, ECB) vs L2 (HomeBase, GCM) by session — the capability just
36
+ * describes the payload.
37
+ */
38
+ export declare function setJson(param: number, data: Record<string, unknown>, ctx: CommandContext): Command;
39
+ /**
40
+ * Set a param carried as BARE JSON, no envelope at all — the wire's outer P2P command IS `cmd`
41
+ * itself, GCM signCode 8, plaintext exactly `{account_id,...data}` (the sink injects `account_id`).
42
+ * Distinct from {@link setJson}, which always wraps in the `1700` CONTROL_PAYLOAD `{commandType,data}`
43
+ * shape. Reversed from a live capture of the app's own SET_SNOOZE_TIME (1271) frame.
44
+ *
45
+ * `channel` overrides the device channel — pass it for a station-scoped bare write (e.g. the
46
+ * alarm-delay config 1255, which rides the station broadcast channel 255, not `ctx.channel`).
47
+ */
48
+ export declare function setJsonRaw(cmd: number, data: Record<string, unknown>, ctx: CommandContext, channel?: number): Command;
49
+ /**
50
+ * Set a param carried in the `SET_PAYLOAD` (1350) envelope — `{account_id,cmd,mChannel,mValue3:cmd,
51
+ * payload}`, GCM signCode 8 — NOT the bare `{commandType,data}` 1700 wrapper `setJson` uses. The wire
52
+ * eufy uses for a few doorbell controls (status-LED 1716 `{light_enable}`). Level-2 by default; the
53
+ * sink resolves the session/account_id and replays the frame. Pass `form: "auto"` when the envelope is
54
+ * also valid at level 1, so a STANDALONE device — which never negotiates a level-2 key — can receive it
55
+ * instead of failing outright.
56
+ */
57
+ export declare function setPayload(cmd: number, payload: Record<string, unknown>, ctx: CommandContext, mValue3?: number, channel?: number, form?: ScalarForm): Command;
58
+ /**
59
+ * Set a station-scoped scalar (132-byte body, no channel field) on an EXPLICIT channel — for the
60
+ * HomeBase's own controls on the station broadcast channel 255 (alarm/speaker volume 1235).
61
+ */
62
+ export declare function setStationScalar(cmd: number, value: number, channel: number): Command;
63
+ /**
64
+ * A one-line device descriptor for error messages — carries every identifier needed to reproduce or
65
+ * triage from a log later (deviceType, model T-code, full serial, channel, codec), so a "capability
66
+ * detected but this device's wire is unknown / unsupported" throw is self-contained instead of naming
67
+ * a bare `deviceType`.
68
+ */
69
+ export declare function describeDevice(ctx: CommandContext): string;
70
+ /** True for a camera-codec device (a camera or a doorbell) — the video / two-way-audio family. */
71
+ export declare function isCameraCodec(ctx: AvailabilityContext): boolean;
72
+ /** True for a station/hub codec (a HomeBase OR an NVR — use `isHomeBase` from device-family to exclude NVRs). */
73
+ export declare function isStationCodec(ctx: AvailabilityContext): boolean;
74
+ /** True when the device's RESOLVED capability set includes `cap` — the same gate `buildCommand` authorizes on. */
75
+ export declare function hasCapability(ctx: AvailabilityContext, cap: Capability): boolean;
76
+ /**
77
+ * Attach a description to an action, at the one place the action is declared.
78
+ *
79
+ * The alternative — a table of descriptions beside `actions()` — restates every method name, which is
80
+ * one rename away from describing a method that no longer exists. Here the object key IS the name, so
81
+ * the two cannot come apart. The description is then readable only off a BUILT action object.
82
+ *
83
+ * See {@link ActionSpec} for what may be described — the value-taking method rather than its aliases,
84
+ * and only a wire confirmed on real hardware.
85
+ */
86
+ export declare function describedAction<F extends (...args: never[]) => unknown>(spec: ActionSpec, fn: F): F;
87
+ /** The {@link ActionSpec} attached to a built action, or `undefined` for one nothing describes. */
88
+ export declare function actionSpecOf(fn: unknown): ActionSpec | undefined;
89
+ /**
90
+ * `vacuum_clean` → `vacuumClean`, `smart_light` → `smartLight` — the fluent accessor name a capability
91
+ * is reached under.
92
+ *
93
+ * Lives here because two callers need the same answer: the barrel installs the accessors under these
94
+ * names, and the manifest publishes them, so a described capability names the accessor its object
95
+ * lives on. A second copy would be a rename away from naming an accessor that doesn't exist.
96
+ * @internal
97
+ */
98
+ export declare function camelCase(cap: Capability): string;
99
+ /**
100
+ * Narrow a capability's live property to a typed value for a fluent read getter (`dev.battery()?.level`).
101
+ * The value already arrives runtime-coerced to its `PropertySpec.type` (`device.ts` `coerceByType`), so
102
+ * this only guards the runtime type — returning `undefined` on a mismatch (or a missing/unbound reader)
103
+ * rather than lie-casting. Read a numeric property, or `undefined` when absent / not a number.
104
+ */
105
+ export declare function readNum(read: CapabilityStateReader | undefined, name: string): number | undefined;
106
+ /** Read a boolean property by name, or `undefined` when absent / not a boolean. */
107
+ export declare function readBool(read: CapabilityStateReader | undefined, name: string): boolean | undefined;
108
+ /** Read a string property by name, or `undefined` when absent / not a string. */
109
+ export declare function readStr(read: CapabilityStateReader | undefined, name: string): string | undefined;
110
+ /**
111
+ * Build a Tuya DP write intent — the clean-line wire for a single data-point value.
112
+ * Named for the wire mechanism (the AIoT "Tuya DP" protocol), not the capability that first
113
+ * uses it, so it is open to any future Tuya-DP device.
114
+ */
115
+ export declare function aiotDp(dp: number, value: boolean | number | string): Command;
116
+ /**
117
+ * Pick a capability's OWN data points out of a realtime report, for its `decodeState`.
118
+ *
119
+ * The transport unwraps the report's envelope into `id → value` without knowing what any id means;
120
+ * this is the other half — each capability names the ids it owns, so one clean-line module never
121
+ * claims another's points off the same message. `undefined` when the report carries none of them,
122
+ * which is what a `decodeState` returns to say "not mine".
123
+ *
124
+ * Values pass through untouched. A structured point stays the base64 its device sent, for the read
125
+ * getter to decode once a codec is in scope.
126
+ */
127
+ export declare function pickDpParams(report: Record<number, string> | undefined, ids: readonly number[]): Record<number, string> | undefined;
@@ -0,0 +1,201 @@
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 guard modes {@link ArmingActions} can SET — the three whose write was captured byte-exact against a
6
+ * real station. `ArmingMode` is both the const value-object (`ArmingMode.home`) and the union type of its
7
+ * values, so callers pass the named constant: `setMode(ArmingMode.home)`.
8
+ *
9
+ * Deliberately NARROWER than the set a device may report. The other six modes are ones the app itself
10
+ * defines and the `mode` read still names them, but no capture shows one being SENT — and
11
+ * on a fire-and-forget wire a wrong one looks exactly like success. Leaving them out of this union is the
12
+ * compile-time half of the refusal; `mode`'s published argument and the generated rejection are the
13
+ * runtime half.
14
+ */
15
+ export declare const ArmingMode: {
16
+ /** Armed — full protection, nobody home (wire value 0). */
17
+ readonly away: "away";
18
+ /** Armed for occupancy — reduced/perimeter protection while home (wire value 1). */
19
+ readonly home: "home";
20
+ /** Disarmed — no alarms; sensors still report state (wire value 63). */
21
+ readonly disarmed: "disarmed";
22
+ };
23
+ export type ArmingMode = (typeof ArmingMode)[keyof typeof ArmingMode];
24
+ /**
25
+ * The P2P **feature-command ids** this arming capability drives. Capability-owned wire vocabulary
26
+ * (transport forwards `cmd.param` opaquely; full id→name catalog in the generated
27
+ * `transport/p2p/commands.ts`).
28
+ */
29
+ export declare const ARMING_CMD: {
30
+ /**
31
+ * Guard/arming mode (app `GUARD_MODE`). ✅ Wire ENVELOPE verified live on a T8030 (
32
+ * 2026-07-23), cycling Away→Disarmed→Home in the
33
+ * real app: `1350` SET_PAYLOAD, cmd 1224 (SAME id as the read param), mChannel 0, explicit
34
+ * mValue3:0, `payload:{mode_type:<int>, user_name:<string>}`.
35
+ *
36
+ * ⚠️ Only 3 of the 9 modes were exercised in that capture — `mode_type` 0 (away), 63 (disarmed), 1
37
+ * (home), all confirmed byte-exact, and those three are the whole of {@link ArmingMode}. Re-confirmed
38
+ * live 2026-08-05: each reported its own MODE_SWITCH push within ~5s of the write. The remaining six are
39
+ * named by the app but never observed leaving it, so this capability reads them and refuses to send
40
+ * them. See `ARMING_MODE_WIRE` for the per-value breakdown.
41
+ */
42
+ readonly SET_ARMING: 1224;
43
+ /**
44
+ * The per-mode alarm/arm-delay configuration write. ✅ WIRE CAPTURED live on a T8030 (
45
+ * 2026-07-23, both directions): a **bare
46
+ * JSON frame, no `1350`/`1700` envelope** — outer P2P cmd IS `1255` itself, station channel 255,
47
+ * plaintext `{account_id, count_down_alarm:{channel_list,delay_time},
48
+ * count_down_arm:{channel_list,delay_time}, devices:[{action,device_channel}], mode_id,
49
+ * siren_sensor_action:[{action,device_channel}]}`.
50
+ *
51
+ * **Disambiguated from a live-countdown-state echo** (2026-07-23): a full arm→wait-through-exit-
52
+ * delay→disarm cycle produced ZERO traffic on this cmd, while every edit of the app's "Alarm Delay"
53
+ * screen did — so this is a genuine settings WRITE, not a realtime push.
54
+ *
55
+ * **Why `setAlarmDelayConfig` takes the FULL config, not just a duration**: `channelList` identifies
56
+ * WHICH sensor channels get the delay (confirmed: setting 45s on one specific sensor produced
57
+ * `channel_list:[<that sensor's channel>], delay_time:45`) — it is not a simple on/off. The
58
+ * corresponding GET command (`1310`/inner cmd `40003`, sent by the app right before editing) always
59
+ * replied `{count:0,data:null}` in every capture — genuinely empty, not a decrypt failure (confirmed
60
+ * via the same bidirectional decrypt this finding used) — so the app does NOT read the current
61
+ * config this way; how it does is still unknown. Without a working GET, safely PATCHING just one
62
+ * channel in or out of an existing list isn't possible without risking clobbering the rest — so
63
+ * this ships as a caller-supplies-everything write instead of guessing a merge.
64
+ * `devices`/`siren_sensor_action` are even less understood (raw per-device action codes, meaning
65
+ * unconfirmed) and MUST come from a value independently read/captured for the target mode —
66
+ * see `AlarmDelayConfig`'s field docs.
67
+ */
68
+ readonly ALARM_DELAY_CONFIG: 1255;
69
+ };
70
+ /**
71
+ * Alarm-delay durations the app's OWN picker UI offers — `AlarmDelaySeconds` is both the const
72
+ * value-object (`AlarmDelaySeconds.sec45`) and the union type of its values, so callers pass the
73
+ * named constant: `{delaySeconds: AlarmDelaySeconds.sec45}`.
74
+ *
75
+ * **NOT a wire constraint** — the device itself does NOT validate against this list: a live test sent
76
+ * `50` (off this list) and the app reflected it correctly, no rejection. Typed as a closed set anyway
77
+ * so `setAlarmDelayConfig` callers get the same choices a human editing the same setting in the app
78
+ * would see, rather than an arbitrary int that could silently diverge from every value the real UI
79
+ * can actually produce.
80
+ */
81
+ export declare const AlarmDelaySeconds: {
82
+ readonly off: 0;
83
+ readonly sec15: 15;
84
+ readonly sec30: 30;
85
+ readonly sec45: 45;
86
+ readonly sec60: 60;
87
+ readonly min3: 180;
88
+ readonly min5: 300;
89
+ };
90
+ export type AlarmDelaySeconds = (typeof AlarmDelaySeconds)[keyof typeof AlarmDelaySeconds];
91
+ /** One `channelList`+`delaySeconds` countdown pair — see {@link AlarmDelayConfig}. */
92
+ export type AlarmDelayCountdown = {
93
+ /** Device channels this countdown applies to. */
94
+ channelList: number[];
95
+ /** Delay duration — one of {@link AlarmDelaySeconds}, shared across every channel in `channelList`. */
96
+ delaySeconds: AlarmDelaySeconds;
97
+ };
98
+ /** One device's participation entry in {@link AlarmDelayConfig.devices} / `.sirenSensorAction`. */
99
+ export type AlarmDelayDeviceAction = {
100
+ deviceChannel: number;
101
+ /** Raw per-device action code. Meaning NOT independently confirmed — pass through verbatim from a
102
+ * value read/captured for this exact mode, never invented. */
103
+ action: number;
104
+ };
105
+ /**
106
+ * The FULL per-mode alarm/arm-delay configuration the device accepts as one write — see the
107
+ * `setAlarmDelayConfig` doc for why this is a caller-supplies-everything shape rather than a simple
108
+ * `setEntryDelay(seconds)` toggle.
109
+ */
110
+ export type AlarmDelayConfig = {
111
+ /** Per-sensor ENTRY/alarm delay — the app's "Alarm Delay" UI setting. Confirmed on-device: a delay
112
+ * set on one sensor lands as that sensor's channel plus the chosen duration. */
113
+ countDownAlarm: AlarmDelayCountdown;
114
+ /** A second, distinct countdown carried alongside `countDownAlarm`. It stayed empty across every
115
+ * observed `countDownAlarm` edit, so its own trigger condition is UNCONFIRMED. */
116
+ countDownArm: AlarmDelayCountdown;
117
+ /** Every device's participation + action for THIS mode. UNCONFIRMED semantics (the action code's
118
+ * meaning is unknown) — it stayed identical across every `countDownAlarm`-only edit within the same
119
+ * mode, so pass through exactly what was read back for that mode; never invent a value. */
120
+ devices: AlarmDelayDeviceAction[];
121
+ /** Siren behavior per device for this mode. Same caveat as `devices`. */
122
+ sirenSensorAction: AlarmDelayDeviceAction[];
123
+ };
124
+ /**
125
+ * Bound guard-mode controls — the object returned by `dev.arming()`.
126
+ *
127
+ * Everything is DERIVED from `ARMING_MEMBERS`. `setMode` is the mode member's own derived setter;
128
+ * `setAlarmDelayConfig` is a `method` because it takes TWO arguments (a mode and a whole config),
129
+ * which no value setter can express.
130
+ */
131
+ export type ArmingActions = Surface<typeof ARMING_MEMBERS>;
132
+ /**
133
+ * Every `arming` feature, declared once.
134
+ *
135
+ * Exported but NOT published: each entry states its wire id and the evidence it was confirmed on,
136
+ * which the reference site does not carry.
137
+ * @internal
138
+ */
139
+ export declare const ARMING_MEMBERS: {
140
+ /**
141
+ * The one member whose write domain is NARROWER than its read: `enumValues` names all nine modes a
142
+ * station can report, and the argument's `values` publishes only the three whose wire was captured. That
143
+ * argument IS the domain the derived setter enforces and the refusal names, so an uncaptured mode is
144
+ * refused by naming the three that work — nine labels for the read and three for the write, off one
145
+ * declaration.
146
+ *
147
+ * `armingCommand` may also throw synchronously (missing account identity) and `bindMembers` turns that
148
+ * into a rejection, so the builder stays plain.
149
+ *
150
+ * The setter takes either vocabulary — see `armingModeOf` — because the getter answers the wire integer,
151
+ * and a value a caller just read has to be one it can write back.
152
+ *
153
+ * MODE_SWITCH carries no value. Live qualification on a standalone camera showed that authoritative
154
+ * readback requires a bounded cloud-list refresh, and that its P2P session must be reset after
155
+ * convergence before a following mode write; an attached device must never reset its shared HomeBase.
156
+ */
157
+ readonly mode: {
158
+ readonly accepts: ArmingMode;
159
+ readonly param: 1224;
160
+ readonly property: "armingMode";
161
+ readonly type: "enum";
162
+ readonly kind: "enum";
163
+ readonly enumValues: Record<number, string>;
164
+ readonly provenance: "verified";
165
+ readonly args: readonly [{
166
+ readonly name: "mode";
167
+ readonly kind: "enum";
168
+ readonly values: readonly number[];
169
+ }];
170
+ readonly description: string;
171
+ readonly observation: {
172
+ readonly event: "armingModeChanged";
173
+ readonly reflects: (value: string | number | boolean) => {
174
+ param: 1224;
175
+ expected: number;
176
+ };
177
+ readonly resetStandaloneSession: true;
178
+ readonly timeoutMs: 20000;
179
+ };
180
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command | undefined;
181
+ };
182
+ /**
183
+ * Write the FULL per-mode alarm/arm-delay configuration. The device accepts the whole config as one
184
+ * write, not a single duration field, so a partial update is not possible. An expert/advanced API: the
185
+ * caller is responsible for supplying `devices`/`sirenSensorAction` (and the `countDownAlarm`/
186
+ * `countDownArm` entries they are NOT changing) from a value they have independently read/captured for
187
+ * this mode — there is no known GET to fetch it automatically, and a wrong guess here can silently
188
+ * misconfigure which sensors arm/trigger for real.
189
+ *
190
+ * Takes {@link ArmingMode}, so a delay can only be configured for a mode whose `mode_id` integer is
191
+ * captured. The frame carries that same integer, so a schedule/custom mode would be the identical guess
192
+ * `setMode` refuses.
193
+ */
194
+ readonly setAlarmDelayConfig: import("./members.js").MethodMember<(mode: ArmingMode, config: AlarmDelayConfig) => Promise<void>>;
195
+ };
196
+ /**
197
+ * `arming` — guard/arming mode. `armingMode` (see {@link ARMING_CMD.SET_ARMING}) has a verified
198
+ * read/write MECHANISM, but only 3 of its 8 {@link ArmingMode} values (away/home/disarmed) are
199
+ * wire-captured — see `ARMING_MODE_WIRE` for which 5 are still unverified third-party integers.
200
+ */
201
+ export declare const ARMING: CapabilityModule;
@@ -0,0 +1,154 @@
1
+ import { isHomeBase } from "../device-family.js";
2
+ import { isCameraCodec } from "./access.js";
3
+ import { type Surface } from "./members.js";
4
+ import type { AvailabilityContext, CapabilityModule, CommandContext } from "./types.js";
5
+ import type { Command } from "../../core/contracts.js";
6
+ /**
7
+ * The P2P **feature-command ids** this audio capability drives — the outer-cmd IS the param id on the
8
+ * camera side (direct-binary struct), or the inner cmd of a `1350` SET_PAYLOAD on the station side.
9
+ * Capability-owned wire vocabulary (transport forwards `cmd.param` opaquely; full id→name catalog in
10
+ * the generated `transport/p2p/commands.ts`). All ✅ wire-verified live — see the module JSDoc below.
11
+ */
12
+ export declare const AUDIO_CMD: {
13
+ /**
14
+ * Microphone on/off (app `AUDIO_MICROPHONE_SWITCH`). ✅ Wire verified live on T8214: direct-binary
15
+ * 136-byte struct, signCode 8, 1=on/0=off (a switch, not a mute).
16
+ */
17
+ readonly AUDIO_MICROPHONE: 1240;
18
+ /** Speaker on/off (app `AUDIO_SPEAKER_SWITCH`). ✅ Wire verified live on T8214 — direct-binary, 1=on/0=off. */
19
+ readonly AUDIO_SPEAKER: 1241;
20
+ /** Speaker volume 0..100 (app `DOORBELL_AUDIO_VOLUME`). ✅ Same direct-binary wire; write verified live on T8214 (set 60 → param 60). */
21
+ readonly SPEAKER_VOLUME: 1230;
22
+ /**
23
+ * Audio recording on/off (app `DOORBELL_AUDIO_RECORDING_SWITCH`) — whether the camera records audio
24
+ * with video. Reported by every camera. ✅ Wire reversed from a live outbound capture + L2 decrypt on
25
+ * T8425 (ch3): `1350` SET_PAYLOAD on the device channel (mChannel = device ch, mValue3 0), payload
26
+ * `{channel:<deviceCh>, record_mute:0|1}`. The key is `record_mute` and it is **INVERTED** —
27
+ * `record_mute:1` = muted (recording OFF), `record_mute:0` = recording ON.
28
+ */
29
+ readonly AUDIO_RECORDING: 1288;
30
+ /**
31
+ * Doorbell ringtone/chime volume 0..100 (app `DOORBELL_RINGTONE_VOLUME`). ✅ Wire verified live on
32
+ * T8214 — same direct-binary 136-byte struct, signCode 8. Doorbell-only: this capability adds it when
33
+ * the device is a doorbell.
34
+ */
35
+ readonly DOORBELL_RINGTONE_VOLUME: 1708;
36
+ /**
37
+ * HomeBase voice **prompt** volume 0..100 (app `APP_CMD_SET_PROMPT_VOLUME`). ✅ Wire verified live on
38
+ * T8030 (audible): the `1350` SET_PAYLOAD wrapper on ch0 with `payload:{value}`. Station-only.
39
+ */
40
+ readonly HUB_PROMPT_VOLUME: 1292;
41
+ };
42
+ /**
43
+ * Bound audio controls — the object returned by `dev.audio()`.
44
+ *
45
+ * Everything is DERIVED from `AUDIO_MEMBERS`. Which methods a device has depends on its FAMILY, so
46
+ * every write is optional and a caller checks: a **camera/doorbell** gets `setMicrophone`/`setSpeaker`/
47
+ * `setVolume`/`setAudioRecording` (+ `setRingtoneVolume` on a doorbell); a **HomeBase/station** gets
48
+ * `setPromptVolume`. Alarm-output configuration belongs to the `siren` capability.
49
+ */
50
+ export type AudioActions = Surface<typeof AUDIO_MEMBERS>;
51
+ /**
52
+ * Every general audio feature, declared once and family-gated across cameras, doorbells, and HomeBases.
53
+ *
54
+ * Camera side (Doorbell T8214): outer-cmd = the param id
55
+ * (1240/1241/1230/1708), signCode 8, device channel, 168B→136B direct-binary struct
56
+ * `[u32 channel][u32 value][account_id pad→128]`; polarity is 1=on. HomeBase prompt volume `1292`
57
+ * rides the `1350` SET_PAYLOAD wrapper on channel 0 with `payload:{value}` and is not cloud-reflected.
58
+ *
59
+ * Exported but NOT published: each entry states its wire id and the evidence it was confirmed on,
60
+ * which the reference site does not carry.
61
+ * @internal
62
+ */
63
+ export declare const AUDIO_MEMBERS: {
64
+ /**
65
+ * A SWITCH, not a mute: `true` powers the mic, so the polarity reads the same way round on the wire
66
+ * (1 = on) with no `invert`. Camera-family only — a HomeBase has no microphone, so the getter and
67
+ * setter are both absent there and a caller must check.
68
+ */
69
+ readonly microphone: {
70
+ readonly param: 1240;
71
+ readonly type: "bool";
72
+ readonly kind: "boolean";
73
+ readonly provenance: "verified";
74
+ readonly available: typeof isCameraCodec;
75
+ readonly description: "Microphone on/off (1240 AUDIO_MICROPHONE_SWITCH; 1=on/0=off). Wire verified live on T8214.";
76
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
77
+ };
78
+ /**
79
+ * The camera's own speaker switch — what talkback and the doorbell's responses play out of. Distinct
80
+ * from `volume`, which sets how loud it is: turning this off silences the camera whatever the level
81
+ * says. Camera-family only, like `microphone` above; HomeBase voice prompts use `promptVolume`.
82
+ */
83
+ readonly speaker: {
84
+ readonly param: 1241;
85
+ readonly type: "bool";
86
+ readonly kind: "boolean";
87
+ readonly provenance: "verified";
88
+ readonly available: typeof isCameraCodec;
89
+ readonly description: "Speaker on/off (1241 AUDIO_SPEAKER_SWITCH; 1=on/0=off). Wire verified live on T8214.";
90
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
91
+ };
92
+ /**
93
+ * `volume` is the accessor a caller sees; the flat property has to be `speakerVolume` because three
94
+ * capabilities claim `volume`. Both names reach the same write through the intent path.
95
+ */
96
+ readonly volume: {
97
+ readonly param: 1230;
98
+ readonly property: "speakerVolume";
99
+ readonly type: "number";
100
+ readonly unit: "%";
101
+ readonly kind: "percent";
102
+ readonly provenance: "verified";
103
+ readonly available: typeof isCameraCodec;
104
+ readonly min: 0;
105
+ readonly max: 100;
106
+ readonly intentNames: readonly ["volume"];
107
+ readonly description: "Speaker volume 0..100 (1230 DOORBELL_AUDIO_VOLUME). Write verified live on T8214 (set 60 → param 60).";
108
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
109
+ };
110
+ /** Raw param 1288 is `record_mute` (1 = muted / recording OFF), so audioRecording = NOT record_mute. */
111
+ readonly audioRecording: {
112
+ readonly param: 1288;
113
+ readonly type: "bool";
114
+ readonly kind: "boolean";
115
+ readonly invert: true;
116
+ readonly provenance: "verified";
117
+ readonly available: typeof isCameraCodec;
118
+ readonly description: string;
119
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
120
+ };
121
+ /**
122
+ * Doorbell ring/chime loudness — WRITE-ONLY here on purpose. The READ property `ringtoneVolume` (1708)
123
+ * lives on the `doorbell` capability: 1708 leaks onto non-doorbell cameras, so it cannot be an `audio`
124
+ * property (which spans all cameras) without over-surfacing junk. Audio owns the write.
125
+ */
126
+ readonly ringtoneVolume: {
127
+ readonly param: 1708;
128
+ readonly type: "number";
129
+ readonly unit: "%";
130
+ readonly kind: "percent";
131
+ readonly writeOnly: true;
132
+ readonly provenance: "verified";
133
+ readonly available: (ctx: AvailabilityContext) => boolean;
134
+ readonly min: 0;
135
+ readonly max: 100;
136
+ readonly description: "Doorbell ring/chime volume 0..100 (1708 DOORBELL_RINGTONE_VOLUME). Wire verified live on T8214.";
137
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
138
+ };
139
+ /** HomeBase voice-prompt volume uses the 1350 SET_PAYLOAD wrapper on channel 0 and has no cloud readback. */
140
+ readonly promptVolume: {
141
+ readonly param: 1292;
142
+ readonly type: "number";
143
+ readonly unit: "%";
144
+ readonly kind: "percent";
145
+ readonly writeOnly: true;
146
+ readonly provenance: "verified";
147
+ readonly available: typeof isHomeBase;
148
+ readonly min: 0;
149
+ readonly max: 100;
150
+ readonly description: "HomeBase voice-prompt volume 0..100 (1292 APP_CMD_SET_PROMPT_VOLUME). Verified audible on a T8030.";
151
+ readonly write: (v: string | number | boolean, ctx: CommandContext) => Command;
152
+ };
153
+ };
154
+ export declare const AUDIO: CapabilityModule;