@eiannone/tesla-api 1.5.0 → 1.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/TeslaApi.js +8 -2
- package/TeslaStream.js +36 -31
- package/package.json +1 -1
package/TeslaApi.js
CHANGED
|
@@ -25,6 +25,11 @@ class TeslaApi {
|
|
|
25
25
|
this.vid = vehicle_id;
|
|
26
26
|
this.token = access_token;
|
|
27
27
|
this.refresh_token = refresh_token;
|
|
28
|
+
this.timeout = 10000;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setTimeout(seconds) {
|
|
32
|
+
this.timeout = seconds * 1000;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
#decodeStatus(statusCode) {
|
|
@@ -34,7 +39,8 @@ class TeslaApi {
|
|
|
34
39
|
case 405: return ApiError.IN_SERVICE;
|
|
35
40
|
case 408: return ApiError.UNAVAILABLE;
|
|
36
41
|
case 500: return ApiError.SERVER;
|
|
37
|
-
case
|
|
42
|
+
case 502: return ApiError.NETWORK; // Bad gateway
|
|
43
|
+
case 503: return ApiError.NETWORK; // Service unavailable
|
|
38
44
|
case 504: return ApiError.TIMEOUT;
|
|
39
45
|
case 540: return ApiError.UNAVAILABLE; // TODO: check. Should be system booting
|
|
40
46
|
default: return ApiError.UNKNOWN;
|
|
@@ -45,7 +51,7 @@ class TeslaApi {
|
|
|
45
51
|
return new Promise((resolve, reject) => {
|
|
46
52
|
const req = request(BASE_URL + "/api/1/vehicles/" + path, {
|
|
47
53
|
headers: { 'user-agent': "TeslaEma", 'Authorization': "Bearer " + this.token },
|
|
48
|
-
timeout:
|
|
54
|
+
timeout: this.timeout,
|
|
49
55
|
method: method
|
|
50
56
|
}, res => {
|
|
51
57
|
if (res.statusCode > 199 && res.statusCode < 300) {
|
package/TeslaStream.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
|
+
import {EventEmitter} from 'events';
|
|
2
3
|
|
|
3
|
-
const IDLE = 0;
|
|
4
4
|
const CONNECTING = 1;
|
|
5
5
|
const CONNECTED = 2;
|
|
6
6
|
const CLOSING = 3;
|
|
7
7
|
const CLOSED = 4;
|
|
8
8
|
|
|
9
|
-
export default class TeslaStream {
|
|
9
|
+
export default class TeslaStream extends EventEmitter {
|
|
10
10
|
static DEFAULT_COLUMNS = ['elevation', 'est_heading', 'est_lat', 'est_lng', 'odometer', 'power', 'shift_state', 'speed', 'soc'];
|
|
11
11
|
|
|
12
|
-
constructor(logger = null
|
|
12
|
+
constructor(logger = null) {
|
|
13
13
|
this.log = logger || this.#internalLog;
|
|
14
14
|
this.ws = null;
|
|
15
|
-
this.state =
|
|
15
|
+
this.state = CLOSED;
|
|
16
16
|
this.columns = TeslaStream.DEFAULT_COLUMNS;
|
|
17
17
|
this.checkTimeout = null;
|
|
18
18
|
this.timeouts = 0;
|
|
19
19
|
this.disconnects = 0;
|
|
20
|
-
this.cbInactive = cb_inactive;
|
|
21
|
-
this.cbDisconnects = cb_disconnects;
|
|
22
20
|
this.reconnect = true;
|
|
23
21
|
this.tag = null;
|
|
24
22
|
this.lastShiftState = null;
|
|
@@ -28,6 +26,10 @@ export default class TeslaStream {
|
|
|
28
26
|
return this.state == CONNECTED;
|
|
29
27
|
}
|
|
30
28
|
|
|
29
|
+
isClosed() {
|
|
30
|
+
return this.state == CLOSED;
|
|
31
|
+
}
|
|
32
|
+
|
|
31
33
|
#internalLog(msg, level = 'debug') {
|
|
32
34
|
console.log("[%s] %s", level, msg);
|
|
33
35
|
}
|
|
@@ -38,7 +40,7 @@ export default class TeslaStream {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
disconnect(reconnect = false, unsubscribe = true) {
|
|
41
|
-
if (this.state ==
|
|
43
|
+
if (this.state == CLOSED) return;
|
|
42
44
|
this.reconnect = reconnect;
|
|
43
45
|
if (this.checkTimeout != null) {
|
|
44
46
|
clearTimeout(this.checkTimeout);
|
|
@@ -69,9 +71,9 @@ export default class TeslaStream {
|
|
|
69
71
|
this.timeouts += 1;
|
|
70
72
|
let level = (this.timeouts < 8)? 'debug' : 'info';
|
|
71
73
|
this.log("Stream connection timed out / " + this.timeouts, level);
|
|
72
|
-
if (this.timeouts %
|
|
74
|
+
if (this.timeouts % 3 == 0) { // Teslamate does it every 5th attempt
|
|
73
75
|
this.log("Stream inactive!");
|
|
74
|
-
|
|
76
|
+
this.emit('inactive');
|
|
75
77
|
}
|
|
76
78
|
this.#reconnect();
|
|
77
79
|
}
|
|
@@ -86,7 +88,24 @@ export default class TeslaStream {
|
|
|
86
88
|
}));
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
|
|
91
|
+
#disconnectResubscribe(tag, token, reason, minDelay) {
|
|
92
|
+
this.disconnects++;
|
|
93
|
+
this.log(reason + ((this.disconnects == 1)? '' : ' / '+this.disconnects), (this.disconnects < 8)? 'debug' : 'info');
|
|
94
|
+
clearTimeout(this.checkTimeout);
|
|
95
|
+
if (this.disconnects % 10 == 0) {
|
|
96
|
+
this.log("Too many disconnects!", "warn");
|
|
97
|
+
this.emit('too-many-diconnects');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const ms = (this.lastShiftState != null && this.lastShiftState != "")?
|
|
101
|
+
this.#expBackOffMs(this.disconnects, 0, 8, 1.3) :
|
|
102
|
+
this.#expBackOffMs(this.disconnects, minDelay, 60); // Teslamate uses min 15, max 30
|
|
103
|
+
this.log("Waiting for " + Math.round(ms / 1000) + " sec...");
|
|
104
|
+
this.checkTimeout = setTimeout(_ => { this.#subscribe(tag, token); }, ms);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
connect(tag, token, columns = null, resubscribeDelay = 30) {
|
|
90
109
|
this.tag = tag.toString();
|
|
91
110
|
if (columns != null) this.columns = columns;
|
|
92
111
|
let shiftStatePos = this.columns.indexOf('shift_state');
|
|
@@ -115,36 +134,22 @@ export default class TeslaStream {
|
|
|
115
134
|
this.state = CONNECTED;
|
|
116
135
|
} else if (d.msg_type == 'data:update') {
|
|
117
136
|
let values = d.value.split(",");
|
|
118
|
-
|
|
137
|
+
this.emit('stream', values);
|
|
119
138
|
this.timeouts = 0;
|
|
120
139
|
this.disconnects = 0;
|
|
121
140
|
if (shiftStatePos > -1) this.lastShiftState = values[shiftStatePos + 1];
|
|
122
141
|
} else if (d.msg_type == 'data:error' && typeof d.error_type != "undefined") {
|
|
123
142
|
switch(d.error_type) {
|
|
124
143
|
case "vehicle_disconnected":
|
|
125
|
-
this
|
|
126
|
-
const level = (this.disconnects < 8)? 'debug' : 'info';
|
|
127
|
-
const nDisconnections = (this.disconnects == 1)? '' : ' / '+this.disconnects;
|
|
128
|
-
this.log("Vehicle disconnected" + nDisconnections, level);
|
|
129
|
-
clearTimeout(this.checkTimeout);
|
|
130
|
-
if (this.disconnects % 10 == 0) {
|
|
131
|
-
this.log("Too many disconnects!", "warn");
|
|
132
|
-
if (this.cbDisconnects != null) this.cbDisconnects();
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
let ms = (this.lastShiftState != null && this.lastShiftState != "")?
|
|
136
|
-
this.#expBackOffMs(this.disconnects, 0, 8, 1.3) :
|
|
137
|
-
this.#expBackOffMs(this.disconnects, 30, 60); // Teslamate uses min 15, max 30
|
|
138
|
-
this.log("Waiting for " + Math.round(ms / 1000) + " sec...");
|
|
139
|
-
this.checkTimeout = setTimeout(_ => { this.#subscribe(tag, token); }, ms);
|
|
140
|
-
}
|
|
144
|
+
this.#disconnectResubscribe(tag, token, "Vehicle disconnected", resubscribeDelay);
|
|
141
145
|
break;
|
|
142
146
|
case "vehicle_error":
|
|
143
|
-
this
|
|
147
|
+
this.#disconnectResubscribe(tag, token, "Vehicle error: " + d.value, resubscribeDelay);
|
|
144
148
|
break;
|
|
145
149
|
case "client_error":
|
|
146
150
|
this.log("Client error: " + d.value, "error");
|
|
147
|
-
|
|
151
|
+
this.emit('error', d.value);
|
|
152
|
+
this.#reconnect();
|
|
148
153
|
break;
|
|
149
154
|
default:
|
|
150
155
|
this.log("Stream API error ["+d.error_type+"]: " + data, "error");
|
|
@@ -152,7 +157,7 @@ export default class TeslaStream {
|
|
|
152
157
|
}
|
|
153
158
|
} else {
|
|
154
159
|
this.log("Unknown message: " + data, "error");
|
|
155
|
-
|
|
160
|
+
this.emit('error', data);
|
|
156
161
|
}
|
|
157
162
|
});
|
|
158
163
|
this.ws.on('error', (error) => {
|
|
@@ -168,7 +173,7 @@ export default class TeslaStream {
|
|
|
168
173
|
if (this.reconnect) {
|
|
169
174
|
this.checkTimeout = setTimeout(() => {
|
|
170
175
|
this.log("Reconnecting...");
|
|
171
|
-
this.connect(tag, token, columns
|
|
176
|
+
this.connect(tag, token, columns);
|
|
172
177
|
}, 1000);
|
|
173
178
|
}
|
|
174
179
|
});
|