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.
- package/README.md +269 -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 +96 -0
- package/dist/bluetooth/constants.d.cts +68 -0
- package/dist/bluetooth/constants.d.mts +68 -0
- package/dist/bluetooth/constants.mjs +70 -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 +165 -0
- package/dist/bluetooth/homey.d.cts +84 -0
- package/dist/bluetooth/homey.d.mts +84 -0
- package/dist/bluetooth/homey.mjs +159 -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 +97 -0
- package/dist/bluetooth/transport.d.mts +97 -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 +52 -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 +56 -3
package/dist/teslemetry.cjs
CHANGED
|
@@ -1,14 +1,76 @@
|
|
|
1
|
+
const require_const = require("./const.cjs");
|
|
2
|
+
const require_exceptions = require("./exceptions.cjs");
|
|
1
3
|
const require_teslafleetapi = require("./teslafleetapi.cjs");
|
|
4
|
+
const require_teslemetryenergy = require("./teslemetryenergy.cjs");
|
|
5
|
+
const require_teslemetryvehicle = require("./teslemetryvehicle.cjs");
|
|
2
6
|
// src/teslemetry.ts
|
|
7
|
+
/** Teslemetry's OAuth dynamic client registration endpoint. */
|
|
8
|
+
const REGISTER_URL = "https://api.teslemetry.com/oauth/register";
|
|
9
|
+
/**
|
|
10
|
+
* Dynamically register an OAuth client with Teslemetry (RFC 7591). This always
|
|
11
|
+
* registers a new client: the caller persists the `client_id` and skips
|
|
12
|
+
* registration on later runs.
|
|
13
|
+
* @param client_name Human-readable client name shown during consent.
|
|
14
|
+
* @param software_id Identifier for the calling application.
|
|
15
|
+
* @param software_version Version string for the calling application.
|
|
16
|
+
* @param options `fetch` replaces the global `fetch`; `timeout` aborts after this many milliseconds.
|
|
17
|
+
* @throws TeslemetryRegistrationError when the endpoint cannot be reached or
|
|
18
|
+
* refuses, the body is not JSON, or it has no usable `client_id`. The reason is in `data`.
|
|
19
|
+
*/
|
|
20
|
+
async function register_client(client_name, software_id, software_version, options = {}) {
|
|
21
|
+
const unreachable = "Could not reach Teslemetry to register a client";
|
|
22
|
+
let text;
|
|
23
|
+
try {
|
|
24
|
+
const resp = await (options.fetch ?? fetch)(REGISTER_URL, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { "Content-Type": "application/json" },
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
client_name,
|
|
29
|
+
software_id,
|
|
30
|
+
software_version
|
|
31
|
+
}),
|
|
32
|
+
signal: options.timeout != null ? AbortSignal.timeout(options.timeout) : null
|
|
33
|
+
});
|
|
34
|
+
if (!resp.ok) throw new require_exceptions.TeslemetryRegistrationError(unreachable, resp.status);
|
|
35
|
+
text = await resp.text();
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error instanceof require_exceptions.TeslemetryRegistrationError) throw error;
|
|
38
|
+
throw new require_exceptions.TeslemetryRegistrationError(unreachable, null, { cause: error });
|
|
39
|
+
}
|
|
40
|
+
let registration;
|
|
41
|
+
try {
|
|
42
|
+
registration = JSON.parse(text);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw new require_exceptions.TeslemetryRegistrationError("Teslemetry returned a malformed registration response", null, { cause: error });
|
|
45
|
+
}
|
|
46
|
+
const raw = registration !== null && typeof registration === "object" && !Array.isArray(registration) ? registration : null;
|
|
47
|
+
const client_id = raw?.client_id;
|
|
48
|
+
if (raw === null || typeof client_id !== "string" || !client_id) throw new require_exceptions.TeslemetryRegistrationError("Teslemetry registration response did not contain a client_id");
|
|
49
|
+
return {
|
|
50
|
+
client_id,
|
|
51
|
+
raw
|
|
52
|
+
};
|
|
53
|
+
}
|
|
3
54
|
var Teslemetry = class extends require_teslafleetapi.default {
|
|
4
|
-
|
|
55
|
+
static Vehicles = require_teslemetryvehicle.TeslemetryVehicles;
|
|
56
|
+
static EnergySites = require_teslemetryenergy.TeslemetryEnergySites;
|
|
57
|
+
_transport_name = "teslemetry";
|
|
58
|
+
constructor(accessToken, region = "api", options = {}) {
|
|
5
59
|
super({
|
|
60
|
+
...options,
|
|
6
61
|
accessToken,
|
|
7
62
|
server: `https://${region}.teslemetry.com`,
|
|
8
|
-
partnerScope: false
|
|
9
|
-
userScope: false
|
|
63
|
+
partnerScope: false
|
|
10
64
|
});
|
|
11
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Set the server from the account's region in `metadata()`.
|
|
68
|
+
* @returns The region, e.g. `"na"`.
|
|
69
|
+
*/
|
|
70
|
+
async find_server() {
|
|
71
|
+
const { region } = await this.metadata(true);
|
|
72
|
+
return region.toLowerCase();
|
|
73
|
+
}
|
|
12
74
|
async _response(resp) {
|
|
13
75
|
if (resp.headers.has("x-region")) {
|
|
14
76
|
const region = resp.headers.get("x-region")?.toLowerCase();
|
|
@@ -16,11 +78,19 @@ var Teslemetry = class extends require_teslafleetapi.default {
|
|
|
16
78
|
}
|
|
17
79
|
return super._response(resp);
|
|
18
80
|
}
|
|
81
|
+
/** Send a ping. */
|
|
82
|
+
async ping() {
|
|
83
|
+
return this._request("GET", "api/ping");
|
|
84
|
+
}
|
|
19
85
|
/**
|
|
20
86
|
* Test the connection to Teslemetry
|
|
21
87
|
*/
|
|
22
88
|
async test() {
|
|
23
|
-
return this._request("GET", "api/test")
|
|
89
|
+
return this._request("GET", "api/test");
|
|
90
|
+
}
|
|
91
|
+
/** Get userdata. */
|
|
92
|
+
async userdata() {
|
|
93
|
+
return this._request("GET", "api/userdata");
|
|
24
94
|
}
|
|
25
95
|
/**
|
|
26
96
|
* Get details about your Teslemetry account
|
|
@@ -28,9 +98,86 @@ var Teslemetry = class extends require_teslafleetapi.default {
|
|
|
28
98
|
*/
|
|
29
99
|
async metadata(update_region = true) {
|
|
30
100
|
return this._request("GET", "api/metadata").then((metadata) => {
|
|
31
|
-
if (update_region
|
|
101
|
+
if (update_region && metadata.region) {
|
|
102
|
+
const region = metadata.region.toLowerCase();
|
|
103
|
+
if (require_const.is_valid_region(region)) this.region = region;
|
|
104
|
+
this.server = `https://${region}.teslemetry.com`;
|
|
105
|
+
this._log(`Using server ${this.server}`);
|
|
106
|
+
}
|
|
32
107
|
return metadata;
|
|
33
108
|
});
|
|
34
109
|
}
|
|
110
|
+
/** Get the user's scopes. Leaves the server unchanged. */
|
|
111
|
+
async scopes() {
|
|
112
|
+
return this.metadata(false).then(({ scopes }) => scopes);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get or set Auto mode.
|
|
116
|
+
* @param vin
|
|
117
|
+
* @param value `true` enables it, `false` disables it, omit to read it.
|
|
118
|
+
*/
|
|
119
|
+
async server_side_polling(vin, value) {
|
|
120
|
+
const method = value === true ? "POST" : value === false ? "DELETE" : "GET";
|
|
121
|
+
return this._request(method, `api/auto/${vin}`).then(({ response }) => response);
|
|
122
|
+
}
|
|
123
|
+
/** Force a refresh of the vehicle data. */
|
|
124
|
+
async vehicle_data_refresh(vin) {
|
|
125
|
+
return this._request("GET", `api/refresh/${vin}`);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Migrate from an access token to OAuth.
|
|
129
|
+
* @param client_id
|
|
130
|
+
* @param name
|
|
131
|
+
* @returns The token response, with `expires_in` as an integer and `expires_at` in epoch seconds.
|
|
132
|
+
* @throws InvalidResponse when the reply has no usable `expires_in`.
|
|
133
|
+
*/
|
|
134
|
+
async migrate_to_oauth(client_id = "homeassistant", name) {
|
|
135
|
+
const access_token = await this.access_token();
|
|
136
|
+
const new_token = await this._request("POST", "oauth/token", null, {
|
|
137
|
+
grant_type: "migrate",
|
|
138
|
+
client_id,
|
|
139
|
+
access_token: access_token.trim(),
|
|
140
|
+
name
|
|
141
|
+
});
|
|
142
|
+
const expires_in = Math.trunc(new_token?.expires_in == null ? NaN : Number(new_token.expires_in));
|
|
143
|
+
if (!Number.isFinite(expires_in)) throw new require_exceptions.InvalidResponse("Teslemetry token response did not contain a valid expires_in");
|
|
144
|
+
new_token.expires_in = expires_in;
|
|
145
|
+
new_token.expires_at = Date.now() / 1e3 + expires_in;
|
|
146
|
+
return new_token;
|
|
147
|
+
}
|
|
148
|
+
/** Get streaming field parameters and metadata. */
|
|
149
|
+
async fields() {
|
|
150
|
+
return this._request("GET", "fields.json");
|
|
151
|
+
}
|
|
152
|
+
/** Get the saved vehicle configuration. */
|
|
153
|
+
async vehicle_config(vin) {
|
|
154
|
+
return this._request("GET", `api/vehicle_config/${vin}`);
|
|
155
|
+
}
|
|
156
|
+
/** Get a vehicle's streaming configuration: certificate, hostname, port and configurable telemetry fields. */
|
|
157
|
+
async streaming_config(vin) {
|
|
158
|
+
return this._request("GET", `api/config/${vin}`);
|
|
159
|
+
}
|
|
160
|
+
/** Stop streaming data from a vehicle. */
|
|
161
|
+
async stop_streaming(vin) {
|
|
162
|
+
return this._request("DELETE", `api/config/${vin}`);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Modify a vehicle's streaming configuration.
|
|
166
|
+
* @param vin
|
|
167
|
+
* @param fields Fields to stream with their configuration.
|
|
168
|
+
*/
|
|
169
|
+
async modify_streaming_config(vin, fields) {
|
|
170
|
+
return this._request("PATCH", `api/config/${vin}`, null, { fields });
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Create or update a vehicle's streaming configuration.
|
|
174
|
+
* @param vin
|
|
175
|
+
* @param fields Fields to stream with their configuration.
|
|
176
|
+
*/
|
|
177
|
+
async create_streaming_config(vin, fields) {
|
|
178
|
+
return this._request("POST", `api/config/${vin}`, null, { fields });
|
|
179
|
+
}
|
|
35
180
|
};
|
|
181
|
+
exports.REGISTER_URL = REGISTER_URL;
|
|
36
182
|
exports.default = Teslemetry;
|
|
183
|
+
exports.register_client = register_client;
|
package/dist/teslemetry.d.cts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import TeslaFleetApi from "./teslafleetapi.cjs";
|
|
1
|
+
import TeslaFleetApi, { AccessToken } from "./teslafleetapi.cjs";
|
|
2
|
+
import { Logger } from "./log.cjs";
|
|
3
|
+
import { TeslemetryEnergySites } from "./teslemetryenergy.cjs";
|
|
4
|
+
import { TeslemetryVehicles } from "./teslemetryvehicle.cjs";
|
|
2
5
|
import { Scope } from "./const.cjs";
|
|
6
|
+
import Vehicle from "./vehicle.cjs";
|
|
3
7
|
type Region = "na" | "eu" | "api";
|
|
4
8
|
type Metadata = {
|
|
5
9
|
uid: string;
|
|
@@ -7,17 +11,103 @@ type Metadata = {
|
|
|
7
11
|
scopes: Scope[];
|
|
8
12
|
vins: string[];
|
|
9
13
|
};
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
export type TeslemetryOptions = {
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
logger?: Logger;
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
timeout?: number;
|
|
19
|
+
};
|
|
20
|
+
/** Teslemetry's OAuth dynamic client registration endpoint. */
|
|
21
|
+
export declare const REGISTER_URL = "https://api.teslemetry.com/oauth/register";
|
|
22
|
+
/** The parsed result of `register_client`. */
|
|
23
|
+
export type TeslemetryClientRegistration = {
|
|
24
|
+
/** The OAuth client identifier to present for every future authorization, including reauthentication. */
|
|
25
|
+
readonly client_id: string;
|
|
26
|
+
/** The full decoded registration response. */
|
|
27
|
+
readonly raw: Record<string, any>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Dynamically register an OAuth client with Teslemetry (RFC 7591). This always
|
|
31
|
+
* registers a new client: the caller persists the `client_id` and skips
|
|
32
|
+
* registration on later runs.
|
|
33
|
+
* @param client_name Human-readable client name shown during consent.
|
|
34
|
+
* @param software_id Identifier for the calling application.
|
|
35
|
+
* @param software_version Version string for the calling application.
|
|
36
|
+
* @param options `fetch` replaces the global `fetch`; `timeout` aborts after this many milliseconds.
|
|
37
|
+
* @throws TeslemetryRegistrationError when the endpoint cannot be reached or
|
|
38
|
+
* refuses, the body is not JSON, or it has no usable `client_id`. The reason is in `data`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function register_client(client_name: string, software_id: string, software_version: string, options?: {
|
|
41
|
+
fetch?: typeof fetch;
|
|
42
|
+
timeout?: number;
|
|
43
|
+
}): Promise<TeslemetryClientRegistration>;
|
|
44
|
+
export default class Teslemetry extends TeslaFleetApi {
|
|
45
|
+
static Vehicles: new (parent: TeslaFleetApi) => Vehicle;
|
|
46
|
+
vehicles: TeslemetryVehicles;
|
|
47
|
+
static EnergySites: typeof TeslemetryEnergySites;
|
|
48
|
+
energySites?: TeslemetryEnergySites;
|
|
49
|
+
readonly _transport_name: string;
|
|
50
|
+
constructor(accessToken: AccessToken, region?: Region, options?: TeslemetryOptions);
|
|
51
|
+
/**
|
|
52
|
+
* Set the server from the account's region in `metadata()`.
|
|
53
|
+
* @returns The region, e.g. `"na"`.
|
|
54
|
+
*/
|
|
55
|
+
find_server(): Promise<string>;
|
|
12
56
|
_response(resp: Response): Promise<any>;
|
|
57
|
+
/** Send a ping. */
|
|
58
|
+
ping(): Promise<{
|
|
59
|
+
response: boolean;
|
|
60
|
+
}>;
|
|
13
61
|
/**
|
|
14
62
|
* Test the connection to Teslemetry
|
|
15
63
|
*/
|
|
16
|
-
test(): Promise<
|
|
64
|
+
test(): Promise<{
|
|
65
|
+
response: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
/** Get userdata. */
|
|
68
|
+
userdata(): Promise<Record<string, any>>;
|
|
17
69
|
/**
|
|
18
70
|
* Get details about your Teslemetry account
|
|
19
71
|
* @param update_region Update the server URL based on the region in the metadata
|
|
20
72
|
*/
|
|
21
73
|
metadata(update_region?: boolean): Promise<Metadata>;
|
|
74
|
+
/** Get the user's scopes. Leaves the server unchanged. */
|
|
75
|
+
scopes(): Promise<Scope[]>;
|
|
76
|
+
/**
|
|
77
|
+
* Get or set Auto mode.
|
|
78
|
+
* @param vin
|
|
79
|
+
* @param value `true` enables it, `false` disables it, omit to read it.
|
|
80
|
+
*/
|
|
81
|
+
server_side_polling(vin: string, value?: boolean | null): Promise<boolean | undefined>;
|
|
82
|
+
/** Force a refresh of the vehicle data. */
|
|
83
|
+
vehicle_data_refresh(vin: string): Promise<Record<string, any>>;
|
|
84
|
+
/**
|
|
85
|
+
* Migrate from an access token to OAuth.
|
|
86
|
+
* @param client_id
|
|
87
|
+
* @param name
|
|
88
|
+
* @returns The token response, with `expires_in` as an integer and `expires_at` in epoch seconds.
|
|
89
|
+
* @throws InvalidResponse when the reply has no usable `expires_in`.
|
|
90
|
+
*/
|
|
91
|
+
migrate_to_oauth(client_id?: string, name?: string | null): Promise<Record<string, any>>;
|
|
92
|
+
/** Get streaming field parameters and metadata. */
|
|
93
|
+
fields(): Promise<Record<string, any>>;
|
|
94
|
+
/** Get the saved vehicle configuration. */
|
|
95
|
+
vehicle_config(vin: string): Promise<Record<string, any>>;
|
|
96
|
+
/** Get a vehicle's streaming configuration: certificate, hostname, port and configurable telemetry fields. */
|
|
97
|
+
streaming_config(vin: string): Promise<Record<string, any>>;
|
|
98
|
+
/** Stop streaming data from a vehicle. */
|
|
99
|
+
stop_streaming(vin: string): Promise<Record<string, any>>;
|
|
100
|
+
/**
|
|
101
|
+
* Modify a vehicle's streaming configuration.
|
|
102
|
+
* @param vin
|
|
103
|
+
* @param fields Fields to stream with their configuration.
|
|
104
|
+
*/
|
|
105
|
+
modify_streaming_config(vin: string, fields: Record<string, any>): Promise<Record<string, any>>;
|
|
106
|
+
/**
|
|
107
|
+
* Create or update a vehicle's streaming configuration.
|
|
108
|
+
* @param vin
|
|
109
|
+
* @param fields Fields to stream with their configuration.
|
|
110
|
+
*/
|
|
111
|
+
create_streaming_config(vin: string, fields: Record<string, any>): Promise<Record<string, any>>;
|
|
22
112
|
}
|
|
23
|
-
export
|
|
113
|
+
export {};
|
package/dist/teslemetry.d.mts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import TeslaFleetApi from "./teslafleetapi.mjs";
|
|
1
|
+
import TeslaFleetApi, { AccessToken } from "./teslafleetapi.mjs";
|
|
2
|
+
import { Logger } from "./log.mjs";
|
|
3
|
+
import { TeslemetryEnergySites } from "./teslemetryenergy.mjs";
|
|
4
|
+
import { TeslemetryVehicles } from "./teslemetryvehicle.mjs";
|
|
2
5
|
import { Scope } from "./const.mjs";
|
|
6
|
+
import Vehicle from "./vehicle.mjs";
|
|
3
7
|
type Region = "na" | "eu" | "api";
|
|
4
8
|
type Metadata = {
|
|
5
9
|
uid: string;
|
|
@@ -7,17 +11,103 @@ type Metadata = {
|
|
|
7
11
|
scopes: Scope[];
|
|
8
12
|
vins: string[];
|
|
9
13
|
};
|
|
14
|
+
export type TeslemetryOptions = {
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
logger?: Logger;
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
timeout?: number;
|
|
19
|
+
};
|
|
20
|
+
/** Teslemetry's OAuth dynamic client registration endpoint. */
|
|
21
|
+
export declare const REGISTER_URL = "https://api.teslemetry.com/oauth/register";
|
|
22
|
+
/** The parsed result of `register_client`. */
|
|
23
|
+
export type TeslemetryClientRegistration = {
|
|
24
|
+
/** The OAuth client identifier to present for every future authorization, including reauthentication. */
|
|
25
|
+
readonly client_id: string;
|
|
26
|
+
/** The full decoded registration response. */
|
|
27
|
+
readonly raw: Record<string, any>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Dynamically register an OAuth client with Teslemetry (RFC 7591). This always
|
|
31
|
+
* registers a new client: the caller persists the `client_id` and skips
|
|
32
|
+
* registration on later runs.
|
|
33
|
+
* @param client_name Human-readable client name shown during consent.
|
|
34
|
+
* @param software_id Identifier for the calling application.
|
|
35
|
+
* @param software_version Version string for the calling application.
|
|
36
|
+
* @param options `fetch` replaces the global `fetch`; `timeout` aborts after this many milliseconds.
|
|
37
|
+
* @throws TeslemetryRegistrationError when the endpoint cannot be reached or
|
|
38
|
+
* refuses, the body is not JSON, or it has no usable `client_id`. The reason is in `data`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function register_client(client_name: string, software_id: string, software_version: string, options?: {
|
|
41
|
+
fetch?: typeof fetch;
|
|
42
|
+
timeout?: number;
|
|
43
|
+
}): Promise<TeslemetryClientRegistration>;
|
|
10
44
|
export default class Teslemetry extends TeslaFleetApi {
|
|
11
|
-
|
|
45
|
+
static Vehicles: new (parent: TeslaFleetApi) => Vehicle;
|
|
46
|
+
vehicles: TeslemetryVehicles;
|
|
47
|
+
static EnergySites: typeof TeslemetryEnergySites;
|
|
48
|
+
energySites?: TeslemetryEnergySites;
|
|
49
|
+
readonly _transport_name: string;
|
|
50
|
+
constructor(accessToken: AccessToken, region?: Region, options?: TeslemetryOptions);
|
|
51
|
+
/**
|
|
52
|
+
* Set the server from the account's region in `metadata()`.
|
|
53
|
+
* @returns The region, e.g. `"na"`.
|
|
54
|
+
*/
|
|
55
|
+
find_server(): Promise<string>;
|
|
12
56
|
_response(resp: Response): Promise<any>;
|
|
57
|
+
/** Send a ping. */
|
|
58
|
+
ping(): Promise<{
|
|
59
|
+
response: boolean;
|
|
60
|
+
}>;
|
|
13
61
|
/**
|
|
14
62
|
* Test the connection to Teslemetry
|
|
15
63
|
*/
|
|
16
|
-
test(): Promise<
|
|
64
|
+
test(): Promise<{
|
|
65
|
+
response: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
/** Get userdata. */
|
|
68
|
+
userdata(): Promise<Record<string, any>>;
|
|
17
69
|
/**
|
|
18
70
|
* Get details about your Teslemetry account
|
|
19
71
|
* @param update_region Update the server URL based on the region in the metadata
|
|
20
72
|
*/
|
|
21
73
|
metadata(update_region?: boolean): Promise<Metadata>;
|
|
74
|
+
/** Get the user's scopes. Leaves the server unchanged. */
|
|
75
|
+
scopes(): Promise<Scope[]>;
|
|
76
|
+
/**
|
|
77
|
+
* Get or set Auto mode.
|
|
78
|
+
* @param vin
|
|
79
|
+
* @param value `true` enables it, `false` disables it, omit to read it.
|
|
80
|
+
*/
|
|
81
|
+
server_side_polling(vin: string, value?: boolean | null): Promise<boolean | undefined>;
|
|
82
|
+
/** Force a refresh of the vehicle data. */
|
|
83
|
+
vehicle_data_refresh(vin: string): Promise<Record<string, any>>;
|
|
84
|
+
/**
|
|
85
|
+
* Migrate from an access token to OAuth.
|
|
86
|
+
* @param client_id
|
|
87
|
+
* @param name
|
|
88
|
+
* @returns The token response, with `expires_in` as an integer and `expires_at` in epoch seconds.
|
|
89
|
+
* @throws InvalidResponse when the reply has no usable `expires_in`.
|
|
90
|
+
*/
|
|
91
|
+
migrate_to_oauth(client_id?: string, name?: string | null): Promise<Record<string, any>>;
|
|
92
|
+
/** Get streaming field parameters and metadata. */
|
|
93
|
+
fields(): Promise<Record<string, any>>;
|
|
94
|
+
/** Get the saved vehicle configuration. */
|
|
95
|
+
vehicle_config(vin: string): Promise<Record<string, any>>;
|
|
96
|
+
/** Get a vehicle's streaming configuration: certificate, hostname, port and configurable telemetry fields. */
|
|
97
|
+
streaming_config(vin: string): Promise<Record<string, any>>;
|
|
98
|
+
/** Stop streaming data from a vehicle. */
|
|
99
|
+
stop_streaming(vin: string): Promise<Record<string, any>>;
|
|
100
|
+
/**
|
|
101
|
+
* Modify a vehicle's streaming configuration.
|
|
102
|
+
* @param vin
|
|
103
|
+
* @param fields Fields to stream with their configuration.
|
|
104
|
+
*/
|
|
105
|
+
modify_streaming_config(vin: string, fields: Record<string, any>): Promise<Record<string, any>>;
|
|
106
|
+
/**
|
|
107
|
+
* Create or update a vehicle's streaming configuration.
|
|
108
|
+
* @param vin
|
|
109
|
+
* @param fields Fields to stream with their configuration.
|
|
110
|
+
*/
|
|
111
|
+
create_streaming_config(vin: string, fields: Record<string, any>): Promise<Record<string, any>>;
|
|
22
112
|
}
|
|
23
113
|
export {};
|
package/dist/teslemetry.mjs
CHANGED
|
@@ -1,14 +1,76 @@
|
|
|
1
|
+
import { is_valid_region } from "./const.mjs";
|
|
2
|
+
import { InvalidResponse, TeslemetryRegistrationError } from "./exceptions.mjs";
|
|
1
3
|
import TeslaFleetApi from "./teslafleetapi.mjs";
|
|
4
|
+
import { TeslemetryEnergySites } from "./teslemetryenergy.mjs";
|
|
5
|
+
import { TeslemetryVehicles } from "./teslemetryvehicle.mjs";
|
|
2
6
|
// src/teslemetry.ts
|
|
7
|
+
/** Teslemetry's OAuth dynamic client registration endpoint. */
|
|
8
|
+
const REGISTER_URL = "https://api.teslemetry.com/oauth/register";
|
|
9
|
+
/**
|
|
10
|
+
* Dynamically register an OAuth client with Teslemetry (RFC 7591). This always
|
|
11
|
+
* registers a new client: the caller persists the `client_id` and skips
|
|
12
|
+
* registration on later runs.
|
|
13
|
+
* @param client_name Human-readable client name shown during consent.
|
|
14
|
+
* @param software_id Identifier for the calling application.
|
|
15
|
+
* @param software_version Version string for the calling application.
|
|
16
|
+
* @param options `fetch` replaces the global `fetch`; `timeout` aborts after this many milliseconds.
|
|
17
|
+
* @throws TeslemetryRegistrationError when the endpoint cannot be reached or
|
|
18
|
+
* refuses, the body is not JSON, or it has no usable `client_id`. The reason is in `data`.
|
|
19
|
+
*/
|
|
20
|
+
async function register_client(client_name, software_id, software_version, options = {}) {
|
|
21
|
+
const unreachable = "Could not reach Teslemetry to register a client";
|
|
22
|
+
let text;
|
|
23
|
+
try {
|
|
24
|
+
const resp = await (options.fetch ?? fetch)(REGISTER_URL, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { "Content-Type": "application/json" },
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
client_name,
|
|
29
|
+
software_id,
|
|
30
|
+
software_version
|
|
31
|
+
}),
|
|
32
|
+
signal: options.timeout != null ? AbortSignal.timeout(options.timeout) : null
|
|
33
|
+
});
|
|
34
|
+
if (!resp.ok) throw new TeslemetryRegistrationError(unreachable, resp.status);
|
|
35
|
+
text = await resp.text();
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error instanceof TeslemetryRegistrationError) throw error;
|
|
38
|
+
throw new TeslemetryRegistrationError(unreachable, null, { cause: error });
|
|
39
|
+
}
|
|
40
|
+
let registration;
|
|
41
|
+
try {
|
|
42
|
+
registration = JSON.parse(text);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw new TeslemetryRegistrationError("Teslemetry returned a malformed registration response", null, { cause: error });
|
|
45
|
+
}
|
|
46
|
+
const raw = registration !== null && typeof registration === "object" && !Array.isArray(registration) ? registration : null;
|
|
47
|
+
const client_id = raw?.client_id;
|
|
48
|
+
if (raw === null || typeof client_id !== "string" || !client_id) throw new TeslemetryRegistrationError("Teslemetry registration response did not contain a client_id");
|
|
49
|
+
return {
|
|
50
|
+
client_id,
|
|
51
|
+
raw
|
|
52
|
+
};
|
|
53
|
+
}
|
|
3
54
|
var Teslemetry = class extends TeslaFleetApi {
|
|
4
|
-
|
|
55
|
+
static Vehicles = TeslemetryVehicles;
|
|
56
|
+
static EnergySites = TeslemetryEnergySites;
|
|
57
|
+
_transport_name = "teslemetry";
|
|
58
|
+
constructor(accessToken, region = "api", options = {}) {
|
|
5
59
|
super({
|
|
60
|
+
...options,
|
|
6
61
|
accessToken,
|
|
7
62
|
server: `https://${region}.teslemetry.com`,
|
|
8
|
-
partnerScope: false
|
|
9
|
-
userScope: false
|
|
63
|
+
partnerScope: false
|
|
10
64
|
});
|
|
11
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Set the server from the account's region in `metadata()`.
|
|
68
|
+
* @returns The region, e.g. `"na"`.
|
|
69
|
+
*/
|
|
70
|
+
async find_server() {
|
|
71
|
+
const { region } = await this.metadata(true);
|
|
72
|
+
return region.toLowerCase();
|
|
73
|
+
}
|
|
12
74
|
async _response(resp) {
|
|
13
75
|
if (resp.headers.has("x-region")) {
|
|
14
76
|
const region = resp.headers.get("x-region")?.toLowerCase();
|
|
@@ -16,11 +78,19 @@ var Teslemetry = class extends TeslaFleetApi {
|
|
|
16
78
|
}
|
|
17
79
|
return super._response(resp);
|
|
18
80
|
}
|
|
81
|
+
/** Send a ping. */
|
|
82
|
+
async ping() {
|
|
83
|
+
return this._request("GET", "api/ping");
|
|
84
|
+
}
|
|
19
85
|
/**
|
|
20
86
|
* Test the connection to Teslemetry
|
|
21
87
|
*/
|
|
22
88
|
async test() {
|
|
23
|
-
return this._request("GET", "api/test")
|
|
89
|
+
return this._request("GET", "api/test");
|
|
90
|
+
}
|
|
91
|
+
/** Get userdata. */
|
|
92
|
+
async userdata() {
|
|
93
|
+
return this._request("GET", "api/userdata");
|
|
24
94
|
}
|
|
25
95
|
/**
|
|
26
96
|
* Get details about your Teslemetry account
|
|
@@ -28,9 +98,84 @@ var Teslemetry = class extends TeslaFleetApi {
|
|
|
28
98
|
*/
|
|
29
99
|
async metadata(update_region = true) {
|
|
30
100
|
return this._request("GET", "api/metadata").then((metadata) => {
|
|
31
|
-
if (update_region
|
|
101
|
+
if (update_region && metadata.region) {
|
|
102
|
+
const region = metadata.region.toLowerCase();
|
|
103
|
+
if (is_valid_region(region)) this.region = region;
|
|
104
|
+
this.server = `https://${region}.teslemetry.com`;
|
|
105
|
+
this._log(`Using server ${this.server}`);
|
|
106
|
+
}
|
|
32
107
|
return metadata;
|
|
33
108
|
});
|
|
34
109
|
}
|
|
110
|
+
/** Get the user's scopes. Leaves the server unchanged. */
|
|
111
|
+
async scopes() {
|
|
112
|
+
return this.metadata(false).then(({ scopes }) => scopes);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get or set Auto mode.
|
|
116
|
+
* @param vin
|
|
117
|
+
* @param value `true` enables it, `false` disables it, omit to read it.
|
|
118
|
+
*/
|
|
119
|
+
async server_side_polling(vin, value) {
|
|
120
|
+
const method = value === true ? "POST" : value === false ? "DELETE" : "GET";
|
|
121
|
+
return this._request(method, `api/auto/${vin}`).then(({ response }) => response);
|
|
122
|
+
}
|
|
123
|
+
/** Force a refresh of the vehicle data. */
|
|
124
|
+
async vehicle_data_refresh(vin) {
|
|
125
|
+
return this._request("GET", `api/refresh/${vin}`);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Migrate from an access token to OAuth.
|
|
129
|
+
* @param client_id
|
|
130
|
+
* @param name
|
|
131
|
+
* @returns The token response, with `expires_in` as an integer and `expires_at` in epoch seconds.
|
|
132
|
+
* @throws InvalidResponse when the reply has no usable `expires_in`.
|
|
133
|
+
*/
|
|
134
|
+
async migrate_to_oauth(client_id = "homeassistant", name) {
|
|
135
|
+
const access_token = await this.access_token();
|
|
136
|
+
const new_token = await this._request("POST", "oauth/token", null, {
|
|
137
|
+
grant_type: "migrate",
|
|
138
|
+
client_id,
|
|
139
|
+
access_token: access_token.trim(),
|
|
140
|
+
name
|
|
141
|
+
});
|
|
142
|
+
const expires_in = Math.trunc(new_token?.expires_in == null ? NaN : Number(new_token.expires_in));
|
|
143
|
+
if (!Number.isFinite(expires_in)) throw new InvalidResponse("Teslemetry token response did not contain a valid expires_in");
|
|
144
|
+
new_token.expires_in = expires_in;
|
|
145
|
+
new_token.expires_at = Date.now() / 1e3 + expires_in;
|
|
146
|
+
return new_token;
|
|
147
|
+
}
|
|
148
|
+
/** Get streaming field parameters and metadata. */
|
|
149
|
+
async fields() {
|
|
150
|
+
return this._request("GET", "fields.json");
|
|
151
|
+
}
|
|
152
|
+
/** Get the saved vehicle configuration. */
|
|
153
|
+
async vehicle_config(vin) {
|
|
154
|
+
return this._request("GET", `api/vehicle_config/${vin}`);
|
|
155
|
+
}
|
|
156
|
+
/** Get a vehicle's streaming configuration: certificate, hostname, port and configurable telemetry fields. */
|
|
157
|
+
async streaming_config(vin) {
|
|
158
|
+
return this._request("GET", `api/config/${vin}`);
|
|
159
|
+
}
|
|
160
|
+
/** Stop streaming data from a vehicle. */
|
|
161
|
+
async stop_streaming(vin) {
|
|
162
|
+
return this._request("DELETE", `api/config/${vin}`);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Modify a vehicle's streaming configuration.
|
|
166
|
+
* @param vin
|
|
167
|
+
* @param fields Fields to stream with their configuration.
|
|
168
|
+
*/
|
|
169
|
+
async modify_streaming_config(vin, fields) {
|
|
170
|
+
return this._request("PATCH", `api/config/${vin}`, null, { fields });
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Create or update a vehicle's streaming configuration.
|
|
174
|
+
* @param vin
|
|
175
|
+
* @param fields Fields to stream with their configuration.
|
|
176
|
+
*/
|
|
177
|
+
async create_streaming_config(vin, fields) {
|
|
178
|
+
return this._request("POST", `api/config/${vin}`, null, { fields });
|
|
179
|
+
}
|
|
35
180
|
};
|
|
36
|
-
export { Teslemetry as default };
|
|
181
|
+
export { REGISTER_URL, Teslemetry as default, register_client };
|