iobroker.bmw 4.3.1 → 4.3.2
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/README.md +10 -0
- package/io-package.json +14 -14
- package/main.js +60 -47
- package/package.json +2 -2
- package/telematic.json +5 -16
- package/lib/tools.js +0 -110
package/README.md
CHANGED
|
@@ -209,6 +209,16 @@ If you're not seeing expected data in `VIN.api.*`:
|
|
|
209
209
|
This adapter is available at: [https://github.com/TA2k/ioBroker.bmw](https://github.com/TA2k/ioBroker.bmw)
|
|
210
210
|
|
|
211
211
|
## Changelog
|
|
212
|
+
|
|
213
|
+
<!--
|
|
214
|
+
Placeholder for the next version (at the beginning of the line):
|
|
215
|
+
### **WORK IN PROGRESS**
|
|
216
|
+
-->
|
|
217
|
+
### 4.3.2 (2025-12-15)
|
|
218
|
+
|
|
219
|
+
- update telemetry ids for container creation
|
|
220
|
+
- optimize dependabot config (#209)
|
|
221
|
+
|
|
212
222
|
### 4.3.1 (2025-10-11)
|
|
213
223
|
|
|
214
224
|
- fix gps coordinate parsing
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "bmw",
|
|
4
|
-
"version": "4.3.
|
|
4
|
+
"version": "4.3.2",
|
|
5
5
|
"news": {
|
|
6
|
+
"4.3.2": {
|
|
7
|
+
"en": "update telemetry ids for container creation\noptimize dependabot config (#209)",
|
|
8
|
+
"de": "aktualisierung von telemetrie-ids für die container-erstellung\noptimieren abhängigabot config (#209)",
|
|
9
|
+
"ru": "обновление телеметрических идентификаторов для создания контейнеров\nоптимизируйте конфигурацию зависимого робота (#209)",
|
|
10
|
+
"pt": "atualizar ids de telemetria para criação de containers\notimizar a configuração do dependebot (# 209)",
|
|
11
|
+
"nl": "update telemetrie-id's voor het aanmaken van containers\ndependabot config optimaliseren (#209)",
|
|
12
|
+
"fr": "mettre à jour les ids de télémétrie pour la création de conteneurs\noptimiser la configuration de base (#209)",
|
|
13
|
+
"it": "aggiornare ids di telemetria per la creazione di container\nottimizzare la configurazione dipendeabot (#209)",
|
|
14
|
+
"es": "actualización de los sistemas de telemetría para la creación de contenedores\noptimizar config de dependabot (#209)",
|
|
15
|
+
"pl": "aktualizacja identyfikatorów telemetrii do tworzenia kontenerów\noptymalizacja konfiguracji zależnej (# 209)",
|
|
16
|
+
"uk": "оновлення телеметрії кришок для створення контейнерів\nоптимізація налаштування залежностей (#209)",
|
|
17
|
+
"zh-cn": "更新用于创建容器的遥测标识\n优化依赖配置 (# 209)"
|
|
18
|
+
},
|
|
6
19
|
"4.3.1": {
|
|
7
20
|
"en": "fix gps coordinate parsing",
|
|
8
21
|
"de": "fix gps koordinate paring",
|
|
@@ -80,19 +93,6 @@
|
|
|
80
93
|
"pl": "Zmiana na zalecaną stabilną wersję admin 7.6.17 (#159)\nMigracja do iobroker/eslint-config (#146)\nNaprawa podatności w form-data\nCzyszczenie kodu\nAktualizacja axios\nAktualizacja adapter-core\nNaprawa problemów wykrytych przez sprawdzacz repozytorium (#170)\nAktualizacja zależności",
|
|
81
94
|
"uk": "Перехід на рекомендовану стабільну версію admin 7.6.17 (#159)\nМіграція на iobroker/eslint-config (#146)\nВиправлення вразливості form-data\nОчищення коду\nОновлення axios\nОновлення adapter-core\nВиправлення проблем, виявлених перевіркою репозиторію (#170)\nОновлення залежностей",
|
|
82
95
|
"zh-cn": "切换到推荐的稳定版 admin 7.6.17 (#159)\n迁移到 iobroker/eslint-config (#146)\n修复 form-data 漏洞\n代码清理\n更新 axios\n更新 adapter-core\n修复仓库检查器检测到的问题 (#170)\n更新依赖项"
|
|
83
|
-
},
|
|
84
|
-
"3.0.0": {
|
|
85
|
-
"en": "BREAKING: Dropped support for Node.js 18 (#88)\nBREAKING: Dropped support for js-controller 5 (#111)\nBREAKING: change to admin 7.4.10 as recommended by ioBroker (#111)\nencrypt and protect second user password - has to be reentered (#111)\nbump dependencies",
|
|
86
|
-
"de": "BREAKING: Unterstützung für Node.js 18 (#88) eingestellt\nBREAKING: Unterstützung für js-controller 5 (#111) eingestellt\nBREAKING: Wechsel zu admin 7.4.10 wie von ioBroker empfohlen (#111)\nzweites Benutzerpasswort verschlüsseln und schützen - muss erneut eingegeben werden (#111)\nAbhängigkeiten aktualisiert",
|
|
87
|
-
"ru": "Отказ от поддержки Node.js 18 (#88)\nОтказ от поддержки js-контроллера 5 (#111)\nBREAKING: изменение на админ 7.4.10 в соответствии с рекомендациями ioBroker (#111)\nшифровать и защищать пароль второго пользователя - должен быть повторно введен (#111)\nбамп зависимости",
|
|
88
|
-
"pt": "BREAKING: Suporte suspenso para Node.js 18 (# 88)\nBREAKING: Suporte suspenso para o js-controller 5 (# 111)\nBREAKING: mudança para admin 7.4.10 conforme recomendado pelo ioBroker (# 111)\ncriptografar e proteger a segunda senha do usuário - tem que ser reentrada (# 111)\ndependências de colisão",
|
|
89
|
-
"nl": "BREAKING: Gestopte ondersteuning voor Node.js 18 (#88)\nBreaking: Dropped support for js-controller 5 (#111)\nBREAKING: overstappen op admin 7.4.10 zoals aanbevolen door ioBroker (#111)\ntweede gebruikerswachtwoord versleutelen en beschermen - moet opnieuw worden ingevoerd (#111)\nbump afhankelijkheden",
|
|
90
|
-
"fr": "Soutien abandonné pour Node.js 18 (#88)\nSoutien abandonné pour le contrôleur de js 5 (#111)\nBREAKING: changement à l'administrateur 7.4.10 recommandé par ioBroker (#111)\nchiffrer et protéger le mot de passe du deuxième utilisateur - doit être réintroduit (#111)\ndépendances des bosses",
|
|
91
|
-
"it": "BREAKING: Supporto a goccia per Node.js 18 (#88)\nBREAKING: Supporto a goccia per js-controller 5 (#111)\nBREAKING: cambiamento a admin 7.4.10 come raccomandato da ioBroker (#111)\ncrittografare e proteggere la seconda password dell'utente - deve essere riattivata (#111)\nindipendenza urto",
|
|
92
|
-
"es": "BREAKING: Soporte abandonado para Node.js 18 (#88)\nBREAKING: Soporte desechado para js-controller 5 (#111)\nBREAKING: cambio a admin 7.4.10 según lo recomendado por ioBroker (#111)\nencriptar y proteger la segunda contraseña de usuario - tiene que ser reiniciado (#111)\ndependencia de los gastos",
|
|
93
|
-
"pl": "BREAKING: Upuszczone wsparcie dla Node.js 18 (# 88)\nBREAKING: Utracone wsparcie dla kontrolera js- 5 (# 111)\nBREAKING: zmiana na admin 7.4.10 zgodnie z zaleceniami jOBrokera (# 111)\nszyfrowanie i ochrona hasła drugiego użytkownika - należy ponownie wprowadzić (# 111)\nuzależnienia od guza",
|
|
94
|
-
"uk": "BREAKING: Dropped підтримка для Node.js 18 (#88)\nBREAKING: Dropped підтримка для js-controller 5 (#111)\nBREAKING: зміни до адміністратора 7.4.10, як рекомендовано ioBroker (#111)\nзашифрувати і захистити другий пароль користувача - повинен бути повторний (#111)\nbump залежності",
|
|
95
|
-
"zh-cn": "对节点18(#88)的支持下降\n中断 : js 控制器 5 已放弃支持 (# 111)\n根据ioBroker的建议(# 111) 改为admin 7.4.10\n加密和保护第二个用户密码 - 必须重新输入( # 111)\n意外依赖"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
package/main.js
CHANGED
|
@@ -74,8 +74,8 @@ class Bmw extends utils.Adapter {
|
|
|
74
74
|
|
|
75
75
|
// Validate configuration
|
|
76
76
|
if (!this.config.clientId) {
|
|
77
|
-
this.log.error(
|
|
78
|
-
this.log.info(
|
|
77
|
+
this.log.error(`BMW CarData Client ID not configured! Please set up in adapter settings.`);
|
|
78
|
+
this.log.info(`Visit BMW ConnectedDrive portal, go to CarData section, and generate a client ID`);
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -156,10 +156,10 @@ class Bmw extends utils.Adapter {
|
|
|
156
156
|
|
|
157
157
|
// Periodic telematic data refresh - MQTT provides real-time updates
|
|
158
158
|
if (!this.containerId) {
|
|
159
|
-
this.log.warn(
|
|
159
|
+
this.log.warn(`No container ID available for periodic telematic data fetch, setting up container...`);
|
|
160
160
|
const setupSuccess = await this.setupTelematicContainer();
|
|
161
161
|
if (!setupSuccess) {
|
|
162
|
-
this.log.error(
|
|
162
|
+
this.log.error(`Failed to setup telematic container for periodic updates`);
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
165
|
}
|
|
@@ -260,22 +260,22 @@ class Bmw extends utils.Adapter {
|
|
|
260
260
|
// Special handling for 400 Bad Request - likely client configuration issue
|
|
261
261
|
if (error.response.status === 400) {
|
|
262
262
|
this.log.error('='.repeat(80));
|
|
263
|
-
this.log.error(
|
|
263
|
+
this.log.error(`BMW CLIENT ID CONFIGURATION ERROR (400 Bad Request)`);
|
|
264
264
|
this.log.error('='.repeat(80));
|
|
265
|
-
this.log.error(
|
|
266
|
-
this.log.error(
|
|
267
|
-
this.log.error(
|
|
268
|
-
this.log.error(
|
|
265
|
+
this.log.error(`This error usually means:`);
|
|
266
|
+
this.log.error(`1. CarData API access is not activated for your Client ID`);
|
|
267
|
+
this.log.error(`2. CarData Streaming is not enabled for your Client ID`);
|
|
268
|
+
this.log.error(`3. Your Client ID is invalid or has been revoked`);
|
|
269
269
|
this.log.error('');
|
|
270
|
-
this.log.error(
|
|
271
|
-
this.log.error(
|
|
272
|
-
this.log.error(
|
|
270
|
+
this.log.error(`To fix this issue:`);
|
|
271
|
+
this.log.error(`1. Visit BMW ConnectedDrive portal: https://www.bmw.de/de-de/mybmw/vehicle-overview`);
|
|
272
|
+
this.log.error(`2. Go to CarData section`);
|
|
273
273
|
this.log.error(
|
|
274
|
-
|
|
274
|
+
`3. Check if CarData API and CarData Streaming are both activated. Sometimes it needs 30s to save the selection`,
|
|
275
275
|
);
|
|
276
|
-
this.log.error(
|
|
277
|
-
this.log.error(
|
|
278
|
-
this.log.error(
|
|
276
|
+
this.log.error(`4. If not activated, enable both services`);
|
|
277
|
+
this.log.error(`5. If already activated, delete and recreate your Client ID`);
|
|
278
|
+
this.log.error(`6. Update the adapter configuration with the new Client ID`);
|
|
279
279
|
this.log.error('='.repeat(80));
|
|
280
280
|
}
|
|
281
281
|
}
|
|
@@ -300,13 +300,13 @@ class Bmw extends utils.Adapter {
|
|
|
300
300
|
|
|
301
301
|
// Show user instructions
|
|
302
302
|
this.log.info('='.repeat(80));
|
|
303
|
-
this.log.info(
|
|
303
|
+
this.log.info(`BMW CARDATA AUTHORIZATION REQUIRED`);
|
|
304
304
|
this.log.info('='.repeat(80));
|
|
305
305
|
this.log.info(`1. Visit: ${verification_uri_complete}`);
|
|
306
306
|
this.log.info(`2. Or visit: ${deviceResponse.data.verification_uri} and enter code: ${user_code}`);
|
|
307
307
|
this.log.info(`3. Login with your BMW account and authorize`);
|
|
308
308
|
this.log.info(`4. Code expires in ${Math.floor(expires_in / 60)} minutes`);
|
|
309
|
-
this.log.info(
|
|
309
|
+
this.log.info(`The adapter will automatically continue after authorization`);
|
|
310
310
|
this.log.info('='.repeat(80));
|
|
311
311
|
|
|
312
312
|
// Step 2: Poll for tokens
|
|
@@ -358,7 +358,7 @@ class Bmw extends utils.Adapter {
|
|
|
358
358
|
|
|
359
359
|
await this.setState('cardataauth.session', JSON.stringify(this.session), true);
|
|
360
360
|
this.setState('info.connection', true, true);
|
|
361
|
-
this.log.info(
|
|
361
|
+
this.log.info(`BMW CarData authorization successful!`);
|
|
362
362
|
|
|
363
363
|
// Mark this as an initial login so basicData will be fetched
|
|
364
364
|
this.initialLogin = true;
|
|
@@ -369,14 +369,14 @@ class Bmw extends utils.Adapter {
|
|
|
369
369
|
this.log.debug(`Token polling error: ${errorCode || error.message}`);
|
|
370
370
|
|
|
371
371
|
if (errorCode === 'authorization_pending') {
|
|
372
|
-
this.log.debug(
|
|
372
|
+
this.log.debug(`Authorization still pending, continuing to poll...`);
|
|
373
373
|
continue; // Keep polling
|
|
374
374
|
} else if (errorCode === 'slow_down') {
|
|
375
|
-
this.log.debug(
|
|
375
|
+
this.log.debug(`Rate limit hit, slowing down polling...`);
|
|
376
376
|
await this.sleep(5000); // Additional delay
|
|
377
377
|
continue;
|
|
378
378
|
} else if (errorCode === 'expired_token') {
|
|
379
|
-
this.log.error(
|
|
379
|
+
this.log.error(`Authorization code expired, please restart adapter`);
|
|
380
380
|
return false;
|
|
381
381
|
} else {
|
|
382
382
|
this.log.error(`Token request failed: ${errorCode || error.message}`);
|
|
@@ -437,7 +437,7 @@ class Bmw extends utils.Adapter {
|
|
|
437
437
|
}
|
|
438
438
|
|
|
439
439
|
if (mappings.length === 0) {
|
|
440
|
-
this.log.info(
|
|
440
|
+
this.log.info(`No BMW vehicles found in CarData mappings`);
|
|
441
441
|
return;
|
|
442
442
|
}
|
|
443
443
|
|
|
@@ -480,7 +480,7 @@ class Bmw extends utils.Adapter {
|
|
|
480
480
|
if (error.response) {
|
|
481
481
|
this.log.error(`Response: ${JSON.stringify(error.response.data)}`);
|
|
482
482
|
if (error.response.status === 403 || error.response.status === 429) {
|
|
483
|
-
this.log.warn(
|
|
483
|
+
this.log.warn(`Rate limit exceeded or access denied`);
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
486
|
});
|
|
@@ -488,13 +488,17 @@ class Bmw extends utils.Adapter {
|
|
|
488
488
|
// Reset initialLogin flag after processing all vehicles
|
|
489
489
|
if (this.initialLogin) {
|
|
490
490
|
this.initialLogin = false;
|
|
491
|
-
this.log.info(
|
|
491
|
+
this.log.info(`Initial login basicData fetching completed`);
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
await this.sleep(2000);
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
-
|
|
497
|
+
/**
|
|
498
|
+
* Create complete vehicle structure including basic states and remote buttons
|
|
499
|
+
*
|
|
500
|
+
* @param {string} vin - The vehicle VIN
|
|
501
|
+
*/
|
|
498
502
|
async createVehicleStates(vin) {
|
|
499
503
|
// Create vehicle device
|
|
500
504
|
await this.extendObject(vin, {
|
|
@@ -640,7 +644,6 @@ class Bmw extends utils.Adapter {
|
|
|
640
644
|
|
|
641
645
|
const used = this.apiCalls.length;
|
|
642
646
|
const remaining = API_QUOTA_LIMIT - used;
|
|
643
|
-
|
|
644
647
|
// Quota states removed - using only apiCallsHistory for persistence
|
|
645
648
|
|
|
646
649
|
return { used, remaining };
|
|
@@ -722,10 +725,20 @@ class Bmw extends utils.Adapter {
|
|
|
722
725
|
}
|
|
723
726
|
}
|
|
724
727
|
|
|
728
|
+
/**
|
|
729
|
+
* Pauses execution for a specified duration.
|
|
730
|
+
*
|
|
731
|
+
* @param {number} ms - The duration to pause in milliseconds.
|
|
732
|
+
*/
|
|
725
733
|
sleep(ms) {
|
|
726
734
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
727
735
|
}
|
|
728
736
|
|
|
737
|
+
/**
|
|
738
|
+
* Function to clean up old states from previous adapter versions
|
|
739
|
+
*
|
|
740
|
+
* @param {string} vin - The vehicle VIN
|
|
741
|
+
*/
|
|
729
742
|
async cleanObjects(vin) {
|
|
730
743
|
// Check if this is an upgrade from old version by looking for remotev2 states
|
|
731
744
|
const remoteState = await this.getObjectAsync(`${vin}.remotev2`);
|
|
@@ -733,7 +746,7 @@ class Bmw extends utils.Adapter {
|
|
|
733
746
|
this.log.info(`Cleaning old states for ${vin} (upgrading from previous version)`);
|
|
734
747
|
|
|
735
748
|
// Delete all old state structures recursively
|
|
736
|
-
await this.delObjectAsync(
|
|
749
|
+
await this.delObjectAsync(vin, { recursive: true });
|
|
737
750
|
|
|
738
751
|
// Create fresh vehicle device
|
|
739
752
|
await this.extendObject(vin, {
|
|
@@ -755,20 +768,20 @@ class Bmw extends utils.Adapter {
|
|
|
755
768
|
await this.delObjectAsync(`${vin}.chargingprofile`, { recursive: true });
|
|
756
769
|
await this.delObjectAsync(`${vin}.serviceExecutionHistory`, { recursive: true });
|
|
757
770
|
await this.delObjectAsync(`${vin}.apiV2`, { recursive: true });
|
|
758
|
-
await this.
|
|
771
|
+
await this.delObjectAsync(`${vin}.remote`, { recursive: true });
|
|
759
772
|
}
|
|
760
773
|
}
|
|
761
774
|
|
|
762
775
|
// Clean up old global states
|
|
763
|
-
await this.
|
|
764
|
-
await this.
|
|
765
|
-
await this.
|
|
776
|
+
await this.delObjectAsync(`_DatenNeuLaden`);
|
|
777
|
+
await this.delObjectAsync(`_LetzterDatenabrufOK`);
|
|
778
|
+
await this.delObjectAsync(`_LetzerFehler`);
|
|
766
779
|
|
|
767
780
|
// Clean up old authentication objects (v3.x used different auth structure)
|
|
768
|
-
const oldAuthObjects = await this.getObjectAsync(
|
|
781
|
+
const oldAuthObjects = await this.getObjectAsync(`auth`);
|
|
769
782
|
if (oldAuthObjects) {
|
|
770
|
-
this.log.info(
|
|
771
|
-
await this.delObjectAsync(
|
|
783
|
+
this.log.info(`Cleaning up complete old auth folder from previous version`);
|
|
784
|
+
await this.delObjectAsync(`auth`, { recursive: true });
|
|
772
785
|
}
|
|
773
786
|
}
|
|
774
787
|
|
|
@@ -778,7 +791,7 @@ class Bmw extends utils.Adapter {
|
|
|
778
791
|
return await this.login();
|
|
779
792
|
}
|
|
780
793
|
|
|
781
|
-
this.log.debug(
|
|
794
|
+
this.log.debug(`Refreshing BMW CarData tokens`);
|
|
782
795
|
this.log.debug(`Refresh token URL: ${this.authApiBase}/token`);
|
|
783
796
|
this.log.debug(`Client ID: ${this.config.clientId}`);
|
|
784
797
|
|
|
@@ -800,9 +813,9 @@ class Bmw extends utils.Adapter {
|
|
|
800
813
|
.then(async res => {
|
|
801
814
|
// Store refreshed tokens (keep existing session structure)
|
|
802
815
|
this.session = res.data;
|
|
803
|
-
this.setState(
|
|
804
|
-
this.setState(
|
|
805
|
-
this.log.debug(
|
|
816
|
+
this.setState(`cardataauth.session`, JSON.stringify(this.session), true);
|
|
817
|
+
this.setState(`info.connection`, true, true);
|
|
818
|
+
this.log.debug(`Tokens refreshed successfully - MQTT will auto-reconnect with new credentials`);
|
|
806
819
|
this.mqtt?.options && (this.mqtt.options.password = this.session.id_token);
|
|
807
820
|
return res.data;
|
|
808
821
|
})
|
|
@@ -817,7 +830,7 @@ class Bmw extends utils.Adapter {
|
|
|
817
830
|
if (status >= 400 && status < 500) {
|
|
818
831
|
// 4xx errors indicate authentication problems - reset needed
|
|
819
832
|
this.log.error(`Token refresh failed with HTTP ${status} auth error - starting new device flow`);
|
|
820
|
-
this.setState(
|
|
833
|
+
this.setState(`info.connection`, false, true);
|
|
821
834
|
return await this.login();
|
|
822
835
|
}
|
|
823
836
|
}
|
|
@@ -825,20 +838,20 @@ class Bmw extends utils.Adapter {
|
|
|
825
838
|
this.log.warn(
|
|
826
839
|
`Token refresh failed, will retry on next refresh cycle. You can also delete bmw.0.cardataauth.session state to force re-login.`,
|
|
827
840
|
);
|
|
828
|
-
this.setState(
|
|
841
|
+
this.setState(`info.connection`, false, true);
|
|
829
842
|
return;
|
|
830
843
|
});
|
|
831
844
|
}
|
|
832
845
|
|
|
833
846
|
async connectMQTT() {
|
|
834
847
|
if (!this.session.id_token) {
|
|
835
|
-
this.log.warn(
|
|
848
|
+
this.log.warn(`No MQTT credentials available (missing ID token)`);
|
|
836
849
|
return false;
|
|
837
850
|
}
|
|
838
851
|
|
|
839
852
|
if (!this.config.cardataStreamingUsername) {
|
|
840
|
-
this.log.error(
|
|
841
|
-
this.log.error(
|
|
853
|
+
this.log.error(`CarData Streaming Username not configured! Please set it in adapter settings.`);
|
|
854
|
+
this.log.error(`Find your streaming username in BMW ConnectedDrive portal under CarData > Streaming section.`);
|
|
842
855
|
return false;
|
|
843
856
|
}
|
|
844
857
|
|
|
@@ -862,12 +875,12 @@ class Bmw extends utils.Adapter {
|
|
|
862
875
|
this.mqtt = mqtt.connect(options);
|
|
863
876
|
|
|
864
877
|
this.mqtt.on('connect', () => {
|
|
865
|
-
this.log.info(
|
|
866
|
-
this.setState(
|
|
878
|
+
this.log.info(`BMW MQTT stream connected`);
|
|
879
|
+
this.setState(`info.mqttConnected`, true, true);
|
|
867
880
|
|
|
868
881
|
// Subscribe to all vehicle topics for this CarData Streaming username
|
|
869
882
|
const topic = `${this.config.cardataStreamingUsername}/+`;
|
|
870
|
-
this.mqtt
|
|
883
|
+
this.mqtt?.subscribe(topic, err => {
|
|
871
884
|
if (err) {
|
|
872
885
|
this.log.error(`MQTT subscription failed: ${err.message}`);
|
|
873
886
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.bmw",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.2",
|
|
4
4
|
"description": "Adapter for BMW",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TA2k",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@iobroker/eslint-config": "^2.2.0",
|
|
37
37
|
"@iobroker/testing": "^5.2.2",
|
|
38
38
|
"@tsconfig/node20": "^20.1.8",
|
|
39
|
-
"@types/node": "^24.10.
|
|
39
|
+
"@types/node": "^24.10.4",
|
|
40
40
|
"@types/qs": "^6.14.0",
|
|
41
41
|
"globals": "^16.5.0",
|
|
42
42
|
"typescript": "~5.9.3"
|
package/telematic.json
CHANGED
|
@@ -101,8 +101,8 @@
|
|
|
101
101
|
{
|
|
102
102
|
"cardata_element": "Charging port status text",
|
|
103
103
|
"cardata_element_de": "Status-Text des Ladeanschlusses",
|
|
104
|
-
"description": "Required alongside Vehicle.Body.ChargingPort.Status to ensure correct handling of charging sessions for G08 BEV vehicles
|
|
105
|
-
"description_de": "Erforderlich zusammen mit Vehicle.Body.ChargingPort.Status, um eine korrekte Handhabbarkeit der Ladesitzungen bei G08 BEV Fahrzeugen sicherzustellen
|
|
104
|
+
"description": "Required alongside Vehicle.Body.ChargingPort.Status to ensure correct handling of charging sessions for G08 BEV vehicles.",
|
|
105
|
+
"description_de": "Erforderlich zusammen mit Vehicle.Body.ChargingPort.Status, um eine korrekte Handhabbarkeit der Ladesitzungen bei G08 BEV Fahrzeugen sicherzustellen.",
|
|
106
106
|
"technical_identifier": "vehicle.body.chargingPort.statusClearText",
|
|
107
107
|
"data_type": "string",
|
|
108
108
|
"typical_value_range": [
|
|
@@ -2712,7 +2712,7 @@
|
|
|
2712
2712
|
"description_de": "Der Fehlerspeicher gibt Auskunft über potenzielle Störungen oder technische Defekte im Fahrzeug. Diese Informationen sind für Werkstätten gedacht. Kundenrelevante Störungen, die dem Fahrer im Fahrzeug angezeigt werden, sind unter dem CarData Element „Check Control Meldungen“ zu finden. Details hierzu sind in der Betriebsanleitung des Fahrzeugs dokumentiert.",
|
|
2713
2713
|
"technical_identifier": "vehicle.electronicControlUnit.diagnosticTroubleCodes.raw",
|
|
2714
2714
|
"data_type": "",
|
|
2715
|
-
"typical_value_range": "
|
|
2715
|
+
"typical_value_range": "-",
|
|
2716
2716
|
"unit": "",
|
|
2717
2717
|
"streaming_capable": true
|
|
2718
2718
|
},
|
|
@@ -2742,17 +2742,6 @@
|
|
|
2742
2742
|
"unit": "",
|
|
2743
2743
|
"streaming_capable": true
|
|
2744
2744
|
},
|
|
2745
|
-
{
|
|
2746
|
-
"cardata_element": "Learning navigation",
|
|
2747
|
-
"cardata_element_de": "Lernende Navigation",
|
|
2748
|
-
"description": "Displays the learned navigation recommendations (preferred routes and destinations of the customer).",
|
|
2749
|
-
"description_de": "Gibt die gelernten Navigationsempfehlungen (bevorzugte Routen und Ziele des Kunden) an.",
|
|
2750
|
-
"technical_identifier": "vehicle.learningNavigation",
|
|
2751
|
-
"data_type": "",
|
|
2752
|
-
"typical_value_range": "Details can be found in the corresponding Swagger documentation: ../learningNavigation",
|
|
2753
|
-
"unit": "",
|
|
2754
|
-
"streaming_capable": false
|
|
2755
|
-
},
|
|
2756
2745
|
{
|
|
2757
2746
|
"cardata_element": "Vehicle image",
|
|
2758
2747
|
"cardata_element_de": "Fahrzeugbild",
|
|
@@ -3485,7 +3474,7 @@
|
|
|
3485
3474
|
"description_de": "Der Wert gibt die letzten relevanten Check-Control-Meldungen an, welche im Fahrzeug angezeigt und an BMW übertragen wurden. Check-Control überwacht Funktionen im Fahrzeug und meldet, wenn in überwachten Systemen eine Störung vorliegt. Eine Check-Control-Meldung wird als Kombination von Kontroll- oder Warnleuchten und Textmeldungen in der Instrumentenkombination und ggf. im Head-Up Display angezeigt. Hinweis: Nicht alle Check-Control-Meldungen, die im Fahrzeug angezeigt werden, werden an BMW übertragen.",
|
|
3486
3475
|
"technical_identifier": "vehicle.status.checkControlMessages",
|
|
3487
3476
|
"data_type": "",
|
|
3488
|
-
"typical_value_range": "
|
|
3477
|
+
"typical_value_range": "-",
|
|
3489
3478
|
"unit": "",
|
|
3490
3479
|
"streaming_capable": false
|
|
3491
3480
|
},
|
|
@@ -3496,7 +3485,7 @@
|
|
|
3496
3485
|
"description_de": "Sensoren und spezielle Algorithmen berücksichtigen die Einsatzbedingungen des Fahrzeugs. CBS ermittelt damit den Wartungsbedarf. Das System ermöglicht somit den Wartungsumfang an das individuelle Nutzungsprofil anzupassen.",
|
|
3497
3486
|
"technical_identifier": "vehicle.status.conditionBasedServices",
|
|
3498
3487
|
"data_type": "",
|
|
3499
|
-
"typical_value_range": "
|
|
3488
|
+
"typical_value_range": "-",
|
|
3500
3489
|
"unit": "",
|
|
3501
3490
|
"streaming_capable": false
|
|
3502
3491
|
},
|
package/lib/tools.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
// DEACTIVATED
|
|
2
|
-
|
|
3
|
-
// const axios = require('axios').default;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Tests whether the given variable is a real object and not an Array
|
|
7
|
-
*
|
|
8
|
-
* @param {any} it The variable to test
|
|
9
|
-
* @returns {it is Record<string, any>} True if the variable is a real object, false otherwise.
|
|
10
|
-
*/
|
|
11
|
-
/*
|
|
12
|
-
function isObject(it) {
|
|
13
|
-
// This is necessary because:
|
|
14
|
-
// typeof null === 'object'
|
|
15
|
-
// typeof [] === 'object'
|
|
16
|
-
// [] instanceof Object === true
|
|
17
|
-
return Object.prototype.toString.call(it) === '[object Object]';
|
|
18
|
-
}*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Tests whether the given variable is really an Array
|
|
22
|
-
*
|
|
23
|
-
* @param {any} it The variable to test
|
|
24
|
-
* @returns {it is any[]} True if the variable is an array, false otherwise.
|
|
25
|
-
*/
|
|
26
|
-
/*function isArray(it) {
|
|
27
|
-
if (typeof Array.isArray === 'function') {
|
|
28
|
-
return Array.isArray(it);
|
|
29
|
-
}
|
|
30
|
-
return Object.prototype.toString.call(it) === '[object Array]';
|
|
31
|
-
}*/
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Translates text to the target language. Automatically chooses the right translation API.
|
|
35
|
-
*
|
|
36
|
-
* @param {string} text The text to translate
|
|
37
|
-
* @param {string} targetLang The target languate
|
|
38
|
-
* @param {string} [yandexApiKey] The yandex API key. You can create one for free at https://translate.yandex.com/developers
|
|
39
|
-
* @returns {Promise<string>} A promise that resolves to the translated text.
|
|
40
|
-
*/
|
|
41
|
-
/*async function translateText(text, targetLang, yandexApiKey) {
|
|
42
|
-
if (targetLang === 'en') {
|
|
43
|
-
return text;
|
|
44
|
-
} else if (!text) {
|
|
45
|
-
return '';
|
|
46
|
-
}
|
|
47
|
-
if (yandexApiKey) {
|
|
48
|
-
return translateYandex(text, targetLang, yandexApiKey);
|
|
49
|
-
}
|
|
50
|
-
return translateGoogle(text, targetLang);
|
|
51
|
-
}*/
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Translates text with Yandex API
|
|
55
|
-
*
|
|
56
|
-
* @param {string} text The text to translate
|
|
57
|
-
* @param {string} targetLang The target languate
|
|
58
|
-
* @param {string} apiKey The yandex API key. You can create one for free at https://translate.yandex.com/developers
|
|
59
|
-
* @returns {Promise<string>} A promise that resolves to the translated text.
|
|
60
|
-
*/
|
|
61
|
-
/*async function translateYandex(text, targetLang, apiKey) {
|
|
62
|
-
if (targetLang === 'zh-cn') {
|
|
63
|
-
targetLang = 'zh';
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
const url = `https://translate.yandex.net/api/v1.5/tr.json/translate?key=${apiKey}&text=${encodeURIComponent(
|
|
67
|
-
text,
|
|
68
|
-
)}&lang=en-${targetLang}`;
|
|
69
|
-
const response = await axios({ url, timeout: 15000 });
|
|
70
|
-
if (response.data && response.data.text && isArray(response.data.text)) {
|
|
71
|
-
return response.data.text[0];
|
|
72
|
-
}
|
|
73
|
-
throw new Error('Invalid response for translate request');
|
|
74
|
-
} catch (e) {
|
|
75
|
-
throw new Error(`Could not translate to "${targetLang}": ${e}`);
|
|
76
|
-
}
|
|
77
|
-
}*/
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Translates text with Google API
|
|
81
|
-
*
|
|
82
|
-
* @param {string} text The text to translate
|
|
83
|
-
* @param {string} targetLang The target languate
|
|
84
|
-
* @returns {Promise<string>} A promise that resolves to the translated text.
|
|
85
|
-
*/
|
|
86
|
-
/*async function translateGoogle(text, targetLang) {
|
|
87
|
-
try {
|
|
88
|
-
const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(
|
|
89
|
-
text,
|
|
90
|
-
)}&ie=UTF-8&oe=UTF-8`;
|
|
91
|
-
const response = await axios({ url, timeout: 15000 });
|
|
92
|
-
if (isArray(response.data)) {
|
|
93
|
-
// we got a valid response
|
|
94
|
-
return response.data[0][0][0];
|
|
95
|
-
}
|
|
96
|
-
throw new Error('Invalid response for translate request');
|
|
97
|
-
} catch (e) {
|
|
98
|
-
if (e.response && e.response.status === 429) {
|
|
99
|
-
throw new Error(`Could not translate to "${targetLang}": Rate-limited by Google Translate`);
|
|
100
|
-
} else {
|
|
101
|
-
throw new Error(`Could not translate to "${targetLang}": ${e}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}*/
|
|
105
|
-
|
|
106
|
-
/*module.exports = {
|
|
107
|
-
isArray,
|
|
108
|
-
isObject,
|
|
109
|
-
translateText,
|
|
110
|
-
};*/
|