iobroker.toyota 0.2.5 → 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.
package/io-package.json CHANGED
@@ -1,8 +1,16 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "toyota",
4
- "version": "0.2.5",
4
+ "version": "0.3.0",
5
5
  "news": {
6
+ "0.3.0": {
7
+ "en": "Fix Login and add Eletric Car data",
8
+ "de": "Login gefixt und elektrische Fahrzeugdaten hinzugefügt"
9
+ },
10
+ "0.2.6": {
11
+ "en": "Skip endpoints that are not available",
12
+ "de": "Überspringen von Endpunkten, die nicht verfügbar sind"
13
+ },
6
14
  "0.2.5": {
7
15
  "en": "Add option to disable trip fetching",
8
16
  "de": "Option zum Deaktivieren des Abrufs von Trips hinzugefügt"
package/lib/tools.js CHANGED
@@ -64,7 +64,7 @@ async function translateYandex(text, targetLang, apiKey) {
64
64
  }
65
65
  throw new Error('Invalid response for translate request');
66
66
  } catch (e) {
67
- throw new Error(`Could not translate to "${targetLang}": ${e}`);
67
+ throw new Error(`Could not translate to "${targetLang}": ${e}`, { cause: e });
68
68
  }
69
69
  }
70
70
 
@@ -87,9 +87,9 @@ async function translateGoogle(text, targetLang) {
87
87
  throw new Error('Invalid response for translate request');
88
88
  } catch (e) {
89
89
  if (e.response && e.response.status === 429) {
90
- throw new Error(`Could not translate to "${targetLang}": Rate-limited by Google Translate`);
90
+ throw new Error(`Could not translate to "${targetLang}": Rate-limited by Google Translate`, { cause: e });
91
91
  } else {
92
- throw new Error(`Could not translate to "${targetLang}": ${e}`);
92
+ throw new Error(`Could not translate to "${targetLang}": ${e}`, { cause: e });
93
93
  }
94
94
  }
95
95
  }
package/main.js CHANGED
@@ -13,6 +13,9 @@ const qs = require('qs');
13
13
  const tough = require('tough-cookie');
14
14
  const { HttpsCookieAgent } = require('http-cookie-agent/http');
15
15
  const jwt = require('jsonwebtoken');
16
+ const { v4: uuidv4 } = require('uuid');
17
+ const crypto = require('crypto');
18
+
16
19
  class Toyota extends utils.Adapter {
17
20
  /**
18
21
  * @param {Partial<utils.AdapterOptions>} [options={}]
@@ -30,6 +33,7 @@ class Toyota extends utils.Adapter {
30
33
  this.token = null;
31
34
  this.uuid = null;
32
35
  this.session = {};
36
+ this.blockedEndpoints = {};
33
37
  this.cookieJar = new tough.CookieJar();
34
38
  this.requestClient = axios.create({
35
39
  withCredentials: true,
@@ -42,8 +46,10 @@ class Toyota extends utils.Adapter {
42
46
  this.updateInterval = null;
43
47
  this.reLoginTimeout = null;
44
48
  this.refreshTokenTimeout = null;
45
- this.hostName = 'myt-agg.toyota-europe.com';
46
49
  this.brand = 'T';
50
+ // Aligned with MyToyota APK 2.23.0
51
+ this.CLIENT_VERSION = '2.23.0';
52
+ this.API_VERSION = 'resource=2.1, protocol=1.0';
47
53
  }
48
54
 
49
55
  /**
@@ -64,7 +70,6 @@ class Toyota extends utils.Adapter {
64
70
  if (this.config.type === 'lexus') {
65
71
  this.log.info('Login to Lexus');
66
72
  this.brand = 'L';
67
- this.hostName = 'lexuslink-agg.toyota-europe.com';
68
73
  }
69
74
  if (!this.config.username || !this.config.password) {
70
75
  this.log.error('No username or password set');
@@ -91,6 +96,7 @@ class Toyota extends utils.Adapter {
91
96
  }, 3500 * 1000);
92
97
  }
93
98
  }
99
+
94
100
  async login() {
95
101
  this.log.info('Login to Toyota');
96
102
  const firstResponse = await this.requestClient({
@@ -100,7 +106,7 @@ class Toyota extends utils.Adapter {
100
106
  headers: {
101
107
  'x-osname': 'iOS',
102
108
  'x-brand': this.brand,
103
- accept: '*/*',
109
+ accept: 'application/json',
104
110
  'x-channel': 'ONEAPP',
105
111
  brand: this.brand,
106
112
  'x-region': 'EU',
@@ -110,8 +116,8 @@ class Toyota extends utils.Adapter {
110
116
  'accept-language': 'de-DE,de;q=0.9',
111
117
  region: 'EU',
112
118
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
113
- 'accept-api-version': 'resource=2.0, protocol=1.0',
114
- 'x-appversion': '2.4.2',
119
+ 'accept-api-version': this.API_VERSION,
120
+ 'x-appversion': this.CLIENT_VERSION,
115
121
  Cookie: 'route=e8e8b55de08efd3c4b34265c0069d319',
116
122
  },
117
123
  })
@@ -126,6 +132,7 @@ class Toyota extends utils.Adapter {
126
132
  this.log.error(JSON.stringify(error.response.data));
127
133
  }
128
134
  });
135
+
129
136
  const secondResponse = await this.requestClient({
130
137
  method: 'post',
131
138
  maxBodyLength: Infinity,
@@ -142,10 +149,10 @@ class Toyota extends utils.Adapter {
142
149
  'x-brand': this.brand,
143
150
  'accept-language': 'de-DE,de;q=0.9',
144
151
  'x-correlationid': 'BDD02A22-CD76-4877-90A9-196EDA5DC695',
145
- 'x-appversion': '2.4.2',
146
- accept: '*/*',
152
+ 'x-appversion': this.CLIENT_VERSION,
153
+ accept: 'application/json',
147
154
  'content-type': 'application/json',
148
- 'accept-api-version': 'resource=2.0, protocol=1.0',
155
+ 'accept-api-version': this.API_VERSION,
149
156
  },
150
157
  data: firstResponse,
151
158
  })
@@ -160,10 +167,13 @@ class Toyota extends utils.Adapter {
160
167
  this.log.error(JSON.stringify(error.response.data));
161
168
  }
162
169
  });
170
+
163
171
  if (!secondResponse || !secondResponse.callbacks) {
164
172
  this.log.error('No Second Response');
173
+ return;
165
174
  }
166
175
  secondResponse.callbacks[0].input[0].value = this.config.username;
176
+
167
177
  const thirdResponse = await this.requestClient({
168
178
  method: 'post',
169
179
  maxBodyLength: Infinity,
@@ -180,10 +190,10 @@ class Toyota extends utils.Adapter {
180
190
  'x-brand': this.brand,
181
191
  'accept-language': 'de-DE,de;q=0.9',
182
192
  'x-correlationid': 'BDD02A22-CD76-4877-90A9-196EDA5DC695',
183
- 'x-appversion': '2.4.2',
184
- accept: '*/*',
193
+ 'x-appversion': this.CLIENT_VERSION,
194
+ accept: 'application/json',
185
195
  'content-type': 'application/json',
186
- 'accept-api-version': 'resource=2.0, protocol=1.0',
196
+ 'accept-api-version': this.API_VERSION,
187
197
  },
188
198
  data: secondResponse,
189
199
  })
@@ -198,12 +208,15 @@ class Toyota extends utils.Adapter {
198
208
  this.log.error(JSON.stringify(error.response.data));
199
209
  }
200
210
  });
211
+
201
212
  if (!thirdResponse || !thirdResponse.callbacks) {
202
213
  this.log.error('No Third Response');
214
+ return;
203
215
  }
204
216
 
205
217
  thirdResponse.callbacks[0].input[0].value = this.config.password;
206
- const idToken = await this.requestClient({
218
+
219
+ await this.requestClient({
207
220
  method: 'post',
208
221
  maxBodyLength: Infinity,
209
222
  url: 'https://b2c-login.toyota-europe.com/json/realms/root/realms/tme/authenticate?authIndexType=service&authIndexValue=oneapp',
@@ -219,10 +232,10 @@ class Toyota extends utils.Adapter {
219
232
  'x-brand': this.brand,
220
233
  'accept-language': 'de-DE,de;q=0.9',
221
234
  'x-correlationid': 'BDD02A22-CD76-4877-90A9-196EDA5DC695',
222
- 'x-appversion': '2.4.2',
223
- accept: '*/*',
235
+ 'x-appversion': this.CLIENT_VERSION,
236
+ accept: 'application/json',
224
237
  'content-type': 'application/json',
225
- 'accept-api-version': 'resource=2.0, protocol=1.0',
238
+ 'accept-api-version': this.API_VERSION,
226
239
  },
227
240
  data: thirdResponse,
228
241
  })
@@ -238,10 +251,21 @@ class Toyota extends utils.Adapter {
238
251
  }
239
252
  });
240
253
 
254
+ // PKCE S256: random 64-byte verifier, base64url(SHA-256(verifier)) as challenge (APK 2.23.0)
255
+ // Keep verifier local so concurrent login() calls do not overwrite each other.
256
+ const codeVerifier = crypto.randomBytes(64).toString('base64url');
257
+ const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');
258
+ const authorizeUrl =
259
+ 'https://b2c-login.toyota-europe.com/oauth2/realms/root/realms/tme/authorize' +
260
+ '?response_type=code&realm=tme&redirect_uri=com.toyota.oneapp:/oauth2Callback' +
261
+ '&client_id=oneapp&scope=' +
262
+ encodeURIComponent('openid profile write') +
263
+ '&code_challenge_method=S256&code_challenge=' +
264
+ codeChallenge;
241
265
  const tokenResponse = await this.requestClient({
242
266
  method: 'get',
243
267
  maxBodyLength: Infinity,
244
- url: 'https://b2c-login.toyota-europe.com/oauth2/realms/root/realms/tme/authorize?response_type=code&realm=tme&redirect_uri=com.toyota.oneapp:/oauth2Callback&client_id=oneapp&scope=openid%20profile%20write&code_challenge_method=S256&code_challenge=Bx88SxgIEnvxrsobwijnUlzg3rrb-zNV4wzDlndWFVc',
268
+ url: authorizeUrl,
245
269
  headers: {
246
270
  'x-osname': 'iOS',
247
271
  'x-brand': this.brand,
@@ -255,8 +279,8 @@ class Toyota extends utils.Adapter {
255
279
  'accept-language': 'de-DE,de;q=0.9',
256
280
  region: 'EU',
257
281
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
258
- 'accept-api-version': 'resource=2.0, protocol=1.0',
259
- 'x-appversion': '2.4.2',
282
+ 'accept-api-version': this.API_VERSION,
283
+ 'x-appversion': this.CLIENT_VERSION,
260
284
  },
261
285
  })
262
286
  .then((res) => {
@@ -271,10 +295,12 @@ class Toyota extends utils.Adapter {
271
295
  this.log.error(error);
272
296
  error.response && this.log.error(JSON.stringify(error.response.data));
273
297
  });
298
+
274
299
  if (!tokenResponse || !tokenResponse.code) {
275
300
  this.log.error('No Token Response');
276
301
  return;
277
302
  }
303
+
278
304
  this.log.info('Start token exchange');
279
305
  await this.requestClient({
280
306
  method: 'post',
@@ -293,16 +319,16 @@ class Toyota extends utils.Adapter {
293
319
  authorization: 'Basic b25lYXBwOm9uZWFwcA==',
294
320
  'accept-language': 'de-DE,de;q=0.9',
295
321
  'x-correlationid': 'E422A08C-1A04-415E-BB08-2386EE06CF90',
296
- 'x-appversion': '2.4.2',
322
+ 'x-appversion': this.CLIENT_VERSION,
297
323
  accept: '*/*',
298
324
  'content-type': 'application/x-www-form-urlencoded',
299
- 'accept-api-version': 'resource=2.0, protocol=1.0',
325
+ 'accept-api-version': this.API_VERSION,
300
326
  },
301
327
  data: {
302
328
  grant_type: 'authorization_code',
303
329
  redirect_uri: 'com.toyota.oneapp:/oauth2Callback',
304
330
  code: tokenResponse.code,
305
- code_verifier: 'tsY5-j-ZLYxNVnmz6wmJ9PTm2Ly7QpfXcQnhyU09Pog',
331
+ code_verifier: codeVerifier,
306
332
  client_id: 'oneapp',
307
333
  },
308
334
  })
@@ -325,6 +351,7 @@ class Toyota extends utils.Adapter {
325
351
  }
326
352
  });
327
353
  }
354
+
328
355
  async refreshToken() {
329
356
  await this.requestClient({
330
357
  method: 'post',
@@ -343,16 +370,15 @@ class Toyota extends utils.Adapter {
343
370
  authorization: 'Basic b25lYXBwOm9uZWFwcA==',
344
371
  'accept-language': 'de-DE,de;q=0.9',
345
372
  'x-correlationid': 'E422A08C-1A04-415E-BB08-2386EE06CF90',
346
- 'x-appversion': '2.4.2',
373
+ 'x-appversion': this.CLIENT_VERSION,
347
374
  accept: '*/*',
348
375
  'content-type': 'application/x-www-form-urlencoded',
349
- 'accept-api-version': 'resource=2.0, protocol=1.0',
376
+ 'accept-api-version': this.API_VERSION,
350
377
  },
351
378
  data: {
352
379
  grant_type: 'refresh_token',
353
380
  redirect_uri: 'com.toyota.oneapp:/oauth2Callback',
354
381
  client_id: 'oneapp',
355
- code_verifier: 'plain',
356
382
  refresh_token: this.session.refresh_token,
357
383
  },
358
384
  })
@@ -368,6 +394,94 @@ class Toyota extends utils.Adapter {
368
394
  }
369
395
  });
370
396
  }
397
+
398
+ generate_hmac_sha256(key, message) {
399
+ return crypto.createHmac('sha256', key).update(message).digest('hex');
400
+ }
401
+
402
+ /**
403
+ * Build the standard set of headers used for all ctpa-oneapi calls.
404
+ * @param {string} [vin] optional vin header for vehicle-scoped endpoints
405
+ */
406
+ buildApiHeaders(vin) {
407
+ const headers = {
408
+ 'x-appbrand': this.brand,
409
+ 'x-device-timezone': 'CEST',
410
+ 'x-osname': 'iOS',
411
+ guid: this.uuid,
412
+ 'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
413
+ 'x-guid': this.uuid,
414
+ region: 'EU',
415
+ brand: this.brand,
416
+ 'x-channel': 'ONEAPP',
417
+ 'x-osversion': '16.7.2',
418
+ 'x-locale': 'de-DE',
419
+ 'x-brand': this.brand,
420
+ authorization: 'Bearer ' + this.session.access_token,
421
+ 'accept-language': 'de-DE,de;q=0.9',
422
+ accept: '*/*',
423
+ 'x-user-region': 'DE',
424
+ 'x-api-key': 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
425
+ API_KEY: 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
426
+ // x-client-ref: HMAC-SHA256 with the app version as key over the user GUID (APK 2.23.0)
427
+ 'x-client-ref': this.generate_hmac_sha256(this.CLIENT_VERSION, this.uuid),
428
+ 'x-correlationid': uuidv4(),
429
+ 'x-appversion': this.CLIENT_VERSION,
430
+ 'x-region': 'EU',
431
+ };
432
+ if (vin) {
433
+ headers.vin = vin;
434
+ }
435
+ return headers;
436
+ }
437
+
438
+ /**
439
+ * Wake the car and trigger a fresh vehicle/climate/EV status pull.
440
+ * After ~20s the regular updateDevices() loop reads the now-current data.
441
+ * @param {string} vin
442
+ */
443
+ async forceRefresh(vin) {
444
+ const refreshEndpoints = [
445
+ { name: 'status', url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/remote/status' },
446
+ {
447
+ name: 'climate',
448
+ url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/remote/refresh-climate-status',
449
+ },
450
+ {
451
+ name: 'electric',
452
+ url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/global/remote/electric/realtime-status',
453
+ },
454
+ ];
455
+ for (const ep of refreshEndpoints) {
456
+ await this.requestClient({
457
+ method: 'post',
458
+ url: ep.url,
459
+ headers: this.buildApiHeaders(vin),
460
+ })
461
+ .then((res) => {
462
+ this.log.debug(`force-refresh ${ep.name}: ${JSON.stringify(res.data)}`);
463
+ })
464
+ .catch((error) => {
465
+ // Non-EV cars typically answer the electric realtime-status request with 404 or 422 - log quietly.
466
+ // 401/403 must NOT be swallowed: they signal an auth problem and have to surface like for any other endpoint.
467
+ if (
468
+ ep.name === 'electric' &&
469
+ error.response &&
470
+ (error.response.status === 404 || error.response.status === 422)
471
+ ) {
472
+ this.log.debug(`force-refresh electric not supported (${error.response.status})`);
473
+ return;
474
+ }
475
+ this.log.warn(`force-refresh ${ep.name} failed: ${error.message}`);
476
+ error.response && this.log.debug(JSON.stringify(error.response.data));
477
+ });
478
+ }
479
+ this.refreshTimeout && clearTimeout(this.refreshTimeout);
480
+ this.refreshTimeout = setTimeout(async () => {
481
+ await this.updateDevices();
482
+ }, 20 * 1000);
483
+ }
484
+
371
485
  async getDeviceList() {
372
486
  await this.requestClient({
373
487
  method: 'get',
@@ -380,7 +494,6 @@ class Toyota extends utils.Adapter {
380
494
  guid: this.uuid,
381
495
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
382
496
  'x-guid': this.uuid,
383
- 'x-region': 'EU',
384
497
  region: 'EU',
385
498
  brand: this.brand,
386
499
  'x-channel': 'ONEAPP',
@@ -389,11 +502,15 @@ class Toyota extends utils.Adapter {
389
502
  'x-brand': this.brand,
390
503
  authorization: 'Bearer ' + this.session.access_token,
391
504
  'accept-language': 'de-DE,de;q=0.9',
392
- 'x-correlationid': '7683DC30-D4DA-4FEC-850E-F3557A7DCEF4',
393
- 'x-appversion': '2.4.2',
394
505
  accept: '*/*',
395
506
  'x-user-region': 'DE',
396
507
  'x-api-key': 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
508
+ API_KEY: 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
509
+ // x-client-ref: HMAC-SHA256 with the app version as key over the user GUID (APK 2.23.0)
510
+ 'x-client-ref': this.generate_hmac_sha256(this.CLIENT_VERSION, this.uuid),
511
+ 'x-correlationid': uuidv4(),
512
+ 'x-appversion': this.CLIENT_VERSION,
513
+ 'x-region': 'EU',
397
514
  },
398
515
  })
399
516
  .then(async (res) => {
@@ -436,7 +553,8 @@ class Toyota extends utils.Adapter {
436
553
 
437
554
  const remoteArray = [
438
555
  { command: 'climate-control', name: 'True = Start, False = Stop' },
439
- { command: 'refresh', name: 'Refresh Status' },
556
+ { command: 'refresh', name: 'Re-poll cached status from cloud' },
557
+ { command: 'force-refresh', name: 'Wake car and request fresh status, climate and EV data' },
440
558
  { command: 'door', name: 'True = Lock, False = Unlock' },
441
559
  ];
442
560
  for (const remote of remoteArray) {
@@ -462,6 +580,8 @@ class Toyota extends utils.Adapter {
462
580
  error.response && this.log.error(JSON.stringify(error.response.data));
463
581
  });
464
582
  }
583
+
584
+ // ...existing code...
465
585
  async cleanOldObjects(vin) {
466
586
  const remoteState = await this.getObjectAsync(vin + '.addtionalInfo');
467
587
  if (remoteState) {
@@ -469,11 +589,12 @@ class Toyota extends utils.Adapter {
469
589
  await this.delObjectAsync(vin, { recursive: true });
470
590
  }
471
591
  }
592
+
472
593
  async updateDevices() {
473
594
  const statusArray = [
474
595
  {
475
596
  path: 'status',
476
- url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/global/remote/status',
597
+ url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/vehicle/status',
477
598
  desc: 'Status of the car',
478
599
  },
479
600
  {
@@ -483,9 +604,24 @@ class Toyota extends utils.Adapter {
483
604
  },
484
605
  {
485
606
  path: 'climate',
486
- url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/global/remote/climate-status',
607
+ url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/vehicle/climate-status',
487
608
  desc: 'Climate of the car',
488
609
  },
610
+ {
611
+ path: 'electric',
612
+ url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/global/remote/electric/status',
613
+ desc: 'Electric/PHEV status (battery, charging, range)',
614
+ },
615
+ {
616
+ path: 'location',
617
+ url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/location',
618
+ desc: 'Current vehicle location',
619
+ },
620
+ {
621
+ path: 'health',
622
+ url: 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/vehiclehealth/status',
623
+ desc: 'Vehicle health and service warnings',
624
+ },
489
625
  ];
490
626
 
491
627
  const headers = {
@@ -495,7 +631,6 @@ class Toyota extends utils.Adapter {
495
631
  guid: this.uuid,
496
632
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
497
633
  'x-guid': this.uuid,
498
- 'x-region': 'EU',
499
634
  region: 'EU',
500
635
  brand: this.brand,
501
636
  'x-channel': 'ONEAPP',
@@ -504,14 +639,21 @@ class Toyota extends utils.Adapter {
504
639
  'x-brand': this.brand,
505
640
  authorization: 'Bearer ' + this.session.access_token,
506
641
  'accept-language': 'de-DE,de;q=0.9',
507
- 'x-correlationid': '7683DC30-D4DA-4FEC-850E-F3557A7DCEF4',
508
- 'x-appversion': '2.4.2',
509
642
  accept: '*/*',
510
643
  'x-user-region': 'DE',
511
644
  'x-api-key': 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
645
+ API_KEY: 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
646
+ // Updated client-ref generation using new key
647
+ 'x-client-ref': this.generate_hmac_sha256(this.CLIENT_VERSION, this.uuid),
648
+ 'x-correlationid': uuidv4(),
649
+ 'x-appversion': this.CLIENT_VERSION,
650
+ 'x-region': 'EU',
512
651
  };
513
652
  for (const vin of this.deviceArray) {
514
- statusArray.forEach(async (element) => {
653
+ for (const element of statusArray) {
654
+ if (this.blockedEndpoints[vin] && this.blockedEndpoints[vin].includes(element.path)) {
655
+ continue;
656
+ }
515
657
  const url = element.url.replace('$vin', vin);
516
658
  headers.vin = vin;
517
659
 
@@ -548,12 +690,19 @@ class Toyota extends utils.Adapter {
548
690
 
549
691
  return;
550
692
  }
693
+ if (error.response.status >= 500) {
694
+ this.log.warn(JSON.stringify(error.response.data));
695
+ this.log.info(element.path + ' receive 500 error. Skip until restart');
696
+ this.blockedEndpoints[vin] = this.blockedEndpoints[vin] || [];
697
+ this.blockedEndpoints[vin].push(element.path);
698
+ return;
699
+ }
551
700
  }
552
701
  this.log.error(url);
553
702
  this.log.error(error);
554
703
  error.response && this.log.error(JSON.stringify(error.response.data));
555
704
  });
556
- });
705
+ }
557
706
  }
558
707
  }
559
708
 
@@ -572,11 +721,9 @@ class Toyota extends utils.Adapter {
572
721
  'x-appbrand': this.brand,
573
722
  'x-device-timezone': 'CEST',
574
723
  'x-osname': 'iOS',
575
-
576
724
  guid: this.uuid,
577
725
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
578
726
  'x-guid': this.uuid,
579
- 'x-region': 'EU',
580
727
  region: 'EU',
581
728
  brand: this.brand,
582
729
  'x-channel': 'ONEAPP',
@@ -585,11 +732,15 @@ class Toyota extends utils.Adapter {
585
732
  'x-brand': this.brand,
586
733
  authorization: 'Bearer ' + this.session.access_token,
587
734
  'accept-language': 'de-DE,de;q=0.9',
588
- 'x-correlationid': '7683DC30-D4DA-4FEC-850E-F3557A7DCEF4',
589
- 'x-appversion': '2.4.2',
590
735
  accept: '*/*',
591
736
  'x-user-region': 'DE',
592
737
  'x-api-key': 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
738
+ API_KEY: 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
739
+ // Updated client-ref generation using new key
740
+ 'x-client-ref': this.generate_hmac_sha256(this.CLIENT_VERSION, this.uuid),
741
+ 'x-correlationid': uuidv4(),
742
+ 'x-appversion': this.CLIENT_VERSION,
743
+ 'x-region': 'EU',
593
744
  };
594
745
  for (const vin of this.deviceArray) {
595
746
  statusArray.forEach(async (element) => {
@@ -607,6 +758,12 @@ class Toyota extends utils.Adapter {
607
758
  return;
608
759
  }
609
760
  const data = res.data.payload;
761
+ for (const trip of data.trips) {
762
+ delete trip.route;
763
+ }
764
+ for (const summary of data.summary) {
765
+ delete summary.histograms;
766
+ }
610
767
  this.json2iob.parse(vin + '.' + element.path, data, {
611
768
  forceIndex: true,
612
769
  channelName: element.desc,
@@ -632,6 +789,7 @@ class Toyota extends utils.Adapter {
632
789
  });
633
790
  }
634
791
  }
792
+
635
793
  /**
636
794
  * Is called when adapter shuts down - callback has to be called under any circumstances!
637
795
  * @param {() => void} callback
@@ -645,7 +803,7 @@ class Toyota extends utils.Adapter {
645
803
  this.updateInterval && clearInterval(this.updateInterval);
646
804
  this.refreshTokenInterval && clearInterval(this.refreshTokenInterval);
647
805
  callback();
648
- } catch (e) {
806
+ } catch {
649
807
  callback();
650
808
  }
651
809
  }
@@ -667,10 +825,14 @@ class Toyota extends utils.Adapter {
667
825
  this.updateDevices();
668
826
  return;
669
827
  }
828
+ if (path === 'force-refresh') {
829
+ await this.forceRefresh(deviceId);
830
+ return;
831
+ }
670
832
  const data = {};
671
833
  let url = 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/global/remote/command';
672
834
  if (path === 'climate-control') {
673
- url = 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/global/remote/climate-control';
835
+ url = 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v2/remote/climate-control';
674
836
  data.command = state.val ? 'engine-start' : 'engine-stop';
675
837
  }
676
838
  if (path === 'door') {
@@ -686,25 +848,27 @@ class Toyota extends utils.Adapter {
686
848
  'x-appbrand': this.brand,
687
849
  'x-device-timezone': 'CEST',
688
850
  'x-osname': 'iOS',
689
- guid: '091a3196-0d1d-4ba6-92c4-d44967cd6567',
851
+ guid: this.uuid,
690
852
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
691
- 'x-guid': '091a3196-0d1d-4ba6-92c4-d44967cd6567',
692
- 'x-region': 'EU',
853
+ 'x-guid': this.uuid,
693
854
  region: 'EU',
694
855
  brand: this.brand,
695
856
  'x-channel': 'ONEAPP',
696
- vin: 'JTMGBRFV90D151864',
857
+ vin: deviceId,
697
858
  'x-osversion': '16.7.2',
698
859
  'x-locale': 'de-DE',
699
860
  'x-brand': this.brand,
700
861
  authorization: 'Bearer ' + this.session.access_token,
701
862
  'accept-language': 'de-DE,de;q=0.9',
702
- 'x-correlationid': 'D7F048C1-F0A1-4920-AA37-264C8A1FB4A3',
703
- 'x-appversion': '2.4.2',
704
863
  accept: '*/*',
705
- 'content-type': 'application/json',
706
- 'x-api-key': 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
707
864
  'x-user-region': 'DE',
865
+ 'x-api-key': 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
866
+ API_KEY: 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
867
+ // Updated client-ref generation using new key
868
+ 'x-client-ref': this.generate_hmac_sha256(this.CLIENT_VERSION, this.uuid),
869
+ 'x-correlationid': uuidv4(),
870
+ 'x-appversion': this.CLIENT_VERSION,
871
+ 'x-region': 'EU',
708
872
  },
709
873
  data: data,
710
874
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.toyota",
3
- "version": "0.2.5",
3
+ "version": "0.3.0",
4
4
  "description": "Adapter for myT Toyota/Lexus App",
5
5
  "author": {
6
6
  "name": "TA2k",
@@ -23,21 +23,27 @@
23
23
  "url": "https://github.com/TA2k/ioBroker.toyota"
24
24
  },
25
25
  "dependencies": {
26
- "@iobroker/adapter-core": "^3.0.4",
27
- "axios": "^1.6.5",
28
- "http-cookie-agent": "^5.0.4",
29
- "json2iob": "^2.6.6",
30
- "jsonwebtoken": "^9.0.2",
31
- "qs": "^6.11.2",
32
- "tough-cookie": "^4.1.3"
26
+ "@iobroker/adapter-core": "^3.4.1",
27
+ "axios": "^1.18.0",
28
+ "http-cookie-agent": "^8.0.0",
29
+ "json2iob": "^2.6.25",
30
+ "jsonwebtoken": "^9.0.3",
31
+ "qs": "^6.15.2",
32
+ "tough-cookie": "^6.0.1",
33
+ "uuid": "^14.0.1"
33
34
  },
34
35
  "devDependencies": {
35
- "@iobroker/testing": "^4.1.0",
36
- "@types/node": "^20.11.4",
37
- "eslint": "^8.56.0",
38
- "typescript": "~5.3.3"
36
+ "@eslint/js": "^10.0.1",
37
+ "@iobroker/testing": "^5.2.2",
38
+ "@types/node": "^26.0.0",
39
+ "eslint": "^10.5.0",
40
+ "globals": "^17.6.0",
41
+ "typescript": "~6.0.3"
39
42
  },
40
43
  "main": "main.js",
44
+ "engines": {
45
+ "node": ">=22.13.0"
46
+ },
41
47
  "files": [
42
48
  "admin{,/!(src)/**}/!(tsconfig|tsconfig.*).json",
43
49
  "admin{,/!(src)/**}/*.{html,css,png,svg,jpg,js}",
@@ -60,4 +66,5 @@
60
66
  "url": "https://github.com/TA2k/ioBroker.toyota/issues"
61
67
  },
62
68
  "readmeFilename": "README.md"
69
+
63
70
  }