tesla-fleet-api 0.4.0 → 0.5.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 (219) hide show
  1. package/README.md +284 -1
  2. package/dist/_virtual/_rolldown/runtime.cjs +10 -0
  3. package/dist/bluetooth/adapters/shared.cjs +38 -0
  4. package/dist/bluetooth/adapters/shared.mjs +36 -0
  5. package/dist/bluetooth/async.cjs +121 -0
  6. package/dist/bluetooth/async.d.cts +10 -0
  7. package/dist/bluetooth/async.d.mts +10 -0
  8. package/dist/bluetooth/async.mjs +116 -0
  9. package/dist/bluetooth/broadcast.cjs +153 -0
  10. package/dist/bluetooth/broadcast.d.cts +59 -0
  11. package/dist/bluetooth/broadcast.d.mts +59 -0
  12. package/dist/bluetooth/broadcast.mjs +151 -0
  13. package/dist/bluetooth/constants.cjs +99 -0
  14. package/dist/bluetooth/constants.d.cts +70 -0
  15. package/dist/bluetooth/constants.d.mts +70 -0
  16. package/dist/bluetooth/constants.mjs +72 -0
  17. package/dist/bluetooth/esphome.cjs +292 -0
  18. package/dist/bluetooth/esphome.d.cts +83 -0
  19. package/dist/bluetooth/esphome.d.mts +83 -0
  20. package/dist/bluetooth/esphome.mjs +291 -0
  21. package/dist/bluetooth/framing.cjs +125 -0
  22. package/dist/bluetooth/framing.d.cts +45 -0
  23. package/dist/bluetooth/framing.d.mts +45 -0
  24. package/dist/bluetooth/framing.mjs +122 -0
  25. package/dist/bluetooth/homey.cjs +155 -0
  26. package/dist/bluetooth/homey.d.cts +75 -0
  27. package/dist/bluetooth/homey.d.mts +75 -0
  28. package/dist/bluetooth/homey.mjs +150 -0
  29. package/dist/bluetooth/names.cjs +24 -0
  30. package/dist/bluetooth/names.d.cts +10 -0
  31. package/dist/bluetooth/names.d.mts +10 -0
  32. package/dist/bluetooth/names.mjs +20 -0
  33. package/dist/bluetooth/noble.cjs +178 -0
  34. package/dist/bluetooth/noble.d.cts +58 -0
  35. package/dist/bluetooth/noble.d.mts +58 -0
  36. package/dist/bluetooth/noble.mjs +177 -0
  37. package/dist/bluetooth/streamglue.cjs +157 -0
  38. package/dist/bluetooth/streamglue.d.cts +37 -0
  39. package/dist/bluetooth/streamglue.d.mts +37 -0
  40. package/dist/bluetooth/streamglue.mjs +154 -0
  41. package/dist/bluetooth/teslabluetooth.cjs +67 -0
  42. package/dist/bluetooth/teslabluetooth.d.cts +41 -0
  43. package/dist/bluetooth/teslabluetooth.d.mts +41 -0
  44. package/dist/bluetooth/teslabluetooth.mjs +66 -0
  45. package/dist/bluetooth/transport.cjs +10 -0
  46. package/dist/bluetooth/transport.d.cts +95 -0
  47. package/dist/bluetooth/transport.d.mts +95 -0
  48. package/dist/bluetooth/transport.mjs +10 -0
  49. package/dist/bluetooth/vehiclebluetooth.cjs +1272 -0
  50. package/dist/bluetooth/vehiclebluetooth.d.cts +495 -0
  51. package/dist/bluetooth/vehiclebluetooth.d.mts +495 -0
  52. package/dist/bluetooth/vehiclebluetooth.mjs +1270 -0
  53. package/dist/bluetooth/verify.cjs +93 -0
  54. package/dist/bluetooth/verify.d.cts +35 -0
  55. package/dist/bluetooth/verify.d.mts +35 -0
  56. package/dist/bluetooth/verify.mjs +91 -0
  57. package/dist/bluetooth.cjs +53 -0
  58. package/dist/bluetooth.d.cts +10 -0
  59. package/dist/bluetooth.d.mts +10 -0
  60. package/dist/bluetooth.mjs +9 -0
  61. package/dist/commands.cjs +1080 -85
  62. package/dist/commands.d.cts +585 -54
  63. package/dist/commands.d.mts +583 -51
  64. package/dist/commands.mjs +1084 -93
  65. package/dist/const.cjs +22 -0
  66. package/dist/const.d.cts +21 -0
  67. package/dist/const.d.mts +21 -0
  68. package/dist/const.mjs +20 -1
  69. package/dist/energy.cjs +24 -19
  70. package/dist/energy.d.cts +21 -15
  71. package/dist/energy.d.mts +21 -15
  72. package/dist/energy.mjs +24 -19
  73. package/dist/energyisland.cjs +38 -0
  74. package/dist/energyisland.mjs +37 -0
  75. package/dist/energyparsers.cjs +118 -0
  76. package/dist/energyparsers.d.cts +39 -0
  77. package/dist/energyparsers.d.mts +39 -0
  78. package/dist/energyparsers.mjs +116 -0
  79. package/dist/energyspecific.cjs +375 -17
  80. package/dist/energyspecific.d.cts +199 -19
  81. package/dist/energyspecific.d.mts +197 -16
  82. package/dist/energyspecific.mjs +370 -18
  83. package/dist/exceptions.cjs +3 -2
  84. package/dist/exceptions.d.cts +3 -2
  85. package/dist/exceptions.d.mts +3 -2
  86. package/dist/exceptions.mjs +3 -2
  87. package/dist/funnel.cjs +297 -0
  88. package/dist/funnel.d.cts +118 -0
  89. package/dist/funnel.d.mts +118 -0
  90. package/dist/funnel.mjs +292 -0
  91. package/dist/index.cjs +61 -0
  92. package/dist/index.d.cts +22 -8
  93. package/dist/index.d.mts +22 -8
  94. package/dist/index.mjs +19 -6
  95. package/dist/log.cjs +75 -0
  96. package/dist/log.d.cts +4 -0
  97. package/dist/log.d.mts +4 -0
  98. package/dist/log.mjs +71 -0
  99. package/dist/oauth.cjs +143 -0
  100. package/dist/oauth.d.cts +69 -0
  101. package/dist/oauth.d.mts +69 -0
  102. package/dist/oauth.mjs +143 -0
  103. package/dist/powerwall/client.cjs +818 -0
  104. package/dist/powerwall/client.d.cts +297 -0
  105. package/dist/powerwall/client.d.mts +297 -0
  106. package/dist/powerwall/client.mjs +807 -0
  107. package/dist/powerwall/components.cjs +188 -0
  108. package/dist/powerwall/components.d.cts +45 -0
  109. package/dist/powerwall/components.d.mts +45 -0
  110. package/dist/powerwall/components.mjs +182 -0
  111. package/dist/powerwall/energysite.cjs +216 -0
  112. package/dist/powerwall/energysite.d.cts +104 -0
  113. package/dist/powerwall/energysite.d.mts +104 -0
  114. package/dist/powerwall/energysite.mjs +214 -0
  115. package/dist/powerwall/exceptions.cjs +59 -0
  116. package/dist/powerwall/exceptions.d.cts +38 -0
  117. package/dist/powerwall/exceptions.d.mts +38 -0
  118. package/dist/powerwall/exceptions.mjs +51 -0
  119. package/dist/powerwall/gateway.cjs +102 -0
  120. package/dist/powerwall/gateway.d.cts +57 -0
  121. package/dist/powerwall/gateway.d.mts +57 -0
  122. package/dist/powerwall/gateway.mjs +101 -0
  123. package/dist/powerwall/merge.cjs +163 -0
  124. package/dist/powerwall/merge.d.cts +63 -0
  125. package/dist/powerwall/merge.d.mts +63 -0
  126. package/dist/powerwall/merge.mjs +159 -0
  127. package/dist/powerwall/models.d.cts +95 -0
  128. package/dist/powerwall/models.d.mts +95 -0
  129. package/dist/powerwall/queries.cjs +17 -0
  130. package/dist/powerwall/queries.mjs +10 -0
  131. package/dist/powerwall/transport.cjs +232 -0
  132. package/dist/powerwall/transport.mjs +227 -0
  133. package/dist/powerwall.cjs +47 -0
  134. package/dist/powerwall.d.cts +9 -0
  135. package/dist/powerwall.d.mts +9 -0
  136. package/dist/powerwall.mjs +8 -0
  137. package/dist/router/base.cjs +203 -0
  138. package/dist/router/base.d.cts +112 -0
  139. package/dist/router/base.d.mts +112 -0
  140. package/dist/router/base.mjs +200 -0
  141. package/dist/router/energysite.cjs +65 -0
  142. package/dist/router/energysite.d.cts +47 -0
  143. package/dist/router/energysite.d.mts +47 -0
  144. package/dist/router/energysite.mjs +60 -0
  145. package/dist/router.cjs +13 -0
  146. package/dist/router.d.cts +3 -1
  147. package/dist/router.d.mts +3 -1
  148. package/dist/router.mjs +3 -1
  149. package/dist/signing/crypto.cjs +28 -0
  150. package/dist/signing/crypto.mjs +26 -1
  151. package/dist/signing/energy.cjs +123 -0
  152. package/dist/signing/energy.d.cts +45 -0
  153. package/dist/signing/energy.d.mts +45 -0
  154. package/dist/signing/energy.mjs +120 -0
  155. package/dist/signing/jws.cjs +229 -0
  156. package/dist/signing/jws.d.cts +45 -0
  157. package/dist/signing/jws.d.mts +45 -0
  158. package/dist/signing/jws.mjs +211 -0
  159. package/dist/signing/keys.cjs +191 -0
  160. package/dist/signing/keys.d.cts +21 -0
  161. package/dist/signing/keys.d.mts +21 -0
  162. package/dist/signing/keys.mjs +182 -0
  163. package/dist/signing/session.cjs +104 -30
  164. package/dist/signing/session.d.cts +60 -23
  165. package/dist/signing/session.d.mts +60 -23
  166. package/dist/signing/session.mjs +105 -31
  167. package/dist/tariff.cjs +27 -0
  168. package/dist/tariff.d.cts +11 -0
  169. package/dist/tariff.d.mts +11 -0
  170. package/dist/tariff.mjs +27 -1
  171. package/dist/teslafleetapi.cjs +256 -55
  172. package/dist/teslafleetapi.d.cts +110 -14
  173. package/dist/teslafleetapi.d.mts +110 -14
  174. package/dist/teslafleetapi.mjs +258 -57
  175. package/dist/teslemetry.cjs +152 -5
  176. package/dist/teslemetry.d.cts +95 -5
  177. package/dist/teslemetry.d.mts +93 -3
  178. package/dist/teslemetry.mjs +151 -6
  179. package/dist/teslemetryenergy.cjs +287 -0
  180. package/dist/teslemetryenergy.d.cts +154 -0
  181. package/dist/teslemetryenergy.d.mts +154 -0
  182. package/dist/teslemetryenergy.mjs +282 -0
  183. package/dist/teslemetryvehicle.cjs +407 -0
  184. package/dist/teslemetryvehicle.d.cts +238 -0
  185. package/dist/teslemetryvehicle.d.mts +238 -0
  186. package/dist/teslemetryvehicle.mjs +405 -0
  187. package/dist/tessie.cjs +42 -5
  188. package/dist/tessie.d.cts +51 -5
  189. package/dist/tessie.d.mts +49 -3
  190. package/dist/tessie.mjs +42 -5
  191. package/dist/tessievehicle.cjs +514 -0
  192. package/dist/tessievehicle.d.cts +333 -0
  193. package/dist/tessievehicle.d.mts +333 -0
  194. package/dist/tessievehicle.mjs +513 -0
  195. package/dist/types/live_status.d.cts +16 -16
  196. package/dist/types/live_status.d.mts +16 -16
  197. package/dist/types/responses.d.cts +5 -1
  198. package/dist/types/responses.d.mts +5 -1
  199. package/dist/types/vehicle.d.cts +7 -1
  200. package/dist/types/vehicle.d.mts +7 -1
  201. package/dist/types.d.cts +3 -3
  202. package/dist/types.d.mts +3 -3
  203. package/dist/util.cjs +41 -0
  204. package/dist/util.d.cts +18 -0
  205. package/dist/util.d.mts +18 -0
  206. package/dist/util.mjs +40 -0
  207. package/dist/vehicle.cjs +79 -36
  208. package/dist/vehicle.d.cts +42 -26
  209. package/dist/vehicle.d.mts +42 -26
  210. package/dist/vehicle.mjs +80 -37
  211. package/dist/vehiclesigned.cjs +20 -5
  212. package/dist/vehiclesigned.d.cts +14 -5
  213. package/dist/vehiclesigned.d.mts +14 -5
  214. package/dist/vehiclesigned.mjs +20 -5
  215. package/dist/vehiclespecific.cjs +126 -15
  216. package/dist/vehiclespecific.d.cts +82 -18
  217. package/dist/vehiclespecific.d.mts +82 -18
  218. package/dist/vehiclespecific.mjs +126 -15
  219. package/package.json +70 -3
@@ -0,0 +1,282 @@
1
+ import { AuthorizedClientState, EnergyDeviceIdentifierType } from "./const.mjs";
2
+ import Energy from "./energy.mjs";
3
+ import { validate_island_mode } from "./energyisland.mjs";
4
+ import { parse_authorized_clients, parse_gateway_address } from "./energyparsers.mjs";
5
+ import EnergySpecific, { backupSchedulingInfo, publicKeyBase64, raise_on_island_rejection, validate_dispatch_battery_power } from "./energyspecific.mjs";
6
+ import { AuthorizedClientPairingTimedOut, AuthorizedClientWaitExpired } from "./exceptions.mjs";
7
+ // src/teslemetryenergy.ts
8
+ /** Default overall bound on `wait_until_paired`, in milliseconds. */
9
+ const DEFAULT_PAIRING_TIMEOUT = 6e5;
10
+ /** Default delay between `wait_until_paired` polls, in milliseconds. */
11
+ const DEFAULT_PAIRING_POLL_INTERVAL = 5e3;
12
+ const EXPIRED = Symbol("expired");
13
+ /**
14
+ * Settle as `step` does, or resolve `EXPIRED` after `ms`, or reject with the
15
+ * signal's reason. JavaScript cannot cancel `step`, so one that outlives this
16
+ * is abandoned, and its late rejection stays handled here.
17
+ */
18
+ function settleWithin(step, ms, signal) {
19
+ return new Promise((resolve, reject) => {
20
+ const finish = (settle) => {
21
+ clearTimeout(timer);
22
+ signal?.removeEventListener("abort", onAbort);
23
+ settle();
24
+ };
25
+ const onAbort = () => finish(() => reject(signal?.reason));
26
+ const timer = setTimeout(() => finish(() => resolve(EXPIRED)), ms);
27
+ signal?.addEventListener("abort", onAbort, { once: true });
28
+ Promise.resolve().then(step).then((value) => finish(() => resolve(value)), (error) => finish(() => reject(error)));
29
+ });
30
+ }
31
+ /**
32
+ * Teslemetry's energy site.
33
+ *
34
+ * With a signing key (see `EnergySpecific`), every device command except the
35
+ * four pre-pairing reads is signed with that key and sent through Teslemetry's
36
+ * `device_command` route. Without a key, a command uses its Teslemetry route.
37
+ * The `set_island_mode`, `backup_events` and `config` routes sign with
38
+ * Teslemetry's own key, which must be verified on the gateway, or the route
39
+ * answers 409 `energy_site_not_paired` and sends nothing. As of 2026-10-29 the
40
+ * `dispatch_battery_power`, `resume_battery_operation` and backup-event
41
+ * schedule and cancel routes still send unsigned, so Tesla answers HTTP 400:
42
+ * keyless dispatch does not work until Teslemetry signs those routes.
43
+ */
44
+ var TeslemetryEnergySite = class extends EnergySpecific {
45
+ /** Send a signed `RoutableMessage` through Teslemetry's route, which takes the flat body and adds Tesla's `data` wrapper itself. */
46
+ async _device_command(routable_message, target_id, identifier_type = EnergyDeviceIdentifierType.GATEWAY_DIN) {
47
+ return this.parent.parent._request("POST", `api/1/energy_sites/${this.energy_site_id}/device_command`, null, this._device_command_body(routable_message, target_id, identifier_type));
48
+ }
49
+ /** `GET` a Teslemetry command route. */
50
+ async _get_route(route) {
51
+ return this.parent.parent._request("GET", `api/1/energy_sites/${this.energy_site_id}/command/${route}`);
52
+ }
53
+ /** `POST` to a Teslemetry command route. */
54
+ async _post_route(route, json = null) {
55
+ return this.parent.parent._request("POST", `api/1/energy_sites/${this.energy_site_id}/command/${route}`, null, json);
56
+ }
57
+ /**
58
+ * Set the island mode on the energy gateway. With a signing key this signs
59
+ * the command with that key. Without one it calls the Teslemetry route, which
60
+ * signs it with Teslemetry's key. See `EnergySpecific.set_island_mode`.
61
+ */
62
+ async set_island_mode(mode, force) {
63
+ validate_island_mode(mode, force);
64
+ if (this.signing) return super.set_island_mode(mode, force);
65
+ const result = await this._post_route("set_island_mode", force == null ? { mode } : {
66
+ mode,
67
+ force
68
+ });
69
+ raise_on_island_rejection(result);
70
+ return result;
71
+ }
72
+ /** Get backup events. Without a signing key, the Teslemetry route signs with Teslemetry's key. */
73
+ async get_backup_events() {
74
+ return this.signing ? super.get_backup_events() : this._get_route("backup_events");
75
+ }
76
+ /** Get the TEG configuration. Without a signing key, the Teslemetry route signs with Teslemetry's key. */
77
+ async get_teg_config() {
78
+ return this.signing ? super.get_teg_config() : this._get_route("config");
79
+ }
80
+ /** Get energy device system information. A pre-pairing read: never signed, even with a key. */
81
+ async get_system_info() {
82
+ return this._get_route("system_info");
83
+ }
84
+ /** Get networking status, which Teslemetry returns already unwrapped. A pre-pairing read: never signed, even with a key. */
85
+ async raw_networking_status() {
86
+ return this._get_route("networking_status");
87
+ }
88
+ /** Scan for WiFi networks on the energy gateway. */
89
+ async wifi_scan() {
90
+ return this.signing ? super.wifi_scan() : this._get_route("wifi_scan");
91
+ }
92
+ /** Get the energy device certificate. */
93
+ async get_device_cert() {
94
+ return this.signing ? super.get_device_cert() : this._get_route("device_cert");
95
+ }
96
+ /** Get cellular modem information. Without a signing key, the Teslemetry route signs with Teslemetry's key. */
97
+ async get_cellular_info() {
98
+ return this.signing ? super.get_cellular_info() : this._get_route("cellular_info");
99
+ }
100
+ /** Check for a firmware update. Without a signing key, the Teslemetry route signs with Teslemetry's key. */
101
+ async check_for_update() {
102
+ return this.signing ? super.check_for_update() : this._get_route("check_for_update");
103
+ }
104
+ /** Check the urgency of any firmware update. Without a signing key, the Teslemetry route signs with Teslemetry's key. */
105
+ async check_for_update_urgency() {
106
+ return this.signing ? super.check_for_update_urgency() : this._get_route("check_for_update_urgency");
107
+ }
108
+ /** Test internet connectivity on the energy gateway. Without a signing key, the Teslemetry route signs with Teslemetry's key. */
109
+ async check_internet() {
110
+ return this.signing ? super.check_internet() : this._get_route("check_internet");
111
+ }
112
+ /**
113
+ * Update local site configuration on the energy gateway. This writes gateway
114
+ * configuration and is an undocumented energy device gRPC command.
115
+ * @param params Raw protobuf request fields.
116
+ */
117
+ async set_local_site_config(params) {
118
+ return this.signing ? super.set_local_site_config(params) : this._post_route("set_local_site_config", params ?? {});
119
+ }
120
+ /** Get the gateway's signed commands public key. A pre-pairing read: never signed, even with a key. */
121
+ async get_signed_commands_public_key() {
122
+ return this._get_route("signed_commands_public_key");
123
+ }
124
+ /**
125
+ * Schedule a manual backup event on the energy gateway.
126
+ * @param start_time ISO 8601 timestamp for when the backup event should start.
127
+ * @param duration_seconds Duration of the backup event in seconds.
128
+ * @param priority Priority level for the backup event. A string carries values beyond 2^53.
129
+ */
130
+ async schedule_backup_event(start_time, duration_seconds, priority) {
131
+ if (this.signing) return super.schedule_backup_event(start_time, duration_seconds, priority);
132
+ const scheduling_info = backupSchedulingInfo(start_time, duration_seconds, priority);
133
+ return this._post_route("schedule_backup_event", scheduling_info ? { scheduling_info } : {});
134
+ }
135
+ /** Cancel a scheduled backup event on the energy gateway. */
136
+ async cancel_backup_event() {
137
+ return this.signing ? super.cancel_backup_event() : this._post_route("cancel_backup_event", {});
138
+ }
139
+ /**
140
+ * Register an authorized client with the energy gateway. All arguments are
141
+ * optional: if omitted, the Teslemetry server fills in its own key details. A
142
+ * site with a signing key signs the request with that key when `public_key`
143
+ * is given. Without a public key the route registers Teslemetry's own key,
144
+ * which the client key cannot sign for.
145
+ * @param public_key Raw DER bytes in the encoding matching `key_type` (RSA PKCS#1 or ECC SPKI), which are base64-encoded, or an already base64-encoded string.
146
+ * @param description Human-readable description of the client.
147
+ * @param key_type The type of key being registered.
148
+ * @param authorized_client_type The authorized client type.
149
+ */
150
+ async add_authorized_client(public_key, description, key_type, authorized_client_type) {
151
+ if (this.signing && public_key != null) return super.add_authorized_client(public_key, description ?? void 0, key_type ?? void 0, authorized_client_type ?? void 0);
152
+ return this._post_route("add_authorized_client", {
153
+ public_key: public_key == null ? null : publicKeyBase64(public_key),
154
+ description,
155
+ key_type,
156
+ authorized_client_type
157
+ });
158
+ }
159
+ /** Get networking status from the gateway's Teslemetry route, including WiFi, Ethernet, and cellular details. */
160
+ async get_networking_status() {
161
+ return this._get_route("networking_status");
162
+ }
163
+ /**
164
+ * Discover the gateway's LAN IPv4 address, for pre-filling a local control
165
+ * host. See `parse_gateway_address` for the selection rule.
166
+ * @returns The address, or `null` when the reply is well-formed but no interface yields one.
167
+ * @throws InvalidResponse on a null body or an unrecognized reply.
168
+ * @throws EnergyGatewayUnreachable when Teslemetry cannot reach the gateway.
169
+ */
170
+ async find_gateway_address() {
171
+ return parse_gateway_address(await this.get_networking_status());
172
+ }
173
+ /**
174
+ * List authorized clients on the energy gateway. Teslemetry may return JSON
175
+ * `null` when no payload is available; that value is returned unchanged.
176
+ */
177
+ async list_authorized_clients() {
178
+ return this._get_route("authorized_clients");
179
+ }
180
+ /**
181
+ * List authorized clients on the energy gateway, parsed into a typed result.
182
+ * For the unparsed reply, use `list_authorized_clients`.
183
+ * @throws InvalidResponse on a null body or an unrecognized reply, rather than treating either as "no clients".
184
+ */
185
+ async find_authorized_clients() {
186
+ return parse_authorized_clients(await this.list_authorized_clients());
187
+ }
188
+ /**
189
+ * Remove an authorized client from the energy gateway. A site with a signing
190
+ * key signs the request with that key. Removal needs no physical presence
191
+ * proof, so any paired key can revoke every other key, including the owner's.
192
+ * @param public_key Raw DER bytes, which are base64-encoded, or an already base64-encoded string as `list_authorized_clients` reports it.
193
+ */
194
+ async remove_authorized_client(public_key) {
195
+ if (this.signing) return super.remove_authorized_client(public_key);
196
+ return this._post_route("remove_authorized_client", { public_key: publicKeyBase64(public_key) });
197
+ }
198
+ /**
199
+ * Request a timed battery power setpoint on the energy gateway. Without a
200
+ * signing key the Teslemetry route still sends unsigned and gets Tesla's HTTP
201
+ * 400 (2026-10-29). See `EnergySpecific.dispatch_battery_power`.
202
+ * @param power_watts Battery power in watts: positive discharges, negative charges.
203
+ * @param duration_seconds Whole seconds from 1 to 86400.
204
+ */
205
+ async dispatch_battery_power(power_watts, duration_seconds) {
206
+ if (this.signing) return super.dispatch_battery_power(power_watts, duration_seconds);
207
+ validate_dispatch_battery_power(power_watts, duration_seconds);
208
+ return this._post_route("dispatch_battery_power", {
209
+ power_watts,
210
+ duration_seconds
211
+ });
212
+ }
213
+ /**
214
+ * Release a `dispatch_battery_power` setpoint. Without a signing key the
215
+ * Teslemetry route still sends unsigned and gets Tesla's HTTP 400 (2026-10-29).
216
+ */
217
+ async resume_battery_operation() {
218
+ return this.signing ? super.resume_battery_operation() : this._post_route("resume_battery_operation");
219
+ }
220
+ /**
221
+ * Wait for a key already registered with `add_authorized_client` to finish
222
+ * pairing, polling `find_authorized_clients` until its entry is `VERIFIED`.
223
+ *
224
+ * The gateway's presence-proof window is about 9 minutes, and a breaker toggle
225
+ * confirmed a key in as little as 59 s, with no cloud auto-verify observed.
226
+ * A poll that fails counts as no signal.
227
+ * @param public_key The key as passed to `add_authorized_client`, raw DER bytes or base64.
228
+ * @param options.verify_by_use A signed local read. Once the cloud reports `VERIFIED`, one successful call confirms the key is usable. A failure means "not yet" and polling continues, because `VERIFIED` can be seen slightly ahead of local usability.
229
+ * @param options.timeout_ms Overall bound on the wait.
230
+ * @param options.poll_interval_ms Delay between polls.
231
+ * @param options.signal Aborting rejects with the signal's reason. An in-flight call is abandoned, not cancelled.
232
+ * @returns The matched entry, once paired and confirmed.
233
+ * @throws AuthorizedClientPairingTimedOut at once on `PENDING_VERIFICATION_TIMEOUT`. The registration is dead: call `add_authorized_client` again with the same key, which resets the window, then wait again.
234
+ * @throws AuthorizedClientWaitExpired when `timeout_ms` elapses first. The registration is still alive: wait again.
235
+ */
236
+ async wait_until_paired(public_key, options = {}) {
237
+ const { verify_by_use, timeout_ms = DEFAULT_PAIRING_TIMEOUT, poll_interval_ms = DEFAULT_PAIRING_POLL_INTERVAL, signal } = options;
238
+ const target = publicKeyBase64(public_key);
239
+ const deadline = performance.now() + timeout_ms;
240
+ let last_state = null;
241
+ const expired = () => new AuthorizedClientWaitExpired({
242
+ public_key: target,
243
+ state: last_state
244
+ });
245
+ /** Run one step under the deadline. A failed step is `undefined`, unless the wait was aborted. */
246
+ const attempt = async (step) => {
247
+ const result = await settleWithin(step, deadline - performance.now(), signal).catch((error) => {
248
+ if (signal?.aborted) throw error;
249
+ });
250
+ if (result === EXPIRED) throw expired();
251
+ return result;
252
+ };
253
+ while (true) {
254
+ signal?.throwIfAborted();
255
+ if (performance.now() >= deadline) throw expired();
256
+ const match = (await attempt(() => this.find_authorized_clients()))?.clients.find((client) => client.public_key === target);
257
+ if (match !== void 0) {
258
+ last_state = match.state;
259
+ if (match.state === AuthorizedClientState.PENDING_VERIFICATION_TIMEOUT) throw new AuthorizedClientPairingTimedOut({
260
+ public_key: target,
261
+ state: match.state
262
+ });
263
+ if (match.state === AuthorizedClientState.VERIFIED) {
264
+ if (verify_by_use === void 0) return match;
265
+ if (performance.now() >= deadline) throw expired();
266
+ if (await attempt(async () => (await verify_by_use(), true)) === true) return match;
267
+ }
268
+ }
269
+ const remaining = deadline - performance.now();
270
+ if (remaining <= 0) throw expired();
271
+ await settleWithin(() => new Promise(() => {}), Math.min(poll_interval_ms, remaining), signal);
272
+ }
273
+ }
274
+ };
275
+ /** Creates and holds Teslemetry energy sites. */
276
+ var TeslemetryEnergySites = class extends Energy {
277
+ static Site = TeslemetryEnergySite;
278
+ create(energy_site_id, private_key, din) {
279
+ return super.create(energy_site_id, private_key, din);
280
+ }
281
+ };
282
+ export { DEFAULT_PAIRING_POLL_INTERVAL, DEFAULT_PAIRING_TIMEOUT, TeslemetryEnergySite, TeslemetryEnergySites, parse_authorized_clients, parse_gateway_address };
@@ -0,0 +1,407 @@
1
+ const require_const = require("./const.cjs");
2
+ const require_exceptions = require("./exceptions.cjs");
3
+ const require_vehicle = require("./vehicle.cjs");
4
+ const require_vehiclespecific = require("./vehiclespecific.cjs");
5
+ // src/teslemetryvehicle.ts
6
+ /** A Teslemetry vehicle: the Fleet REST commands plus Teslemetry's `custom_command` routes. */
7
+ var TeslemetryVehicle = class extends require_vehiclespecific.default {
8
+ _request(method, path, params, json) {
9
+ return this.parent.parent._request(method, path, params, json);
10
+ }
11
+ _custom_command(name, json) {
12
+ return this._request("POST", `api/1/vehicles/${this.vin}/custom_command/${name}`, null, json);
13
+ }
14
+ /**
15
+ * Get or set Auto mode.
16
+ * @deprecated Python marks this for removal in a future release.
17
+ * @param value `true` enables it, `false` disables it, omit to read it.
18
+ */
19
+ async server_side_polling(value) {
20
+ this.parent.parent._log("This method is deprecated and will be removed in a future release.");
21
+ return this.parent.parent.server_side_polling(this.vin, value);
22
+ }
23
+ /** Force a refresh of the vehicle data. */
24
+ async data_refresh() {
25
+ return this.parent.parent.vehicle_data_refresh(this.vin);
26
+ }
27
+ /** Performs a no-op on the vehicle. */
28
+ async ping() {
29
+ return this._custom_command("ping");
30
+ }
31
+ /**
32
+ * Open, close, move and stop the vehicle's doors, frunk, trunk, charge port and tonneau.
33
+ * @example await vehicle.closure({ front_trunk: ClosureState.OPEN });
34
+ */
35
+ async closure(options = {}) {
36
+ const keys = [
37
+ ["frontDriverDoor", options.front_driver_door],
38
+ ["frontPassengerDoor", options.front_passenger_door],
39
+ ["rearDriverDoor", options.rear_driver_door],
40
+ ["rearPassengerDoor", options.rear_passenger_door],
41
+ ["rearTrunk", options.rear_trunk],
42
+ ["frontTrunk", options.front_trunk],
43
+ ["chargePort", options.charge_port],
44
+ ["tonneau", options.tonneau]
45
+ ];
46
+ return this._custom_command("closure", Object.fromEntries(keys.filter(([, state]) => state !== require_const.ClosureState.NONE)));
47
+ }
48
+ /** Opens the rear trunk; unlike `actuate_trunk`, never closes it. */
49
+ async open_rear_trunk() {
50
+ return this.closure({ rear_trunk: require_const.ClosureState.OPEN });
51
+ }
52
+ /** Closes the rear trunk; unlike `actuate_trunk`, never opens it. */
53
+ async close_rear_trunk() {
54
+ return this.closure({ rear_trunk: require_const.ClosureState.CLOSE });
55
+ }
56
+ /** Opens the tonneau cover. */
57
+ async open_tonneau() {
58
+ return this.closure({ tonneau: require_const.ClosureState.OPEN });
59
+ }
60
+ /** Closes the tonneau cover. */
61
+ async close_tonneau() {
62
+ return this.closure({ tonneau: require_const.ClosureState.CLOSE });
63
+ }
64
+ /** Stops the tonneau cover. */
65
+ async stop_tonneau() {
66
+ return this.closure({ tonneau: require_const.ClosureState.STOP });
67
+ }
68
+ /**
69
+ * Sets multiple seat heaters at once.
70
+ * @example await vehicle.seat_heater({ front_left: SeatHeaterLevel.HIGH });
71
+ */
72
+ async seat_heater(options = {}) {
73
+ return this._custom_command("seat_heater", {
74
+ frontLeft: options.front_left,
75
+ frontRight: options.front_right,
76
+ rearLeft: options.rear_left,
77
+ rearRight: options.rear_right,
78
+ rearLeftBack: options.rear_left_back,
79
+ rearCenter: options.rear_center,
80
+ rearRightBack: options.rear_right_back,
81
+ thirdRowLeft: options.third_row_left,
82
+ thirdRowRight: options.third_row_right
83
+ });
84
+ }
85
+ /**
86
+ * Enable or disable charging on solar, and set its charge limits.
87
+ * @param enabled
88
+ * @param lower_charge_limit 0 to `upper_charge_limit`
89
+ * @param upper_charge_limit `lower_charge_limit` to 100
90
+ */
91
+ async charge_on_solar(enabled, lower_charge_limit, upper_charge_limit) {
92
+ return this._custom_command("charge_on_solar", {
93
+ enabled,
94
+ lowerChargeLimit: lower_charge_limit,
95
+ upperChargeLimit: upper_charge_limit
96
+ });
97
+ }
98
+ /** Gets the current charge-on-solar settings. */
99
+ async get_charge_on_solar() {
100
+ return this._request("GET", `api/1/vehicles/${this.vin}/custom_command/charge_on_solar`);
101
+ }
102
+ /** Save the last 10 minutes of dashcam footage. */
103
+ async dashcam_save() {
104
+ return this._custom_command("dashcam_save");
105
+ }
106
+ /**
107
+ * Play a supported video URL in the vehicle.
108
+ * @param url Video URL, e.g. a YouTube URL.
109
+ */
110
+ async play_video(url) {
111
+ return this._custom_command("play_video", { url });
112
+ }
113
+ /** Stop the currently playing light show. */
114
+ async stop_light_show() {
115
+ return this._custom_command("stop_light_show");
116
+ }
117
+ /**
118
+ * Start a light show on the vehicle.
119
+ * @param show_index Index of the light show to play.
120
+ * @param start_time Start time in milliseconds since epoch.
121
+ * @param volume Volume level for the light show.
122
+ * @param dance_moves Enable dance moves during the light show.
123
+ */
124
+ async start_light_show(show_index, start_time, volume, dance_moves) {
125
+ return this._custom_command("start_light_show", {
126
+ show_index,
127
+ start_time,
128
+ volume,
129
+ dance_moves
130
+ });
131
+ }
132
+ /**
133
+ * Deactivates PIN to Drive.
134
+ * @param pin 4-digit PIN to clear.
135
+ */
136
+ async clear_pin_to_drive(pin) {
137
+ return this._custom_command("clear_pin_to_drive", { pin });
138
+ }
139
+ /** Remove all impermanent keys from the vehicle. */
140
+ async remove_key() {
141
+ return this._custom_command("remove_key");
142
+ }
143
+ /**
144
+ * Sets the front zone light level. Named to match the signed sibling for router failover.
145
+ * @param level 0 off, 1 low, 2 medium, 3 high.
146
+ */
147
+ async set_front_zone_lights(level) {
148
+ return this._custom_command("front_zone_light", { level });
149
+ }
150
+ /**
151
+ * Sets the rear zone light level. Named to match the signed sibling for router failover.
152
+ * @param level 0 off, 1 low, 2 medium, 3 high.
153
+ */
154
+ async set_rear_zone_lights(level) {
155
+ return this._custom_command("rear_zone_light", { level });
156
+ }
157
+ /**
158
+ * Sets outlets on or off.
159
+ * @param request 0 off, 1 cabin and bed, 2 cabin.
160
+ */
161
+ async set_outlets(request) {
162
+ return this._custom_command("set_outlets", { request });
163
+ }
164
+ /** Sets the outlet SOC limit percentage. */
165
+ async set_outlet_soc_limit(percent) {
166
+ return this._custom_command("set_outlet_soc_limit", { percent });
167
+ }
168
+ /**
169
+ * Sets power feed on or off.
170
+ * @param request 0 off, 1 feed 1, 2 feed 2, 3 both.
171
+ */
172
+ async set_power_feed(request) {
173
+ return this._custom_command("set_power_feed", { request });
174
+ }
175
+ /** Sets the power feed SOC limit percentage. */
176
+ async set_power_feed_soc_limit(percent) {
177
+ return this._custom_command("set_power_feed_soc_limit", { percent });
178
+ }
179
+ /** Sets the lightbar brightness. */
180
+ async set_lightbar_brightness(brightness) {
181
+ return this._custom_command("set_lightbar_brightness", { brightness });
182
+ }
183
+ /** Enables or disables the lightbar middle light. */
184
+ async set_lightbar_middle(on) {
185
+ return this._custom_command("set_lightbar_middle", { on });
186
+ }
187
+ /** Enables or disables the ditch lights. */
188
+ async set_lightbar_ditch(on) {
189
+ return this._custom_command("set_lightbar_ditch", { on });
190
+ }
191
+ /** Starts or stops the trailer light test. */
192
+ async set_trailer_light_test(on) {
193
+ return this._custom_command("set_trailer_light_test", { on });
194
+ }
195
+ /** Sets the truck bed light auto state. */
196
+ async set_truck_bed_light_auto(on) {
197
+ return this._custom_command("set_truck_bed_light_auto", { on });
198
+ }
199
+ /** Sets the truck bed light brightness. */
200
+ async set_truck_bed_light_brightness(brightness) {
201
+ return this._custom_command("set_truck_bed_light_brightness", { brightness });
202
+ }
203
+ /** Enables or disables the Powershare feature. */
204
+ async set_powershare_feature(on) {
205
+ return this._custom_command("set_powershare_feature", { on });
206
+ }
207
+ /** Enables or disables an active Powershare session. */
208
+ async set_powershare_request(on) {
209
+ return this._custom_command("set_powershare_request", { on });
210
+ }
211
+ /** Sets the Powershare discharge limit percentage. */
212
+ async set_powershare_discharge_limit(percent) {
213
+ return this._custom_command("set_powershare_discharge_limit", { percent });
214
+ }
215
+ /** Sets HVAC recirculation on or off. Named to match the signed sibling for router failover. */
216
+ async set_recirculation(on) {
217
+ return this._custom_command("hvac_recirculation", { on });
218
+ }
219
+ /** Enables or disables tent mode. */
220
+ async set_tent_mode(on) {
221
+ return this._custom_command("set_tent_mode", { on });
222
+ }
223
+ /**
224
+ * Sets the suspension level.
225
+ * @param level 1 entry, 2 low, 3 medium, 4 high, 5 very high, 6 extract.
226
+ */
227
+ async set_suspension_level(level) {
228
+ return this._custom_command("set_suspension_level", { level });
229
+ }
230
+ /** Turns Low Power mode on or off, reducing standby consumption while parked. */
231
+ async set_low_power_mode(on) {
232
+ return this._custom_command("set_low_power_mode", { on });
233
+ }
234
+ /** Turns Keep Accessory Power mode on or off, keeping 12V accessory power available while parked. */
235
+ async set_keep_accessory_power_mode(on) {
236
+ return this._custom_command("set_keep_accessory_power_mode", { on });
237
+ }
238
+ /** Sets the displayed temperature unit. */
239
+ async set_temperature_unit(unit) {
240
+ return this._custom_command("set_temperature_unit", { unit });
241
+ }
242
+ /** Sets the displayed distance unit. */
243
+ async set_distance_unit(unit) {
244
+ return this._custom_command("set_distance_unit", { unit });
245
+ }
246
+ /** Sets the displayed clock format (12h or 24h). */
247
+ async set_time_display_format(format) {
248
+ return this._custom_command("set_time_display_format", { format });
249
+ }
250
+ /** Sets the displayed tire pressure unit. */
251
+ async set_tire_pressure_unit(unit) {
252
+ return this._custom_command("set_tire_pressure_unit", { unit });
253
+ }
254
+ /** Sets the displayed energy unit (percentage or distance). */
255
+ async set_energy_display_format(format) {
256
+ return this._custom_command("set_energy_display_format", { format });
257
+ }
258
+ /** Activates or deactivates parental controls with a PIN. */
259
+ async parental_controls(activate, pin) {
260
+ return this._custom_command("parental_controls", {
261
+ activate,
262
+ pin
263
+ });
264
+ }
265
+ /** Clears the parental controls PIN. */
266
+ async parental_controls_clear_pin(pin) {
267
+ return this._custom_command("parental_controls_clear_pin", { pin });
268
+ }
269
+ /** Clears the parental controls PIN as admin (fleet manager or owner). */
270
+ async parental_controls_clear_pin_admin() {
271
+ return this._custom_command("parental_controls_clear_pin_admin");
272
+ }
273
+ /**
274
+ * Enables or disables a parental controls setting.
275
+ * @param setting 1 speed limit, 2 acceleration, 3 safety features, 4 curfew.
276
+ * @param enable
277
+ */
278
+ async parental_controls_enable_setting(setting, enable) {
279
+ return this._custom_command("parental_controls_enable_setting", {
280
+ setting,
281
+ enable
282
+ });
283
+ }
284
+ /** Sets the parental controls speed limit. */
285
+ async parental_controls_set_speed_limit(limit_mph) {
286
+ return this._custom_command("parental_controls_set_speed_limit", { limit_mph });
287
+ }
288
+ /**
289
+ * Navigates to coordinates with a named destination. Named to match the signed sibling for router failover.
290
+ * @param lat
291
+ * @param lon
292
+ * @param destination
293
+ * @param order Sent as given: on tesla-protocol 6, `RemoteNavTripOrder_E` is 0 replace, 1 prepend, 2 append.
294
+ */
295
+ async navigation_gps_destination_request(lat, lon, destination, order) {
296
+ return this._custom_command("navigation_gps_destination", {
297
+ lat,
298
+ lon,
299
+ destination,
300
+ order
301
+ });
302
+ }
303
+ /** Auto-secures the vehicle: locks it, closes windows, and so on. */
304
+ async auto_secure_vehicle() {
305
+ return this._custom_command("auto_secure_vehicle");
306
+ }
307
+ /** Cancels a State of Health test. */
308
+ async cancel_soh_test() {
309
+ return this._custom_command("cancel_soh_test");
310
+ }
311
+ /** Returns the charging sites near the vehicle's current location. */
312
+ async get_nearby_charging_sites(count, radius, detail) {
313
+ return this._custom_command("get_nearby_charging_sites", {
314
+ count,
315
+ radius,
316
+ detail
317
+ });
318
+ }
319
+ /** Gets the current time-of-use rate tariff schedule. */
320
+ async get_rate_tariff() {
321
+ return this._custom_command("get_rate_tariff");
322
+ }
323
+ /**
324
+ * Triggers a time-of-use rate tariff update, with no schedule payload.
325
+ * Deliberately not named `set_rate_tariff`: the signed sibling takes a
326
+ * required schedule, which a router failing over here would silently drop.
327
+ */
328
+ async trigger_rate_tariff_update() {
329
+ return this._custom_command("set_rate_tariff");
330
+ }
331
+ /**
332
+ * Gets a snapshot of cached vehicle telemetry.
333
+ * @param topics Topics to include.
334
+ * @param cursor Return only what changed since this cursor.
335
+ */
336
+ async telemetry(topics, cursor) {
337
+ return this._request("GET", `api/1/vehicles/${this.vin}/telemetry`, {
338
+ topics: joinTopics(topics),
339
+ cursor
340
+ });
341
+ }
342
+ /** Creates a cursor-aware reader of this vehicle's cached telemetry. */
343
+ create_telemetry_tracker(topics) {
344
+ return new TeslemetryVehicleTelemetry(this, topics);
345
+ }
346
+ };
347
+ function joinTopics(topics) {
348
+ return Array.isArray(topics) ? topics.join(";") : topics;
349
+ }
350
+ /** Python truthiness, which an update's fields are tested by: an empty object or list counts as absent. */
351
+ function hasContent(value) {
352
+ return value !== null && typeof value === "object" ? Object.keys(value).length > 0 : !!value;
353
+ }
354
+ /** A cursor-aware vehicle telemetry reader, keeping a snapshot merged from each update. */
355
+ var TeslemetryVehicleTelemetry = class {
356
+ /** The cursor the next `update()` sends. */
357
+ cursor = 0;
358
+ /** Everything received so far. `data` merges field by field; other topics are replaced whole. */
359
+ snapshot = {};
360
+ _parent;
361
+ _topics;
362
+ constructor(parent, topics) {
363
+ this._parent = parent;
364
+ this._topics = joinTopics(topics);
365
+ }
366
+ /**
367
+ * Get the changes since the last update, and merge them into `snapshot`.
368
+ * @returns The update's `response`.
369
+ * @throws InvalidResponse when the update has an empty `response`, or no `cursor`.
370
+ */
371
+ async update() {
372
+ const response = (await this._parent.telemetry(this._topics, this.cursor))?.response;
373
+ if (!hasContent(response)) throw new require_exceptions.InvalidResponse("No response from telemetry");
374
+ if (typeof response !== "object" || !("cursor" in response)) throw new require_exceptions.InvalidResponse("Telemetry response did not contain a cursor");
375
+ this.cursor = response.cursor;
376
+ if (hasContent(response.state)) this.snapshot.state = response.state;
377
+ if (hasContent(response.data)) this.snapshot.data = {
378
+ ...this.snapshot.data,
379
+ ...response.data
380
+ };
381
+ for (const topic of [
382
+ "connectivity",
383
+ "alerts",
384
+ "errors",
385
+ "vehicle_data"
386
+ ]) if (hasContent(response[topic])) this.snapshot[topic] = response[topic];
387
+ return response;
388
+ }
389
+ };
390
+ /** Creates `TeslemetryVehicle`s. Teslemetry cannot use the Fleet API directly, so `create` is the only factory. */
391
+ var TeslemetryVehicles = class extends require_vehicle.default {
392
+ /** Class `create` instantiates; a subclass overrides it to supply its own vehicle type. */
393
+ static Vehicle = TeslemetryVehicle;
394
+ createFleet = void 0;
395
+ createSigned = void 0;
396
+ specific = void 0;
397
+ specificSigned = void 0;
398
+ /** Creates a vehicle, and records it by VIN. */
399
+ create(vin) {
400
+ const vehicle = new this.constructor.Vehicle(this, vin);
401
+ this.byVin.set(vin, vehicle);
402
+ return vehicle;
403
+ }
404
+ };
405
+ exports.TeslemetryVehicle = TeslemetryVehicle;
406
+ exports.TeslemetryVehicleTelemetry = TeslemetryVehicleTelemetry;
407
+ exports.TeslemetryVehicles = TeslemetryVehicles;