@eiannone/tesla-api 1.9.1 → 1.13.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.
@@ -0,0 +1,17 @@
1
+ {
2
+ // Usare IntelliSense per informazioni sui possibili attributi.
3
+ // Al passaggio del mouse vengono visualizzate le descrizioni degli attributi esistenti.
4
+ // Per altre informazioni, visitare: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "type": "pwa-node",
9
+ "request": "launch",
10
+ "name": "Get-id",
11
+ "skipFiles": [
12
+ "<node_internals>/**"
13
+ ],
14
+ "program": "${workspaceFolder}\\examples\\get-id.js"
15
+ }
16
+ ]
17
+ }
package/TeslaApi.js CHANGED
@@ -21,8 +21,8 @@ class ApiError extends Error {
21
21
  }
22
22
 
23
23
  class TeslaApi {
24
- constructor(access_token = null, vehicle_id = null, refresh_token = null) {
25
- this.vid = vehicle_id;
24
+ constructor(access_token = null, id = null, refresh_token = null) {
25
+ this.vid = id;
26
26
  this.token = access_token;
27
27
  this.refresh_token = refresh_token;
28
28
  this.timeout = 10000;
@@ -47,10 +47,17 @@ class TeslaApi {
47
47
  }
48
48
  }
49
49
 
50
- async #apiCall(path, method = 'GET') {
51
- return new Promise((resolve, reject) => {
50
+ async #apiCall(path = "", method = 'GET', params = undefined) {
51
+ return new Promise((resolve, reject) => {
52
+ const postData = (typeof params != 'undefined')? JSON.stringify(params) : '';
53
+ let headers = { 'user-agent': "TeslaEma", 'Authorization': "Bearer " + this.token };
54
+ if (postData.length > 0) {
55
+ headers['Content-Type'] = 'application/json';
56
+ headers['Content-Length'] = postData.length;
57
+ }
58
+
52
59
  const req = request(BASE_URL + "/api/1/vehicles/" + path, {
53
- headers: { 'user-agent': "TeslaEma", 'Authorization': "Bearer " + this.token },
60
+ headers: headers,
54
61
  timeout: this.timeout,
55
62
  method: method
56
63
  }, res => {
@@ -89,46 +96,48 @@ class TeslaApi {
89
96
  // - ENOTFOUND
90
97
  reject(new ApiError(e.message + " ("+e.code+")", ApiError.NETWORK));
91
98
  });
99
+ if (postData.length > 0) req.write(postData);
92
100
  req.end();
93
101
  });
94
102
  }
95
103
 
96
104
  async getVehicles() {
97
- return this.#apiCall("");
105
+ return this.#apiCall();
98
106
  }
99
107
 
100
- async getVehicle(vehicle_id = null) {
101
- return this.#apiCall((vehicle_id == null)? this.vid : vehicle_id);
108
+ async getVehicle(id = null) {
109
+ return this.#apiCall((id == null)? this.vid : id);
102
110
  }
103
111
 
104
- async getVehicleData(vehicle_id = null) {
105
- const vid = (vehicle_id == null)? this.vid : vehicle_id;
112
+ async getVehicleData(id = null) {
113
+ const vid = (id == null)? this.vid : id;
106
114
  return this.#apiCall(vid + "/vehicle_data");
107
115
  }
108
116
 
109
- async getChargeState(vehicle_id = null) {
110
- const vid = (vehicle_id == null)? this.vid : vehicle_id;
117
+ async getChargeState(id = null) {
118
+ const vid = (id == null)? this.vid : id;
111
119
  return this.#apiCall(vid + "/data_request/charge_state");
112
120
  }
113
121
 
114
- async wakeUp(vehicle_id = null) {
115
- const vid = (vehicle_id == null)? this.vid : vehicle_id;
122
+ async wakeUp(id = null) {
123
+ const vid = (id == null)? this.vid : id;
116
124
  return this.#apiCall(vid + "/wake_up", "POST");
117
125
  }
118
126
 
119
- async command(command, vehicle_id = null) {
120
- const vid = (vehicle_id == null)? this.vid : vehicle_id;
121
- return this.#apiCall(vid + "/command/" + command, "POST");
127
+ async command(command, params = undefined, id = null) {
128
+ const vid = (id == null)? this.vid : id;
129
+ return this.#apiCall(vid + "/command/" + command, "POST", params);
122
130
  }
123
131
 
124
- async #oauthCall(params) {
125
- const post_data = JSON.stringify(params);
132
+ async #oauthCall(params, bearer_token) {
126
133
  return new Promise((resolve, reject) => {
134
+ const postData = JSON.stringify(params);
127
135
  const req = request(BASE_URL + '/oauth/token', {
128
136
  headers: {
129
137
  'user-agent': "TeslaEma",
138
+ 'Authorization': "Bearer " + bearer_token,
130
139
  'Content-Type': 'application/json',
131
- 'Content-Length': post_data.length
140
+ 'Content-Length': postData.length
132
141
  },
133
142
  timeout: 30000,
134
143
  method: 'POST'
@@ -157,9 +166,50 @@ class TeslaApi {
157
166
  // - ENOTFOUND
158
167
  reject(new ApiError(e.message + " ("+e.code+")", ApiError.NETWORK));
159
168
  });
160
- req.write(post_data);
169
+ req.write(postData);
161
170
  req.end();
162
- });s
171
+ });
172
+ }
173
+
174
+ async #oauthCall2(params) {
175
+ return new Promise((resolve, reject) => {
176
+ const postData = JSON.stringify(params);
177
+ const req = request('https://auth.tesla.com/oauth2/v3/token', {
178
+ headers: {
179
+ 'user-agent': "TeslaEma",
180
+ 'Content-Type': 'application/json',
181
+ 'Content-Length': postData.length
182
+ },
183
+ timeout: 30000,
184
+ method: 'POST'
185
+ }, res => {
186
+ if (res.statusCode > 199 && res.statusCode < 300) {
187
+ res.setEncoding('utf8');
188
+ let rawData = '';
189
+ res.on('data', chunk => { rawData += chunk; });
190
+ res.on('end', () => {
191
+ try {
192
+ resolve(JSON.parse(rawData));
193
+ } catch(err) {
194
+ reject(new ApiError(err));
195
+ }
196
+ });
197
+ } else {
198
+ let errMsg = res.statusMessage + " ("+res.statusCode+")";
199
+ reject(new ApiError(errMsg, this.#decodeStatus(res.statusCode)));
200
+ }
201
+ });
202
+ req.on('error', e => {
203
+ // Error code examples:
204
+ // - EAI_AGAIN (DNS lookup timeout)
205
+ // - ECONNRESET
206
+ // - ECONNREFUSED
207
+ // - ENOTFOUND
208
+ reject(new ApiError(e.message + " ("+e.code+")", ApiError.NETWORK));
209
+ });
210
+ req.write(postData);
211
+ req.end();
212
+ });
163
213
  }
164
214
 
165
215
  onTokenRefreh(callback) {
@@ -167,28 +217,39 @@ class TeslaApi {
167
217
  }
168
218
 
169
219
  async refreshToken(refresh_token) {
170
- return this.#oauthCall({'grant_type': "refresh_token", 'refresh_token': refresh_token})
171
- .then(async (resp) => {
172
- this.token = resp.access_token;
173
- this.refresh_token = resp.refresh_token;
174
- if (typeof this.cb_refreshToken == 'function') {
175
- this.cb_refreshToken(this.token, this.refresh_token);
176
- }
177
- return resp;
178
- })
179
- .catch(error => {
180
- if (error instanceof Error) error.message += " - Unable to refresh Token";
181
- throw error;
182
- });
220
+ const payLoad = {
221
+ grant_type: 'refresh_token',
222
+ client_id: 'ownerapi',
223
+ refresh_token,
224
+ scope: 'openid email offline_access'
225
+ };
226
+ try {
227
+ let resp = await this.#oauthCall2(payLoad);
228
+ this.refresh_token = resp.refresh_token;
229
+ let oauth = await this.#oauthCall({
230
+ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
231
+ client_id: '81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384',
232
+ client_secret: 'c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3'
233
+ }, resp.access_token);
234
+ this.token = oauth.access_token;
235
+ if (typeof this.cb_refreshToken == 'function') {
236
+ this.cb_refreshToken(this.token, this.refresh_token);
237
+ }
238
+ return oauth;
239
+ }
240
+ catch(error) {
241
+ if (error instanceof Error) error.message += " - Unable to refresh Token";
242
+ throw error;
243
+ }
183
244
  }
184
245
 
185
- async getTokens(email, password) {
186
- return this.#oauthCall({
187
- 'grant_type': "password",
188
- 'email': email,
189
- 'password': password,
190
- 'client_id': "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384",
191
- 'client_secret': "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3"
246
+ async getId(vehicle_id) {
247
+ return this.#apiCall().then(vehicles => {
248
+ for (let v = 0; v < vehicles.length; v++) {
249
+ if (!vehicles[v].hasOwnProperty('vehicle_id') || !vehicles[v].hasOwnProperty('id_s')) continue;
250
+ if (vehicles[v].vehicle_id == vehicle_id) return vehicles[v].id_s;
251
+ }
252
+ throw new ApiError("Vehicle not found");
192
253
  });
193
254
  }
194
255
  }
package/TeslaStream.js CHANGED
@@ -164,7 +164,7 @@ export default class TeslaStream extends EventEmitter {
164
164
  case "client_error":
165
165
  this.log("Client error: " + d.value, "error");
166
166
  this.emit('error', d.value);
167
- this.#reconnect();
167
+ if (d.value != "Can't validate token. ") this.#reconnect();
168
168
  break;
169
169
  default:
170
170
  this.log("Stream API error ["+d.error_type+"]: " + data, "error");
@@ -0,0 +1,4 @@
1
+ import {TeslaApi, ApiError} from '../TeslaApi.js';
2
+
3
+ const api = new TeslaApi('eu-abcde');
4
+ api.getId('1234').then(console.log);
@@ -0,0 +1,11 @@
1
+ import {TeslaApi, ApiError} from '../TeslaApi.js';
2
+
3
+ const api = new TeslaApi();
4
+ api.refreshToken('1234')
5
+ .then(result => console.log(JSON.stringify(result)))
6
+ .catch(err => {
7
+ let reason = (err instanceof ApiError)? err.reason : ApiError.UNKNOWN;
8
+ const errMsg = (err instanceof Error)? err.message : err;
9
+ console.error("ApiError " + reason + ": " + errMsg);
10
+ process.exit(-1);
11
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eiannone/tesla-api",
3
- "version": "1.9.1",
3
+ "version": "1.13.0",
4
4
  "description": "Nodejs Tesla API",
5
5
  "type": "module",
6
6
  "main": "index.js",