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.
- package/README.md +284 -1
- package/dist/_virtual/_rolldown/runtime.cjs +10 -0
- package/dist/bluetooth/adapters/shared.cjs +38 -0
- package/dist/bluetooth/adapters/shared.mjs +36 -0
- package/dist/bluetooth/async.cjs +121 -0
- package/dist/bluetooth/async.d.cts +10 -0
- package/dist/bluetooth/async.d.mts +10 -0
- package/dist/bluetooth/async.mjs +116 -0
- package/dist/bluetooth/broadcast.cjs +153 -0
- package/dist/bluetooth/broadcast.d.cts +59 -0
- package/dist/bluetooth/broadcast.d.mts +59 -0
- package/dist/bluetooth/broadcast.mjs +151 -0
- package/dist/bluetooth/constants.cjs +99 -0
- package/dist/bluetooth/constants.d.cts +70 -0
- package/dist/bluetooth/constants.d.mts +70 -0
- package/dist/bluetooth/constants.mjs +72 -0
- package/dist/bluetooth/esphome.cjs +292 -0
- package/dist/bluetooth/esphome.d.cts +83 -0
- package/dist/bluetooth/esphome.d.mts +83 -0
- package/dist/bluetooth/esphome.mjs +291 -0
- package/dist/bluetooth/framing.cjs +125 -0
- package/dist/bluetooth/framing.d.cts +45 -0
- package/dist/bluetooth/framing.d.mts +45 -0
- package/dist/bluetooth/framing.mjs +122 -0
- package/dist/bluetooth/homey.cjs +155 -0
- package/dist/bluetooth/homey.d.cts +75 -0
- package/dist/bluetooth/homey.d.mts +75 -0
- package/dist/bluetooth/homey.mjs +150 -0
- package/dist/bluetooth/names.cjs +24 -0
- package/dist/bluetooth/names.d.cts +10 -0
- package/dist/bluetooth/names.d.mts +10 -0
- package/dist/bluetooth/names.mjs +20 -0
- package/dist/bluetooth/noble.cjs +178 -0
- package/dist/bluetooth/noble.d.cts +58 -0
- package/dist/bluetooth/noble.d.mts +58 -0
- package/dist/bluetooth/noble.mjs +177 -0
- package/dist/bluetooth/streamglue.cjs +157 -0
- package/dist/bluetooth/streamglue.d.cts +37 -0
- package/dist/bluetooth/streamglue.d.mts +37 -0
- package/dist/bluetooth/streamglue.mjs +154 -0
- package/dist/bluetooth/teslabluetooth.cjs +67 -0
- package/dist/bluetooth/teslabluetooth.d.cts +41 -0
- package/dist/bluetooth/teslabluetooth.d.mts +41 -0
- package/dist/bluetooth/teslabluetooth.mjs +66 -0
- package/dist/bluetooth/transport.cjs +10 -0
- package/dist/bluetooth/transport.d.cts +95 -0
- package/dist/bluetooth/transport.d.mts +95 -0
- package/dist/bluetooth/transport.mjs +10 -0
- package/dist/bluetooth/vehiclebluetooth.cjs +1272 -0
- package/dist/bluetooth/vehiclebluetooth.d.cts +495 -0
- package/dist/bluetooth/vehiclebluetooth.d.mts +495 -0
- package/dist/bluetooth/vehiclebluetooth.mjs +1270 -0
- package/dist/bluetooth/verify.cjs +93 -0
- package/dist/bluetooth/verify.d.cts +35 -0
- package/dist/bluetooth/verify.d.mts +35 -0
- package/dist/bluetooth/verify.mjs +91 -0
- package/dist/bluetooth.cjs +53 -0
- package/dist/bluetooth.d.cts +10 -0
- package/dist/bluetooth.d.mts +10 -0
- package/dist/bluetooth.mjs +9 -0
- package/dist/commands.cjs +1080 -85
- package/dist/commands.d.cts +585 -54
- package/dist/commands.d.mts +583 -51
- package/dist/commands.mjs +1084 -93
- package/dist/const.cjs +22 -0
- package/dist/const.d.cts +21 -0
- package/dist/const.d.mts +21 -0
- package/dist/const.mjs +20 -1
- package/dist/energy.cjs +24 -19
- package/dist/energy.d.cts +21 -15
- package/dist/energy.d.mts +21 -15
- package/dist/energy.mjs +24 -19
- package/dist/energyisland.cjs +38 -0
- package/dist/energyisland.mjs +37 -0
- package/dist/energyparsers.cjs +118 -0
- package/dist/energyparsers.d.cts +39 -0
- package/dist/energyparsers.d.mts +39 -0
- package/dist/energyparsers.mjs +116 -0
- package/dist/energyspecific.cjs +375 -17
- package/dist/energyspecific.d.cts +199 -19
- package/dist/energyspecific.d.mts +197 -16
- package/dist/energyspecific.mjs +370 -18
- package/dist/exceptions.cjs +3 -2
- package/dist/exceptions.d.cts +3 -2
- package/dist/exceptions.d.mts +3 -2
- package/dist/exceptions.mjs +3 -2
- package/dist/funnel.cjs +297 -0
- package/dist/funnel.d.cts +118 -0
- package/dist/funnel.d.mts +118 -0
- package/dist/funnel.mjs +292 -0
- package/dist/index.cjs +61 -0
- package/dist/index.d.cts +22 -8
- package/dist/index.d.mts +22 -8
- package/dist/index.mjs +19 -6
- package/dist/log.cjs +75 -0
- package/dist/log.d.cts +4 -0
- package/dist/log.d.mts +4 -0
- package/dist/log.mjs +71 -0
- package/dist/oauth.cjs +143 -0
- package/dist/oauth.d.cts +69 -0
- package/dist/oauth.d.mts +69 -0
- package/dist/oauth.mjs +143 -0
- package/dist/powerwall/client.cjs +818 -0
- package/dist/powerwall/client.d.cts +297 -0
- package/dist/powerwall/client.d.mts +297 -0
- package/dist/powerwall/client.mjs +807 -0
- package/dist/powerwall/components.cjs +188 -0
- package/dist/powerwall/components.d.cts +45 -0
- package/dist/powerwall/components.d.mts +45 -0
- package/dist/powerwall/components.mjs +182 -0
- package/dist/powerwall/energysite.cjs +216 -0
- package/dist/powerwall/energysite.d.cts +104 -0
- package/dist/powerwall/energysite.d.mts +104 -0
- package/dist/powerwall/energysite.mjs +214 -0
- package/dist/powerwall/exceptions.cjs +59 -0
- package/dist/powerwall/exceptions.d.cts +38 -0
- package/dist/powerwall/exceptions.d.mts +38 -0
- package/dist/powerwall/exceptions.mjs +51 -0
- package/dist/powerwall/gateway.cjs +102 -0
- package/dist/powerwall/gateway.d.cts +57 -0
- package/dist/powerwall/gateway.d.mts +57 -0
- package/dist/powerwall/gateway.mjs +101 -0
- package/dist/powerwall/merge.cjs +163 -0
- package/dist/powerwall/merge.d.cts +63 -0
- package/dist/powerwall/merge.d.mts +63 -0
- package/dist/powerwall/merge.mjs +159 -0
- package/dist/powerwall/models.d.cts +95 -0
- package/dist/powerwall/models.d.mts +95 -0
- package/dist/powerwall/queries.cjs +17 -0
- package/dist/powerwall/queries.mjs +10 -0
- package/dist/powerwall/transport.cjs +232 -0
- package/dist/powerwall/transport.mjs +227 -0
- package/dist/powerwall.cjs +47 -0
- package/dist/powerwall.d.cts +9 -0
- package/dist/powerwall.d.mts +9 -0
- package/dist/powerwall.mjs +8 -0
- package/dist/router/base.cjs +203 -0
- package/dist/router/base.d.cts +112 -0
- package/dist/router/base.d.mts +112 -0
- package/dist/router/base.mjs +200 -0
- package/dist/router/energysite.cjs +65 -0
- package/dist/router/energysite.d.cts +47 -0
- package/dist/router/energysite.d.mts +47 -0
- package/dist/router/energysite.mjs +60 -0
- package/dist/router.cjs +13 -0
- package/dist/router.d.cts +3 -1
- package/dist/router.d.mts +3 -1
- package/dist/router.mjs +3 -1
- package/dist/signing/crypto.cjs +28 -0
- package/dist/signing/crypto.mjs +26 -1
- package/dist/signing/energy.cjs +123 -0
- package/dist/signing/energy.d.cts +45 -0
- package/dist/signing/energy.d.mts +45 -0
- package/dist/signing/energy.mjs +120 -0
- package/dist/signing/jws.cjs +229 -0
- package/dist/signing/jws.d.cts +45 -0
- package/dist/signing/jws.d.mts +45 -0
- package/dist/signing/jws.mjs +211 -0
- package/dist/signing/keys.cjs +191 -0
- package/dist/signing/keys.d.cts +21 -0
- package/dist/signing/keys.d.mts +21 -0
- package/dist/signing/keys.mjs +182 -0
- package/dist/signing/session.cjs +104 -30
- package/dist/signing/session.d.cts +60 -23
- package/dist/signing/session.d.mts +60 -23
- package/dist/signing/session.mjs +105 -31
- package/dist/tariff.cjs +27 -0
- package/dist/tariff.d.cts +11 -0
- package/dist/tariff.d.mts +11 -0
- package/dist/tariff.mjs +27 -1
- package/dist/teslafleetapi.cjs +256 -55
- package/dist/teslafleetapi.d.cts +110 -14
- package/dist/teslafleetapi.d.mts +110 -14
- package/dist/teslafleetapi.mjs +258 -57
- package/dist/teslemetry.cjs +152 -5
- package/dist/teslemetry.d.cts +95 -5
- package/dist/teslemetry.d.mts +93 -3
- package/dist/teslemetry.mjs +151 -6
- package/dist/teslemetryenergy.cjs +287 -0
- package/dist/teslemetryenergy.d.cts +154 -0
- package/dist/teslemetryenergy.d.mts +154 -0
- package/dist/teslemetryenergy.mjs +282 -0
- package/dist/teslemetryvehicle.cjs +407 -0
- package/dist/teslemetryvehicle.d.cts +238 -0
- package/dist/teslemetryvehicle.d.mts +238 -0
- package/dist/teslemetryvehicle.mjs +405 -0
- package/dist/tessie.cjs +42 -5
- package/dist/tessie.d.cts +51 -5
- package/dist/tessie.d.mts +49 -3
- package/dist/tessie.mjs +42 -5
- package/dist/tessievehicle.cjs +514 -0
- package/dist/tessievehicle.d.cts +333 -0
- package/dist/tessievehicle.d.mts +333 -0
- package/dist/tessievehicle.mjs +513 -0
- package/dist/types/live_status.d.cts +16 -16
- package/dist/types/live_status.d.mts +16 -16
- package/dist/types/responses.d.cts +5 -1
- package/dist/types/responses.d.mts +5 -1
- package/dist/types/vehicle.d.cts +7 -1
- package/dist/types/vehicle.d.mts +7 -1
- package/dist/types.d.cts +3 -3
- package/dist/types.d.mts +3 -3
- package/dist/util.cjs +41 -0
- package/dist/util.d.cts +18 -0
- package/dist/util.d.mts +18 -0
- package/dist/util.mjs +40 -0
- package/dist/vehicle.cjs +79 -36
- package/dist/vehicle.d.cts +42 -26
- package/dist/vehicle.d.mts +42 -26
- package/dist/vehicle.mjs +80 -37
- package/dist/vehiclesigned.cjs +20 -5
- package/dist/vehiclesigned.d.cts +14 -5
- package/dist/vehiclesigned.d.mts +14 -5
- package/dist/vehiclesigned.mjs +20 -5
- package/dist/vehiclespecific.cjs +126 -15
- package/dist/vehiclespecific.d.cts +82 -18
- package/dist/vehiclespecific.d.mts +82 -18
- package/dist/vehiclespecific.mjs +126 -15
- package/package.json +70 -3
package/dist/energyspecific.cjs
CHANGED
|
@@ -1,10 +1,357 @@
|
|
|
1
|
+
const require_const = require("./const.cjs");
|
|
2
|
+
const require_energyisland = require("./energyisland.cjs");
|
|
3
|
+
const require_exceptions = require("./exceptions.cjs");
|
|
4
|
+
const require_energy = require("./signing/energy.cjs");
|
|
5
|
+
let _teslemetry_tesla_protocol_energy_device_transport = require("@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 _teslemetry_tesla_protocol_energy_device_transport.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 require_exceptions.InvalidCommand(data);
|
|
64
|
+
throw new require_exceptions.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 = require_energyisland.island_mode_rejection(reported);
|
|
75
|
+
if (rejection) throw new require_exceptions.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
|
-
|
|
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 require_energy.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 = require_const.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 = require_const.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 = require_const.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 require_exceptions.SignedCommandRequired();
|
|
175
|
+
const envelope = require_energy.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 = require_const.AuthorizedClientKeyType.RSA, authorized_client_type = require_const.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
|
+
require_energyisland.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(require_const.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(require_const.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
|
|
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(
|
|
27
|
-
return this.parent.backup_history(this.energy_site_id,
|
|
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
|
|
32
|
-
* @param
|
|
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(
|
|
38
|
-
return this.parent.charge_history(this.energy_site_id,
|
|
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
|
|
42
|
-
* @param
|
|
43
|
-
* @param start_date Start date of the window to retrieve
|
|
44
|
-
* @param end_date End date of the window to retrieve
|
|
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(
|
|
50
|
-
return this.parent.energy_history(this.energy_site_id,
|
|
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,19 @@ 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
|
};
|
|
451
|
+
exports.MAX_DISPATCH_DURATION_SECONDS = MAX_DISPATCH_DURATION_SECONDS;
|
|
452
|
+
exports.UNSIGNED_DEVICE_COMMANDS = UNSIGNED_DEVICE_COMMANDS;
|
|
453
|
+
exports.backupSchedulingInfo = backupSchedulingInfo;
|
|
99
454
|
exports.default = EnergySpecific;
|
|
455
|
+
exports.publicKeyBase64 = publicKeyBase64;
|
|
456
|
+
exports.raise_on_island_rejection = raise_on_island_rejection;
|
|
457
|
+
exports.validate_dispatch_battery_power = validate_dispatch_battery_power;
|