@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 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 503: return ApiError.NETWORK;
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: 30000,
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, cb_inactive = null, cb_disconnects = null) {
12
+ constructor(logger = null) {
13
13
  this.log = logger || this.#internalLog;
14
14
  this.ws = null;
15
- this.state = IDLE;
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 == CLOSING || this.state == CLOSED) return;
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 % 10 == 3) { // Teslamate does it on the 5th attempt
74
+ if (this.timeouts % 3 == 0) { // Teslamate does it every 5th attempt
73
75
  this.log("Stream inactive!");
74
- if (this.cbInactive != null) this.cbInactive();
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
- connect(tag, token, columns = null, cb_data = null, cb_error = null) {
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
- if (cb_data != null) cb_data(values);
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.disconnects++;
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.log("Vehicle error: " + d.value, "error");
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
- if (cb_error != null) cb_error(d.value);
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
- if (cb_error != null) cb_error(data);
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, cb_data, cb_error);
176
+ this.connect(tag, token, columns);
172
177
  }, 1000);
173
178
  }
174
179
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eiannone/tesla-api",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Nodejs Tesla API",
5
5
  "type": "module",
6
6
  "main": "index.js",