iobroker.toyota 0.2.6 → 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,12 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "toyota",
4
- "version": "0.2.6",
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
+ },
6
10
  "0.2.6": {
7
11
  "en": "Skip endpoints that are not available",
8
12
  "de": "Überspringen von Endpunkten, die nicht verfügbar sind"
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={}]
@@ -43,8 +46,10 @@ class Toyota extends utils.Adapter {
43
46
  this.updateInterval = null;
44
47
  this.reLoginTimeout = null;
45
48
  this.refreshTokenTimeout = null;
46
- this.hostName = 'myt-agg.toyota-europe.com';
47
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';
48
53
  }
49
54
 
50
55
  /**
@@ -65,7 +70,6 @@ class Toyota extends utils.Adapter {
65
70
  if (this.config.type === 'lexus') {
66
71
  this.log.info('Login to Lexus');
67
72
  this.brand = 'L';
68
- this.hostName = 'lexuslink-agg.toyota-europe.com';
69
73
  }
70
74
  if (!this.config.username || !this.config.password) {
71
75
  this.log.error('No username or password set');
@@ -92,6 +96,7 @@ class Toyota extends utils.Adapter {
92
96
  }, 3500 * 1000);
93
97
  }
94
98
  }
99
+
95
100
  async login() {
96
101
  this.log.info('Login to Toyota');
97
102
  const firstResponse = await this.requestClient({
@@ -101,7 +106,7 @@ class Toyota extends utils.Adapter {
101
106
  headers: {
102
107
  'x-osname': 'iOS',
103
108
  'x-brand': this.brand,
104
- accept: '*/*',
109
+ accept: 'application/json',
105
110
  'x-channel': 'ONEAPP',
106
111
  brand: this.brand,
107
112
  'x-region': 'EU',
@@ -111,8 +116,8 @@ class Toyota extends utils.Adapter {
111
116
  'accept-language': 'de-DE,de;q=0.9',
112
117
  region: 'EU',
113
118
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
114
- 'accept-api-version': 'resource=2.0, protocol=1.0',
115
- 'x-appversion': '2.4.2',
119
+ 'accept-api-version': this.API_VERSION,
120
+ 'x-appversion': this.CLIENT_VERSION,
116
121
  Cookie: 'route=e8e8b55de08efd3c4b34265c0069d319',
117
122
  },
118
123
  })
@@ -127,6 +132,7 @@ class Toyota extends utils.Adapter {
127
132
  this.log.error(JSON.stringify(error.response.data));
128
133
  }
129
134
  });
135
+
130
136
  const secondResponse = await this.requestClient({
131
137
  method: 'post',
132
138
  maxBodyLength: Infinity,
@@ -143,10 +149,10 @@ class Toyota extends utils.Adapter {
143
149
  'x-brand': this.brand,
144
150
  'accept-language': 'de-DE,de;q=0.9',
145
151
  'x-correlationid': 'BDD02A22-CD76-4877-90A9-196EDA5DC695',
146
- 'x-appversion': '2.4.2',
147
- accept: '*/*',
152
+ 'x-appversion': this.CLIENT_VERSION,
153
+ accept: 'application/json',
148
154
  'content-type': 'application/json',
149
- 'accept-api-version': 'resource=2.0, protocol=1.0',
155
+ 'accept-api-version': this.API_VERSION,
150
156
  },
151
157
  data: firstResponse,
152
158
  })
@@ -161,10 +167,13 @@ class Toyota extends utils.Adapter {
161
167
  this.log.error(JSON.stringify(error.response.data));
162
168
  }
163
169
  });
170
+
164
171
  if (!secondResponse || !secondResponse.callbacks) {
165
172
  this.log.error('No Second Response');
173
+ return;
166
174
  }
167
175
  secondResponse.callbacks[0].input[0].value = this.config.username;
176
+
168
177
  const thirdResponse = await this.requestClient({
169
178
  method: 'post',
170
179
  maxBodyLength: Infinity,
@@ -181,10 +190,10 @@ class Toyota extends utils.Adapter {
181
190
  'x-brand': this.brand,
182
191
  'accept-language': 'de-DE,de;q=0.9',
183
192
  'x-correlationid': 'BDD02A22-CD76-4877-90A9-196EDA5DC695',
184
- 'x-appversion': '2.4.2',
185
- accept: '*/*',
193
+ 'x-appversion': this.CLIENT_VERSION,
194
+ accept: 'application/json',
186
195
  'content-type': 'application/json',
187
- 'accept-api-version': 'resource=2.0, protocol=1.0',
196
+ 'accept-api-version': this.API_VERSION,
188
197
  },
189
198
  data: secondResponse,
190
199
  })
@@ -199,12 +208,15 @@ class Toyota extends utils.Adapter {
199
208
  this.log.error(JSON.stringify(error.response.data));
200
209
  }
201
210
  });
211
+
202
212
  if (!thirdResponse || !thirdResponse.callbacks) {
203
213
  this.log.error('No Third Response');
214
+ return;
204
215
  }
205
216
 
206
217
  thirdResponse.callbacks[0].input[0].value = this.config.password;
207
- const idToken = await this.requestClient({
218
+
219
+ await this.requestClient({
208
220
  method: 'post',
209
221
  maxBodyLength: Infinity,
210
222
  url: 'https://b2c-login.toyota-europe.com/json/realms/root/realms/tme/authenticate?authIndexType=service&authIndexValue=oneapp',
@@ -220,10 +232,10 @@ class Toyota extends utils.Adapter {
220
232
  'x-brand': this.brand,
221
233
  'accept-language': 'de-DE,de;q=0.9',
222
234
  'x-correlationid': 'BDD02A22-CD76-4877-90A9-196EDA5DC695',
223
- 'x-appversion': '2.4.2',
224
- accept: '*/*',
235
+ 'x-appversion': this.CLIENT_VERSION,
236
+ accept: 'application/json',
225
237
  'content-type': 'application/json',
226
- 'accept-api-version': 'resource=2.0, protocol=1.0',
238
+ 'accept-api-version': this.API_VERSION,
227
239
  },
228
240
  data: thirdResponse,
229
241
  })
@@ -239,10 +251,21 @@ class Toyota extends utils.Adapter {
239
251
  }
240
252
  });
241
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;
242
265
  const tokenResponse = await this.requestClient({
243
266
  method: 'get',
244
267
  maxBodyLength: Infinity,
245
- 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,
246
269
  headers: {
247
270
  'x-osname': 'iOS',
248
271
  'x-brand': this.brand,
@@ -256,8 +279,8 @@ class Toyota extends utils.Adapter {
256
279
  'accept-language': 'de-DE,de;q=0.9',
257
280
  region: 'EU',
258
281
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
259
- 'accept-api-version': 'resource=2.0, protocol=1.0',
260
- 'x-appversion': '2.4.2',
282
+ 'accept-api-version': this.API_VERSION,
283
+ 'x-appversion': this.CLIENT_VERSION,
261
284
  },
262
285
  })
263
286
  .then((res) => {
@@ -272,10 +295,12 @@ class Toyota extends utils.Adapter {
272
295
  this.log.error(error);
273
296
  error.response && this.log.error(JSON.stringify(error.response.data));
274
297
  });
298
+
275
299
  if (!tokenResponse || !tokenResponse.code) {
276
300
  this.log.error('No Token Response');
277
301
  return;
278
302
  }
303
+
279
304
  this.log.info('Start token exchange');
280
305
  await this.requestClient({
281
306
  method: 'post',
@@ -294,16 +319,16 @@ class Toyota extends utils.Adapter {
294
319
  authorization: 'Basic b25lYXBwOm9uZWFwcA==',
295
320
  'accept-language': 'de-DE,de;q=0.9',
296
321
  'x-correlationid': 'E422A08C-1A04-415E-BB08-2386EE06CF90',
297
- 'x-appversion': '2.4.2',
322
+ 'x-appversion': this.CLIENT_VERSION,
298
323
  accept: '*/*',
299
324
  'content-type': 'application/x-www-form-urlencoded',
300
- 'accept-api-version': 'resource=2.0, protocol=1.0',
325
+ 'accept-api-version': this.API_VERSION,
301
326
  },
302
327
  data: {
303
328
  grant_type: 'authorization_code',
304
329
  redirect_uri: 'com.toyota.oneapp:/oauth2Callback',
305
330
  code: tokenResponse.code,
306
- code_verifier: 'tsY5-j-ZLYxNVnmz6wmJ9PTm2Ly7QpfXcQnhyU09Pog',
331
+ code_verifier: codeVerifier,
307
332
  client_id: 'oneapp',
308
333
  },
309
334
  })
@@ -326,6 +351,7 @@ class Toyota extends utils.Adapter {
326
351
  }
327
352
  });
328
353
  }
354
+
329
355
  async refreshToken() {
330
356
  await this.requestClient({
331
357
  method: 'post',
@@ -344,16 +370,15 @@ class Toyota extends utils.Adapter {
344
370
  authorization: 'Basic b25lYXBwOm9uZWFwcA==',
345
371
  'accept-language': 'de-DE,de;q=0.9',
346
372
  'x-correlationid': 'E422A08C-1A04-415E-BB08-2386EE06CF90',
347
- 'x-appversion': '2.4.2',
373
+ 'x-appversion': this.CLIENT_VERSION,
348
374
  accept: '*/*',
349
375
  'content-type': 'application/x-www-form-urlencoded',
350
- 'accept-api-version': 'resource=2.0, protocol=1.0',
376
+ 'accept-api-version': this.API_VERSION,
351
377
  },
352
378
  data: {
353
379
  grant_type: 'refresh_token',
354
380
  redirect_uri: 'com.toyota.oneapp:/oauth2Callback',
355
381
  client_id: 'oneapp',
356
- code_verifier: 'plain',
357
382
  refresh_token: this.session.refresh_token,
358
383
  },
359
384
  })
@@ -369,6 +394,94 @@ class Toyota extends utils.Adapter {
369
394
  }
370
395
  });
371
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
+
372
485
  async getDeviceList() {
373
486
  await this.requestClient({
374
487
  method: 'get',
@@ -381,7 +494,6 @@ class Toyota extends utils.Adapter {
381
494
  guid: this.uuid,
382
495
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
383
496
  'x-guid': this.uuid,
384
- 'x-region': 'EU',
385
497
  region: 'EU',
386
498
  brand: this.brand,
387
499
  'x-channel': 'ONEAPP',
@@ -390,11 +502,15 @@ class Toyota extends utils.Adapter {
390
502
  'x-brand': this.brand,
391
503
  authorization: 'Bearer ' + this.session.access_token,
392
504
  'accept-language': 'de-DE,de;q=0.9',
393
- 'x-correlationid': '7683DC30-D4DA-4FEC-850E-F3557A7DCEF4',
394
- 'x-appversion': '2.4.2',
395
505
  accept: '*/*',
396
506
  'x-user-region': 'DE',
397
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',
398
514
  },
399
515
  })
400
516
  .then(async (res) => {
@@ -437,7 +553,8 @@ class Toyota extends utils.Adapter {
437
553
 
438
554
  const remoteArray = [
439
555
  { command: 'climate-control', name: 'True = Start, False = Stop' },
440
- { 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' },
441
558
  { command: 'door', name: 'True = Lock, False = Unlock' },
442
559
  ];
443
560
  for (const remote of remoteArray) {
@@ -463,6 +580,8 @@ class Toyota extends utils.Adapter {
463
580
  error.response && this.log.error(JSON.stringify(error.response.data));
464
581
  });
465
582
  }
583
+
584
+ // ...existing code...
466
585
  async cleanOldObjects(vin) {
467
586
  const remoteState = await this.getObjectAsync(vin + '.addtionalInfo');
468
587
  if (remoteState) {
@@ -470,11 +589,12 @@ class Toyota extends utils.Adapter {
470
589
  await this.delObjectAsync(vin, { recursive: true });
471
590
  }
472
591
  }
592
+
473
593
  async updateDevices() {
474
594
  const statusArray = [
475
595
  {
476
596
  path: 'status',
477
- 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',
478
598
  desc: 'Status of the car',
479
599
  },
480
600
  {
@@ -484,9 +604,24 @@ class Toyota extends utils.Adapter {
484
604
  },
485
605
  {
486
606
  path: 'climate',
487
- 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',
488
608
  desc: 'Climate of the car',
489
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
+ },
490
625
  ];
491
626
 
492
627
  const headers = {
@@ -496,7 +631,6 @@ class Toyota extends utils.Adapter {
496
631
  guid: this.uuid,
497
632
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
498
633
  'x-guid': this.uuid,
499
- 'x-region': 'EU',
500
634
  region: 'EU',
501
635
  brand: this.brand,
502
636
  'x-channel': 'ONEAPP',
@@ -505,11 +639,15 @@ class Toyota extends utils.Adapter {
505
639
  'x-brand': this.brand,
506
640
  authorization: 'Bearer ' + this.session.access_token,
507
641
  'accept-language': 'de-DE,de;q=0.9',
508
- 'x-correlationid': '7683DC30-D4DA-4FEC-850E-F3557A7DCEF4',
509
- 'x-appversion': '2.4.2',
510
642
  accept: '*/*',
511
643
  'x-user-region': 'DE',
512
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',
513
651
  };
514
652
  for (const vin of this.deviceArray) {
515
653
  for (const element of statusArray) {
@@ -583,11 +721,9 @@ class Toyota extends utils.Adapter {
583
721
  'x-appbrand': this.brand,
584
722
  'x-device-timezone': 'CEST',
585
723
  'x-osname': 'iOS',
586
-
587
724
  guid: this.uuid,
588
725
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
589
726
  'x-guid': this.uuid,
590
- 'x-region': 'EU',
591
727
  region: 'EU',
592
728
  brand: this.brand,
593
729
  'x-channel': 'ONEAPP',
@@ -596,11 +732,15 @@ class Toyota extends utils.Adapter {
596
732
  'x-brand': this.brand,
597
733
  authorization: 'Bearer ' + this.session.access_token,
598
734
  'accept-language': 'de-DE,de;q=0.9',
599
- 'x-correlationid': '7683DC30-D4DA-4FEC-850E-F3557A7DCEF4',
600
- 'x-appversion': '2.4.2',
601
735
  accept: '*/*',
602
736
  'x-user-region': 'DE',
603
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',
604
744
  };
605
745
  for (const vin of this.deviceArray) {
606
746
  statusArray.forEach(async (element) => {
@@ -618,6 +758,12 @@ class Toyota extends utils.Adapter {
618
758
  return;
619
759
  }
620
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
+ }
621
767
  this.json2iob.parse(vin + '.' + element.path, data, {
622
768
  forceIndex: true,
623
769
  channelName: element.desc,
@@ -643,6 +789,7 @@ class Toyota extends utils.Adapter {
643
789
  });
644
790
  }
645
791
  }
792
+
646
793
  /**
647
794
  * Is called when adapter shuts down - callback has to be called under any circumstances!
648
795
  * @param {() => void} callback
@@ -656,7 +803,7 @@ class Toyota extends utils.Adapter {
656
803
  this.updateInterval && clearInterval(this.updateInterval);
657
804
  this.refreshTokenInterval && clearInterval(this.refreshTokenInterval);
658
805
  callback();
659
- } catch (e) {
806
+ } catch {
660
807
  callback();
661
808
  }
662
809
  }
@@ -678,10 +825,14 @@ class Toyota extends utils.Adapter {
678
825
  this.updateDevices();
679
826
  return;
680
827
  }
828
+ if (path === 'force-refresh') {
829
+ await this.forceRefresh(deviceId);
830
+ return;
831
+ }
681
832
  const data = {};
682
833
  let url = 'https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io/v1/global/remote/command';
683
834
  if (path === 'climate-control') {
684
- 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';
685
836
  data.command = state.val ? 'engine-start' : 'engine-stop';
686
837
  }
687
838
  if (path === 'door') {
@@ -697,25 +848,27 @@ class Toyota extends utils.Adapter {
697
848
  'x-appbrand': this.brand,
698
849
  'x-device-timezone': 'CEST',
699
850
  'x-osname': 'iOS',
700
- guid: '091a3196-0d1d-4ba6-92c4-d44967cd6567',
851
+ guid: this.uuid,
701
852
  'user-agent': 'Toyota/134 CFNetwork/1410.0.3 Darwin/22.6.0',
702
- 'x-guid': '091a3196-0d1d-4ba6-92c4-d44967cd6567',
703
- 'x-region': 'EU',
853
+ 'x-guid': this.uuid,
704
854
  region: 'EU',
705
855
  brand: this.brand,
706
856
  'x-channel': 'ONEAPP',
707
- vin: 'JTMGBRFV90D151864',
857
+ vin: deviceId,
708
858
  'x-osversion': '16.7.2',
709
859
  'x-locale': 'de-DE',
710
860
  'x-brand': this.brand,
711
861
  authorization: 'Bearer ' + this.session.access_token,
712
862
  'accept-language': 'de-DE,de;q=0.9',
713
- 'x-correlationid': 'D7F048C1-F0A1-4920-AA37-264C8A1FB4A3',
714
- 'x-appversion': '2.4.2',
715
863
  accept: '*/*',
716
- 'content-type': 'application/json',
717
- 'x-api-key': 'tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF',
718
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',
719
872
  },
720
873
  data: data,
721
874
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.toyota",
3
- "version": "0.2.6",
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
  }