tesla-fleet-api 0.2.0 → 0.3.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 (53) hide show
  1. package/dist/commands.d.ts +352 -0
  2. package/dist/commands.js +875 -0
  3. package/dist/commands.js.map +1 -0
  4. package/dist/energy.js +6 -2
  5. package/dist/energy.js.map +1 -1
  6. package/dist/index.d.ts +8 -1
  7. package/dist/index.js +7 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/signing/crypto.d.ts +10 -0
  10. package/dist/signing/crypto.js +26 -0
  11. package/dist/signing/crypto.js.map +1 -0
  12. package/dist/signing/errors.d.ts +29 -0
  13. package/dist/signing/errors.js +40 -0
  14. package/dist/signing/errors.js.map +1 -0
  15. package/dist/signing/session.d.ts +69 -0
  16. package/dist/signing/session.js +102 -0
  17. package/dist/signing/session.js.map +1 -0
  18. package/dist/tariff.d.ts +29 -0
  19. package/dist/tariff.js +302 -0
  20. package/dist/tariff.js.map +1 -0
  21. package/dist/teslafleetapi.d.ts +5 -0
  22. package/dist/teslafleetapi.js +23 -9
  23. package/dist/teslafleetapi.js.map +1 -1
  24. package/dist/teslemetry.d.ts +4 -0
  25. package/dist/teslemetry.js +15 -1
  26. package/dist/teslemetry.js.map +1 -1
  27. package/dist/types/site_info.d.ts +37 -8
  28. package/dist/types/vehicle.d.ts +1 -0
  29. package/dist/types/vehicle.js +9 -1
  30. package/dist/types/vehicle.js.map +1 -1
  31. package/dist/vehicle.d.ts +12 -0
  32. package/dist/vehicle.js +13 -8
  33. package/dist/vehicle.js.map +1 -1
  34. package/dist/vehiclesigned.d.ts +13 -0
  35. package/dist/vehiclesigned.js +19 -0
  36. package/dist/vehiclesigned.js.map +1 -0
  37. package/dist/vehiclespecific.d.ts +3 -15
  38. package/dist/vehiclespecific.js.map +1 -1
  39. package/package.json +16 -6
  40. package/src/commands.ts +1134 -0
  41. package/src/energy.ts +197 -135
  42. package/src/index.ts +13 -1
  43. package/src/signing/crypto.ts +29 -0
  44. package/src/signing/errors.ts +50 -0
  45. package/src/signing/session.ts +132 -0
  46. package/src/tariff.ts +375 -0
  47. package/src/teslafleetapi.ts +160 -121
  48. package/src/teslemetry.ts +39 -24
  49. package/src/types/site_info.ts +38 -8
  50. package/src/types/vehicle.ts +10 -0
  51. package/src/vehicle.ts +16 -10
  52. package/src/vehiclesigned.ts +20 -0
  53. package/src/vehiclespecific.ts +811 -721
package/src/teslemetry.ts CHANGED
@@ -6,34 +6,49 @@ type Region = "na" | "eu" | "api";
6
6
  type Test = { response: boolean };
7
7
 
8
8
  type Metadata = {
9
- uid: string;
10
- region: "NA" | "EU";
11
- scopes: Scope[];
12
- vins: string[];
9
+ uid: string;
10
+ region: "NA" | "EU";
11
+ scopes: Scope[];
12
+ vins: string[];
13
13
  };
14
14
 
15
15
  export default class Telemetry extends TeslaFleetApi {
16
- constructor(accessToken: string, region: Region = "api") {
17
- super({ accessToken, server: `https://${region}.teslemetry.com`, partnerScope: false, userScope: false });
18
- }
16
+ constructor(accessToken: string, region: Region = "api") {
17
+ super({
18
+ accessToken,
19
+ server: `https://${region}.teslemetry.com`,
20
+ partnerScope: false,
21
+ userScope: false,
22
+ });
23
+ }
19
24
 
20
- /**
21
- * Test the connection to Teslemetry
22
- */
23
- async test(): Promise<Test> {
24
- return this._request("GET", "/api/test").then(({ response }) => response);
25
+ async _response(resp: Response) {
26
+ if (resp.headers.has("x-region")) {
27
+ const region = resp.headers.get("x-region")?.toLowerCase();
28
+ if (region) {
29
+ this.server = `https://${region}.teslemetry.com`;
30
+ }
25
31
  }
32
+ return super._response(resp);
33
+ }
26
34
 
27
- /**
28
- * Get details about your Teslemetry account
29
- * @param update_region Update the server URL based on the region in the metadata
30
- */
31
- async metadata(update_region = true): Promise<Metadata> {
32
- return this._request("GET", "/api/metadata").then((metadata) => {
33
- if (update_region) {
34
- this.server = `https://${metadata.region.toLowerCase()}.teslemetry.com`;
35
- }
36
- return metadata;
37
- });
38
- }
35
+ /**
36
+ * Test the connection to Teslemetry
37
+ */
38
+ async test(): Promise<Test> {
39
+ return this._request("GET", "/api/test").then(({ response }) => response);
40
+ }
41
+
42
+ /**
43
+ * Get details about your Teslemetry account
44
+ * @param update_region Update the server URL based on the region in the metadata
45
+ */
46
+ async metadata(update_region = true): Promise<Metadata> {
47
+ return this._request("GET", "/api/metadata").then((metadata) => {
48
+ if (update_region) {
49
+ this.server = `https://${metadata.region.toLowerCase()}.teslemetry.com`;
50
+ }
51
+ return metadata;
52
+ });
53
+ }
39
54
  }
@@ -110,20 +110,50 @@ export type UserSettings = {
110
110
  off_grid_vehicle_charging_enabled: boolean;
111
111
  };
112
112
 
113
+ export type TariffPeriodWindow = {
114
+ fromDayOfWeek?: number;
115
+ toDayOfWeek?: number;
116
+ fromHour?: number;
117
+ toHour?: number;
118
+ fromMinute?: number;
119
+ toMinute?: number;
120
+ };
121
+
122
+ export type TouPeriods = Record<string, { periods: TariffPeriodWindow[] }>;
123
+
124
+ export type Season = {
125
+ fromDay?: number;
126
+ toDay?: number;
127
+ fromMonth?: number;
128
+ toMonth?: number;
129
+ tou_periods?: TouPeriods;
130
+ };
131
+
132
+ export type EnergyCharges = Record<string, { rates?: Record<string, number> }>;
133
+
134
+ export type SellTariff = {
135
+ name?: string;
136
+ utility?: string;
137
+ daily_charges?: Record<string, any>[];
138
+ demand_charges?: Record<string, any>;
139
+ energy_charges: EnergyCharges;
140
+ seasons?: Record<string, Season>;
141
+ };
142
+
113
143
  export type TariffContentV2 = {
114
144
  version: number;
115
- monthly_minimum_bill: number;
116
- min_applicable_demand: number;
117
- max_applicable_demand: number;
118
- monthly_charges: number;
145
+ monthly_minimum_bill?: number;
146
+ min_applicable_demand?: number;
147
+ max_applicable_demand?: number;
148
+ monthly_charges?: number;
119
149
  utility: string;
120
150
  code: string;
121
151
  name: string;
122
152
  currency: string;
123
153
  daily_charges: Record<string, any>[];
124
- daily_demand_charges: Record<string, any>;
154
+ daily_demand_charges?: Record<string, any>;
125
155
  demand_charges: Record<string, any>;
126
- energy_charges: Record<string, any>;
127
- seasons: Record<string, any>[];
128
- sell_tariff: Record<string, any>;
156
+ energy_charges: EnergyCharges;
157
+ seasons: Record<string, Season>;
158
+ sell_tariff?: SellTariff;
129
159
  };
@@ -21,3 +21,13 @@ export type VehicleResponse = {
21
21
  export type GranularAccess = {
22
22
  hide_private: boolean;
23
23
  };
24
+
25
+ export const Models: Record<string, string> = {
26
+ S: "Model S",
27
+ "3": "Model 3",
28
+ X: "Model X",
29
+ Y: "Model Y",
30
+ C: "Cybertruck",
31
+ T: "Semi",
32
+ A: "Cybercab",
33
+ };
package/src/vehicle.ts CHANGED
@@ -1,21 +1,15 @@
1
1
  import TeslaFleetApi from "./teslafleetapi.js";
2
2
  import { ClimateMode, ClimateModes, CommandResponse, FleetTelemetryConfig, Level, Seat, Seats, Trunk, VehicleDataEndpoint } from "./types/commands.js";
3
3
  import { OptionsResponse } from "./types/responses.js";
4
- import { VehicleResponse } from "./types/vehicle.js";
4
+ import { Models, VehicleResponse } from "./types/vehicle.js";
5
5
  import { VehicleDataResponse } from "./types/vehicle_data.js";
6
6
  import VehicleSpecific from "./vehiclespecific.js";
7
-
8
- const Models: Record<string, string> = {
9
- S: "Model S",
10
- "3": "Model 3",
11
- X: "Model X",
12
- Y: "Model Y",
13
- C: "Cybertruck",
14
- T: "Semi",
15
- };
7
+ import VehicleSigned from "./vehiclesigned.js";
16
8
 
17
9
  export default class Vehicle {
18
10
  parent: TeslaFleetApi;
11
+ /** Fallback private key for `signed()` when a call site doesn't pass one explicitly. */
12
+ private_key?: Buffer;
19
13
 
20
14
  constructor(parent: TeslaFleetApi) {
21
15
  this.parent = parent;
@@ -30,6 +24,18 @@ export default class Vehicle {
30
24
  return new VehicleSpecific(this, vin);
31
25
  }
32
26
 
27
+ /**
28
+ * Returns a class for a single vehicle that signs commands with the
29
+ * Tesla Vehicle Command Protocol and dispatches them through the Fleet
30
+ * API `/signed_command` endpoint, instead of the plaintext REST commands.
31
+ * @param vin
32
+ * @param private_key Raw 32-byte P-256 private key scalar. Falls back to `this.private_key`.
33
+ * @param public_key Uncompressed P-256 public key point. Derived from `private_key` if omitted.
34
+ */
35
+ signed(vin: string, private_key?: Buffer, public_key?: Buffer): VehicleSigned {
36
+ return new VehicleSigned(this, vin, private_key, public_key);
37
+ }
38
+
33
39
  /**
34
40
  * Return the vehicle model name
35
41
  * @param vin
@@ -0,0 +1,20 @@
1
+ import { RoutableMessage } from "@teslemetry/tesla-protocol/command/universal_message";
2
+ import { VehicleStatus } from "@teslemetry/tesla-protocol/command/vcsec";
3
+ import Commands from "./commands.js";
4
+
5
+ /**
6
+ * The Fleet API `/signed_command` transport: fills `Commands`'s abstract
7
+ * `_send` seam by base64-encoding the signed `RoutableMessage`, POSTing it to
8
+ * the cloud `signed_command` endpoint, and decoding the reply. A future BLE
9
+ * transport (Story 3) fills the same seam over a GATT link instead - nothing
10
+ * else in `Commands` needs to change for that to work.
11
+ */
12
+ export default class VehicleSigned extends Commands {
13
+ protected async _send(msg: RoutableMessage, _requires: string, _expectsData?: boolean, _confirmBroadcast?: (status: VehicleStatus) => boolean): Promise<RoutableMessage> {
14
+ const encoded = Buffer.from(RoutableMessage.encode(msg).finish()).toString("base64");
15
+ const json = await this.parent.signed_command(this.vin, encoded);
16
+ const resp = RoutableMessage.decode(Buffer.from(json.response, "base64"));
17
+ this.validateAndUpdateSession(resp, Buffer.from(msg.uuid));
18
+ return resp;
19
+ }
20
+ }