@eiannone/tesla-api 1.10.0 → 1.14.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/.vscode/launch.json +17 -0
- package/TeslaApi.js +92 -36
- package/examples/get-id.js +4 -0
- package/examples/refresh-token.js +11 -0
- package/package.json +1 -1
|
@@ -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,
|
|
25
|
-
this.vid =
|
|
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,7 +47,7 @@ class TeslaApi {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
async #apiCall(path, method = 'GET', params = undefined) {
|
|
50
|
+
async #apiCall(path = "", method = 'GET', params = undefined) {
|
|
51
51
|
return new Promise((resolve, reject) => {
|
|
52
52
|
const postData = (typeof params != 'undefined')? JSON.stringify(params) : '';
|
|
53
53
|
let headers = { 'user-agent': "TeslaEma", 'Authorization': "Bearer " + this.token };
|
|
@@ -102,39 +102,40 @@ class TeslaApi {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
async getVehicles() {
|
|
105
|
-
return this.#apiCall(
|
|
105
|
+
return this.#apiCall();
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
async getVehicle(
|
|
109
|
-
return this.#apiCall((
|
|
108
|
+
async getVehicle(id = null) {
|
|
109
|
+
return this.#apiCall((id == null)? this.vid : id);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
async getVehicleData(
|
|
113
|
-
const vid = (
|
|
112
|
+
async getVehicleData(id = null) {
|
|
113
|
+
const vid = (id == null)? this.vid : id;
|
|
114
114
|
return this.#apiCall(vid + "/vehicle_data");
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
async getChargeState(
|
|
118
|
-
const vid = (
|
|
117
|
+
async getChargeState(id = null) {
|
|
118
|
+
const vid = (id == null)? this.vid : id;
|
|
119
119
|
return this.#apiCall(vid + "/data_request/charge_state");
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
async wakeUp(
|
|
123
|
-
const vid = (
|
|
122
|
+
async wakeUp(id = null) {
|
|
123
|
+
const vid = (id == null)? this.vid : id;
|
|
124
124
|
return this.#apiCall(vid + "/wake_up", "POST");
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
async command(command, params = undefined,
|
|
128
|
-
const vid = (
|
|
127
|
+
async command(command, params = undefined, id = null) {
|
|
128
|
+
const vid = (id == null)? this.vid : id;
|
|
129
129
|
return this.#apiCall(vid + "/command/" + command, "POST", params);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
async #oauthCall(params) {
|
|
132
|
+
async #oauthCall(params, bearer_token) {
|
|
133
133
|
return new Promise((resolve, reject) => {
|
|
134
134
|
const postData = JSON.stringify(params);
|
|
135
135
|
const req = request(BASE_URL + '/oauth/token', {
|
|
136
136
|
headers: {
|
|
137
137
|
'user-agent': "TeslaEma",
|
|
138
|
+
'Authorization': "Bearer " + bearer_token,
|
|
138
139
|
'Content-Type': 'application/json',
|
|
139
140
|
'Content-Length': postData.length
|
|
140
141
|
},
|
|
@@ -167,7 +168,48 @@ class TeslaApi {
|
|
|
167
168
|
});
|
|
168
169
|
req.write(postData);
|
|
169
170
|
req.end();
|
|
170
|
-
});
|
|
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
|
+
});
|
|
171
213
|
}
|
|
172
214
|
|
|
173
215
|
onTokenRefreh(callback) {
|
|
@@ -175,28 +217,42 @@ class TeslaApi {
|
|
|
175
217
|
}
|
|
176
218
|
|
|
177
219
|
async refreshToken(refresh_token) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
+
}
|
|
191
244
|
}
|
|
192
245
|
|
|
193
|
-
async
|
|
194
|
-
return this.#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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) {
|
|
251
|
+
this.vid = vehicles[v].id_s;
|
|
252
|
+
return this.vid;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
throw new ApiError("Vehicle not found");
|
|
200
256
|
});
|
|
201
257
|
}
|
|
202
258
|
}
|
|
@@ -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
|
+
});
|