tesla-fleet-api 0.4.0 → 0.6.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 (215) hide show
  1. package/README.md +269 -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 +96 -0
  14. package/dist/bluetooth/constants.d.cts +68 -0
  15. package/dist/bluetooth/constants.d.mts +68 -0
  16. package/dist/bluetooth/constants.mjs +70 -0
  17. package/dist/bluetooth/framing.cjs +125 -0
  18. package/dist/bluetooth/framing.d.cts +45 -0
  19. package/dist/bluetooth/framing.d.mts +45 -0
  20. package/dist/bluetooth/framing.mjs +122 -0
  21. package/dist/bluetooth/homey.cjs +165 -0
  22. package/dist/bluetooth/homey.d.cts +84 -0
  23. package/dist/bluetooth/homey.d.mts +84 -0
  24. package/dist/bluetooth/homey.mjs +159 -0
  25. package/dist/bluetooth/names.cjs +24 -0
  26. package/dist/bluetooth/names.d.cts +10 -0
  27. package/dist/bluetooth/names.d.mts +10 -0
  28. package/dist/bluetooth/names.mjs +20 -0
  29. package/dist/bluetooth/noble.cjs +178 -0
  30. package/dist/bluetooth/noble.d.cts +58 -0
  31. package/dist/bluetooth/noble.d.mts +58 -0
  32. package/dist/bluetooth/noble.mjs +177 -0
  33. package/dist/bluetooth/streamglue.cjs +157 -0
  34. package/dist/bluetooth/streamglue.d.cts +37 -0
  35. package/dist/bluetooth/streamglue.d.mts +37 -0
  36. package/dist/bluetooth/streamglue.mjs +154 -0
  37. package/dist/bluetooth/teslabluetooth.cjs +67 -0
  38. package/dist/bluetooth/teslabluetooth.d.cts +41 -0
  39. package/dist/bluetooth/teslabluetooth.d.mts +41 -0
  40. package/dist/bluetooth/teslabluetooth.mjs +66 -0
  41. package/dist/bluetooth/transport.cjs +10 -0
  42. package/dist/bluetooth/transport.d.cts +97 -0
  43. package/dist/bluetooth/transport.d.mts +97 -0
  44. package/dist/bluetooth/transport.mjs +10 -0
  45. package/dist/bluetooth/vehiclebluetooth.cjs +1272 -0
  46. package/dist/bluetooth/vehiclebluetooth.d.cts +495 -0
  47. package/dist/bluetooth/vehiclebluetooth.d.mts +495 -0
  48. package/dist/bluetooth/vehiclebluetooth.mjs +1270 -0
  49. package/dist/bluetooth/verify.cjs +93 -0
  50. package/dist/bluetooth/verify.d.cts +35 -0
  51. package/dist/bluetooth/verify.d.mts +35 -0
  52. package/dist/bluetooth/verify.mjs +91 -0
  53. package/dist/bluetooth.cjs +52 -0
  54. package/dist/bluetooth.d.cts +10 -0
  55. package/dist/bluetooth.d.mts +10 -0
  56. package/dist/bluetooth.mjs +9 -0
  57. package/dist/commands.cjs +1080 -85
  58. package/dist/commands.d.cts +585 -54
  59. package/dist/commands.d.mts +583 -51
  60. package/dist/commands.mjs +1084 -93
  61. package/dist/const.cjs +22 -0
  62. package/dist/const.d.cts +21 -0
  63. package/dist/const.d.mts +21 -0
  64. package/dist/const.mjs +20 -1
  65. package/dist/energy.cjs +24 -19
  66. package/dist/energy.d.cts +21 -15
  67. package/dist/energy.d.mts +21 -15
  68. package/dist/energy.mjs +24 -19
  69. package/dist/energyisland.cjs +38 -0
  70. package/dist/energyisland.mjs +37 -0
  71. package/dist/energyparsers.cjs +118 -0
  72. package/dist/energyparsers.d.cts +39 -0
  73. package/dist/energyparsers.d.mts +39 -0
  74. package/dist/energyparsers.mjs +116 -0
  75. package/dist/energyspecific.cjs +375 -17
  76. package/dist/energyspecific.d.cts +199 -19
  77. package/dist/energyspecific.d.mts +197 -16
  78. package/dist/energyspecific.mjs +370 -18
  79. package/dist/exceptions.cjs +3 -2
  80. package/dist/exceptions.d.cts +3 -2
  81. package/dist/exceptions.d.mts +3 -2
  82. package/dist/exceptions.mjs +3 -2
  83. package/dist/funnel.cjs +297 -0
  84. package/dist/funnel.d.cts +118 -0
  85. package/dist/funnel.d.mts +118 -0
  86. package/dist/funnel.mjs +292 -0
  87. package/dist/index.cjs +61 -0
  88. package/dist/index.d.cts +22 -8
  89. package/dist/index.d.mts +22 -8
  90. package/dist/index.mjs +19 -6
  91. package/dist/log.cjs +75 -0
  92. package/dist/log.d.cts +4 -0
  93. package/dist/log.d.mts +4 -0
  94. package/dist/log.mjs +71 -0
  95. package/dist/oauth.cjs +143 -0
  96. package/dist/oauth.d.cts +69 -0
  97. package/dist/oauth.d.mts +69 -0
  98. package/dist/oauth.mjs +143 -0
  99. package/dist/powerwall/client.cjs +818 -0
  100. package/dist/powerwall/client.d.cts +297 -0
  101. package/dist/powerwall/client.d.mts +297 -0
  102. package/dist/powerwall/client.mjs +807 -0
  103. package/dist/powerwall/components.cjs +188 -0
  104. package/dist/powerwall/components.d.cts +45 -0
  105. package/dist/powerwall/components.d.mts +45 -0
  106. package/dist/powerwall/components.mjs +182 -0
  107. package/dist/powerwall/energysite.cjs +216 -0
  108. package/dist/powerwall/energysite.d.cts +104 -0
  109. package/dist/powerwall/energysite.d.mts +104 -0
  110. package/dist/powerwall/energysite.mjs +214 -0
  111. package/dist/powerwall/exceptions.cjs +59 -0
  112. package/dist/powerwall/exceptions.d.cts +38 -0
  113. package/dist/powerwall/exceptions.d.mts +38 -0
  114. package/dist/powerwall/exceptions.mjs +51 -0
  115. package/dist/powerwall/gateway.cjs +102 -0
  116. package/dist/powerwall/gateway.d.cts +57 -0
  117. package/dist/powerwall/gateway.d.mts +57 -0
  118. package/dist/powerwall/gateway.mjs +101 -0
  119. package/dist/powerwall/merge.cjs +163 -0
  120. package/dist/powerwall/merge.d.cts +63 -0
  121. package/dist/powerwall/merge.d.mts +63 -0
  122. package/dist/powerwall/merge.mjs +159 -0
  123. package/dist/powerwall/models.d.cts +95 -0
  124. package/dist/powerwall/models.d.mts +95 -0
  125. package/dist/powerwall/queries.cjs +17 -0
  126. package/dist/powerwall/queries.mjs +10 -0
  127. package/dist/powerwall/transport.cjs +232 -0
  128. package/dist/powerwall/transport.mjs +227 -0
  129. package/dist/powerwall.cjs +47 -0
  130. package/dist/powerwall.d.cts +9 -0
  131. package/dist/powerwall.d.mts +9 -0
  132. package/dist/powerwall.mjs +8 -0
  133. package/dist/router/base.cjs +203 -0
  134. package/dist/router/base.d.cts +112 -0
  135. package/dist/router/base.d.mts +112 -0
  136. package/dist/router/base.mjs +200 -0
  137. package/dist/router/energysite.cjs +65 -0
  138. package/dist/router/energysite.d.cts +47 -0
  139. package/dist/router/energysite.d.mts +47 -0
  140. package/dist/router/energysite.mjs +60 -0
  141. package/dist/router.cjs +13 -0
  142. package/dist/router.d.cts +3 -1
  143. package/dist/router.d.mts +3 -1
  144. package/dist/router.mjs +3 -1
  145. package/dist/signing/crypto.cjs +28 -0
  146. package/dist/signing/crypto.mjs +26 -1
  147. package/dist/signing/energy.cjs +123 -0
  148. package/dist/signing/energy.d.cts +45 -0
  149. package/dist/signing/energy.d.mts +45 -0
  150. package/dist/signing/energy.mjs +120 -0
  151. package/dist/signing/jws.cjs +229 -0
  152. package/dist/signing/jws.d.cts +45 -0
  153. package/dist/signing/jws.d.mts +45 -0
  154. package/dist/signing/jws.mjs +211 -0
  155. package/dist/signing/keys.cjs +191 -0
  156. package/dist/signing/keys.d.cts +21 -0
  157. package/dist/signing/keys.d.mts +21 -0
  158. package/dist/signing/keys.mjs +182 -0
  159. package/dist/signing/session.cjs +104 -30
  160. package/dist/signing/session.d.cts +60 -23
  161. package/dist/signing/session.d.mts +60 -23
  162. package/dist/signing/session.mjs +105 -31
  163. package/dist/tariff.cjs +27 -0
  164. package/dist/tariff.d.cts +11 -0
  165. package/dist/tariff.d.mts +11 -0
  166. package/dist/tariff.mjs +27 -1
  167. package/dist/teslafleetapi.cjs +256 -55
  168. package/dist/teslafleetapi.d.cts +110 -14
  169. package/dist/teslafleetapi.d.mts +110 -14
  170. package/dist/teslafleetapi.mjs +258 -57
  171. package/dist/teslemetry.cjs +152 -5
  172. package/dist/teslemetry.d.cts +95 -5
  173. package/dist/teslemetry.d.mts +93 -3
  174. package/dist/teslemetry.mjs +151 -6
  175. package/dist/teslemetryenergy.cjs +287 -0
  176. package/dist/teslemetryenergy.d.cts +154 -0
  177. package/dist/teslemetryenergy.d.mts +154 -0
  178. package/dist/teslemetryenergy.mjs +282 -0
  179. package/dist/teslemetryvehicle.cjs +407 -0
  180. package/dist/teslemetryvehicle.d.cts +238 -0
  181. package/dist/teslemetryvehicle.d.mts +238 -0
  182. package/dist/teslemetryvehicle.mjs +405 -0
  183. package/dist/tessie.cjs +42 -5
  184. package/dist/tessie.d.cts +51 -5
  185. package/dist/tessie.d.mts +49 -3
  186. package/dist/tessie.mjs +42 -5
  187. package/dist/tessievehicle.cjs +514 -0
  188. package/dist/tessievehicle.d.cts +333 -0
  189. package/dist/tessievehicle.d.mts +333 -0
  190. package/dist/tessievehicle.mjs +513 -0
  191. package/dist/types/live_status.d.cts +16 -16
  192. package/dist/types/live_status.d.mts +16 -16
  193. package/dist/types/responses.d.cts +5 -1
  194. package/dist/types/responses.d.mts +5 -1
  195. package/dist/types/vehicle.d.cts +7 -1
  196. package/dist/types/vehicle.d.mts +7 -1
  197. package/dist/types.d.cts +3 -3
  198. package/dist/types.d.mts +3 -3
  199. package/dist/util.cjs +41 -0
  200. package/dist/util.d.cts +18 -0
  201. package/dist/util.d.mts +18 -0
  202. package/dist/util.mjs +40 -0
  203. package/dist/vehicle.cjs +79 -36
  204. package/dist/vehicle.d.cts +42 -26
  205. package/dist/vehicle.d.mts +42 -26
  206. package/dist/vehicle.mjs +80 -37
  207. package/dist/vehiclesigned.cjs +20 -5
  208. package/dist/vehiclesigned.d.cts +14 -5
  209. package/dist/vehiclesigned.d.mts +14 -5
  210. package/dist/vehiclesigned.mjs +20 -5
  211. package/dist/vehiclespecific.cjs +126 -15
  212. package/dist/vehiclespecific.d.cts +82 -18
  213. package/dist/vehiclespecific.d.mts +82 -18
  214. package/dist/vehiclespecific.mjs +126 -15
  215. package/package.json +56 -3
@@ -1,10 +1,357 @@
1
+ import { AuthorizedClientKeyType, AuthorizedClientType, EnergyDeviceIdentifierType, EnergyIslandMode } from "./const.mjs";
2
+ import { island_mode_rejection, validate_island_mode } from "./energyisland.mjs";
3
+ import { DeviceUnexpectedResponse, InvalidCommand, SignedCommandRequired } from "./exceptions.mjs";
4
+ import { EnergyDeviceSigner } from "./signing/energy.mjs";
5
+ import { MessageEnvelope } from "@teslemetry/tesla-protocol/energy_device/transport";
1
6
  // src/energyspecific.ts
7
+ /**
8
+ * The only device commands Tesla's cloud accepts unsigned, as
9
+ * `category.command`: the reads a gateway answers before any key is paired.
10
+ * Unsigned, every other command was refused with HTTP 400 live on a Powerwall 3
11
+ * (2026-10-29), and signed, each one reached the gateway. A site with a signing
12
+ * key signs everything else.
13
+ */
14
+ const UNSIGNED_DEVICE_COMMANDS = /* @__PURE__ */ new Set([
15
+ "common.get_system_info_request",
16
+ "common.get_networking_status_request",
17
+ "authorization.get_signed_commands_public_key_request",
18
+ "authorization.list_authorized_clients_request"
19
+ ]);
20
+ /** Upper bound on a `dispatch_battery_power` setpoint duration (one day). */
21
+ const MAX_DISPATCH_DURATION_SECONDS = 86400;
22
+ /**
23
+ * Check `dispatch_battery_power` arguments before any request.
24
+ * @throws TypeError unless both are numbers.
25
+ * @throws RangeError on a non-finite power or a duration that is not a whole number from 1 to `MAX_DISPATCH_DURATION_SECONDS`.
26
+ */
27
+ function validate_dispatch_battery_power(power_watts, duration_seconds) {
28
+ if (typeof power_watts !== "number") throw new TypeError("power_watts must be a number");
29
+ if (!Number.isFinite(power_watts)) throw new RangeError("power_watts must be a finite number");
30
+ if (typeof duration_seconds !== "number") throw new TypeError("duration_seconds must be a number");
31
+ if (!Number.isInteger(duration_seconds) || duration_seconds < 1 || duration_seconds > 86400) throw new RangeError(`duration_seconds must be an integer from 1 to ${MAX_DISPATCH_DURATION_SECONDS}`);
32
+ }
33
+ /** `google.rpc.Code` UNIMPLEMENTED: the gateway firmware has no handler. */
34
+ const UNIMPLEMENTED = 12;
35
+ /** The `MessageEnvelope` in a `device_command` reply, or `null` for a reply without decodable envelope bytes. */
36
+ function replyEnvelope(result) {
37
+ const response = result?.response;
38
+ if (typeof response !== "object" || response === null || Array.isArray(response)) return null;
39
+ const encoded = response.message_envelope_as_bytes;
40
+ if (typeof encoded !== "string" || encoded.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded)) return null;
41
+ try {
42
+ return MessageEnvelope.decode(Buffer.from(encoded, "base64"));
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+ /**
48
+ * Raise if a `device_command` reply holds `common.error_response`.
49
+ *
50
+ * The gateway refuses a command inside an HTTP 200 reply, for example code 11
51
+ * (OUT_OF_RANGE) on a dispatch above its power limit. Code 12 raises
52
+ * `InvalidCommand`; any other code raises `DeviceUnexpectedResponse`. Both
53
+ * carry `{code, message}` as `data`. A reply without decodable envelope bytes
54
+ * is left alone.
55
+ */
56
+ function _raise_on_error_response(result) {
57
+ const error = replyEnvelope(result)?.common?.errorResponse;
58
+ if (error === void 0) return;
59
+ const data = {
60
+ code: error.status?.code ?? 0,
61
+ message: error.status?.message ?? ""
62
+ };
63
+ if (data.code === UNIMPLEMENTED) throw new InvalidCommand(data);
64
+ throw new DeviceUnexpectedResponse(data);
65
+ }
66
+ /**
67
+ * Raise if a `set_island_mode` reply reports a `REJECTED_*` result, on the same
68
+ * codes as the LAN client. A signed reply carries it in the envelope, and
69
+ * Teslemetry's route decodes that and answers `{ response: { result } }`.
70
+ * @throws DeviceUnexpectedResponse with `{code, message}` as `data`, `message` being the result's name.
71
+ */
72
+ function raise_on_island_rejection(result) {
73
+ const reported = replyEnvelope(result)?.teg?.setIslandModeResponse?.result ?? result?.response?.result;
74
+ const rejection = island_mode_rejection(reported);
75
+ if (rejection) throw new DeviceUnexpectedResponse({
76
+ code: rejection.code,
77
+ message: rejection.name
78
+ });
79
+ }
80
+ /** A public key as the gateway reports it: raw DER bytes are base64-encoded, and a string passes as it is. */
81
+ function publicKeyBase64(public_key) {
82
+ return typeof public_key === "string" ? public_key : Buffer.from(public_key).toString("base64");
83
+ }
84
+ /** The `scheduling_info` holding only the given fields, or `null` when none was given. */
85
+ function backupSchedulingInfo(start_time, duration_seconds, priority) {
86
+ const given = Object.entries({
87
+ start_time,
88
+ duration_seconds,
89
+ priority
90
+ }).filter(([, value]) => value != null);
91
+ return given.length ? Object.fromEntries(given) : null;
92
+ }
2
93
  var EnergySpecific = class {
3
94
  parent;
4
95
  energy_site_id;
5
- constructor(parent, energy_site_id) {
96
+ /** Gateway DIN (the energy site's `gateway_id` in `products`), the target of device commands. */
97
+ din;
98
+ #signer = null;
99
+ /**
100
+ * Pass `private_key` and `din` to send client-signed device commands through
101
+ * the cloud `device_command` endpoint. With a signer, every device command is
102
+ * signed except the four pre-pairing reads in `UNSIGNED_DEVICE_COMMANDS`.
103
+ * Without one, device commands are sent unsigned, and Tesla's cloud refuses
104
+ * most of them with 400.
105
+ * @param parent
106
+ * @param energy_site_id
107
+ * @param private_key RSA key `VERIFIED` on this gateway. Without one the site is unsigned: unlike vehicles, there is no fallback to the parent's key.
108
+ * @param din Gateway DIN (the energy site's `gateway_id` in `products`). Required with `private_key`.
109
+ * @throws TypeError for a key that is not RSA.
110
+ */
111
+ constructor(parent, energy_site_id, private_key, din) {
6
112
  this.parent = parent;
7
113
  this.energy_site_id = energy_site_id;
114
+ this.din = din;
115
+ if (private_key != null) {
116
+ if (!din) throw new Error("A signing private_key requires the gateway din.");
117
+ this.#signer = new EnergyDeviceSigner(private_key);
118
+ }
119
+ }
120
+ /** Whether device commands are signed with a client key. */
121
+ get signing() {
122
+ return this.#signer !== null;
123
+ }
124
+ /**
125
+ * Send a gRPC command to the energy device gateway.
126
+ *
127
+ * With a signing key, this signs every command except the ones in
128
+ * `UNSIGNED_DEVICE_COMMANDS`. Otherwise it sends the unsigned `grpc_command`.
129
+ * Energy device gRPC commands are based on research from
130
+ * https://github.com/jasonacox/pypowerwall (MIT licensed).
131
+ */
132
+ async _command(category, command, params, identifier_type = EnergyDeviceIdentifierType.GATEWAY_DIN) {
133
+ if (this.#signer !== null && !UNSIGNED_DEVICE_COMMANDS.has(`${category}.${command}`)) return this._signed_command(category, command, params);
134
+ return this._unsigned_command(category, command, params, identifier_type);
135
+ }
136
+ /** Send an unsigned `grpc_command` to the energy device gateway. */
137
+ async _unsigned_command(category, command, params, identifier_type = EnergyDeviceIdentifierType.GATEWAY_DIN) {
138
+ return this.parent.parent._request("POST", `api/1/energy_sites/${this.energy_site_id}/command`, null, {
139
+ command_type: "grpc_command",
140
+ command_properties: {
141
+ message: { [category]: { [command]: params ?? {} } },
142
+ identifier_type
143
+ }
144
+ });
145
+ }
146
+ /** Build the flat `device_command` body around a signed message. */
147
+ _device_command_body(routable_message, target_id, identifier_type) {
148
+ return {
149
+ identifier_type,
150
+ routable_message: Buffer.from(routable_message).toString("base64"),
151
+ target_id
152
+ };
153
+ }
154
+ /**
155
+ * Send a signed `RoutableMessage` to the energy device. Tesla requires the
156
+ * body wrapped in `data` and `identifier_type` as an integer. The reply is
157
+ * returned unchanged; `_signed_command` checks it for a gateway error.
158
+ */
159
+ async _device_command(routable_message, target_id, identifier_type = EnergyDeviceIdentifierType.GATEWAY_DIN) {
160
+ return this.parent.parent._request("POST", `api/1/energy_sites/${this.energy_site_id}/device_command`, null, { data: this._device_command_body(routable_message, target_id, identifier_type) });
161
+ }
162
+ /**
163
+ * Sign a gRPC command with the site's key and send it to the gateway.
164
+ *
165
+ * A resend of the same signed bytes inside the TTL executes again, so this
166
+ * never retries.
167
+ * @throws SignedCommandRequired when the site has no signing key.
168
+ * @throws InvalidCommand when the gateway firmware has no handler for the command.
169
+ * @throws DeviceUnexpectedResponse when the gateway refuses the command.
170
+ */
171
+ async _signed_command(category, command, params) {
172
+ const signer = this.#signer;
173
+ const din = this.din;
174
+ if (signer === null || din === void 0) throw new SignedCommandRequired();
175
+ const envelope = EnergyDeviceSigner.envelope(din, category, command, params);
176
+ const routable_message = await signer.sign(envelope, din);
177
+ const result = await this._device_command(routable_message, din);
178
+ _raise_on_error_response(result);
179
+ return result;
180
+ }
181
+ /** Get energy device system information including firmware version, device type, part number, serial number, and DIN. */
182
+ async get_system_info() {
183
+ return this._command("common", "get_system_info_request");
184
+ }
185
+ /** Get raw energy device networking status including WiFi, Ethernet, and cellular connectivity. */
186
+ async raw_networking_status() {
187
+ return this._command("common", "get_networking_status_request");
188
+ }
189
+ /** Get energy device networking status including WiFi, Ethernet, and cellular connectivity. */
190
+ async get_networking_status() {
191
+ return (await this.raw_networking_status()).response?.message?.Payload?.Common?.Message?.GetNetworkingStatusResponse ?? {};
192
+ }
193
+ /** Scan for available WiFi networks from the energy gateway. */
194
+ async wifi_scan() {
195
+ return this._command("common", "wifi_scan_request");
196
+ }
197
+ /** Get the energy device certificate including subject, issuer, and validity. */
198
+ async get_device_cert() {
199
+ return this._command("common", "device_cert_request");
200
+ }
201
+ /** Get cellular modem information from the energy gateway including the embedded SIM identifier (EID) and supported cellular profiles. */
202
+ async get_cellular_info() {
203
+ return this._command("common", "get_cellular_info_request");
204
+ }
205
+ /** Check if a firmware update is available for the energy gateway. */
206
+ async check_for_update() {
207
+ return this._command("common", "check_for_update_request");
208
+ }
209
+ /** Check the urgency level of any available firmware update for the energy gateway. */
210
+ async check_for_update_urgency() {
211
+ return this._command("common", "check_for_update_urgency_request");
212
+ }
213
+ /** Test internet connectivity on the energy gateway across all network interfaces (WiFi, Ethernet, GSM). */
214
+ async check_internet() {
215
+ return this._command("common", "check_internet_request");
216
+ }
217
+ /**
218
+ * Update local site configuration on the energy gateway. This writes gateway
219
+ * configuration and is an undocumented energy device gRPC command.
220
+ * @param params Raw protobuf request fields.
221
+ */
222
+ async set_local_site_config(params) {
223
+ return this._command("common", "set_local_site_config_request", params);
224
+ }
225
+ /** List authorized clients (paired keys) on the energy gateway including their roles and state. */
226
+ async list_authorized_clients() {
227
+ return this._command("authorization", "list_authorized_clients_request");
228
+ }
229
+ /**
230
+ * Register an authorized client (public key) with the energy gateway, to
231
+ * pair a local key with a Powerwall for the LAN TEDapi v1r protocol. The
232
+ * encoding follows `key_type`: RSA is raw PKCS#1 `RSAPublicKey` DER
233
+ * (`rsa_public_der_pkcs1`), and ECC is DER SubjectPublicKeyInfo
234
+ * (`ec_public_der_spki`), not a raw point, which the gateway rejects. A
235
+ * VERIFIED ECC key still cannot sign v1r: `SignatureData` has no ECDSA member.
236
+ *
237
+ * The key stays PENDING_VERIFICATION until a physical breaker toggle confirms
238
+ * it inside a window of about 9 minutes. A missed window is the terminal
239
+ * PENDING_VERIFICATION_TIMEOUT; registering the same key again resets it
240
+ * without a second record.
241
+ * @param public_key Raw DER bytes, which are base64-encoded, or an already base64-encoded string.
242
+ * @param description Human-readable description of the client.
243
+ * @param key_type The type of key being registered.
244
+ * @param authorized_client_type The authorized client type.
245
+ */
246
+ async add_authorized_client(public_key, description = "Powerwall LAN Client", key_type = AuthorizedClientKeyType.RSA, authorized_client_type = AuthorizedClientType.CUSTOMER_MOBILE_APP) {
247
+ return this._command("authorization", "add_authorized_client_request", {
248
+ key_type,
249
+ public_key: publicKeyBase64(public_key),
250
+ [this.signing ? "type" : "authorized_client_type"]: authorized_client_type,
251
+ description
252
+ });
253
+ }
254
+ /**
255
+ * Remove an authorized client (public key) from the energy gateway. Removal
256
+ * needs no physical presence proof, so any paired key can revoke every other
257
+ * key, including the owner's. The reply is empty, and some firmware may
258
+ * return no body at all. This cloud route is inferred from
259
+ * `add_authorized_client`; removal was only live-verified over local v1r.
260
+ * @param public_key Raw DER bytes, which are base64-encoded, or an already base64-encoded string as `list_authorized_clients` reports it.
261
+ */
262
+ async remove_authorized_client(public_key) {
263
+ return this._command("authorization", "remove_authorized_client_request", { public_key: publicKeyBase64(public_key) });
264
+ }
265
+ /** Get the energy gateway's public key for signed commands. */
266
+ async get_signed_commands_public_key() {
267
+ return this._command("authorization", "get_signed_commands_public_key_request");
268
+ }
269
+ /** Get backup events from the energy gateway. May time out on some firmware versions. */
270
+ async get_backup_events() {
271
+ return this._command("teg", "get_backup_events_request");
272
+ }
273
+ /**
274
+ * Schedule a manual backup event on the energy gateway.
275
+ * @param start_time ISO 8601 timestamp for when the backup event should start.
276
+ * @param duration_seconds Duration of the backup event in seconds.
277
+ * @param priority Priority level for the backup event. A string carries values beyond 2^53.
278
+ */
279
+ async schedule_backup_event(start_time, duration_seconds, priority) {
280
+ const scheduling_info = backupSchedulingInfo(start_time, duration_seconds, priority);
281
+ return this._command("teg", "schedule_manual_backup_event_request", scheduling_info ? { scheduling_info } : null);
282
+ }
283
+ /** Cancel a scheduled manual backup event on the energy gateway. */
284
+ async cancel_backup_event() {
285
+ return this._command("teg", "cancel_manual_backup_event_request");
286
+ }
287
+ /** Get Tesla Energy Gateway configuration from the TEG service. */
288
+ async get_teg_config() {
289
+ return this._command("teg", "get_config_request");
290
+ }
291
+ /**
292
+ * Request a timed battery power setpoint on the energy gateway.
293
+ *
294
+ * The gateway holds the setpoint for `duration_seconds`, then returns to its
295
+ * configured operating mode by itself. A new call replaces an active
296
+ * setpoint. Keep durations short and re-send to renew, so a caller that stops
297
+ * cannot leave the battery on a stale setpoint.
298
+ *
299
+ * The reply is an empty acknowledgement, not readback. A setpoint was seen to
300
+ * discharge below the backup reserve and to export with export set to
301
+ * `pv_only` or `never`. The gateway refuses a request above its power limit
302
+ * rather than clamping it: the signed path raises `DeviceUnexpectedResponse`
303
+ * with code 11.
304
+ *
305
+ * Tesla's cloud refuses this command unsigned (HTTP 400), so it acts only on
306
+ * a site with a signing key.
307
+ * @param power_watts Battery power in watts: positive discharges, negative charges.
308
+ * @param duration_seconds Whole seconds from 1 to 86400.
309
+ * @throws RangeError or TypeError before any request on invalid arguments.
310
+ */
311
+ async dispatch_battery_power(power_watts, duration_seconds) {
312
+ validate_dispatch_battery_power(power_watts, duration_seconds);
313
+ return this._command("teg", "dispatch_battery_power_request", { real_power: {
314
+ power_watts,
315
+ duration_seconds
316
+ } });
317
+ }
318
+ /**
319
+ * Release a `dispatch_battery_power` setpoint, returning the gateway to its
320
+ * configured operating mode. The gateway also acknowledges this when no
321
+ * setpoint is active. It acts only on a site with a signing key.
322
+ */
323
+ async resume_battery_operation() {
324
+ return this._command("teg", "dispatch_battery_power_request", { resume_device_mode: {} });
325
+ }
326
+ /**
327
+ * Set the island mode on the energy gateway.
328
+ *
329
+ * Signed only: gateways acknowledge the unsigned `grpc_command` without
330
+ * operating the grid contactor, so this never sends one. A success-shaped
331
+ * reply still does not prove the contactor moved: confirm `island_status`
332
+ * with a status read before relying on it.
333
+ * @param mode `EnergyIslandMode.OFF_GRID` (6) or `ON_GRID` (1).
334
+ * @param force Sent when given. Some gateways need `true` to open the contactor.
335
+ * @throws TypeError on any other mode, before signing.
336
+ * @throws SignedCommandRequired without a signing key, before any request.
337
+ * @throws DeviceUnexpectedResponse when the gateway refuses, as `REJECTED_LOW_ENERGY` and the like.
338
+ */
339
+ async set_island_mode(mode, force) {
340
+ validate_island_mode(mode, force);
341
+ const result = await this._signed_command("teg", "set_island_mode_request", force == null ? { mode } : {
342
+ mode,
343
+ force
344
+ });
345
+ raise_on_island_rejection(result);
346
+ return result;
347
+ }
348
+ /** Request off-grid mode, with `force`. Signed only: see `set_island_mode`. */
349
+ async go_off_grid() {
350
+ return this.set_island_mode(EnergyIslandMode.OFF_GRID, true);
351
+ }
352
+ /** Request on-grid mode. Signed only: see `set_island_mode`. */
353
+ async reconnect_grid() {
354
+ return this.set_island_mode(EnergyIslandMode.ON_GRID);
8
355
  }
9
356
  /**
10
357
  * Adjust the site's backup reserve.
@@ -16,38 +363,35 @@ var EnergySpecific = class {
16
363
  }
17
364
  /**
18
365
  * Returns the backup (off-grid) event history of the site in duration of seconds.
19
- * @param kind The kind of history to be requested. 'backup', 'charge', 'energy' are all supported.
366
+ * @param period Period of the window to retrieve backup events. This can be day, week, month, year, lifetime and should align with the window requested.
20
367
  * @param start_date Start date of the window to retrieve backup events in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
21
368
  * @param end_date End date of the window to retrieve backup events in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
22
- * @param period Period of the window to retrieve backup events. This can be day, week, month, year, lifetime and should align with the window requested.
23
369
  * @param time_zone Timezone of the requested window in IANA name format. i.e. 'America/Los_Angeles'
24
370
  * @returns
25
371
  */
26
- async backup_history(kind, start_date, end_date, period, time_zone) {
27
- return this.parent.backup_history(this.energy_site_id, kind, start_date, end_date, period, time_zone);
372
+ async backup_history(period, start_date, end_date, time_zone) {
373
+ return this.parent.backup_history(this.energy_site_id, period, start_date, end_date, time_zone);
28
374
  }
29
375
  /**
30
376
  * Returns the charging history of a wall connector.
31
- * @param kind The kind of history to be requested. 'backup', 'charge', 'energy' are all supported.
32
- * @param start_date Start date of the window to retrieve backup events in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
33
- * @param end_date End date of the window to retrieve backup events in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
377
+ * @param start_date Start date of the window to retrieve charging events in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
378
+ * @param end_date End date of the window to retrieve charging events in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
34
379
  * @param time_zone Timezone of the requested window in IANA name format. i.e. 'America/Los_Angeles'
35
380
  * @returns
36
381
  */
37
- async charge_history(kind, start_date, end_date, time_zone) {
38
- return this.parent.charge_history(this.energy_site_id, kind, start_date, end_date, time_zone);
382
+ async charge_history(start_date, end_date, time_zone) {
383
+ return this.parent.charge_history(this.energy_site_id, start_date, end_date, time_zone);
39
384
  }
40
385
  /**
41
- * Returns the energy history of the site in duration of seconds.
42
- * @param kind The kind of history to be requested. 'backup', 'charge', 'energy' are all supported.
43
- * @param start_date Start date of the window to retrieve backup events in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
44
- * @param end_date End date of the window to retrieve backup events in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
45
- * @param period Period of the window to retrieve backup events. This can be day, week, month, year, lifetime and should align with the window requested.
386
+ * Returns the energy measurements of the site, aggregated to the requested period.
387
+ * @param period Period of the window to retrieve energy measurements. This can be day, week, month, year, lifetime and should align with the window requested.
388
+ * @param start_date Start date of the window to retrieve energy measurements in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
389
+ * @param end_date End date of the window to retrieve energy measurements in RFC3339 format. i.e. 2023-01-01T00:00:00-08:00
46
390
  * @param time_zone Timezone of the requested window in IANA name format. i.e. 'America/Los_Angeles'
47
391
  * @returns
48
392
  */
49
- async energy_history(kind, start_date, end_date, period, time_zone) {
50
- return this.parent.energy_history(this.energy_site_id, kind, start_date, end_date, period, time_zone);
393
+ async energy_history(period, start_date, end_date, time_zone) {
394
+ return this.parent.energy_history(this.energy_site_id, period, start_date, end_date, time_zone);
51
395
  }
52
396
  /**
53
397
  * Allow/disallow charging from the grid and exporting energy to the grid.
@@ -95,5 +439,13 @@ var EnergySpecific = class {
95
439
  async storm_mode(enabled) {
96
440
  return this.parent.storm_mode(this.energy_site_id, enabled);
97
441
  }
442
+ /**
443
+ * Update the time of use settings for the energy site.
444
+ * @param settings The `tariff_content_v2` document.
445
+ * @returns
446
+ */
447
+ async time_of_use_settings(settings) {
448
+ return this.parent.parent._request("POST", `api/1/energy_sites/${this.energy_site_id}/time_of_use_settings`, null, { tou_settings: { tariff_content_v2: settings } });
449
+ }
98
450
  };
99
- export { EnergySpecific as default };
451
+ export { MAX_DISPATCH_DURATION_SECONDS, UNSIGNED_DEVICE_COMMANDS, backupSchedulingInfo, EnergySpecific as default, publicKeyBase64, raise_on_island_rejection, validate_dispatch_battery_power };
@@ -502,8 +502,9 @@ var AuthorizedClientWaitExpired = class extends TeslaFleetError {
502
502
  * epoch. Also raised when the reply carries an empty `publicKey` (so no
503
503
  * shared key can be derived to verify a tag) with a status other than
504
504
  * `SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST`, which raises
505
- * `NotOnWhitelistFault` instead. The session's prior state is left
506
- * unmodified.
505
+ * `NotOnWhitelistFault` instead, when it names a vehicle public key other than
506
+ * the one the session pinned at first contact, or when its clock or counter
507
+ * could not sign a command. The session's prior state is left unmodified.
507
508
  */
508
509
  var SessionInfoAuthenticationFault = class extends TeslaFleetError {
509
510
  static message = "Session info reply failed authentication and was discarded.";
@@ -496,8 +496,9 @@ export declare class AuthorizedClientWaitExpired extends TeslaFleetError {
496
496
  * epoch. Also raised when the reply carries an empty `publicKey` (so no
497
497
  * shared key can be derived to verify a tag) with a status other than
498
498
  * `SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST`, which raises
499
- * `NotOnWhitelistFault` instead. The session's prior state is left
500
- * unmodified.
499
+ * `NotOnWhitelistFault` instead, when it names a vehicle public key other than
500
+ * the one the session pinned at first contact, or when its clock or counter
501
+ * could not sign a command. The session's prior state is left unmodified.
501
502
  */
502
503
  export declare class SessionInfoAuthenticationFault extends TeslaFleetError {
503
504
  static readonly message: string;
@@ -496,8 +496,9 @@ export declare class AuthorizedClientWaitExpired extends TeslaFleetError {
496
496
  * epoch. Also raised when the reply carries an empty `publicKey` (so no
497
497
  * shared key can be derived to verify a tag) with a status other than
498
498
  * `SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST`, which raises
499
- * `NotOnWhitelistFault` instead. The session's prior state is left
500
- * unmodified.
499
+ * `NotOnWhitelistFault` instead, when it names a vehicle public key other than
500
+ * the one the session pinned at first contact, or when its clock or counter
501
+ * could not sign a command. The session's prior state is left unmodified.
501
502
  */
502
503
  export declare class SessionInfoAuthenticationFault extends TeslaFleetError {
503
504
  static readonly message: string;
@@ -502,8 +502,9 @@ var AuthorizedClientWaitExpired = class extends TeslaFleetError {
502
502
  * epoch. Also raised when the reply carries an empty `publicKey` (so no
503
503
  * shared key can be derived to verify a tag) with a status other than
504
504
  * `SESSION_INFO_STATUS_KEY_NOT_ON_WHITELIST`, which raises
505
- * `NotOnWhitelistFault` instead. The session's prior state is left
506
- * unmodified.
505
+ * `NotOnWhitelistFault` instead, when it names a vehicle public key other than
506
+ * the one the session pinned at first contact, or when its clock or counter
507
+ * could not sign a command. The session's prior state is left unmodified.
507
508
  */
508
509
  var SessionInfoAuthenticationFault = class extends TeslaFleetError {
509
510
  static message = "Session info reply failed authentication and was discarded.";