iobroker.mg-saic 0.1.3 → 0.1.4

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 CHANGED
@@ -25,14 +25,16 @@ For manufacturer information, see [MG Motor](https://www.mgmotor.eu/) and [SAIC
25
25
  1. Install the adapter and create an instance in ioBroker Admin.
26
26
  2. Select the account region and login type (email or phone number).
27
27
  3. Enter the account's email address or phone number and password. For phone login, also enter the phone country calling code.
28
- 4. Set the status polling interval >60 seconds. The default is 1800 seconds.
28
+ 4. Set the status polling interval to 60 seconds or more. The default is 1800 seconds.
29
29
  5. Save the settings and start the instance. Vehicles available to the account appear under `vehicles.<VIN>`.
30
+ Existing vehicle object IDs are reused if the cloud API changes the VIN casing. Device labels use the localized generic vehicle name; the VIN, brand, model, and other available vehicle metadata are provided as read-only states.
30
31
 
31
32
  ## Features
32
33
 
33
34
  - Connects ioBroker to the iSmart cloud service and retrieves vehicle status.
34
35
  - Provides remote controls for supported vehicle functions.
35
36
  - Supports climate, heating, and charging features where available for the vehicle.
37
+ - Omits unrecognized free-form charging text while retaining numeric measurements and validated charging-point identifiers.
36
38
 
37
39
  ## Installation
38
40
 
@@ -43,6 +45,10 @@ The adapter is published on npm and available from ioBroker's Latest adapter rep
43
45
  [![Donate](https://camo.githubusercontent.com/ef8c60305542390406855547e32850909812a73c4abada236de22a6dc88b313f/68747470733a2f2f7777772e70617970616c6f626a656374732e636f6d2f64655f44452f44452f692f62746e2f62746e5f646f6e61746543435f4c472e676966)](https://www.paypal.com/donate/?hosted_button_id=3RJQGSJNRSHUJ)
44
46
 
45
47
  ## Changelog
48
+ ### 0.1.4 (2026-11-11)
49
+
50
+ - (Voodoo2man) Corrected command-response validation, status values, charging-field handling, vehicle ID reuse, and cloud polling intervals; removed the non-informative vehicle name state.
51
+
46
52
  ### 0.1.3 (2026-11-08)
47
53
 
48
54
  - (Voodoo2man) Retry startup initialization after temporary cloud login and vehicle-list outages with bounded backoff, prevent false setting writes from triggering remote setting actions, and add the missing region labels in the Admin UI.
@@ -64,13 +70,6 @@ The adapter is published on npm and available from ioBroker's Latest adapter rep
64
70
  - (Voodoo2man) Applied confirmed profile-specific charging conversions.
65
71
  - (Voodoo2man) Calculated available battery energy from battery level and usable capacity.
66
72
 
67
- ### 0.0.4 (2026-10-24)
68
-
69
- - (Voodoo2man) Corrected GPS coordinates.
70
- - (Voodoo2man) Fixed incorrect datapoint values.
71
- - (Voodoo2man) Removed duplicate and irrelevant datapoints.
72
- - (Voodoo2man) Set min version of admin to 8.2.0, js-controller 7.2.2, node.js 24
73
-
74
73
  Older changelog entries are available in [CHANGELOG_OLD.md](CHANGELOG_OLD.md).
75
74
 
76
75
  ## License
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var initialization_retry_exports = {};
20
+ __export(initialization_retry_exports, {
21
+ calculateInitializationRetryDelayMs: () => calculateInitializationRetryDelayMs
22
+ });
23
+ module.exports = __toCommonJS(initialization_retry_exports);
24
+ const INITIALIZATION_RETRY_DELAY_MS = 6e4;
25
+ const MAX_INITIALIZATION_RETRY_DELAY_MS = 5 * INITIALIZATION_RETRY_DELAY_MS;
26
+ function calculateInitializationRetryDelayMs(retryCount) {
27
+ const attempts = Math.max(1, retryCount);
28
+ if (attempts <= 3) {
29
+ return INITIALIZATION_RETRY_DELAY_MS * attempts;
30
+ }
31
+ return MAX_INITIALIZATION_RETRY_DELAY_MS;
32
+ }
33
+ // Annotate the CommonJS export names for ESM import in node:
34
+ 0 && (module.exports = {
35
+ calculateInitializationRetryDelayMs
36
+ });
37
+ //# sourceMappingURL=initialization-retry.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/initialization-retry.ts"],
4
+ "sourcesContent": ["const INITIALIZATION_RETRY_DELAY_MS = 60_000;\nconst MAX_INITIALIZATION_RETRY_DELAY_MS = 5 * INITIALIZATION_RETRY_DELAY_MS;\n\n/**\n * Returns the bounded delay before retrying initialization.\n *\n * @param retryCount Number of initialization failures.\n * @returns Retry delay in milliseconds.\n */\nexport function calculateInitializationRetryDelayMs(retryCount: number): number {\n\tconst attempts = Math.max(1, retryCount);\n\tif (attempts <= 3) {\n\t\treturn INITIALIZATION_RETRY_DELAY_MS * attempts;\n\t}\n\treturn MAX_INITIALIZATION_RETRY_DELAY_MS;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAM,gCAAgC;AACtC,MAAM,oCAAoC,IAAI;AAQvC,SAAS,oCAAoC,YAA4B;AAC/E,QAAM,WAAW,KAAK,IAAI,GAAG,UAAU;AACvC,MAAI,YAAY,GAAG;AAClB,WAAO,gCAAgC;AAAA,EACxC;AACA,SAAO;AACR;",
6
+ "names": []
7
+ }
package/build/main.js CHANGED
@@ -5,10 +5,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
8
  var __copyProps = (to, from, except, desc) => {
13
9
  if (from && typeof from === "object" || typeof from === "function") {
14
10
  for (let key of __getOwnPropNames(from))
@@ -25,16 +21,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
21
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
22
  mod
27
23
  ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
- var main_exports = {};
30
- __export(main_exports, {
31
- calculateInitializationRetryDelayMs: () => calculateInitializationRetryDelayMs
32
- });
33
- module.exports = __toCommonJS(main_exports);
34
24
  var utils = __toESM(require("@iobroker/adapter-core"));
35
25
  var import_promises = require("node:fs/promises");
36
26
  var import_node_path = require("node:path");
37
27
  var import_config = require("./config");
28
+ var import_initialization_retry = require("./initialization-retry");
38
29
  var import_saic_client = require("./saic-client");
39
30
  var import_regions = require("./regions");
40
31
  var import_refresh_command = require("./refresh-command");
@@ -43,6 +34,7 @@ var import_refresh_poll_timer = require("./refresh-poll-timer");
43
34
  var import_pending_setting_edits = require("./pending-setting-edits");
44
35
  var import_status_metadata = require("./status-metadata");
45
36
  var import_runtime_translations = require("./runtime-translations");
37
+ var import_vehicle_object_ids = require("./vehicle-object-ids");
46
38
  const chargingGroups = [
47
39
  { source: "chrgMgmtData", id: "session", name: "chargingManagement" },
48
40
  { source: "rvsChargeStatus", id: "statistics", name: "chargeStatus" }
@@ -120,15 +112,6 @@ const settingInputsByAction = {
120
112
  };
121
113
  const EXPOSE_SOURCE_STATUS_DATAPOINTS = false;
122
114
  const PENDING_SETTING_EDITS_FILE = "pending-setting-edits.json";
123
- const INITIALIZATION_RETRY_DELAY_MS = 6e4;
124
- const MAX_INITIALIZATION_RETRY_DELAY_MS = 5 * INITIALIZATION_RETRY_DELAY_MS;
125
- function calculateInitializationRetryDelayMs(retryCount) {
126
- const attempts = Math.max(1, retryCount);
127
- if (attempts <= 3) {
128
- return INITIALIZATION_RETRY_DELAY_MS * attempts;
129
- }
130
- return MAX_INITIALIZATION_RETRY_DELAY_MS;
131
- }
132
115
  class MgSaic extends utils.Adapter {
133
116
  constructor(options = {}) {
134
117
  super({
@@ -156,6 +139,7 @@ class MgSaic extends utils.Adapter {
156
139
  accessToken;
157
140
  accessTokenExpiresAt = 0;
158
141
  vehicles = [];
142
+ vehicleObjectIds = /* @__PURE__ */ new Map();
159
143
  vehicleStatusFieldIds = /* @__PURE__ */ new Map();
160
144
  lastStatusTimes = /* @__PURE__ */ new Map();
161
145
  lastCanonicalStatusTimes = /* @__PURE__ */ new Map();
@@ -268,15 +252,17 @@ class MgSaic extends utils.Adapter {
268
252
  return;
269
253
  }
270
254
  try {
255
+ await this.loadVehicleObjectIds();
271
256
  for (const vehicle of this.vehicles) {
272
257
  if (this.stopping) {
273
258
  return;
274
259
  }
275
260
  await this.createVehicleObjects(vehicle);
261
+ const vehicleObjectId = this.getVehicleObjectId(vehicle.vin);
276
262
  if (!EXPOSE_SOURCE_STATUS_DATAPOINTS) {
277
- await this.delObjectAsync(`vehicles.${vehicle.vin}.cachedStatus`, { recursive: true });
263
+ await this.delObjectAsync(`vehicles.${vehicleObjectId}.cachedStatus`, { recursive: true });
278
264
  }
279
- await this.delObjectAsync(`vehicles.${vehicle.vin}.canonicalStatus`, { recursive: true });
265
+ await this.delObjectAsync(`vehicles.${vehicleObjectId}.canonicalStatus`, { recursive: true });
280
266
  }
281
267
  try {
282
268
  await this.restorePendingSettingEdits();
@@ -309,7 +295,7 @@ class MgSaic extends utils.Adapter {
309
295
  return;
310
296
  }
311
297
  this.initializationRetryCount += 1;
312
- const retryDelayMs = calculateInitializationRetryDelayMs(this.initializationRetryCount);
298
+ const retryDelayMs = (0, import_initialization_retry.calculateInitializationRetryDelayMs)(this.initializationRetryCount);
313
299
  if (this.initializationRetryTimer !== void 0) {
314
300
  this.clearTimeout(this.initializationRetryTimer);
315
301
  }
@@ -564,10 +550,12 @@ class MgSaic extends utils.Adapter {
564
550
  return;
565
551
  }
566
552
  const settingAction = settingActions.find(
567
- ({ id: actionId }) => this.vehicles.some(({ vin }) => eventId === `${this.namespace}.${vehicleCommandPath(vin, actionId)}`)
553
+ ({ id: actionId }) => this.vehicles.some(
554
+ ({ vin }) => eventId === `${this.namespace}.${this.vehicleCommandStatePath(vin, actionId)}`
555
+ )
568
556
  );
569
557
  const settingVehicle = this.vehicles.find(
570
- ({ vin }) => settingAction && eventId === `${this.namespace}.${vehicleCommandPath(vin, settingAction.id)}`
558
+ ({ vin }) => settingAction && eventId === `${this.namespace}.${this.vehicleCommandStatePath(vin, settingAction.id)}`
571
559
  );
572
560
  if (settingAction && settingVehicle) {
573
561
  if (state.val === true) {
@@ -577,16 +565,16 @@ class MgSaic extends utils.Adapter {
577
565
  }
578
566
  const settingInput = this.vehicles.flatMap(
579
567
  ({ vin }) => Object.keys(settingGroups).filter((stateId) => stateId !== "driverSeatHeatLevel" && stateId !== "passengerSeatHeatLevel").map((stateId) => ({ vin, stateId }))
580
- ).find(({ vin, stateId }) => eventId === `${this.namespace}.${vehicleCommandPath(vin, stateId)}`);
568
+ ).find(({ vin, stateId }) => eventId === `${this.namespace}.${this.vehicleCommandStatePath(vin, stateId)}`);
581
569
  if (settingInput) {
582
- const stateId = vehicleCommandPath(settingInput.vin, settingInput.stateId);
570
+ const stateId = this.vehicleCommandStatePath(settingInput.vin, settingInput.stateId);
583
571
  this.pendingSettingEdits.set(stateId, ++this.settingEditSequence);
584
572
  void this.acknowledgeSettingEdit(stateId, state.val);
585
573
  return;
586
574
  }
587
575
  const seatHeating = this.vehicles.flatMap(
588
576
  ({ vin }) => ["driverSeatHeatLevel", "passengerSeatHeatLevel"].map((stateId) => ({ vin, stateId }))
589
- ).find(({ vin, stateId }) => eventId === `${this.namespace}.${vehicleCommandPath(vin, stateId)}`);
577
+ ).find(({ vin, stateId }) => eventId === `${this.namespace}.${this.vehicleCommandStatePath(vin, stateId)}`);
590
578
  if (seatHeating) {
591
579
  const seat = seatHeating.stateId === "driverSeatHeatLevel" ? "driver" : "passenger";
592
580
  void this.executeSeatHeating(seatHeating.vin, seat, state.val);
@@ -596,10 +584,12 @@ class MgSaic extends utils.Adapter {
596
584
  return;
597
585
  }
598
586
  const commandEntry = vehicleCommands.find(
599
- ({ id: commandId }) => this.vehicles.some(({ vin }) => eventId === `${this.namespace}.${vehicleCommandPath(vin, commandId)}`)
587
+ ({ id: commandId }) => this.vehicles.some(
588
+ ({ vin }) => eventId === `${this.namespace}.${this.vehicleCommandStatePath(vin, commandId)}`
589
+ )
600
590
  );
601
591
  const vehicle = this.vehicles.find(
602
- ({ vin }) => commandEntry && eventId === `${this.namespace}.${vehicleCommandPath(vin, commandEntry.id)}`
592
+ ({ vin }) => commandEntry && eventId === `${this.namespace}.${this.vehicleCommandStatePath(vin, commandEntry.id)}`
603
593
  );
604
594
  if (!commandEntry || !vehicle) {
605
595
  return;
@@ -667,7 +657,7 @@ class MgSaic extends utils.Adapter {
667
657
  async restorePendingSettingEdits() {
668
658
  const validStateIds = new Set(
669
659
  this.vehicles.flatMap(
670
- ({ vin }) => Object.keys(settingGroups).filter((stateId) => stateId !== "driverSeatHeatLevel" && stateId !== "passengerSeatHeatLevel").map((stateId) => vehicleCommandPath(vin, stateId))
660
+ ({ vin }) => Object.keys(settingGroups).filter((stateId) => stateId !== "driverSeatHeatLevel" && stateId !== "passengerSeatHeatLevel").map((stateId) => this.vehicleCommandStatePath(vin, stateId))
671
661
  )
672
662
  );
673
663
  const savedFileValue = await this.readPendingSettingEditsFile();
@@ -682,8 +672,28 @@ class MgSaic extends utils.Adapter {
682
672
  }
683
673
  await this.persistPendingSettingEdits();
684
674
  }
675
+ async loadVehicleObjectIds() {
676
+ const objects = await this.getObjectListAsync({
677
+ startkey: `${this.namespace}.vehicles.`,
678
+ endkey: `${this.namespace}.vehicles.\u9999`
679
+ });
680
+ const namespacePrefix = `${this.namespace}.`;
681
+ const existingDeviceIds = objects.rows.filter((row) => row.value.type === "device" && row.id.startsWith(namespacePrefix)).map((row) => row.id.slice(namespacePrefix.length));
682
+ const existingVehicleObjectIds = (0, import_vehicle_object_ids.indexExistingVehicleObjectIds)(existingDeviceIds);
683
+ this.vehicleObjectIds = /* @__PURE__ */ new Map();
684
+ for (const { vin } of this.vehicles) {
685
+ this.vehicleObjectIds.set(vin.toLowerCase(), (0, import_vehicle_object_ids.resolveVehicleObjectId)(existingVehicleObjectIds, vin));
686
+ }
687
+ }
688
+ getVehicleObjectId(vin) {
689
+ var _a;
690
+ return (_a = this.vehicleObjectIds.get(vin.toLowerCase())) != null ? _a : vin;
691
+ }
692
+ vehicleCommandStatePath(vin, id) {
693
+ return vehicleCommandPath(this.getVehicleObjectId(vin), id);
694
+ }
685
695
  async executeSeatHeating(vin, seat, value) {
686
- const stateId = vehicleCommandPath(vin, `${seat}SeatHeatLevel`);
696
+ const stateId = this.vehicleCommandStatePath(vin, `${seat}SeatHeatLevel`);
687
697
  if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > 3) {
688
698
  await this.setStateAsync(stateId, { ack: true });
689
699
  this.log.warn(`Ignored ${seat} seat heating level for ${vin}: expected an integer from 0 to 3`);
@@ -723,9 +733,9 @@ class MgSaic extends utils.Adapter {
723
733
  }
724
734
  }
725
735
  async executeVehicleSetting(vin, action) {
726
- const stateId = vehicleCommandPath(vin, action);
736
+ const stateId = this.vehicleCommandStatePath(vin, action);
727
737
  const editVersions = new Map(
728
- settingInputsByAction[action].map((settingId) => vehicleCommandPath(vin, settingId)).filter((settingId) => this.pendingSettingEdits.has(settingId)).map((settingId) => [settingId, this.pendingSettingEdits.get(settingId)])
738
+ settingInputsByAction[action].map((settingId) => this.vehicleCommandStatePath(vin, settingId)).filter((settingId) => this.pendingSettingEdits.has(settingId)).map((settingId) => [settingId, this.pendingSettingEdits.get(settingId)])
729
739
  );
730
740
  if (this.vehicleCommandsInProgress.has(vin)) {
731
741
  await this.setStateAsync(stateId, { val: false, ack: true });
@@ -781,14 +791,14 @@ class MgSaic extends utils.Adapter {
781
791
  }
782
792
  async readVehicleSetting(vin, action) {
783
793
  const readNumber = async (id) => {
784
- const state = await this.getStateAsync(vehicleCommandPath(vin, id));
794
+ const state = await this.getStateAsync(this.vehicleCommandStatePath(vin, id));
785
795
  if (!state || typeof state.val !== "number" || !Number.isFinite(state.val)) {
786
796
  throw new Error(`Setting ${id} must contain a finite number`);
787
797
  }
788
798
  return state.val;
789
799
  };
790
800
  const readString = async (id) => {
791
- const state = await this.getStateAsync(vehicleCommandPath(vin, id));
801
+ const state = await this.getStateAsync(this.vehicleCommandStatePath(vin, id));
792
802
  if (!state || typeof state.val !== "string") {
793
803
  throw new Error(`Setting ${id} must contain a time in HH:MM format`);
794
804
  }
@@ -816,7 +826,7 @@ class MgSaic extends utils.Adapter {
816
826
  mode: await readNumber("chargingScheduleMode")
817
827
  };
818
828
  }
819
- const enabled = await this.getStateAsync(vehicleCommandPath(vin, "batteryHeatingScheduleEnabled"));
829
+ const enabled = await this.getStateAsync(this.vehicleCommandStatePath(vin, "batteryHeatingScheduleEnabled"));
820
830
  if (!enabled || typeof enabled.val !== "boolean") {
821
831
  throw new Error("Battery heating schedule enabled must be boolean");
822
832
  }
@@ -827,7 +837,7 @@ class MgSaic extends utils.Adapter {
827
837
  };
828
838
  }
829
839
  async executeVehicleCommand(vin, command) {
830
- const stateId = vehicleCommandPath(vin, command);
840
+ const stateId = this.vehicleCommandStatePath(vin, command);
831
841
  if (this.vehicleCommandsInProgress.has(vin)) {
832
842
  await this.setStateAsync(stateId, { val: false, ack: true });
833
843
  this.log.warn(`Ignored ${command} command for ${vin}: another vehicle command is still running`);
@@ -881,7 +891,8 @@ class MgSaic extends utils.Adapter {
881
891
  if (!EXPOSE_SOURCE_STATUS_DATAPOINTS && (channel === "liveStatus" || channel === "cachedStatus")) {
882
892
  return;
883
893
  }
884
- const statusId = `vehicles.${vin}.${channel}`;
894
+ const vehicleObjectId = this.getVehicleObjectId(vin);
895
+ const statusId = `vehicles.${vehicleObjectId}.${channel}`;
885
896
  await this.setObjectNotExistsAsync(statusId, {
886
897
  type: "channel",
887
898
  common: { name: (0, import_runtime_translations.getRuntimeName)(channelName) },
@@ -973,7 +984,7 @@ class MgSaic extends utils.Adapter {
973
984
  }
974
985
  const seatSetting = fieldName === "frontLeftSeatHeatLevel" ? "driverSeatHeatLevel" : fieldName === "frontRightSeatHeatLevel" ? "passengerSeatHeatLevel" : void 0;
975
986
  if (seatSetting && Number.isInteger(value) && value >= 0 && value <= 3) {
976
- const settingId = vehicleCommandPath(vin, seatSetting);
987
+ const settingId = this.vehicleCommandStatePath(vin, seatSetting);
977
988
  const settingState = await this.getStateAsync(settingId);
978
989
  if (!settingState || settingState.ack) {
979
990
  await this.setStateAsync(settingId, { val: value, ack: true });
@@ -983,9 +994,9 @@ class MgSaic extends utils.Adapter {
983
994
  }
984
995
  }
985
996
  async createVehicleChargingObjects(vin, charging, series) {
986
- var _a, _b;
997
+ var _a, _b, _c, _d, _e, _f, _g;
987
998
  await this.ensureVehicleChargingGroups(vin);
988
- const rootId = `vehicles.${vin}.status.charging`;
999
+ const rootId = `vehicles.${this.getVehicleObjectId(vin)}.status.charging`;
989
1000
  const existingObjects = await this.getObjectListAsync({
990
1001
  startkey: `${this.namespace}.${rootId}.`,
991
1002
  endkey: `${this.namespace}.${rootId}.\u9999`
@@ -1057,8 +1068,8 @@ class MgSaic extends utils.Adapter {
1057
1068
  val: normalizedValue,
1058
1069
  ack: true
1059
1070
  });
1060
- if (field === "bmsOnBdChrgTrgtSOCDspCmd" && typeof value === "number" && value >= 1 && value <= 7) {
1061
- const targetSocId = vehicleCommandPath(vin, "chargeTargetSoc");
1071
+ if (canonicalField === "bmsOnBdChrgTrgtSOCDspCmd" && typeof value === "number" && value >= 1 && value <= 7) {
1072
+ const targetSocId = this.vehicleCommandStatePath(vin, "chargeTargetSoc");
1062
1073
  const targetSocState = await this.getStateAsync(targetSocId);
1063
1074
  if ((0, import_status_metadata.shouldApplySettingFeedback)(targetSocState, this.pendingSettingEdits.has(targetSocId))) {
1064
1075
  await this.setStateAsync(targetSocId, {
@@ -1069,35 +1080,41 @@ class MgSaic extends utils.Adapter {
1069
1080
  }
1070
1081
  }
1071
1082
  }
1072
- const capacityState = await this.getStateAsync(`${rootId}.statistics.totalBatteryCapacity`);
1073
- const socState = await this.getStateAsync(`${rootId}.session.bmsPackSOCDsp`);
1083
+ const statisticsFields = (_c = existingFieldIds.get("statistics")) != null ? _c : [];
1084
+ const sessionFields = (_d = existingFieldIds.get("session")) != null ? _d : [];
1085
+ const capacityField = (_e = statisticsFields.find((field) => field.toLowerCase() === "totalbatterycapacity")) != null ? _e : "totalBatteryCapacity";
1086
+ const availableEnergyField = (_f = statisticsFields.find((field) => field.toLowerCase() === "batteryavailableenergy")) != null ? _f : "batteryAvailableEnergy";
1087
+ const socField = (_g = sessionFields.find((field) => field.toLowerCase() === "bmspacksocdsp")) != null ? _g : "bmsPackSOCDsp";
1088
+ const capacityState = await this.getStateAsync(`${rootId}.statistics.${capacityField}`);
1089
+ const socState = await this.getStateAsync(`${rootId}.session.${socField}`);
1074
1090
  const availableEnergy = (0, import_status_metadata.calculateAvailableBatteryEnergy)(
1075
1091
  capacityState == null ? void 0 : capacityState.val,
1076
1092
  socState == null ? void 0 : socState.val,
1077
1093
  charging.rvsChargeStatus.packEnergyKwh
1078
1094
  );
1079
1095
  if (availableEnergy !== void 0) {
1080
- await this.setStateAsync(`${rootId}.statistics.batteryAvailableEnergy`, {
1096
+ await this.setStateAsync(`${rootId}.statistics.${availableEnergyField}`, {
1081
1097
  val: availableEnergy,
1082
1098
  ack: true
1083
1099
  });
1084
1100
  } else {
1085
- await this.setStateAsync(`${rootId}.statistics.batteryAvailableEnergy`, {
1101
+ await this.setStateAsync(`${rootId}.statistics.${availableEnergyField}`, {
1086
1102
  val: null,
1087
1103
  ack: true
1088
1104
  });
1089
1105
  }
1090
1106
  for (const [setting, value] of Object.entries((0, import_status_metadata.getChargingSettingFeedback)(charging.chrgMgmtData))) {
1091
- const stateId = vehicleCommandPath(vin, setting);
1107
+ const stateId = this.vehicleCommandStatePath(vin, setting);
1092
1108
  const state = await this.getStateAsync(stateId);
1093
1109
  if ((0, import_status_metadata.shouldApplySettingFeedback)(state, this.pendingSettingEdits.has(stateId))) {
1094
1110
  await this.setStateAsync(stateId, { val: value, ack: true });
1095
1111
  }
1096
1112
  }
1097
- await this.delObjectAsync(`vehicles.${vin}.charging`, { recursive: true });
1113
+ await this.delObjectAsync(`vehicles.${this.getVehicleObjectId(vin)}.charging`, { recursive: true });
1098
1114
  }
1099
1115
  async ensureVehicleChargingGroups(vin) {
1100
- const statusId = `vehicles.${vin}.status`;
1116
+ const vehicleObjectId = this.getVehicleObjectId(vin);
1117
+ const statusId = `vehicles.${vehicleObjectId}.status`;
1101
1118
  const rootId = `${statusId}.charging`;
1102
1119
  await this.setObjectNotExistsAsync(statusId, {
1103
1120
  type: "channel",
@@ -1130,7 +1147,8 @@ class MgSaic extends utils.Adapter {
1130
1147
  if (this.stopping) {
1131
1148
  return;
1132
1149
  }
1133
- const sourceId = `vehicles.${vin}.status.source`;
1150
+ const vehicleObjectId = this.getVehicleObjectId(vin);
1151
+ const sourceId = `vehicles.${vehicleObjectId}.status.source`;
1134
1152
  await this.setObjectNotExistsAsync(sourceId, {
1135
1153
  type: "state",
1136
1154
  common: { name: (0, import_runtime_translations.getRuntimeName)("statusSource"), type: "string", role: "text", read: true, write: false },
@@ -1142,7 +1160,7 @@ class MgSaic extends utils.Adapter {
1142
1160
  }
1143
1161
  await this.setStateAsync(sourceId, { val: source, ack: true });
1144
1162
  if (status.statusTime !== void 0) {
1145
- const sourceTimestampId = `vehicles.${vin}.status.sourceTimestamp`;
1163
+ const sourceTimestampId = `vehicles.${vehicleObjectId}.status.sourceTimestamp`;
1146
1164
  await this.setObjectNotExistsAsync(sourceTimestampId, {
1147
1165
  type: "state",
1148
1166
  common: {
@@ -1166,10 +1184,11 @@ class MgSaic extends utils.Adapter {
1166
1184
  if (this.stopping) {
1167
1185
  return;
1168
1186
  }
1169
- const deviceId = `vehicles.${vehicle.vin}`;
1187
+ const vehicleObjectId = this.getVehicleObjectId(vehicle.vin);
1188
+ this.vehicleObjectIds.set(vehicle.vin.toLowerCase(), vehicleObjectId);
1189
+ const deviceId = `vehicles.${vehicleObjectId}`;
1170
1190
  const values = {
1171
1191
  vin: vehicle.vin,
1172
- name: vehicle.name,
1173
1192
  brandName: vehicle.brandName,
1174
1193
  modelName: vehicle.modelName,
1175
1194
  series: vehicle.series,
@@ -1178,14 +1197,20 @@ class MgSaic extends utils.Adapter {
1178
1197
  isCurrentVehicle: vehicle.isCurrentVehicle,
1179
1198
  isSubaccount: vehicle.isSubaccount
1180
1199
  };
1200
+ const existingDevice = await this.getObjectAsync(deviceId);
1181
1201
  await this.setObjectNotExistsAsync(deviceId, {
1182
1202
  type: "device",
1183
- common: { name: vehicle.name || vehicle.modelName || vehicle.vin },
1203
+ common: { name: (0, import_runtime_translations.getRuntimeName)("vehicle") },
1184
1204
  native: {}
1185
1205
  });
1206
+ const existingName = existingDevice == null ? void 0 : existingDevice.common.name;
1207
+ if (typeof existingName === "string" && [vehicle.name, vehicle.modelName, vehicle.vin].includes(existingName)) {
1208
+ await this.extendObjectAsync(deviceId, { common: { name: (0, import_runtime_translations.getRuntimeName)("vehicle") } });
1209
+ }
1186
1210
  if (this.stopping) {
1187
1211
  return;
1188
1212
  }
1213
+ await this.delObjectAsync(`${deviceId}.name`);
1189
1214
  await this.ensureVehicleChargingGroups(vehicle.vin);
1190
1215
  const commandsId = `${deviceId}.commands`;
1191
1216
  await this.setObjectNotExistsAsync(commandsId, {
@@ -1205,7 +1230,7 @@ class MgSaic extends utils.Adapter {
1205
1230
  await this.extendObjectAsync(groupId, { common: { name: groupName } });
1206
1231
  }
1207
1232
  for (const command of vehicleCommands) {
1208
- const commandId = vehicleCommandPath(vehicle.vin, command.id);
1233
+ const commandId = this.vehicleCommandStatePath(vehicle.vin, command.id);
1209
1234
  await this.setObjectNotExistsAsync(commandId, {
1210
1235
  type: "state",
1211
1236
  common: {
@@ -1305,7 +1330,7 @@ class MgSaic extends utils.Adapter {
1305
1330
  }
1306
1331
  ];
1307
1332
  for (const setting of settingStates) {
1308
- const stateId = vehicleCommandPath(vehicle.vin, setting.id);
1333
+ const stateId = this.vehicleCommandStatePath(vehicle.vin, setting.id);
1309
1334
  const legacyStateId = `${commandsId}.${setting.id}`;
1310
1335
  const stateObject = await this.getObjectAsync(stateId);
1311
1336
  const legacyState = await this.getStateAsync(legacyStateId);
@@ -1356,7 +1381,7 @@ class MgSaic extends utils.Adapter {
1356
1381
  await this.delObjectAsync(legacyStateId);
1357
1382
  }
1358
1383
  for (const action of settingActions) {
1359
- const actionId = vehicleCommandPath(vehicle.vin, action.id);
1384
+ const actionId = this.vehicleCommandStatePath(vehicle.vin, action.id);
1360
1385
  await this.setObjectNotExistsAsync(actionId, {
1361
1386
  type: "state",
1362
1387
  common: { name: (0, import_runtime_translations.getRuntimeName)(action.id), type: "boolean", role: "button", read: false, write: true },
@@ -1431,8 +1456,4 @@ if (require.main !== module) {
1431
1456
  } else {
1432
1457
  (() => new MgSaic())();
1433
1458
  }
1434
- // Annotate the CommonJS export names for ESM import in node:
1435
- 0 && (module.exports = {
1436
- calculateInitializationRetryDelayMs
1437
- });
1438
1459
  //# sourceMappingURL=main.js.map