iobroker.bmw 2.5.7 → 2.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.
Files changed (3) hide show
  1. package/io-package.json +5 -1
  2. package/main.js +203 -13
  3. package/package.json +17 -16
package/io-package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "bmw",
4
- "version": "2.5.7",
4
+ "version": "2.6.0",
5
5
  "news": {
6
+ "2.6.0": {
7
+ "en": "Fix Status Updat. New status under bmw.0.VIN.state",
8
+ "de": "Status Update gefixt. Neuer Status unter bmw.0.VIN.state"
9
+ },
6
10
  "2.5.7": {
7
11
  "en": "Fix Quota problem",
8
12
  "de": "Quota Problem es muss jetzt explizit die Marke BMW oder Mini in den Optionen gewählt werden."
package/main.js CHANGED
@@ -12,6 +12,7 @@ const axios = require("axios").default;
12
12
  const { HttpsCookieAgent } = require("http-cookie-agent/http");
13
13
  const crypto = require("crypto");
14
14
  const qs = require("qs");
15
+ const Json2iob = require("json2iob");
15
16
  const { extractKeys } = require("./lib/extractKeys");
16
17
  const tough = require("tough-cookie");
17
18
  class Bmw extends utils.Adapter {
@@ -37,6 +38,131 @@ class Bmw extends utils.Adapter {
37
38
  this.session = {};
38
39
  this.statusBlock = {};
39
40
  this.nonChargingHistory = {};
41
+ this.json2iob = new Json2iob(this);
42
+ this.description = {
43
+ allTrips: "alle Fahrten des Autos",
44
+ avgCombinedConsumption: "Durchschnittlicher kombinierter Verbrauch",
45
+ communityAverage: "Gesamt Durchschnitt",
46
+ communityHigh: "Gesamt max.",
47
+ communityLow: "Gesamt min.",
48
+ userAverage: "Fahrer Durchschnitt",
49
+ avgElectricConsumption: "Durchschnittlicher elektrischer Verbrauch",
50
+ avgRecuperation: "Durchschnittliche Rekuperation",
51
+ chargecycleRange: "Ladezyklus Reichweite",
52
+ userCurrentChargeCycle: "aktueller Ladezyklus",
53
+ userHigh: "Fahrer max.",
54
+ totalElectricDistance: "gesamte elektrische Distanz",
55
+ batterySizeMax: "max. Batterie Ladeleistung in Wh",
56
+ resetDate: "Werte zur+ckgesetz am",
57
+ savedCO2: "Eingespartes CO2",
58
+ savedCO2greenEnergy: "Eingespartes CO2 grüne Energie",
59
+ totalSavedFuel: "Gesamt gesparter Kraftstoff",
60
+ apiV2: "limitierte v2 Api des Autos",
61
+ basicType: "Grundtyp",
62
+ bodyType: "Fahrzeugtyp",
63
+ brand: "Marke",
64
+ modelName: "Model Name",
65
+ series: "Serie",
66
+ vin: "Fahrzeugidentifikationsnummer",
67
+ chargingprofile: "Ladeprofil",
68
+ overrideTimer: "Einmalige Abfahrtszeit",
69
+ weekdays: "Wochentag",
70
+ departureTime: "Abfahrtszeit",
71
+ timerEnabled: "Timer Aktiviert",
72
+ preferredChargingWindow: "Tägliches Ladefenster",
73
+ endTime: "Ende Uhrzeit",
74
+ startTime: "Start Uhrzeit",
75
+ MONDAY: "Montag",
76
+ TUESDAY: "Dienstag",
77
+ WEDNESDAY: "Mittwoch",
78
+ THURSDAY: "Donnerstag",
79
+ FRIDAY: "Freitag",
80
+ SATURDAY: "Samstag",
81
+ SUNDAY: "Sonntag",
82
+ chargingMode: "Lademodus",
83
+ chargingPreferences: "Ladeeinstellungen",
84
+ climatizationEnabled: "Klimatisierung Aktiviert",
85
+ general: "Allgemeine Fahrzeuginformationen",
86
+ dealer: "Händler",
87
+ city: "Stadt",
88
+ country: "Land",
89
+ phone: "Telefon",
90
+ postalCode: "Postleitzahl",
91
+ street: "Straße",
92
+ supportedChargingModes: "unterstützte Lademodi",
93
+ accelerationValue: "Beschleunigungs Wert",
94
+ anticipationValue: "Erwartungswert",
95
+ auxiliaryConsumptionValue: "Hilfsverbrauchswert",
96
+ date: "Datum",
97
+ drivingModeValue: "Fahrmodus",
98
+ duration: "Dauer",
99
+ efficiencyValue: "Effizienz Wert",
100
+ electricDistance: "elektrische Distanz",
101
+ electricDistanceRatio: "elektrisches Distanzverhältnis in %",
102
+ savedFuel: "Eingesparter Kraftstoff",
103
+ totalConsumptionValue: "Gesamtverbrauchswert",
104
+ totalDistance: "Gesamtstrecke",
105
+ rangemap: "Reichweitenkarte",
106
+ center: "Mitte",
107
+ remote: "Fernbedienung",
108
+ CHARGE_NOW: "jetzt Aufladen",
109
+ CLIMATE_NOW: "Klimatisierung starten",
110
+ DOOR_LOCK: "Autotüren zusperren",
111
+ DOOR_UNLOCK: "Autotüren aufsperren",
112
+ GET_VEHICLES: "Fahrzeuginformationen abrufen",
113
+ GET_VEHICLE_STATUS: "Fahrzeug Status abrufen",
114
+ HORN_BLOW: "Hupe einschalten",
115
+ LIGHT_FLASH: "Lichthupe einschalten",
116
+ START_CHARGING: "Laden starten",
117
+ START_PRECONDITIONING: "Startvoraussetzung",
118
+ STOP_CHARGING: "Laden stoppen",
119
+ VEHICLE_FINDER: "Positionsdaten Fahrzeug abrufen",
120
+ serviceExecutionHistory: "Verlauf der Remote-Ausführung",
121
+ status: "Aktueller Status",
122
+ BRAKE_FLUID: "Bremsflüssigkeit",
123
+ cbsDescription: "Service Beschreibung",
124
+ cbsDueDate: "Service Fälligkeitsdatum",
125
+ cbsState: "Service Status",
126
+ cbsType: "Service Art",
127
+ VEHICLE_CHECK: "Fahrzeug Überprüfung",
128
+ position: "Position",
129
+ heading: "Richtung",
130
+ lat: "Latitude",
131
+ lon: "Longitude",
132
+ DCS_CCH_Activation: "DCS CCH Aktivierung",
133
+ DCS_CCH_Ongoing: "DCS CHH Laufend",
134
+ chargingConnectionType: "Ladeverbindungstyp",
135
+ chargingInductivePositioning: "Aufladen Induktive Positionierung",
136
+ chargingLevelHv: "Batterie SoC in %",
137
+ chargingStatus: "Ladestatus",
138
+ chargingTimeRemaining: "Verbleibende Ladezeit",
139
+ connectionStatus: "Verbindungsstatus Ladestecker",
140
+ doorDriverFront: "Fahrertüren",
141
+ driverFront: "Fahrertüren",
142
+ doorDriverRear: "Hintere Türe Fahrerseite",
143
+ doorLockState: "Fahrzeug Verriegelungszustand Türen und Fenster",
144
+ doorPassengerFront: "Beifahrertüre",
145
+ doorPassengerRear: "Hintere Türe Beifahrerseite",
146
+ hood: "Motorhaube",
147
+ internalDataTimeUTC: "Fahrzeugzeit UTC",
148
+ lastChargingEndReason: "letzter Grund für das Ende des Ladevorgangs",
149
+ lastChargingEndResult: "letztes Ladeendergebnis",
150
+ maxRangeElectric: "max. elektrische Reichweite in km",
151
+ maxRangeElectricMls: "max. elektrische Reichweite in mi",
152
+ mileage: "Kilometerstand",
153
+ remainingFuel: "Tankinhalt",
154
+ remainingRangeElectric: "restliche Reichweite Elektrisch in km",
155
+ remainingRangeElectricMls: "restliche Reichweite Elektrisch in mi",
156
+ remainingRangeFuel: "restliche Reichweite Kraftstoff in km",
157
+ remainingRangeFuelMls: "restliche Reichweite Kraftstoff in mi",
158
+ singleImmediateCharging: "einmalige Sofortaufladung",
159
+ trunk: "Kofferraum",
160
+ updateReason: "Aktualisierungsgrund",
161
+ updateTime: "Aktualisierungszeit",
162
+ vehicleCountry: "Fahrzeug Land",
163
+ windowDriverFront: "Fenster Fahrerseite",
164
+ windowPassengerFront: "Fenster Beifahrerseite",
165
+ };
40
166
  this.cookieJar = new tough.CookieJar(null, { ignoreError: true });
41
167
 
42
168
  this.requestClient = axios.create({
@@ -64,12 +190,13 @@ class Bmw extends utils.Adapter {
64
190
  await this.login();
65
191
  if (this.session.access_token) {
66
192
  // await this.getVehicles(); //old depracted api
67
- await this.cleanObjects();
68
193
 
69
194
  this.log.info(`Start getting ${this.config.brand} vehicles`);
70
- await this.getVehiclesv2();
195
+ await this.getVehiclesv2(true);
196
+ await this.cleanObjects();
197
+ await this.updateDevices();
71
198
  this.updateInterval = setInterval(async () => {
72
- await this.getVehiclesv2();
199
+ await this.updateDevices();
73
200
  }, this.config.interval * 60 * 1000);
74
201
  this.refreshTokenInterval = setInterval(() => {
75
202
  this.refreshToken();
@@ -233,6 +360,7 @@ class Bmw extends utils.Adapter {
233
360
  // },
234
361
  // native: {},
235
362
  // });
363
+
236
364
  await this.setObjectNotExistsAsync(vehicle.vin + ".general", {
237
365
  type: "channel",
238
366
  common: {
@@ -250,7 +378,7 @@ class Bmw extends utils.Adapter {
250
378
  error.response && this.log.error(JSON.stringify(error.response.data));
251
379
  });
252
380
  }
253
- async getVehiclesv2() {
381
+ async getVehiclesv2(firstStart) {
254
382
  const brand = this.config.brand;
255
383
  const headers = {
256
384
  "user-agent": this.userAgentDart,
@@ -263,17 +391,20 @@ class Bmw extends utils.Adapter {
263
391
 
264
392
  await this.requestClient({
265
393
  method: "get",
266
- url: "https://cocoapi.bmwgroup.com/eadrax-vcs/v1/vehicles?apptimezone=120&appDateTime=" + Date.now() + "&tireGuardMode=ENABLED",
394
+ url: "https://cocoapi.bmwgroup.com/eadrax-vcs/v4/vehicles?apptimezone=120&appDateTime=" + Date.now() + "&tireGuardMode=ENABLED",
267
395
  headers: headers,
268
396
  })
269
397
  .then(async (res) => {
270
398
  this.log.debug(JSON.stringify(res.data));
271
- this.log.info(`Found ${res.data.length} ${brand} vehicles`);
399
+ if (firstStart) {
400
+ this.log.info(`Found ${res.data.length} ${brand} vehicles`);
401
+ }
272
402
  if (res.data.length === 0) {
273
403
  this.log.info(`No ${brand} vehicles found please check brand in instance settings`);
274
404
  return;
275
405
  }
276
406
  for (const vehicle of res.data) {
407
+ this.vinArray.push(vehicle.vin);
277
408
  await this.setObjectNotExistsAsync(vehicle.vin, {
278
409
  type: "device",
279
410
  common: {
@@ -281,11 +412,10 @@ class Bmw extends utils.Adapter {
281
412
  },
282
413
  native: {},
283
414
  });
284
-
285
- await this.setObjectNotExistsAsync(vehicle.vin + ".properties", {
415
+ await this.setObjectNotExistsAsync(vehicle.vin + ".state", {
286
416
  type: "channel",
287
417
  common: {
288
- name: "Current status of the car v2",
418
+ name: "Current status of the car v4",
289
419
  },
290
420
  native: {},
291
421
  });
@@ -320,7 +450,12 @@ class Bmw extends utils.Adapter {
320
450
  native: {},
321
451
  });
322
452
  });
323
- this.extractKeys(this, vehicle.vin, vehicle, null, true);
453
+ // this.extractKeys(this, vehicle.vin, vehicle, null, true);
454
+ this.json2iob.parse(vehicle.vin, vehicle, {
455
+ forceIndex: true,
456
+ descriptions: this.description,
457
+ });
458
+
324
459
  await this.sleep(5000);
325
460
  this.updateChargingSessionv2(vehicle.vin);
326
461
  }
@@ -332,6 +467,36 @@ class Bmw extends utils.Adapter {
332
467
  });
333
468
  await this.sleep(5000);
334
469
  }
470
+ async updateDevices() {
471
+ const brand = this.config.brand;
472
+ const headers = {
473
+ "user-agent": this.userAgentDart,
474
+ "x-user-agent": this.xuserAgent.replace(";brand;", `;${brand};`),
475
+ authorization: "Bearer " + this.session.access_token,
476
+ "accept-language": "de-DE",
477
+ host: "cocoapi.bmwgroup.com",
478
+ "24-hour-format": "true",
479
+ };
480
+ for (const vin of this.vinArray) {
481
+ this.log.debug("update " + vin);
482
+ headers["bmw-vin"] = vin;
483
+ await this.requestClient({
484
+ method: "get",
485
+ url:
486
+ "https://cocoapi.bmwgroup.com/eadrax-vcs/v4/vehicles/state?apptimezone=120&appDateTime=" + Date.now() + "&tireGuardMode=ENABLED",
487
+ headers: headers,
488
+ })
489
+ .then(async (res) => {
490
+ this.log.debug(JSON.stringify(res.data));
491
+ this.json2iob.parse(vin, res.data, { forceIndex: true, descriptions: this.description });
492
+ })
493
+ .catch((error) => {
494
+ this.log.error("update failed");
495
+ this.log.error(error);
496
+ error.response && this.log.error(JSON.stringify(error.response.data));
497
+ });
498
+ }
499
+ }
335
500
  sleep(ms) {
336
501
  return new Promise((resolve) => setTimeout(resolve, ms));
337
502
  }
@@ -369,7 +534,7 @@ class Bmw extends utils.Adapter {
369
534
  urlArray.push({
370
535
  url: "https://cocoapi.bmwgroup.com/eadrax-chs/v1/charging-statistics?vin=" + vin + "&currentDate=" + fullDate,
371
536
  path: ".charging-statistics.",
372
- name: "Charging statistics",
537
+ name: "charging statistics",
373
538
  });
374
539
  for (const element of urlArray) {
375
540
  await this.requestClient({
@@ -390,8 +555,31 @@ class Bmw extends utils.Adapter {
390
555
  },
391
556
  native: {},
392
557
  });
558
+ await this.extractKeys(this, vin + element.path + dateFormatted, data);
393
559
 
394
- this.extractKeys(this, vin + element.path + dateFormatted, data);
560
+ if (element.name === "chargingSessions" && data.sessions && data.sessions.length > 0) {
561
+ try {
562
+ const datal = data.sessions[0];
563
+ datal._date = datal.id.split("_")[0];
564
+ datal._id = datal.id.split("_")[1];
565
+ datal.timestamp = new Date(datal._date).valueOf();
566
+ if (datal.energyCharged.replace) {
567
+ datal.energy = datal.energyCharged.replace("~", "").trim().split(" ")[0];
568
+ datal.unit = datal.energyCharged.replace("~", "").trim().split(" ")[1];
569
+ }
570
+ datal.id = "latest";
571
+ await this.setObjectNotExistsAsync(vin + element.path + "latest", {
572
+ type: "channel",
573
+ common: {
574
+ name: element.name + "latest of the car v2",
575
+ },
576
+ native: {},
577
+ });
578
+ await this.extractKeys(this, vin + element.path + "latest", datal);
579
+ } catch (error) {
580
+ this.log.debug(error);
581
+ }
582
+ }
395
583
  })
396
584
  .catch((error) => {
397
585
  if (error.response) {
@@ -409,7 +597,7 @@ class Bmw extends utils.Adapter {
409
597
 
410
598
  async cleanObjects() {
411
599
  for (const vin of this.vinArray) {
412
- const remoteState = await this.getObjectAsync(vin + ".apiV2");
600
+ const remoteState = await this.getObjectAsync(vin + ".properties");
413
601
 
414
602
  if (remoteState) {
415
603
  this.log.debug("clean old states" + vin);
@@ -417,6 +605,8 @@ class Bmw extends utils.Adapter {
417
605
  await this.delObjectAsync(vin + ".lastTrip", { recursive: true });
418
606
  await this.delObjectAsync(vin + ".allTrips", { recursive: true });
419
607
  await this.delObjectAsync(vin + ".status", { recursive: true });
608
+ await this.delObjectAsync(vin + ".properties", { recursive: true });
609
+ await this.delObjectAsync(vin + ".capabilities", { recursive: true });
420
610
  await this.delObjectAsync(vin + ".chargingprofile", { recursive: true });
421
611
  await this.delObjectAsync(vin + ".serviceExecutionHistory", { recursive: true });
422
612
  await this.delObjectAsync(vin + ".apiV2", { recursive: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.bmw",
3
- "version": "2.5.7",
3
+ "version": "2.6.0",
4
4
  "description": "Adapter for BMW",
5
5
  "author": {
6
6
  "name": "TA2k",
@@ -16,30 +16,31 @@
16
16
  "url": "https://github.com/TA2k/ioBroker.bmw"
17
17
  },
18
18
  "dependencies": {
19
- "@iobroker/adapter-core": "^2.6.6",
20
- "axios": "^0.27.2",
21
- "http-cookie-agent": "^4.0.2",
19
+ "@iobroker/adapter-core": "^2.6.8",
20
+ "axios": "^1.4.0",
21
+ "http-cookie-agent": "^5.0.4",
22
22
  "json-bigint": "^1.0.0",
23
- "qs": "^6.11.0",
24
- "tough-cookie": "^4.1.2"
23
+ "json2iob": "^2.4.4",
24
+ "qs": "^6.11.2",
25
+ "tough-cookie": "^4.1.3"
25
26
  },
26
27
  "devDependencies": {
27
28
  "@iobroker/testing": "^4.1.0",
28
- "@types/chai": "^4.3.3",
29
+ "@types/chai": "^4.3.5",
29
30
  "@types/chai-as-promised": "^7.1.5",
30
- "@types/mocha": "^9.1.1",
31
- "@types/node": "^18.7.18",
31
+ "@types/mocha": "^10.0.1",
32
+ "@types/node": "^18.16.18",
32
33
  "@types/proxyquire": "^1.3.28",
33
- "@types/sinon": "^10.0.13",
34
- "@types/sinon-chai": "^3.2.8",
35
- "chai": "^4.3.6",
34
+ "@types/sinon": "^10.0.15",
35
+ "@types/sinon-chai": "^3.2.9",
36
+ "chai": "^4.3.7",
36
37
  "chai-as-promised": "^7.1.1",
37
- "eslint": "^8.23.1",
38
- "mocha": "^10.0.0",
38
+ "eslint": "^8.43.0",
39
+ "mocha": "^10.2.0",
39
40
  "proxyquire": "^2.1.3",
40
- "sinon": "^14.0.0",
41
+ "sinon": "^15.2.0",
41
42
  "sinon-chai": "^3.7.0",
42
- "typescript": "^4.8.3"
43
+ "typescript": "^5.1.6"
43
44
  },
44
45
  "main": "main.js",
45
46
  "scripts": {