iobroker.mg-saic 0.1.1 → 0.1.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 +7 -5
- package/admin/i18n/de.json +0 -1
- package/admin/i18n/en.json +0 -1
- package/admin/i18n/es.json +0 -1
- package/admin/i18n/fr.json +0 -1
- package/admin/i18n/it.json +0 -1
- package/admin/i18n/nl.json +0 -1
- package/admin/i18n/pl.json +0 -1
- package/admin/i18n/pt.json +0 -1
- package/admin/i18n/ru.json +0 -1
- package/admin/i18n/uk.json +0 -1
- package/admin/i18n/zh-cn.json +0 -1
- package/build/main.js +98 -41
- package/build/main.js.map +2 -2
- package/build/refresh-poll-queue.js +68 -0
- package/build/refresh-poll-queue.js.map +7 -0
- package/build/refresh-poll-timer.js +65 -0
- package/build/refresh-poll-timer.js.map +7 -0
- package/build/saic-client.js +128 -44
- package/build/saic-client.js.map +3 -3
- package/build/status-metadata.js +115 -24
- package/build/status-metadata.js.map +2 -2
- package/io-package.json +16 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,13 +36,19 @@ For manufacturer information, see [MG Motor](https://www.mgmotor.eu/) and [SAIC
|
|
|
36
36
|
|
|
37
37
|
## Installation
|
|
38
38
|
|
|
39
|
-
The adapter is published on npm
|
|
39
|
+
The adapter is published on npm and available from ioBroker's Latest adapter repository.
|
|
40
40
|
|
|
41
41
|
## Support
|
|
42
42
|
|
|
43
43
|
[](https://www.paypal.com/donate/?hosted_button_id=3RJQGSJNRSHUJ)
|
|
44
44
|
|
|
45
45
|
## Changelog
|
|
46
|
+
### 0.1.2 (2026-11-08)
|
|
47
|
+
|
|
48
|
+
- (Voodoo2man) Prevented unsafe or case-colliding API fields and vehicle IDs from creating duplicate datapoints, kept status IDs stable across field-casing changes, scheduled polling after each completed cycle while honoring queued manual refreshes, and removed an unused cloud-backend translation.
|
|
49
|
+
- (Voodoo2man) Replaced untrusted cloud error text with English code-based diagnostics and standardized the product title as MG/SAIC across locales.
|
|
50
|
+
- (Voodoo2man) Reused dynamic charging datapoints across field-name casing changes and normalized known status fields.
|
|
51
|
+
|
|
46
52
|
### 0.1.1 (2026-11-07)
|
|
47
53
|
|
|
48
54
|
- (Voodoo2man) Lowered the minimum Node.js requirement to 22.22.2.
|
|
@@ -69,10 +75,6 @@ The adapter is published on npm but has not yet been added to the ioBroker adapt
|
|
|
69
75
|
|
|
70
76
|
Older changelog entries are available in [CHANGELOG_OLD.md](CHANGELOG_OLD.md).
|
|
71
77
|
|
|
72
|
-
### 0.0.2 (2026-10-20)
|
|
73
|
-
|
|
74
|
-
- (Voodoo2man) Added regional iSmart account setup, vehicle status, and supported remote controls.
|
|
75
|
-
|
|
76
78
|
## License
|
|
77
79
|
|
|
78
80
|
MIT License
|
package/admin/i18n/de.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Region",
|
|
8
8
|
"European Union": "Europäische Union",
|
|
9
9
|
"Rest of World": "Rest der Welt",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "Die Cloud-Anbindung ist noch nicht implementiert. Dieser Wert wählt kein funktionsfähiges Backend aus.",
|
|
11
10
|
"Login type": "Anmeldetyp",
|
|
12
11
|
"Email": "E-Mail",
|
|
13
12
|
"Phone number": "Telefonnummer",
|
package/admin/i18n/en.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Region",
|
|
8
8
|
"European Union": "European Union",
|
|
9
9
|
"Rest of World": "Rest of World",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "Cloud connectivity is not implemented yet. This value does not select a working backend.",
|
|
11
10
|
"Login type": "Login type",
|
|
12
11
|
"Email": "Email",
|
|
13
12
|
"Phone number": "Phone number",
|
package/admin/i18n/es.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Región",
|
|
8
8
|
"European Union": "Unión Europea",
|
|
9
9
|
"Rest of World": "Resto del mundo",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "La conexión a la nube aún no está implementada. Este valor no selecciona un servicio operativo.",
|
|
11
10
|
"Login type": "Tipo de inicio de sesión",
|
|
12
11
|
"Email": "Correo electrónico",
|
|
13
12
|
"Phone number": "Número de teléfono",
|
package/admin/i18n/fr.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Région",
|
|
8
8
|
"European Union": "Union européenne",
|
|
9
9
|
"Rest of World": "Reste du monde",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "La connexion au cloud n'est pas encore implémentée. Cette valeur ne sélectionne pas de service fonctionnel.",
|
|
11
10
|
"Login type": "Type de connexion",
|
|
12
11
|
"Email": "E-mail",
|
|
13
12
|
"Phone number": "Numéro de téléphone",
|
package/admin/i18n/it.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Regione",
|
|
8
8
|
"European Union": "Unione europea",
|
|
9
9
|
"Rest of World": "Resto del mondo",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "La connessione cloud non è ancora implementata. Questo valore non seleziona un servizio funzionante.",
|
|
11
10
|
"Login type": "Tipo di accesso",
|
|
12
11
|
"Email": "E-mail",
|
|
13
12
|
"Phone number": "Numero di telefono",
|
package/admin/i18n/nl.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Regio",
|
|
8
8
|
"European Union": "Europese Unie",
|
|
9
9
|
"Rest of World": "Rest van de wereld",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "Cloudverbinding is nog niet geïmplementeerd. Deze waarde selecteert geen werkende backend.",
|
|
11
10
|
"Login type": "Aanmeldtype",
|
|
12
11
|
"Email": "E-mailadres",
|
|
13
12
|
"Phone number": "Telefoonnummer",
|
package/admin/i18n/pl.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Region",
|
|
8
8
|
"European Union": "Unia Europejska",
|
|
9
9
|
"Rest of World": "Reszta świata",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "Połączenie z chmurą nie jest jeszcze zaimplementowane. Ta wartość nie wybiera działającej usługi.",
|
|
11
10
|
"Login type": "Typ logowania",
|
|
12
11
|
"Email": "E-mail",
|
|
13
12
|
"Phone number": "Numer telefonu",
|
package/admin/i18n/pt.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Região",
|
|
8
8
|
"European Union": "União Europeia",
|
|
9
9
|
"Rest of World": "Resto do mundo",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "A ligação à nuvem ainda não está implementada. Este valor não seleciona um serviço funcional.",
|
|
11
10
|
"Login type": "Tipo de início de sessão",
|
|
12
11
|
"Email": "E-mail",
|
|
13
12
|
"Phone number": "Número de telefone",
|
package/admin/i18n/ru.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Регион",
|
|
8
8
|
"European Union": "Европейский союз",
|
|
9
9
|
"Rest of World": "Остальной мир",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "Подключение к облаку пока не реализовано. Это значение не выбирает работающий сервер.",
|
|
11
10
|
"Login type": "Тип входа",
|
|
12
11
|
"Email": "Электронная почта",
|
|
13
12
|
"Phone number": "Номер телефона",
|
package/admin/i18n/uk.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"Region": "Регіон",
|
|
8
8
|
"European Union": "Європейський Союз",
|
|
9
9
|
"Rest of World": "Решта світу",
|
|
10
|
-
"Cloud connectivity is not implemented yet. This value does not select a working backend.": "Підключення до хмари ще не реалізовано. Це значення не вибирає робочий сервер.",
|
|
11
10
|
"Login type": "Тип входу",
|
|
12
11
|
"Email": "Електронна пошта",
|
|
13
12
|
"Phone number": "Номер телефону",
|
package/admin/i18n/zh-cn.json
CHANGED
package/build/main.js
CHANGED
|
@@ -28,6 +28,8 @@ var import_config = require("./config");
|
|
|
28
28
|
var import_saic_client = require("./saic-client");
|
|
29
29
|
var import_regions = require("./regions");
|
|
30
30
|
var import_refresh_command = require("./refresh-command");
|
|
31
|
+
var import_refresh_poll_queue = require("./refresh-poll-queue");
|
|
32
|
+
var import_refresh_poll_timer = require("./refresh-poll-timer");
|
|
31
33
|
var import_pending_setting_edits = require("./pending-setting-edits");
|
|
32
34
|
var import_status_metadata = require("./status-metadata");
|
|
33
35
|
var import_runtime_translations = require("./runtime-translations");
|
|
@@ -114,13 +116,17 @@ class MgSaic extends utils.Adapter {
|
|
|
114
116
|
...options,
|
|
115
117
|
name: "mg-saic"
|
|
116
118
|
});
|
|
119
|
+
this.pollTimer = new import_refresh_poll_timer.RefreshPollTimer(
|
|
120
|
+
(callback, milliseconds) => this.setTimeout(callback, milliseconds),
|
|
121
|
+
(timer) => this.clearTimeout(timer)
|
|
122
|
+
);
|
|
117
123
|
this.on("ready", this.onReady.bind(this));
|
|
118
124
|
this.on("stateChange", this.onStateChange.bind(this));
|
|
119
125
|
this.on("unload", this.onUnload.bind(this));
|
|
120
126
|
}
|
|
121
127
|
pollTimer;
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
pollIntervalMilliseconds;
|
|
129
|
+
refreshPollQueue = new import_refresh_poll_queue.RefreshPollQueue();
|
|
124
130
|
stopping = false;
|
|
125
131
|
unloadCallbackCalled = false;
|
|
126
132
|
abortController = new AbortController();
|
|
@@ -129,6 +135,7 @@ class MgSaic extends utils.Adapter {
|
|
|
129
135
|
accessToken;
|
|
130
136
|
accessTokenExpiresAt = 0;
|
|
131
137
|
vehicles = [];
|
|
138
|
+
vehicleStatusFieldIds = /* @__PURE__ */ new Map();
|
|
132
139
|
lastStatusTimes = /* @__PURE__ */ new Map();
|
|
133
140
|
lastCanonicalStatusTimes = /* @__PURE__ */ new Map();
|
|
134
141
|
vehicleCommandsInProgress = /* @__PURE__ */ new Set();
|
|
@@ -262,11 +269,9 @@ class MgSaic extends utils.Adapter {
|
|
|
262
269
|
this.log.warn("No vehicles are linked to this SAIC account.");
|
|
263
270
|
return;
|
|
264
271
|
}
|
|
265
|
-
await this.refreshVehicleStatuses();
|
|
266
272
|
const intervalSeconds = (_e = config.pollIntervalSeconds) != null ? _e : import_config.DEFAULT_POLL_INTERVAL_SECONDS;
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
273
|
+
this.pollIntervalMilliseconds = intervalSeconds * 1e3;
|
|
274
|
+
await this.refreshVehicleStatuses();
|
|
270
275
|
} catch (error) {
|
|
271
276
|
if (this.stopping) {
|
|
272
277
|
return;
|
|
@@ -289,26 +294,31 @@ class MgSaic extends utils.Adapter {
|
|
|
289
294
|
await this.setState("info.connection", true, true);
|
|
290
295
|
}
|
|
291
296
|
triggerRefresh() {
|
|
297
|
+
this.pollTimer.clear();
|
|
292
298
|
void this.refreshVehicleStatuses().catch((error) => {
|
|
293
299
|
const details = error instanceof Error ? error.message : "Unknown refresh error";
|
|
294
300
|
this.log.error(`Vehicle status refresh failed: ${details}`);
|
|
295
301
|
});
|
|
296
302
|
}
|
|
303
|
+
scheduleNextPoll() {
|
|
304
|
+
if (this.stopping || this.pollIntervalMilliseconds === void 0) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
this.pollTimer.schedule(() => this.triggerRefresh(), this.pollIntervalMilliseconds);
|
|
308
|
+
}
|
|
297
309
|
async refreshVehicleStatuses() {
|
|
298
310
|
var _a;
|
|
299
311
|
if (this.stopping || !this.client || !this.loginOptions || !this.accessToken) {
|
|
300
312
|
return;
|
|
301
313
|
}
|
|
302
|
-
if (this.
|
|
303
|
-
this.refreshQueued = true;
|
|
314
|
+
if (!this.refreshPollQueue.request()) {
|
|
304
315
|
return;
|
|
305
316
|
}
|
|
306
|
-
this.pollInProgress = true;
|
|
307
317
|
const statusErrors = [];
|
|
308
318
|
let followUpUsed = false;
|
|
309
319
|
try {
|
|
310
320
|
do {
|
|
311
|
-
this.
|
|
321
|
+
this.refreshPollQueue.clearQueuedRefresh();
|
|
312
322
|
for (const vehicle of this.vehicles) {
|
|
313
323
|
if (this.stopping) {
|
|
314
324
|
break;
|
|
@@ -375,7 +385,7 @@ class MgSaic extends utils.Adapter {
|
|
|
375
385
|
}
|
|
376
386
|
} else {
|
|
377
387
|
const details = error instanceof Error ? error.message : "Unknown error";
|
|
378
|
-
const diagnostic = error instanceof import_saic_client.SaicRemoteInstructionError ? `SAIC/MG returned remote instruction code 4 for ${vehicle.vin}; this is a response from the MG/SAIC service or vehicle, not an adapter parsing/processing error.
|
|
388
|
+
const diagnostic = error instanceof import_saic_client.SaicRemoteInstructionError ? `SAIC/MG returned remote instruction code 4 for ${vehicle.vin}; this is a response from the MG/SAIC service or vehicle, not an adapter parsing/processing error. Diagnostic: ${details}` : details;
|
|
379
389
|
statusErrors.push(`Vehicle status request failed for ${vehicle.vin}: ${diagnostic}`);
|
|
380
390
|
this.log.warn(
|
|
381
391
|
`Could not retrieve current status for vehicle ${vehicle.vin}: ${diagnostic}`
|
|
@@ -478,17 +488,21 @@ class MgSaic extends utils.Adapter {
|
|
|
478
488
|
this.log.error(`Could not write live status for ${vehicle.vin}: ${details}`);
|
|
479
489
|
}
|
|
480
490
|
}
|
|
481
|
-
if (this.
|
|
482
|
-
this.
|
|
483
|
-
} else if (this.
|
|
491
|
+
if (this.refreshPollQueue.hasQueuedRefresh && followUpUsed) {
|
|
492
|
+
this.refreshPollQueue.clearQueuedRefresh();
|
|
493
|
+
} else if (this.refreshPollQueue.hasQueuedRefresh) {
|
|
484
494
|
followUpUsed = true;
|
|
485
495
|
}
|
|
486
|
-
} while (this.
|
|
496
|
+
} while (this.refreshPollQueue.hasQueuedRefresh && !this.stopping);
|
|
487
497
|
if (!this.stopping) {
|
|
488
498
|
await this.setState("info.lastError", statusErrors.join("; "), true);
|
|
489
499
|
}
|
|
490
500
|
} finally {
|
|
491
|
-
this.
|
|
501
|
+
this.refreshPollQueue.finish(
|
|
502
|
+
this.stopping,
|
|
503
|
+
() => this.triggerRefresh(),
|
|
504
|
+
() => this.scheduleNextPoll()
|
|
505
|
+
);
|
|
492
506
|
}
|
|
493
507
|
}
|
|
494
508
|
onStateChange(id, state) {
|
|
@@ -801,6 +815,21 @@ class MgSaic extends utils.Adapter {
|
|
|
801
815
|
this.vehicleCommandsInProgress.delete(vin);
|
|
802
816
|
}
|
|
803
817
|
}
|
|
818
|
+
async getVehicleStatusFieldIds(channelId) {
|
|
819
|
+
const cachedIds = this.vehicleStatusFieldIds.get(channelId);
|
|
820
|
+
if (cachedIds) {
|
|
821
|
+
return cachedIds;
|
|
822
|
+
}
|
|
823
|
+
const objectIdPrefix = `${this.namespace}.${channelId}.`;
|
|
824
|
+
const objects = await this.getObjectListAsync({
|
|
825
|
+
startkey: objectIdPrefix,
|
|
826
|
+
endkey: `${objectIdPrefix}\u9999`
|
|
827
|
+
});
|
|
828
|
+
const stateIds = objects.rows.filter((row) => row.value.type === "state" && row.id.startsWith(`${this.namespace}.`)).map((row) => row.id.slice(this.namespace.length + 1));
|
|
829
|
+
const fieldIds = (0, import_status_metadata.indexVehicleStatusFieldIds)(stateIds, channelId);
|
|
830
|
+
this.vehicleStatusFieldIds.set(channelId, fieldIds);
|
|
831
|
+
return fieldIds;
|
|
832
|
+
}
|
|
804
833
|
async createVehicleStatusObjects(vin, status, channel = "status", channelName = "currentStatus") {
|
|
805
834
|
if (this.stopping) {
|
|
806
835
|
return;
|
|
@@ -809,10 +838,6 @@ class MgSaic extends utils.Adapter {
|
|
|
809
838
|
return;
|
|
810
839
|
}
|
|
811
840
|
const statusId = `vehicles.${vin}.${channel}`;
|
|
812
|
-
for (const key of import_status_metadata.excludedVehicleStatusFields) {
|
|
813
|
-
const stateId = `${statusId}.${(0, import_status_metadata.getVehicleStatusGroup)(key)}.${key}`;
|
|
814
|
-
await this.delObjectAsync(stateId);
|
|
815
|
-
}
|
|
816
841
|
await this.setObjectNotExistsAsync(statusId, {
|
|
817
842
|
type: "channel",
|
|
818
843
|
common: { name: (0, import_runtime_translations.getRuntimeName)(channelName) },
|
|
@@ -822,6 +847,11 @@ class MgSaic extends utils.Adapter {
|
|
|
822
847
|
if (this.stopping) {
|
|
823
848
|
return;
|
|
824
849
|
}
|
|
850
|
+
const fieldIds = await this.getVehicleStatusFieldIds(statusId);
|
|
851
|
+
for (const key of import_status_metadata.excludedVehicleStatusFields) {
|
|
852
|
+
const stateId = (0, import_status_metadata.resolveVehicleStatusFieldId)(fieldIds, statusId, (0, import_status_metadata.getVehicleStatusGroup)(key), key);
|
|
853
|
+
await this.delObjectAsync(stateId);
|
|
854
|
+
}
|
|
825
855
|
const values = { ...status.basicVehicleStatus };
|
|
826
856
|
if (status.statusTime !== void 0) {
|
|
827
857
|
values.statusTime = status.statusTime;
|
|
@@ -834,15 +864,17 @@ class MgSaic extends utils.Adapter {
|
|
|
834
864
|
if (this.stopping) {
|
|
835
865
|
return;
|
|
836
866
|
}
|
|
837
|
-
|
|
838
|
-
|
|
867
|
+
const stateId = (0, import_status_metadata.resolveVehicleStatusFieldId)(fieldIds, statusId, (0, import_status_metadata.getVehicleStatusGroup)(key), key);
|
|
868
|
+
const separator = stateId.lastIndexOf(".");
|
|
869
|
+
const fieldName = stateId.slice(separator + 1);
|
|
870
|
+
const groupId = stateId.slice(0, separator);
|
|
871
|
+
const group = groupId.slice(statusId.length + 1);
|
|
872
|
+
if (import_status_metadata.excludedVehicleStatusFields.some((excludedKey) => excludedKey.toLowerCase() === fieldName.toLowerCase())) {
|
|
873
|
+
await this.delObjectAsync(stateId);
|
|
839
874
|
continue;
|
|
840
875
|
}
|
|
841
|
-
const group = (0, import_status_metadata.getVehicleStatusGroup)(key);
|
|
842
|
-
const groupId = `${statusId}.${group}`;
|
|
843
|
-
const stateId = `${groupId}.${key}`;
|
|
844
876
|
if (value === void 0) {
|
|
845
|
-
if ((0, import_status_metadata.shouldDeleteUnavailableStatusValue)(
|
|
877
|
+
if ((0, import_status_metadata.shouldDeleteUnavailableStatusValue)(fieldName)) {
|
|
846
878
|
await this.delObjectAsync(stateId);
|
|
847
879
|
} else {
|
|
848
880
|
const previousState = await this.getStateAsync(stateId);
|
|
@@ -852,7 +884,7 @@ class MgSaic extends utils.Adapter {
|
|
|
852
884
|
}
|
|
853
885
|
continue;
|
|
854
886
|
}
|
|
855
|
-
const normalizedValue = (0, import_status_metadata.normalizeVehicleStatusValue)(
|
|
887
|
+
const normalizedValue = (0, import_status_metadata.normalizeVehicleStatusValue)(fieldName, value);
|
|
856
888
|
if (normalizedValue === void 0) {
|
|
857
889
|
await this.delObjectAsync(stateId);
|
|
858
890
|
continue;
|
|
@@ -866,7 +898,7 @@ class MgSaic extends utils.Adapter {
|
|
|
866
898
|
if (this.stopping) {
|
|
867
899
|
return;
|
|
868
900
|
}
|
|
869
|
-
const metadata = (0, import_status_metadata.getVehicleStatusMetadata)(
|
|
901
|
+
const metadata = (0, import_status_metadata.getVehicleStatusMetadata)(fieldName);
|
|
870
902
|
const common = {
|
|
871
903
|
name: metadata.name,
|
|
872
904
|
type: metadata.type,
|
|
@@ -895,7 +927,7 @@ class MgSaic extends utils.Adapter {
|
|
|
895
927
|
if (this.stopping) {
|
|
896
928
|
return;
|
|
897
929
|
}
|
|
898
|
-
const seatSetting =
|
|
930
|
+
const seatSetting = fieldName === "frontLeftSeatHeatLevel" ? "driverSeatHeatLevel" : fieldName === "frontRightSeatHeatLevel" ? "passengerSeatHeatLevel" : void 0;
|
|
899
931
|
if (seatSetting && Number.isInteger(value) && value >= 0 && value <= 3) {
|
|
900
932
|
const settingId = vehicleCommandPath(vin, seatSetting);
|
|
901
933
|
const settingState = await this.getStateAsync(settingId);
|
|
@@ -903,12 +935,32 @@ class MgSaic extends utils.Adapter {
|
|
|
903
935
|
await this.setStateAsync(settingId, { val: value, ack: true });
|
|
904
936
|
}
|
|
905
937
|
}
|
|
906
|
-
await this.delObjectAsync(`${statusId}.${
|
|
938
|
+
await this.delObjectAsync(`${statusId}.${fieldName}`);
|
|
907
939
|
}
|
|
908
940
|
}
|
|
909
941
|
async createVehicleChargingObjects(vin, charging, series) {
|
|
942
|
+
var _a, _b;
|
|
910
943
|
await this.ensureVehicleChargingGroups(vin);
|
|
911
944
|
const rootId = `vehicles.${vin}.status.charging`;
|
|
945
|
+
const existingObjects = await this.getObjectListAsync({
|
|
946
|
+
startkey: `${this.namespace}.${rootId}.`,
|
|
947
|
+
endkey: `${this.namespace}.${rootId}.\u9999`
|
|
948
|
+
});
|
|
949
|
+
const existingFieldIds = new Map(chargingGroups.map(({ id }) => [id, []]));
|
|
950
|
+
for (const { id: objectId, value: object } of existingObjects.rows) {
|
|
951
|
+
if (object.type !== "state") {
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
const group = chargingGroups.find(({ id }) => objectId.startsWith(`${this.namespace}.${rootId}.${id}.`));
|
|
955
|
+
if (!group) {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
const prefix = `${this.namespace}.${rootId}.${group.id}.`;
|
|
959
|
+
const fieldId = objectId.slice(prefix.length);
|
|
960
|
+
if (fieldId && !fieldId.includes(".")) {
|
|
961
|
+
(_a = existingFieldIds.get(group.id)) == null ? void 0 : _a.push(fieldId);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
912
964
|
for (const { source, id } of chargingGroups) {
|
|
913
965
|
const groupId = `${rootId}.${id}`;
|
|
914
966
|
const excludedFields = id === "session" ? import_status_metadata.excludedChargingSessionFields : import_status_metadata.excludedChargingStatisticsFields;
|
|
@@ -916,27 +968,34 @@ class MgSaic extends utils.Adapter {
|
|
|
916
968
|
for (const field of excludedFields) {
|
|
917
969
|
await this.delObjectAsync(`${groupId}.${field}`);
|
|
918
970
|
}
|
|
919
|
-
|
|
971
|
+
const valuesByField = new Map(fields);
|
|
972
|
+
const resolvedFieldIds = (0, import_status_metadata.resolveChargingFieldIds)(
|
|
973
|
+
fields.map(([field]) => field),
|
|
974
|
+
(_b = existingFieldIds.get(id)) != null ? _b : []
|
|
975
|
+
);
|
|
976
|
+
for (const [responseField, field] of resolvedFieldIds) {
|
|
920
977
|
if (this.stopping) {
|
|
921
978
|
return;
|
|
922
979
|
}
|
|
923
|
-
|
|
980
|
+
const value = valuesByField.get(responseField);
|
|
981
|
+
const canonicalField = (0, import_status_metadata.resolveChargingFieldName)(field);
|
|
982
|
+
if (excludedFields.some((excludedField) => excludedField === canonicalField)) {
|
|
924
983
|
await this.delObjectAsync(`${groupId}.${field}`);
|
|
925
984
|
continue;
|
|
926
985
|
}
|
|
927
986
|
const stateId = `${groupId}.${field}`;
|
|
928
|
-
if (value === void 0 &&
|
|
987
|
+
if (value === void 0 && canonicalField !== "realtimePower" && canonicalField !== "batteryAvailableEnergy") {
|
|
929
988
|
await this.delObjectAsync(stateId);
|
|
930
989
|
continue;
|
|
931
990
|
}
|
|
932
|
-
const normalizedValue =
|
|
933
|
-
if (normalizedValue === void 0 &&
|
|
991
|
+
const normalizedValue = canonicalField === "realtimePower" ? (0, import_status_metadata.calculateRealtimePower)(charging.chrgMgmtData.bmsPackCrnt, charging.chrgMgmtData.bmsPackVol) : value === void 0 ? void 0 : (0, import_status_metadata.normalizeChargingValue)(canonicalField, value, series);
|
|
992
|
+
if (normalizedValue === void 0 && canonicalField !== "batteryAvailableEnergy") {
|
|
934
993
|
await this.delObjectAsync(stateId);
|
|
935
994
|
continue;
|
|
936
995
|
}
|
|
937
|
-
const metadata = (0, import_status_metadata.getChargingFieldMetadata)(
|
|
996
|
+
const metadata = (0, import_status_metadata.getChargingFieldMetadata)(canonicalField, series);
|
|
938
997
|
const common = {
|
|
939
|
-
name: (0, import_runtime_translations.getChargingFieldName)(
|
|
998
|
+
name: (0, import_runtime_translations.getChargingFieldName)(canonicalField),
|
|
940
999
|
type: metadata.type === "boolean" ? "boolean" : typeof normalizedValue === "string" ? "string" : "number",
|
|
941
1000
|
role: typeof normalizedValue === "string" ? "text" : metadata.role,
|
|
942
1001
|
read: true,
|
|
@@ -1314,10 +1373,8 @@ class MgSaic extends utils.Adapter {
|
|
|
1314
1373
|
this.unloadCallbackCalled = true;
|
|
1315
1374
|
this.stopping = true;
|
|
1316
1375
|
this.abortController.abort(new DOMException("Adapter is stopping", "AbortError"));
|
|
1317
|
-
this.
|
|
1318
|
-
|
|
1319
|
-
this.clearInterval(this.pollTimer);
|
|
1320
|
-
}
|
|
1376
|
+
this.refreshPollQueue.clearQueuedRefresh();
|
|
1377
|
+
this.pollTimer.clear();
|
|
1321
1378
|
callback();
|
|
1322
1379
|
}
|
|
1323
1380
|
}
|