iobroker.bydhvs 1.5.0 → 1.5.3

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/main.js CHANGED
@@ -1,965 +1,1461 @@
1
- "use strict";
2
-
3
- /*
4
- * Created with @iobroker/create-adapter v1.31.0
5
- */
6
-
7
- // battery icon from
8
- // https://freepsdfiles.net/graphics/battery-icon-psd
9
-
10
- // The adapter-core module gives you access to the core ioBroker functions
11
- // you need to create an adapter
12
- const utils = require("@iobroker/adapter-core"); // Get common adapter utils
13
- const crc = require("crc");
14
- //const ioBLib = require('strathcole/iob-lib').ioBLib;
15
- const net = require("net");
16
- const IPClient = new net.Socket();
17
-
18
-
19
- // Load your modules here, e.g.:
20
- // const fs = require("fs");
21
-
22
- /**
23
- * The adapter instance
24
- * @type {ioBroker.Adapter}
25
- */
26
- let adapter;
27
- const hvsBatteryVoltsperCell = [];
28
- const hvsBatteryTempperCell = [];
29
- // globale Variablen
30
- /** @type {number | any } */
31
- let myState; // Aktueller Status
32
- let hvsSOC;
33
- let hvsSOCDiagnosis;
34
- let hvsMaxVolt;
35
- let hvsMinVolt;
36
- let hvsSOH;
37
- let hvsMaxmVolt;
38
- let hvsMinmVolt;
39
- let hvsMaxmVoltCell;
40
- let hvsMinmVoltCell;
41
- let hvsA;
42
- let hvsBattVolt;
43
- let hvsMaxTemp;
44
- let hvsMinTemp;
45
- let hvsMaxTempCell;
46
- let hvsMinTempCell;
47
- let hvsBatTemp;
48
- let hvsOutVolt;
49
- let hvsError;
50
- let hvsModules;
51
- let hvsDiffVolt;
52
- let hvsPower;
53
- let hvsBattType;
54
- let hvsBattType_fromSerial;
55
- let hvsInvType;
56
- let hvsInvType_String;
57
- let hvsNumCells; //number of cells in system
58
- let hvsNumTemps; // number of temperatures to count with
59
- let ConfBatDetailshowoften;
60
- let confBatPollTime;
61
- let myNumberforDetails;
62
- let ConfTestMode;
63
- let FirstRun;
64
-
65
-
66
- /** @type {string} */
67
- let hvsSerial;
68
- let hvsBMU;
69
- let hvsBMUA;
70
- let hvsBMUB;
71
- let hvsBMS;
72
- let hvsGrid;
73
- let hvsErrorString;
74
- let hvsParamT;
75
-
76
- /** @type {boolean} */
77
- let ConfBatDetails;
78
-
79
- /*const myStates = [
80
- "no state",
81
- "waiting for initial connect",
82
- "waiting for 1st answer",
83
- "waiting for 2nd answer"
84
-
85
- ];*/
86
-
87
-
88
-
89
- /** @type {NodeJS.Timeout} */
90
- let idInterval1;
91
- let idTimeout1;
92
-
93
-
94
- const myRequests = [
95
- Buffer.from("010300000066c5e0", "hex"), //0
96
- Buffer.from("01030500001984cc", "hex"), //1
97
- Buffer.from("010300100003040e", "hex"), //2
98
- Buffer.from("0110055000020400018100f853", "hex"), //3 start measuring
99
- Buffer.from("010305510001d517", "hex"), //4
100
- Buffer.from("01030558004104e5", "hex"), //5
101
- Buffer.from("01030558004104e5", "hex"), //6
102
- Buffer.from("01030558004104e5", "hex"), //7
103
- Buffer.from("01030558004104e5", "hex"), //8
104
- // to read the 5th module, the box must first be reconfigured
105
- Buffer.from("01100100000306444542554700176f", "hex"), //9 switch to second turn for the last few cells
106
- Buffer.from("0110055000020400018100f853", "hex"), //10 start measuring remaining cells (like 3)
107
- Buffer.from("010305510001d517", "hex"), //11 (like 4)
108
- Buffer.from("01030558004104e5", "hex"), //12 (like 5)
109
- Buffer.from("01030558004104e5", "hex"), //13 (like 6)
110
- // The BYD tool also issues two more requests, probably to gather even more cells in some larger setups
111
- // Buffer.from("01030558004104e5", "hex"), //14 (like 7)
112
- // Buffer.from("01030558004104e5", "hex"), //15 (like 8)
113
- ];
114
-
115
-
116
- /* Während des Updates des BMS funktioniert das Auslesen offensichtlich nicht, hier die Antworten des Speichers (Seriennummer verfälscht und CRC des ersten Paketes nicht neu berechnet)
117
- 0103cc503030303030303030303030303030303030307878787878030d030f031401000312020101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000015040c12382b82b2
118
- 0103320043014a014a0063fff852a80015001400140000030f0000000000000902000252761703000013840000000209020000042c925b
119
- 010306031202010100c8ad
120
- 0190044dc3 <- Das scheint eine Fehlercondition zu sein.
121
- 5 min. später klappte es wieder und dann war auch die neue F/W-Version in der Antwort enthalten
122
- */
123
- const myErrors = [
124
- "High Temperature Charging (Cells)",
125
- "Low Temperature Charging (Cells)",
126
- "Over Current Discharging",
127
- "Over Current Charging",
128
- "Main circuit Failure",
129
- "Short Current Alarm",
130
- "Cells Imbalance",
131
- "Current Sensor Failure",
132
- "Battery Over Voltage",
133
- "Battery Under Voltage",
134
- "Cell Over Voltage",
135
- "Cell Under Voltage",
136
- "Voltage Sensor Failure",
137
- "Temperature Sensor Failure",
138
- "High Temperature Discharging (Cells)",
139
- "Low Temperature Discharging (Cells)"
140
- ];
141
-
142
- const myINVs = [
143
- "Fronius HV", //0
144
- "Goodwe HV", //1
145
- "Fronius HV", //2
146
- "Kostal HV", //3
147
- "Goodwe HV", //4
148
- "SMA SBS3.7/5.0", //5
149
- "Kostal HV", //6
150
- "SMA SBS3.7/5.0", //7
151
- "Sungrow HV", //8
152
- "Sungrow HV", //9
153
- "Kaco HV", //10
154
- "Kaco HV", //11
155
- "Ingeteam HV", //12
156
- "Ingeteam HV", //13
157
- "SMA SBS 2.5 HV", //14
158
- "undefined", //15
159
- "SMA SBS 2.5 HV", //16
160
- "Fronius HV", //17
161
- "undefined", //18
162
- "SMA STP" //19
163
- ];
164
-
165
- const myINVsLVS = [
166
- "Fronius HV",
167
- "Goodwe HV",
168
- "Goodwe HV",
169
- "Kostal HV",
170
- "Selectronic LV",
171
- "SMA SBS3.7/5.0",
172
- "SMA LV",
173
- "Victron LV",
174
- "Suntech LV",
175
- "Sungrow HV",
176
- "Kaco HV",
177
- "Studer LV",
178
- "Solar Edge LV",
179
- "Ingeteam HV",
180
- "Sungrow LV",
181
- "Schneider LV",
182
- "SMA SBS2.5 HV",
183
- "Solar Edge LV",
184
- "Solar Edge LV",
185
- "Solar Edge LV",
186
- "unknown"
187
- ];
188
-
189
-
190
- const myBattTypes = [
191
- "HVL",
192
- "HVM",
193
- "HVS"
194
- ];
195
- /* HVM: 16 cells per module
196
- HVS: 32 cells per module
197
- HVL: unknown so I count 0 cells per module
198
- */
199
-
200
- /**
201
- * Starts the adapter instance
202
- * @param {Partial<utils.AdapterOptions>} [options]
203
- */
204
- function startAdapter(options) {
205
- // Create the adapter and define its methods
206
- return adapter = utils.adapter(Object.assign({}, options, {
207
- name: "bydhvs",
208
-
209
- // The ready callback is called when databases are connected and adapter received configuration.
210
- // start here!
211
- ready: main, // Main method defined below for readability
212
-
213
- // is called when adapter shuts down - callback has to be called under any circumstances!
214
- unload: (callback) => {
215
- adapter.log.silly("got unload event");
216
- try {
217
- clearInterval(idInterval1);
218
- clearTimeout(idTimeout1);
219
- stopPoll();
220
- IPClient.destroy();
221
-
222
- callback();
223
- } catch (e) {
224
- callback();
225
- }
226
- },
227
- }));
228
- }
229
-
230
-
231
- function setObjectsCells() {
232
- //Diagnose-data only if necessary.
233
- const myObjects = [
234
- ["Diagnosis.mVoltMax", "state", "Max Cell Voltage (mv)", "number", "value.voltage", true, false, "mV"],
235
- ["Diagnosis.mVoltMin", "state", "Min Cell Voltage (mv)", "number", "value.voltage", true, false, "mV"],
236
- ["Diagnosis.mVoltMaxCell", "state", "Max Cell Volt (Cellnr)", "number", "value.voltage", true, false, ""],
237
- ["Diagnosis.mVoltMinCell", "state", "Min Cell Volt (Cellnr)", "number", "value.voltage", true, false, ""],
238
- ["Diagnosis.TempMaxCell", "state", "Max Cell Temp (Cellnr)", "number", "value.temperature", true, false, ""],
239
- ["Diagnosis.TempMinCell", "state", "Min Cell Temp(Cellnr)", "number", "value.temperature", true, false, ""],
240
- ["Diagnosis.SOC", "state", "SOC (Diagnosis)", "number", "value.battery", true, false, "%"],
241
- ];
242
-
243
- for (let i = 0; i < myObjects.length; i++) {
244
- adapter.setObjectNotExists(myObjects[i][0], {
245
- type: myObjects[i][1],
246
- common: {
247
- name: myObjects[i][2],
248
- type: myObjects[i][3],
249
- role: myObjects[i][4],
250
- read: myObjects[i][5],
251
- write: myObjects[i][6],
252
- unit: myObjects[i][7],
253
- },
254
- native: {}
255
- });
256
- }
257
- for (let i = 0; i < myObjects.length; i++) {
258
- //console.log("****extend " + i + " " + myObjects[i][0] + " " + myObjects[i][7]);
259
- checkandrepairUnit(myObjects[i][0], myObjects[i][7], myObjects[i][5]);
260
- }
261
-
262
-
263
- for (let i = 1; i <= hvsNumCells; i++) {
264
- adapter.setObjectNotExists("CellDetails.CellVolt" + pad(i, 3), {
265
- type: "state",
266
- common: {
267
- name: "Voltage Cell: " + pad(i, 3),
268
- type: "number",
269
- role: "value.voltage",
270
- read: true,
271
- write: false,
272
- unit: "mV"
273
- },
274
- native: {}
275
- });
276
- checkandrepairUnit("CellDetails.CellVolt" + pad(i, 3), "mV", "value.voltage"); //repair forgotten units in first version
277
-
278
- for (let i = 1; i <= hvsNumTemps; i++) {
279
- adapter.setObjectNotExists("CellDetails.CellTemp" + pad(i, 3), {
280
- type: "state",
281
- common: {
282
- name: "Temp Cell: " + pad(i, 3),
283
- type: "number",
284
- role: "value.temperature",
285
- read: true,
286
- write: false,
287
- unit: "°C"
288
- },
289
- native: {}
290
- });
291
- checkandrepairUnit("CellDetails.CellTemp" + pad(i, 3), "°C", "value.temperature"); //repair forgotten units in first version
292
- }
293
- }
294
- }
295
-
296
- function setObjects() {
297
- const myObjects = [
298
- ["System.Serial", "state", "Serial number", "string", "text", true, false, ""],
299
- ["System.BMU", "state", "F/W BMU", "string", "text", true, false, ""],
300
- ["System.BMS", "state", "F/W BMS", "string", "text", true, false, ""],
301
- ["System.BMUBankA", "state", "F/W BMU-BankA", "string", "text", true, false, ""],
302
- ["System.BMUBankB", "state", "F/W BMU-BankB", "string", "text", true, false, ""],
303
- ["System.Modules", "state", "modules (count)", "number", "value", true, false, ""],
304
- ["System.Grid", "state", "Parameter Table", "string", "text", true, false, ""],
305
- ["System.ParamT", "state", "F/W BMU", "string", "text", true, false, ""],
306
- ["System.BattType", "state", "Battery Type", "string", "text", true, false, ""],
307
- ["System.InvType", "state", "Inverter Type", "string", "text", true, false, ""],
308
- ["State.SOC", "state", "SOC", "number", "value.battery", true, false, "%"],
309
- ["State.VoltMax", "state", "Max Cell Voltage", "number", "value.voltage", true, false, "V"],
310
- ["State.VoltMin", "state", "Min Cell Voltage", "number", "value.voltage", true, false, "V"],
311
- ["State.SOH", "state", "SOH", "number", "value.battery", true, false, "%"],
312
- ["State.Current", "state", "Charge / Discharge Current", "number", "value.current", true, false, "A"],
313
- ["State.Power_Consumption", "state", "Discharge Power", "number", "value.power", true, false, "W"],
314
- ["State.Power_Delivery", "state", "Charge Power", "number", "value.power", true, false, "W"],
315
- ["State.VoltBatt", "state", "Battery Voltage", "number", "value.voltage", true, false, "V"],
316
- ["State.TempMax", "state", "Max Cell Temp", "number", "value.temperature", true, false, "°C"],
317
- ["State.TempMin", "state", "Min Cell Temp", "number", "value.temperature", true, false, "°C"],
318
- ["State.VoltDiff", "state", "Max - Min Cell Voltage", "number", "value.temperature", true, false, "V"],
319
- ["State.Power", "state", "Power", "number", "value.power", true, false, "W"],
320
- ["State.TempBatt", "state", "Battery Temperature", "number", "value.temperature", true, false, "°C"],
321
- ["State.VoltOut", "state", "Output Voltage", "number", "value.voltage", true, false, "V"],
322
- ["System.ErrorNum", "state", "Error (numeric)", "number", "value", true, false, ""],
323
- //["State.ErrorNum", "state", "Error (numeric)", "number", "", true, false, ""], // ERROR ERROR ERROR
324
- ["System.ErrorStr", "state", "Error (string)", "string", "text", true, false, ""],
325
- ];
326
-
327
- for (let i = 0; i < myObjects.length; i++) {
328
- adapter.setObjectNotExists(myObjects[i][0], {
329
- type: myObjects[i][1],
330
- common: {
331
- name: myObjects[i][2],
332
- type: myObjects[i][3],
333
- role: myObjects[i][4],
334
- read: myObjects[i][5],
335
- write: myObjects[i][6],
336
- unit: myObjects[i][7], //works only for new objects, so check later for existing objects
337
- },
338
- native: {}
339
- });
340
- }
341
- //repair forgotten units in first version and required roles
342
- for (let i = 0; i < myObjects.length; i++) {
343
- //console.log("****extend " + i + " " + myObjects[i][0] + " " + myObjects[i][7]);
344
- checkandrepairUnit(myObjects[i][0], myObjects[i][7], myObjects[i][4]);
345
- }
346
- }
347
-
348
- /* setTimeout(() => {
349
- adapter.log.error("deleting State State.ErrorNum");
350
- adapter.deleteState("State.ErrorNum", "", function (err, obj) {
351
- adapter.log.error("callback deletestate called: " + err + " " + obj);
352
- });
353
- }, 4000);*/
354
- //changeErrorNum(); //not a really good idea but I do not know how to delete -- did not work :-(
355
-
356
-
357
-
358
- /*async function changeErrorNum() {
359
- //did not work, this part created a state with "getObjectAsync"
360
- try {
361
- const obj = await adapter.getObjectAsync("State.ErrorNum");
362
- adapter.extendObject("State.ErrorNum", { common: { type: "string", name: "deprecated" } });
363
- setTimeout(() => {
364
- adapter.setState("State.ErrorNum", "moved to System.ErrorNum");
365
- }, 4000);
366
- }
367
- catch (err) {
368
- //dann eben nicht.
369
- }
370
- }*/
371
-
372
-
373
- async function checkandrepairUnit(id, NewUnit, NewRole) {
374
- //want to test and understand async and await, so it's introduced here.
375
- //check for forgotten unit in first version and if it's missing add unit.
376
- try {
377
- const obj = await adapter.getObjectAsync(id);
378
- if (NewUnit != "") {
379
- if (obj.common.unit != NewUnit) {
380
- adapter.extendObject(id, { common: { unit: NewUnit } });
381
- }
382
- }
383
- if (obj.common.role == "") {
384
- adapter.extendObject(id, { common: { role: NewRole } });
385
- }
386
- }
387
- catch (err) {
388
- //dann eben nicht.
389
- }
390
- }
391
-
392
- function checkPacket(data) {
393
- const byteArray = new Uint8Array(data);
394
- const packetLength = data[2] + 5;// 3 header, 2 crc
395
- if (byteArray[0] != 1) { return false; }
396
- if (byteArray[1] === 3) { //habe die Kodierung der Antwort mit 1 an zweiter Stelle nicht verstanden, daher hier keine Längenprüfung
397
- if (packetLength != byteArray.length) {
398
- return (false);
399
- }
400
- } else {
401
- if (byteArray[1] != 16) { return false; }
402
- }
403
- return (crc.crc16modbus(byteArray) === 0);
404
- }
405
-
406
- function pad(n, width, z) {
407
- z = z || "0";
408
- n = n + "";
409
- return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
410
- }
411
-
412
- function buf2int16SI(byteArray, pos) { //signed
413
- let result = 0;
414
- result = byteArray[pos] * 256 + byteArray[pos + 1];
415
- if (result > 32768) {
416
- result -= 65536;
417
- }
418
- return result;
419
- }
420
-
421
- function buf2int16US(byteArray, pos) { //unsigned
422
- let result = 0;
423
- result = byteArray[pos] * 256 + byteArray[pos + 1];
424
- return result;
425
- }
426
-
427
- function decodePacket0(data) {
428
- const byteArray = new Uint8Array(data);
429
- hvsSerial = "";
430
- for (let i = 3; i < 22; i++) {
431
- hvsSerial += String.fromCharCode(byteArray[i]);
432
- }
433
- //leider dazugestrickt, wollte in die andere Logik nicht eingreifen
434
- if (byteArray[5] == 51 ) {hvsBattType_fromSerial = "HVS";}
435
- if (byteArray[5] == 50 ) {hvsBattType_fromSerial = "LVS";}
436
- if (byteArray[5] == 49 ) {hvsBattType_fromSerial = "LVS";}
437
- hvsBMUA = "V" + byteArray[27].toString() + "." + byteArray[28].toString();
438
- hvsBMUB = "V" + byteArray[29].toString() + "." + byteArray[30].toString();
439
- if (byteArray[33] === 0) {
440
- hvsBMU = hvsBMUA + "-A";
441
- } else {
442
- hvsBMU = hvsBMUB + "-B";
443
- }
444
- hvsBMS = "V" + byteArray[31].toString() + "." + byteArray[32].toString() + "-" + String.fromCharCode(byteArray[34] + 65);
445
- hvsModules = parseInt((byteArray[36] - 16).toString());
446
- if (byteArray[38] === 0) {hvsGrid = "OffGrid";}
447
- if (byteArray[38] === 1) {hvsGrid = "OnGrid";}
448
- if (byteArray[38] === 2) {hvsGrid = "Backup";}
449
- /* if ((ConfBatDetails) && (hvsModules > 2)) {
450
- adapter.log.error("Sorry, Details at the moment only for two modules. I need a wireshark dump from bigger systems to adjust the adapter.");
451
- ConfBatDetails = false;
452
- }*/
453
- }
454
-
455
- function decodePacket1(data) {
456
- const byteArray = new Uint8Array(data);
457
- hvsSOC = buf2int16SI(byteArray, 3);
458
- hvsMaxVolt = parseFloat((buf2int16SI(byteArray, 5) * 1.0 / 100.0).toFixed(2));
459
- hvsMinVolt = parseFloat((buf2int16SI(byteArray, 7) * 1.0 / 100.0).toFixed(2));
460
- hvsSOH = buf2int16SI(byteArray, 9);
461
- hvsA = parseFloat((buf2int16SI(byteArray, 11) * 1.0 / 10.0).toFixed(1));
462
- hvsBattVolt = parseFloat((buf2int16US(byteArray, 13) * 1.0 / 100.0).toFixed(1));
463
- hvsMaxTemp = buf2int16SI(byteArray, 15);
464
- hvsMinTemp = buf2int16SI(byteArray, 17);
465
- hvsBatTemp = buf2int16SI(byteArray, 19);
466
- hvsError = buf2int16SI(byteArray, 29);
467
- hvsParamT = byteArray[31].toString() + "." + byteArray[32].toString();
468
- hvsOutVolt = parseFloat((buf2int16US(byteArray, 35) * 1.0 / 100.0).toFixed(1));
469
- hvsPower = Math.round((hvsA * hvsOutVolt) * 100) / 100;
470
- hvsDiffVolt = Math.round((hvsMaxVolt - hvsMinVolt) * 100) / 100;
471
- hvsErrorString = "";
472
- // hvsError = 65535;
473
- for (let j = 0; j < 16; j++) {
474
- if (((1 << j) & hvsError) !== 0) {
475
- if (hvsErrorString.length > 0) {
476
- hvsErrorString += "; ";
477
- }
478
- hvsErrorString += myErrors[j];
479
- }
480
- }
481
- if (hvsErrorString.length === 0) { hvsErrorString = "no Error"; }
482
- }
483
-
484
- function decodePacketNOP(data) {
485
- adapter.log.silly("Packet NOP");
486
- }
487
-
488
- function decodePacket2(data) {
489
- const byteArray = new Uint8Array(data);
490
- hvsBattType = byteArray[5];
491
- hvsInvType = byteArray[3];
492
- hvsNumCells = 0;
493
- hvsNumTemps = 0;
494
- switch (hvsBattType) {
495
- case 0: //HVL -> unknown specification, so 0 cells and 0 temps
496
- //hvsNumCells = 0;
497
- //hvsNumTemps = 0;
498
- //see above, is default
499
- break;
500
- case 1: //HVM 16 Cells per module
501
- hvsNumCells = hvsModules * 16;
502
- hvsNumTemps = hvsModules * 8;
503
- break;
504
- //crosscheck
505
- // 5 modules, 80 voltages, 40 temps
506
- case 2: //HVS 32 cells per module
507
- hvsNumCells = hvsModules * 32;
508
- hvsNumTemps = hvsModules * 12;
509
- break;
510
- //crosscheck:
511
- //Counts from real data:
512
- //mine: 2 modules, 64 voltages, 24 temps
513
- //4 modules, 128 voltages, 48 temps
514
- }
515
- //leider hässlich dazugestrickt, wollte in die andere Logik nicht eingreifen
516
- if (hvsBattType_fromSerial == "LVS") {
517
- hvsBattType = "LVS";
518
- hvsNumCells = hvsModules * 7;
519
- hvsNumTemps = 0;
520
- }
521
- if (hvsBattType_fromSerial == "LVS") { //unterschiedliche WR-Tabelle je nach Batt-Typ
522
- hvsInvType_String = myINVsLVS[hvsInvType];
523
- }
524
- else {
525
- hvsInvType_String = myINVs[hvsInvType];
526
- }
527
- if (hvsInvType_String == undefined) {
528
- hvsInvType_String = "undefined";
529
- }
530
-
531
- if (hvsNumCells > 160) { hvsNumCells = 160; }
532
- if (hvsNumTemps > 64) { hvsNumTemps = 64; }
533
- if (ConfBatDetails && FirstRun) {
534
- FirstRun = false;
535
- setObjectsCells();
536
- }
537
- adapter.log.silly ("NumCells: " + hvsNumCells +" Numtemps: " + hvsNumTemps + " Modules: " + hvsModules);
538
- }
539
-
540
-
541
- function decodePacket5(data) {
542
- const byteArray = new Uint8Array(data);
543
- hvsMaxmVolt = buf2int16SI(byteArray, 5);
544
- hvsMinmVolt = buf2int16SI(byteArray, 7);
545
- hvsMaxmVoltCell = byteArray[9];
546
- hvsMinmVoltCell = byteArray[10];
547
- hvsMaxTempCell = byteArray[15];
548
- hvsMinTempCell = byteArray[16];
549
- hvsSOCDiagnosis = parseFloat((buf2int16SI(byteArray, 53) * 1.0 / 10.0).toFixed(1));
550
-
551
- //starting with byte 101, ending with 131, Cell voltage 1-16
552
- const MaxCells = 16;
553
- for (let i = 0; i < MaxCells; i++) {
554
- adapter.log.silly("Battery Voltage-" + pad((i + 1), 3) + " :" + buf2int16SI(byteArray, i * 2 + 101));
555
- hvsBatteryVoltsperCell[i + 1] = buf2int16SI(byteArray, i * 2 + 101);
556
- }
557
- }
558
-
559
- function decodePacket6(data) {
560
- const byteArray = new Uint8Array(data);
561
- // e.g. hvsNumCells = 80
562
- // first Voltage in byte 5+6
563
- // Count = 80-17 --> 63
564
- let MaxCells = hvsNumCells - 16; //0 to n-1 is the same like 1 to n
565
- if (MaxCells > 64) { MaxCells = 64; }
566
- for (let i = 0; i < MaxCells; i++) {
567
- adapter.log.silly("Battery Voltage-" + pad((i + 17), 3) + " :" + buf2int16SI(byteArray, i * 2 + 5));
568
- hvsBatteryVoltsperCell[i + 17] = buf2int16SI(byteArray, i * 2 + 5);
569
- }
570
- }
571
-
572
- function decodePacket7(data) {
573
- const byteArray = new Uint8Array(data);
574
- //starting with byte 5, ending 101, voltage for cell 81 to 128
575
- //starting with byte 103, ending 132, temp for cell 1 to 30
576
-
577
- // e.g. hvsNumCells = 128
578
- // first Voltage in byte 5+6
579
- // Count = 128-80 --> 48
580
- let MaxCells = hvsNumCells - 80; //0 to n-1 is the same like 1 to n
581
- if (MaxCells > 48) { MaxCells = 48; }
582
- adapter.log.silly("hvsModules =" + hvsModules + " maxCells= " + MaxCells);
583
- for (let i = 0; i < MaxCells; i++) {
584
- adapter.log.silly("Battery Voltage-" + pad((i + 81), 3) + " :" + buf2int16SI(byteArray, i * 2 + 5));
585
- hvsBatteryVoltsperCell[i + 81] = buf2int16SI(byteArray, i * 2 + 5);
586
- }
587
-
588
- let MaxTemps = hvsNumTemps - 0; //0 to n-1 is the same like 1 to n
589
- if (MaxTemps > 30) { MaxTemps = 30; }
590
- adapter.log.silly("hvsModules =" + hvsModules + " MaxTemps= " + MaxTemps);
591
- for (let i = 0; i < MaxTemps; i++) {
592
- adapter.log.silly("Battery Temp " + pad(i + 1, 3) + " :" + byteArray[i + 103]);
593
- hvsBatteryTempperCell[i + 1] = byteArray[i + 103];
594
- }
595
- }
596
-
597
- function decodePacket8(data) {
598
- const byteArray = new Uint8Array(data);
599
- let MaxTemps = hvsNumTemps - 30; //0 to n-1 is the same like 1 to n
600
- if (MaxTemps > 34) { MaxTemps = 34; }
601
- adapter.log.silly("hvsModules =" + hvsModules + " MaxTemps= " + MaxTemps);
602
- for (let i = 0; i < MaxTemps; i++) {
603
- adapter.log.silly("Battery Temp " + pad(i + 31, 3) + " :" + byteArray[i + 5]);
604
- hvsBatteryTempperCell[i + 31] = byteArray[i + 5];
605
- }
606
- }
607
-
608
- /*
609
- * decode response to request[12]
610
- * @see #decodePacket5()
611
- */
612
- function decodeResponse12(data) {
613
- const byteArray = new Uint8Array(data);
614
- //starting with byte 101, ending with 131, Cell voltage 129-144
615
- const MaxCells = 16;
616
- for (let i = 0; i < MaxCells; i++) {
617
- adapter.log.silly("Battery Voltage-" + pad((i + 1 + 128), 3) + " :" + buf2int16SI(byteArray, i * 2 + 101));
618
- hvsBatteryVoltsperCell[i + 1 + 128] = buf2int16SI(byteArray, i * 2 + 101);
619
- }
620
- }
621
-
622
- /*
623
- * decode response to request[13]
624
- * @see #decodePacket6()
625
- */
626
- function decodeResponse13(data) {
627
- const byteArray = new Uint8Array(data);
628
- let MaxCells = hvsNumCells - 128 - 16; // The first round measured up to 128 cells, request[12] then get another 16
629
- if (MaxCells > 16) { MaxCells = 16; } // With 5 HVS Modules, only 16 cells are remaining
630
- for (let i = 0; i < MaxCells; i++) {
631
- adapter.log.silly("Battery Voltage-" + pad((i + 1 + 16 + 128), 3) + " :" + buf2int16SI(byteArray, i * 2 + 5));
632
- hvsBatteryVoltsperCell[i + 1 + 16 + 128] = buf2int16SI(byteArray, i * 2 + 5);
633
- }
634
- }
635
-
636
- function setConnected(adapter, isConnected) {
637
- if (adapter._connected !== isConnected) {
638
- adapter._connected = isConnected;
639
- adapter.setState("info.connection", adapter._connected, true, err =>
640
- // analyse if the state could be set (because of permissions)
641
- err ? adapter.log.error("Can not update adapter._connected state: " + err) :
642
- adapter.log.debug("connected set to " + adapter._connected));
643
- }
644
- }
645
-
646
-
647
- function setStates() {
648
-
649
- adapter.log.silly("hvsSerial >" + hvsSerial + "<");
650
- adapter.log.silly("hvsBMU >" + hvsBMU + "<");
651
- adapter.log.silly("hvsBMUA >" + hvsBMUA + "<");
652
- adapter.log.silly("hvsBMUB >" + hvsBMUB + "<");
653
- adapter.log.silly("hvsBMS >" + hvsBMS + "<");
654
- adapter.log.silly("hvsModules >" + hvsModules + "<");
655
- adapter.log.silly("hvsGrid >" + hvsGrid + "<");
656
- adapter.log.silly("hvsSOC >" + hvsSOC + "<");
657
- adapter.log.silly("hvsMaxVolt >" + hvsMaxVolt + "<");
658
- adapter.log.silly("hvsMinVolt >" + hvsMinVolt + "<");
659
- adapter.log.silly("hvsSOH >" + hvsSOH + "<");
660
- adapter.log.silly("hvsA >" + hvsA + "<");
661
- adapter.log.silly("hvsBattVolt >" + hvsBattVolt + "<");
662
- adapter.log.silly("hvsMaxTemp >" + hvsMaxTemp + "<");
663
- adapter.log.silly("hvsMinTemp >" + hvsMinTemp + "<");
664
- adapter.log.silly("hvsDiffVolt >" + hvsDiffVolt + "<");
665
- adapter.log.silly("hvsPower >" + hvsPower + "<");
666
- adapter.log.silly("hvsParamT >" + hvsParamT + "<");
667
- adapter.log.silly("hvsBatTemp >" + hvsBatTemp + "<");
668
- adapter.log.silly("hvsOutVolt >" + hvsOutVolt + "<");
669
- adapter.log.silly("hvsError >" + hvsError + "<");
670
- adapter.log.silly("hvsErrorStr >" + hvsErrorString + "<");
671
- adapter.log.silly("hvsSOC (Diag) >" + hvsSOCDiagnosis + "<");
672
- adapter.log.silly("BattType >" + hvsBattType_fromSerial + "<");
673
- adapter.log.silly("Invert. Type >" + hvsInvType_String + ", Nr: " + hvsInvType + "<");
674
-
675
- adapter.setState("System.Serial", hvsSerial, true);
676
- adapter.setState("System.BMU", hvsBMU, true);
677
- adapter.setState("System.BMUBankA", hvsBMUA, true);
678
- adapter.setState("System.BMUBankB", hvsBMUB, true);
679
- adapter.setState("System.BMS", hvsBMS, true);
680
- adapter.setState("System.Modules", hvsModules, true);
681
- adapter.setState("System.Grid", hvsGrid, true);
682
- adapter.setState("State.SOC", hvsSOC, true);
683
- adapter.setState("State.VoltMax", hvsMaxVolt, true);
684
- adapter.setState("State.VoltMin", hvsMinVolt, true);
685
- adapter.setState("State.SOH", hvsSOH, true);
686
- adapter.setState("State.Current", hvsA, true);
687
- adapter.setState("State.VoltBatt", hvsBattVolt, true);
688
- adapter.setState("State.TempMax", hvsMaxTemp, true);
689
- adapter.setState("State.TempMin", hvsMinTemp, true);
690
- adapter.setState("State.VoltDiff", hvsDiffVolt, true);
691
- adapter.setState("State.Power", hvsPower, true /*ack*/);
692
- adapter.setState("System.ParamT", hvsParamT, true);
693
- adapter.setState("State.TempBatt", hvsBatTemp, true);
694
- adapter.setState("State.VoltOut", hvsOutVolt, true);
695
- adapter.setState("System.ErrorNum", hvsError, true);
696
- adapter.setState("System.ErrorStr", hvsErrorString, true);
697
- if (hvsPower >= 0) {
698
- adapter.setState("State.Power_Consumption", hvsPower, true);
699
- adapter.setState("State.Power_Delivery", 0, true);
700
- } else {
701
- adapter.setState("State.Power_Consumption", 0, true);
702
- adapter.setState("State.Power_Delivery", -hvsPower, true);
703
- }
704
-
705
- adapter.setState("System.BattType", hvsBattType_fromSerial, true);
706
- adapter.setState("System.InvType", hvsInvType_String, true);
707
-
708
- if (myNumberforDetails == 0) {
709
- adapter.setState("Diagnosis.mVoltMax", hvsMaxmVolt, true);
710
- adapter.setState("Diagnosis.mVoltMin", hvsMinmVolt, true);
711
- adapter.setState("Diagnosis.mVoltMaxCell", hvsMaxmVoltCell, true);
712
- adapter.setState("Diagnosis.mVoltMinCell", hvsMinmVoltCell, true);
713
- adapter.setState("Diagnosis.TempMaxCell", hvsMaxTempCell, true);
714
- adapter.setState("Diagnosis.TempMinCell", hvsMinTempCell, true);
715
- adapter.setState("Diagnosis.SOC", hvsSOCDiagnosis, true);
716
-
717
- for (let i = 1; i <= hvsNumCells; i++) {
718
- adapter.setState("CellDetails.CellVolt" + pad(i, 3), hvsBatteryVoltsperCell[i], true);
719
- }
720
- for (let i = 1; i <= hvsNumTemps; i++) {
721
- adapter.setState("CellDetails.CellTemp" + pad(i, 3), hvsBatteryTempperCell[i], true);
722
- }
723
- adapter.log.silly("hvsMaxmVolt >" + hvsMaxmVolt + "<");
724
- adapter.log.silly("hvsMinmVolt >" + hvsMinmVolt + "<");
725
- adapter.log.silly("hvsMaxmVoltCell >" + hvsMaxmVoltCell + "<");
726
- adapter.log.silly("hvsMinmVoltCell >" + hvsMinmVoltCell + "<");
727
- adapter.log.silly("hvsMaxTempCell >" + hvsMaxTempCell + "<");
728
- adapter.log.silly("hvsMinTempCell >" + hvsMinTempCell + "<");
729
- }
730
-
731
- }
732
-
733
- function startPoll(adapter) {
734
- //erster Start sofort (500ms), dann entsprechend der Config - dann muss man nicht beim Entwickeln warten bis der erste Timer durch ist.
735
- FirstRun = true;
736
- idTimeout1 = setTimeout(() => { Poll(adapter); }, 500);
737
- idInterval1 = setInterval(() => Poll(adapter), confBatPollTime * 1000);
738
- adapter.log.info("gestartet: " + adapter.config.ConfPollInterval + " " + idInterval1);
739
- }
740
-
741
- function stopPoll() {
742
- idInterval1 && clearInterval(idInterval1);
743
- }
744
-
745
- IPClient.on("data", function (data) {
746
- adapter.log.silly("Received, State: " + myState + " Data: " + data.toString("hex"));
747
- if (ConfTestMode) {
748
- const PacketNumber = myState - 1;
749
- adapter.log.info("Received, Packet: " + PacketNumber + " Data: " + data.toString("hex"));
750
- }
751
- if (checkPacket(data) == false) {
752
- adapter.log.error("error: no valid data");
753
- IPClient.destroy();
754
- setConnected(adapter, false);
755
- myState = 0;
756
- }
757
- setConnected(adapter, true);
758
- switch (myState) {
759
- case 2:
760
- decodePacket0(data); // decode request 0
761
- IPClient.setTimeout(1000);
762
- setTimeout(() => {
763
- myState = 3;
764
- IPClient.write(myRequests[1]);
765
- }, 200);
766
- break;
767
- case 3:
768
- decodePacket1(data);
769
- IPClient.setTimeout(1000);
770
- setTimeout(() => {
771
- myState = 4;
772
- IPClient.write(myRequests[2]);
773
- }, 200);
774
- break;
775
- case 4: //test if it is time for reading all data. If not stop here
776
- decodePacket2(data);
777
- if ((myNumberforDetails < ConfBatDetailshowoften) || (ConfBatDetails == false)) {
778
- setStates();
779
- IPClient.destroy();
780
- myState = 0;
781
- } else {
782
- myNumberforDetails = 0; //restart counting
783
- IPClient.setTimeout(1000);
784
- setTimeout(() => {
785
- myState = 5;
786
- IPClient.write(myRequests[3]);
787
- }, 200);
788
- }
789
- break;
790
- case 5:
791
- decodePacketNOP(data);
792
- IPClient.setTimeout(8000);
793
- myState = 6;
794
- adapter.log.silly("waiting 3 seconds to measure cells");
795
- setTimeout(() => {
796
- IPClient.write(myRequests[4]);
797
- }, 3000);
798
- break;
799
- case 6:
800
- decodePacketNOP(data);
801
- IPClient.setTimeout(1000);
802
- myState = 7;
803
- setTimeout(() => {
804
- IPClient.write(myRequests[5]);
805
- }, 200);
806
- break;
807
- case 7:
808
- decodePacket5(data);
809
- IPClient.setTimeout(1000);
810
- setTimeout(() => {
811
- myState = 8;
812
- IPClient.write(myRequests[6]);
813
- }, 200);
814
- break;
815
- case 8:
816
- decodePacket6(data);
817
- IPClient.setTimeout(1000);
818
- setTimeout(() => {
819
- myState = 9;
820
- IPClient.write(myRequests[7]);
821
- }, 200);
822
- break;
823
- case 9:
824
- decodePacket7(data);
825
- IPClient.setTimeout(1000);
826
- setTimeout(() => {
827
- myState = 10;
828
- IPClient.write(myRequests[8]);
829
- }, 200);
830
- break;
831
- case 10:
832
- decodePacket8(data);
833
- if (hvsNumCells > 128) {
834
- setTimeout(() => {
835
- myState = 11;
836
- IPClient.write(myRequests[9]); // Switch to second turn for the last module
837
- }, 200);
838
- } else {
839
- setStates();
840
- IPClient.destroy();
841
- myState = 0;
842
- }
843
- break;
844
- case 11:
845
- decodePacketNOP(data);
846
- setTimeout(() => {
847
- myState = 12;
848
- IPClient.write(myRequests[10]);
849
- }, 200);
850
- break;
851
- case 12:
852
- decodePacketNOP(data);
853
- IPClient.setTimeout(8000);
854
- adapter.log.silly("waiting 3 seconds to measure cells");
855
- setTimeout(() => {
856
- myState = 13;
857
- IPClient.write(myRequests[11]);
858
- }, 3000);
859
- break;
860
- case 13:
861
- decodePacketNOP(data);
862
- IPClient.setTimeout(1000);
863
- setTimeout(() => {
864
- myState = 14;
865
- IPClient.write(myRequests[12]);
866
- }, 200);
867
- break;
868
- case 14:
869
- decodeResponse12(data);
870
- setTimeout(() => {
871
- myState = 15;
872
- IPClient.write(myRequests[13]);
873
- }, 200);
874
- break;
875
- case 15:
876
- decodeResponse13(data);
877
-
878
- setStates();
879
- IPClient.destroy();
880
- myState = 0;
881
- break;
882
- default:
883
- IPClient.destroy();
884
- }
885
- });
886
-
887
-
888
- IPClient.on("timeout", function () {
889
- IPClient.destroy();
890
- setConnected(adapter, false);
891
- myState = 0;
892
- adapter.log.error("no connection to IP: " + adapter.config.ConfIPAdress);
893
- });
894
-
895
- IPClient.on("error", function () {
896
- IPClient.destroy();
897
- setConnected(adapter, false);
898
- myState = 0;
899
- adapter.log.error("Error connecting to " + adapter.config.ConfIPAdress);
900
- });
901
-
902
-
903
- function Poll(adapter) {
904
- myState = 1;
905
- IPClient.setTimeout(1000);
906
- myNumberforDetails += 1;
907
- adapter.log.silly("myNumberforDetails:" + myNumberforDetails);
908
- adapter.log.silly("Poll start, IP:" + adapter.config.ConfIPAdress);
909
- IPClient.connect(8080, adapter.config.ConfIPAdress, function () {
910
- myState = 2;
911
- setConnected(adapter, true);
912
- IPClient.write(myRequests[0]);
913
- });
914
- }
915
-
916
- async function main() {
917
-
918
- // Reset the connection indicator during startup
919
- // await this.setStateAsync("info.connection", false, true);
920
- setConnected(adapter, false);
921
- setObjects();
922
- myState = 0;
923
-
924
- // The adapters config (in the instance object everything under the attribute "native") is accessible via
925
- // adapter.config:
926
- adapter.log.info("Poll Interval: " + adapter.config.ConfPollInterval);
927
- confBatPollTime = parseInt(adapter.config.ConfPollInterval);
928
- if (confBatPollTime < 60) {
929
- confBatPollTime = 60;
930
- adapter.log.error("polling to often - max once per minute ");
931
- }
932
- adapter.log.info("BYD IP Adress: " + adapter.config.ConfIPAdress);
933
- ConfBatDetails = (adapter.config.ConfBatDetails ? true : false);
934
- adapter.log.info("Bat Details : " + adapter.config.ConfBatDetails);
935
- ConfBatDetailshowoften = parseInt(adapter.config.ConfDetailshowoften);
936
- /*if (ConfBatDetailshowoften < 10) {
937
- ConfBatDetails = false;
938
- adapter.log.error("Details polling to often - disabling ");
939
- }*/
940
- ConfTestMode = (adapter.config.ConfTestMode ? true : false);
941
- adapter.log.info("BatDetailshowoften: " + ConfBatDetailshowoften);
942
- adapter.log.silly("TestMode= " + ConfTestMode);
943
- myNumberforDetails = ConfBatDetailshowoften;
944
- // adapter.config.ConfPollInterval = parseInt(adapter.config.ConfPollInterval, 10) || 60;
945
-
946
- adapter.log.info("starte poll");
947
- startPoll(adapter);
948
-
949
- // examples for the checkPassword/checkGroup functions
950
- /* adapter.checkPassword("admin", "iobroker", (res) => {
951
- adapter.log.info("check user admin pw iobroker: " + res);
952
- });
953
- adapter.checkGroup("admin", "admin", (res) => {
954
- adapter.log.info("check group user admin group admin: " + res);
955
- });*/
956
- }
957
-
958
- // @ts-ignore parent is a valid property on module
959
- if (module.parent) {
960
- // Export startAdapter in compact mode
961
- module.exports = startAdapter;
962
- } else {
963
- // otherwise start the instance directly
964
- startAdapter();
965
- }
1
+ 'use strict';
2
+
3
+ /*
4
+ * Created with @iobroker/create-adapter v1.31.0
5
+ */
6
+
7
+ const utils = require('@iobroker/adapter-core'); // Get common adapter utils
8
+ const crc = require('crc');
9
+ const net = require('net');
10
+
11
+ const _methods = require('./lib/methods');
12
+
13
+ const myRequests = require('./lib/constants').myRequests;
14
+ /* eslint-disable @typescript-eslint/no-unused-vars */
15
+ const byd_stat_tower = require('./lib/constants').byd_stat_tower;
16
+ const myINVs = require('./lib/constants').myINVs;
17
+ const myINVsLVS = require('./lib/constants').myINVsLVS;
18
+
19
+ const socket = new net.Socket();
20
+ /**
21
+ * The adapter instance
22
+ *
23
+ */
24
+ let myState; // Aktueller Status
25
+ let hvsSOC;
26
+ let hvsMaxVolt;
27
+ let hvsMinVolt;
28
+ let hvsSOH;
29
+ /**
30
+ * Removed attributes because they can now occour mor than once.
31
+ * let hvsMaxmVolt;
32
+ * let hvsMinmVolt;
33
+ * let hvsMaxmVoltCell;
34
+ * let hvsMinmVoltCell;
35
+ * let hvsMaxTempCell;
36
+ * let hvsMinTempCell;
37
+ * let hvsSOCDiagnosis;
38
+ * const hvsBatteryVoltsperCell = [];
39
+ * const hvsBatteryTempperCell = [];
40
+ */
41
+ let towerAttributes = [];
42
+
43
+ let hvsA;
44
+ let hvsBattVolt;
45
+ let hvsMaxTemp;
46
+ let hvsMinTemp;
47
+ let hvsBatTemp;
48
+ let hvsOutVolt;
49
+ let hvsChargeTotal;
50
+ let hvsDischargeTotal;
51
+ let hvsETA;
52
+ let hvsError;
53
+ let hvsModules;
54
+ let hvsTowers;
55
+ let hvsDiffVolt;
56
+ let hvsPower;
57
+ let hvsBattType;
58
+ let hvsBattType_fromSerial;
59
+ let hvsInvType;
60
+ let hvsInvType_String;
61
+ let hvsNumCells; //number of cells in system
62
+ let hvsNumTemps; // number of temperatures to count with
63
+ let ConfBatDetailshowoften;
64
+ let ConfBydTowerCount;
65
+ let confBatPollTime;
66
+ let myNumberforDetails;
67
+ let ConfTestMode;
68
+ let _firstRun = false;
69
+
70
+ let hvsSerial;
71
+ let hvsBMU;
72
+ let hvsBMUA;
73
+ let hvsBMUB;
74
+ let hvsBMS;
75
+ let hvsGrid;
76
+ let hvsErrorString;
77
+ let hvsParamT;
78
+
79
+ let ConfBatDetails;
80
+ let ConfOverridePollInterval = 0;
81
+
82
+ /*const myStates = [
83
+ "no state",
84
+ "waiting for initial connect",
85
+ "waiting for 1st answer",
86
+ "waiting for 2nd answer"
87
+
88
+ ];*/
89
+
90
+ let idInterval1 = 0;
91
+
92
+ const myBattTypes = ['HVL', 'HVM', 'HVS'];
93
+ /* HVM: 16 cells per module
94
+ HVS: 32 cells per module
95
+ HVL: unknown so I count 0 cells per module
96
+ */
97
+
98
+ class bydhvsControll extends utils.Adapter {
99
+ constructor(options = {}) {
100
+ super({
101
+ ...options,
102
+ name: 'bydhvs',
103
+ });
104
+
105
+ this.on('ready', this.onReady.bind(this));
106
+ // this.on('stateChange', this.onStateChange.bind(this));
107
+ this.on('unload', this.onUnload.bind(this));
108
+ }
109
+
110
+ async onReady() {
111
+ //first check account settings
112
+ this.setState('info.connection', false, true);
113
+ this.setState('info.socketConnection', false, true);
114
+
115
+ this.buf2int16SI = _methods.buf2int16SI.bind(this);
116
+ this.buf2int16US = _methods.buf2int16US.bind(this);
117
+ this.buf2int32US = _methods.buf2int32US.bind(this);
118
+ this.decodePacketNOP = _methods.decodePacketNOP.bind(this);
119
+ this.countSetBits = _methods.countSetBits.bind(this);
120
+
121
+ this.setStateAsync(`info.socketConnection`, false, true);
122
+
123
+ this.initData();
124
+
125
+ this.log.info('starte polling');
126
+ this.startQuery();
127
+ }
128
+
129
+ /**
130
+ * Is called when adapter shuts down - callback has to be called under any circumstances!
131
+ *
132
+ * @param callback
133
+ */
134
+ onUnload(callback) {
135
+ try {
136
+ clearTimeout(idInterval1);
137
+ socket.destroy();
138
+ this.log.info('Adapter bluelink cleaned up everything...');
139
+ callback();
140
+ } catch (error) {
141
+ callback();
142
+ }
143
+ }
144
+
145
+ initData() {
146
+ this.setObjects();
147
+
148
+ myState = 0;
149
+
150
+ ConfOverridePollInterval = this.config.ConfOverridePollInterval ? this.config.ConfOverridePollInterval : 0;
151
+
152
+ if (ConfOverridePollInterval == 0) {
153
+ confBatPollTime = parseInt(this.config.ConfPollInterval);
154
+ } else {
155
+ const OverridePollState = this.getState('System.OverridePoll');
156
+ confBatPollTime = OverridePollState ? OverridePollState.val : 60;
157
+ }
158
+
159
+ if (confBatPollTime < 30) {
160
+ //confBatPollTime = 60;
161
+ this.log.warn('poll to often - recommendation is not more than every 30 seconds');
162
+ }
163
+
164
+ ConfBydTowerCount = this.config.ConfBydTowerCount ? this.config.ConfBydTowerCount : 1;
165
+ ConfBatDetails = this.config.ConfBatDetails ? true : false;
166
+ ConfBatDetailshowoften = parseInt(this.config.ConfDetailshowoften);
167
+ ConfTestMode = this.config.ConfTestMode ? true : false;
168
+ myNumberforDetails = ConfBatDetailshowoften;
169
+
170
+ this.log.info(`BYD IP Adress: ${this.config.ConfIPAdress}`);
171
+ this.log.info(`Bat Details : ${this.config.ConfBatDetails}`);
172
+ this.log.info(`Tower count: ${this.config.ConfBydTowerCount}`);
173
+ this.log.info(
174
+ `Override Poll, so use from state and not from settings: ${this.config.ConfOverridePollInterval}`,
175
+ );
176
+ this.log.info(`Battery Poll Time: ${confBatPollTime}`);
177
+ this.log.info(`BatDetailshowoften: ${ConfBatDetailshowoften}`);
178
+ this.log.silly(`TestMode= ${ConfTestMode}`);
179
+ }
180
+
181
+ async checkandrepairUnit(id, NewUnit, NewRole, newName) {
182
+ //want to test and understand async and await, so it's introduced here.
183
+ //check for forgotten unit in first version and if it's missing add unit.
184
+ try {
185
+ const obj = await this.getObjectAsync(id);
186
+ if (NewUnit != '') {
187
+ if (obj.common.unit != NewUnit) {
188
+ this.extendObject(id, { common: { unit: NewUnit } });
189
+ }
190
+ }
191
+ if (obj.common.role == '') {
192
+ this.extendObject(id, { common: { role: NewRole } });
193
+ }
194
+ if (newName != '') {
195
+ if (obj.common.name != newName) {
196
+ this.extendObject(id, { common: { name: newName } });
197
+ }
198
+ }
199
+ } catch {
200
+ //dann eben nicht.
201
+ }
202
+ }
203
+
204
+ checkPacket(data) {
205
+ const byteArray = new Uint8Array(data);
206
+ const packetLength = data[2] + 5; // 3 header, 2 crc
207
+ if (byteArray[0] != 1) {
208
+ return false;
209
+ }
210
+ if (byteArray[1] === 3) {
211
+ //habe die Kodierung der Antwort mit 1 an zweiter Stelle nicht verstanden, daher hier keine Längenprüfung
212
+ if (packetLength != byteArray.length) {
213
+ return false;
214
+ }
215
+ } else {
216
+ if (byteArray[1] != 16) {
217
+ return false;
218
+ }
219
+ }
220
+ return crc.crc16modbus(byteArray) === 0;
221
+ }
222
+
223
+ pad(n, width, z) {
224
+ z = z || '0';
225
+ n = `${n}`;
226
+ return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
227
+ }
228
+
229
+ setObjects() {
230
+ let myObjects = [
231
+ ['System.Serial', 'state', 'Serial number', 'string', 'text', true, false, ''],
232
+ ['System.BMU', 'state', 'F/W BMU', 'string', 'text', true, false, ''],
233
+ ['System.BMS', 'state', 'F/W BMS', 'string', 'text', true, false, ''],
234
+ ['System.BMUBankA', 'state', 'F/W BMU-BankA', 'string', 'text', true, false, ''],
235
+ ['System.BMUBankB', 'state', 'F/W BMU-BankB', 'string', 'text', true, false, ''],
236
+ ['System.Modules', 'state', 'modules (count)', 'number', 'value', true, false, ''],
237
+ ['System.Towers', 'state', 'towers (count)', 'number', 'value', true, false, ''],
238
+ ['System.Grid', 'state', 'Parameter Table', 'string', 'text', true, false, ''],
239
+ ['System.ParamT', 'state', 'F/W BMU', 'string', 'text', true, false, ''],
240
+ ['System.BattType', 'state', 'Battery Type', 'string', 'text', true, false, ''],
241
+ ['System.InvType', 'state', 'Inverter Type', 'string', 'text', true, false, ''],
242
+ ['State.SOC', 'state', 'SOC', 'number', 'value.battery', true, false, '%'],
243
+ ['State.VoltMax', 'state', 'Max Cell Voltage', 'number', 'value.voltage', true, false, 'V'],
244
+ ['State.VoltMin', 'state', 'Min Cell Voltage', 'number', 'value.voltage', true, false, 'V'],
245
+ ['State.SOH', 'state', 'SOH', 'number', 'value.battery', true, false, '%'],
246
+ ['State.Current', 'state', 'Charge / Discharge Current', 'number', 'value.current', true, false, 'A'],
247
+ ['State.Power_Consumption', 'state', 'Charge Power', 'number', 'value.power', true, false, 'W'],
248
+ ['State.Power_Delivery', 'state', 'Discharge Power', 'number', 'value.power', true, false, 'W'],
249
+ ['State.VoltBatt', 'state', 'Battery Voltage', 'number', 'value.voltage', true, false, 'V'],
250
+ ['State.TempMax', 'state', 'Max Cell Temp', 'number', 'value.temperature', true, false, '°C'],
251
+ ['State.TempMin', 'state', 'Min Cell Temp', 'number', 'value.temperature', true, false, '°C'],
252
+ ['State.VoltDiff', 'state', 'Max - Min Cell Voltage', 'number', 'value.temperature', true, false, 'V'],
253
+ ['State.Power', 'state', 'Power', 'number', 'value.power', true, false, 'W'],
254
+ ['State.TempBatt', 'state', 'Battery Temperature', 'number', 'value.temperature', true, false, '°C'],
255
+ ['State.VoltOut', 'state', 'Output Voltage', 'number', 'value.voltage', true, false, 'V'],
256
+ ['System.ErrorNum', 'state', 'Error (numeric)', 'number', 'value', true, false, ''],
257
+ ['System.ErrorStr', 'state', 'Error (string)', 'string', 'text', true, false, ''],
258
+ ['System.ChargeTotal', 'state', 'Total Charge of the system', 'number', 'value.energy', true, false, 'Wh'],
259
+ [
260
+ 'System.DischargeTotal',
261
+ 'state',
262
+ 'Total Discharge of the system',
263
+ 'number',
264
+ 'value.energy',
265
+ true,
266
+ false,
267
+ 'Wh',
268
+ ],
269
+ ['System.ETA', 'state', 'Efficiency of in percent', 'number', 'value', true, false, ''],
270
+ ['System.OverridePoll', 'state', 'Poll interval if set per state', 'number', 'value', true, true, ''],
271
+ ];
272
+
273
+ const rawObjects = [
274
+ ['System.Raw_00', 'state', 'Raw Message of sequence 00', 'string', 'text', true, false, ''],
275
+ ['System.Raw_01', 'state', 'Raw Message of sequence 01', 'string', 'text', true, false, ''],
276
+ ['System.Raw_02', 'state', 'Raw Message of sequence 02', 'string', 'text', true, false, ''],
277
+ ];
278
+ if (this.config.ConfStoreRawMessages) {
279
+ myObjects = myObjects.concat(rawObjects);
280
+ }
281
+
282
+ for (let i = 0; i < myObjects.length; i++) {
283
+ this.setObjectNotExists(myObjects[i][0], {
284
+ type: myObjects[i][1],
285
+ common: {
286
+ name: myObjects[i][2],
287
+ type: myObjects[i][3],
288
+ role: myObjects[i][4],
289
+ read: myObjects[i][5],
290
+ write: myObjects[i][6],
291
+ unit: myObjects[i][7], //works only for new objects, so check later for existing objects
292
+ },
293
+ native: {},
294
+ });
295
+ }
296
+ //repair forgotten units in first version and required roles
297
+ for (const myObject of myObjects) {
298
+ //console.log("****extend " + i + " " + myObjects[i][0] + " " + myObjects[i][7]);
299
+ this.checkandrepairUnit(myObject[0], myObject[7], myObject[4], myObject[2]);
300
+ }
301
+ }
302
+
303
+ setStates() {
304
+ let ObjTowerString = '';
305
+
306
+ this.log.silly(`hvsSerial >${hvsSerial}<
307
+ hvsBMU >${hvsBMU}<;
308
+ hvsBMUA >${hvsBMUA}<;
309
+ hvsBMUB >${hvsBMUB}<;
310
+ hvsBMS >${hvsBMS}<;
311
+ hvsModules >${hvsModules}<;
312
+ hvsGrid >${hvsGrid}<;
313
+ hvsSOC >${hvsSOC}<;
314
+ hvsMaxVolt >${hvsMaxVolt}<;
315
+ hvsMinVolt >${hvsMinVolt}<;
316
+ hvsSOH >${hvsSOH}<;
317
+ hvsA >${hvsA}<;
318
+ hvsBattVolt >${hvsBattVolt}<;
319
+ hvsMaxTemp >${hvsMaxTemp}<;
320
+ hvsMinTemp >${hvsMinTemp}<;
321
+ hvsDiffVolt >${hvsDiffVolt}<;
322
+ hvsPower >${hvsPower}<;
323
+ hvsParamT >${hvsParamT}<;
324
+ hvsBatTemp >${hvsBatTemp}<;
325
+ hvsOutVolt >${hvsOutVolt}<,
326
+ hvsError >${hvsError}<,
327
+ hvsErrorStr >${hvsErrorString}<,
328
+ BattType >${hvsBattType_fromSerial}<,
329
+ Invert. Type >${hvsInvType_String}, Nr: ${hvsInvType}<`);
330
+
331
+ this.setState('System.Serial', hvsSerial, true);
332
+ this.setState('System.BMU', hvsBMU, true);
333
+ this.setState('System.BMUBankA', hvsBMUA, true);
334
+ this.setState('System.BMUBankB', hvsBMUB, true);
335
+ this.setState('System.BMS', hvsBMS, true);
336
+ this.setState('System.Modules', hvsModules, true);
337
+ this.setState('System.Towers', hvsTowers, true);
338
+ this.setState('System.Grid', hvsGrid, true);
339
+ this.setState('State.SOC', hvsSOC, true);
340
+ this.setState('State.VoltMax', hvsMaxVolt, true);
341
+ this.setState('State.VoltMin', hvsMinVolt, true);
342
+ this.setState('State.SOH', hvsSOH, true);
343
+ this.setState('State.Current', hvsA, true);
344
+ this.setState('State.VoltBatt', hvsBattVolt, true);
345
+ this.setState('State.TempMax', hvsMaxTemp, true);
346
+ this.setState('State.TempMin', hvsMinTemp, true);
347
+ this.setState('State.VoltDiff', hvsDiffVolt, true);
348
+ this.setState('State.Power', hvsPower, true /*ack*/);
349
+ this.setState('System.ParamT', hvsParamT, true);
350
+ this.setState('State.TempBatt', hvsBatTemp, true);
351
+ this.setState('State.VoltOut', hvsOutVolt, true);
352
+ this.setState('System.ErrorNum', hvsError, true);
353
+ this.setState('System.ErrorStr', hvsErrorString, true);
354
+
355
+ if (hvsPower >= 0) {
356
+ this.setState('State.Power_Consumption', hvsPower, true);
357
+ this.setState('State.Power_Delivery', 0, true);
358
+ } else {
359
+ this.setState('State.Power_Consumption', 0, true);
360
+ this.setState('State.Power_Delivery', -hvsPower, true);
361
+ }
362
+
363
+ this.setState('System.BattType', hvsBattType_fromSerial, true);
364
+ this.setState('System.InvType', hvsInvType_String, true);
365
+ this.setState('System.ChargeTotal', hvsChargeTotal, true);
366
+ this.setState('System.DischargeTotal', hvsDischargeTotal, true);
367
+ this.setState('System.ETA', hvsETA, true);
368
+
369
+ if (myNumberforDetails == 0) {
370
+ // For every tower
371
+ this.log.silly(`Tower attributes: ${JSON.stringify(towerAttributes)}`);
372
+ for (let t = 0; t < towerAttributes.length; t++) {
373
+ try {
374
+ if (ConfBydTowerCount > 1) {
375
+ ObjTowerString = `.Tower_${t + 1}`;
376
+ }
377
+
378
+ // Test if all required msg received.
379
+ if (towerAttributes[t].hvsMaxmVolt) {
380
+ this.setState(`Diagnosis${ObjTowerString}.mVoltMax`, towerAttributes[t].hvsMaxmVolt, true);
381
+ this.setState(`Diagnosis${ObjTowerString}.mVoltMin`, towerAttributes[t].hvsMinmVolt, true);
382
+ this.setState(
383
+ `Diagnosis${ObjTowerString}.mVoltMaxCell`,
384
+ towerAttributes[t].hvsMaxmVoltCell,
385
+ true,
386
+ );
387
+ this.setState(
388
+ `Diagnosis${ObjTowerString}.mVoltMinCell`,
389
+ towerAttributes[t].hvsMinmVoltCell,
390
+ true,
391
+ );
392
+ this.setState(`Diagnosis${ObjTowerString}.TempMax`, towerAttributes[t].hvsMaxmTemp, true);
393
+ this.setState(`Diagnosis${ObjTowerString}.TempMin`, towerAttributes[t].hvsMinmTemp, true);
394
+ this.setState(
395
+ `Diagnosis${ObjTowerString}.TempMaxCell`,
396
+ towerAttributes[t].hvsMaxTempCell,
397
+ true,
398
+ );
399
+ this.setState(
400
+ `Diagnosis${ObjTowerString}.TempMinCell`,
401
+ towerAttributes[t].hvsMinTempCell,
402
+ true,
403
+ );
404
+ this.setState(`Diagnosis${ObjTowerString}.ChargeTotal`, towerAttributes[t].chargeTotal, true);
405
+ this.setState(
406
+ `Diagnosis${ObjTowerString}.DischargeTotal`,
407
+ towerAttributes[t].dischargeTotal,
408
+ true,
409
+ );
410
+ this.setState(`Diagnosis${ObjTowerString}.ETA`, towerAttributes[t].eta, true);
411
+ this.setState(`Diagnosis${ObjTowerString}.BatteryVolt`, towerAttributes[t].batteryVolt, true);
412
+ this.setState(`Diagnosis${ObjTowerString}.OutVolt`, towerAttributes[t].outVolt, true);
413
+ this.setState(`Diagnosis${ObjTowerString}.SOC`, towerAttributes[t].hvsSOCDiagnosis, true);
414
+ this.setState(`Diagnosis${ObjTowerString}.SOH`, towerAttributes[t].soh, true);
415
+ this.setState(`Diagnosis${ObjTowerString}.State`, towerAttributes[t].state, true);
416
+ this.setState(`Diagnosis${ObjTowerString}.BalancingCells`, towerAttributes[t].balancing, true);
417
+ this.setState(
418
+ `Diagnosis${ObjTowerString}.BalancingCellsCount`,
419
+ towerAttributes[t].balancingcount,
420
+ true,
421
+ );
422
+
423
+ this.log.debug(`Tower_${t + 1} balancing >${towerAttributes[t].balancing}<`);
424
+ this.log.debug(`Tower_${t + 1} balcount >${towerAttributes[t].balancingcount}<`);
425
+
426
+ if (t == 0) {
427
+ this.setState(
428
+ `Diagnosis${ObjTowerString}.BalancingOne`,
429
+ towerAttributes[t].balancing ? towerAttributes[t].balancing : '',
430
+ true,
431
+ );
432
+ this.setState(
433
+ `Diagnosis${ObjTowerString}.BalancingCountOne`,
434
+ towerAttributes[t].balancingcount,
435
+ true,
436
+ );
437
+ } else {
438
+ this.setState(
439
+ `Diagnosis${ObjTowerString}.BalancingTwo`,
440
+ towerAttributes[t].balancing ? towerAttributes[t].balancing : '',
441
+ true,
442
+ );
443
+ this.setState(
444
+ `Diagnosis${ObjTowerString}.BalancingCountTwo`,
445
+ towerAttributes[t].balancingcount,
446
+ true,
447
+ );
448
+ }
449
+ /*
450
+ if (towerAttributes[t].balancing) this.setState(`Diagnosis` + ObjTowerString + `.BalancingOne`, towerAttributes[t].balancing_one, true);
451
+ if (towerAttributes[t].balancingcount_one) this.setState(`Diagnosis` + ObjTowerString + `.BalancingOne`, towerAttributes[t].balancing_one, true);
452
+ this.setState(`Diagnosis` + ObjTowerString + `.BalancingTwo`, towerAttributes[t].balancing_two ? towerAttributes[t].balancing_two : "", true);
453
+ this.setState(`Diagnosis` + ObjTowerString + `.BalancingCountTwo`, towerAttributes[t].balancingcount_two ? towerAttributes[t].balancingcount_two : 0, true );
454
+ */
455
+ for (let i = 1; i <= hvsNumCells; i++) {
456
+ this.setState(
457
+ `CellDetails${ObjTowerString}.CellVolt${this.pad(i, 3)}`,
458
+ towerAttributes[t].hvsBatteryVoltsperCell[i]
459
+ ? towerAttributes[t].hvsBatteryVoltsperCell[i]
460
+ : 0,
461
+ true,
462
+ );
463
+ }
464
+ const mVoltDefDeviation = this.calcDeviation(
465
+ towerAttributes[t].hvsBatteryVoltsperCell.filter(v => v > 0),
466
+ );
467
+ const mVoltMean = this.calcAverage(
468
+ towerAttributes[t].hvsBatteryVoltsperCell.filter(v => v > 0),
469
+ );
470
+ this.setState(`Diagnosis${ObjTowerString}.mVoltDefDeviation`, mVoltDefDeviation, true);
471
+ this.setState(`Diagnosis${ObjTowerString}.mVoltMean`, mVoltMean, true);
472
+ this.setState(
473
+ `Diagnosis${ObjTowerString}.mVoltGt150DefVar`,
474
+ towerAttributes[t].hvsBatteryVoltsperCell.filter(
475
+ v => v > mVoltMean + mVoltDefDeviation * 1.5,
476
+ ).length,
477
+ true,
478
+ );
479
+ this.setState(
480
+ `Diagnosis${ObjTowerString}.mVoltLt150DefVar`,
481
+ towerAttributes[t].hvsBatteryVoltsperCell
482
+ .filter(v => v > 0)
483
+ .filter(v => v < mVoltMean - mVoltDefDeviation * 1.5).length,
484
+ true,
485
+ );
486
+
487
+ for (let i = 1; i <= hvsNumTemps; i++) {
488
+ this.setState(
489
+ `CellDetails${ObjTowerString}.CellTemp${this.pad(i, 3)}`,
490
+ towerAttributes[t].hvsBatteryTempperCell[i]
491
+ ? towerAttributes[t].hvsBatteryTempperCell[i]
492
+ : 0,
493
+ true,
494
+ );
495
+ }
496
+ const tempDefDeviation = this.calcDeviation(
497
+ towerAttributes[t].hvsBatteryTempperCell.filter(v => v > 0),
498
+ );
499
+ const tempMean = this.calcAverage(towerAttributes[t].hvsBatteryTempperCell.filter(v => v > 0));
500
+ this.setState(`Diagnosis${ObjTowerString}.TempDefDeviation`, tempDefDeviation, true);
501
+ this.setState(`Diagnosis${ObjTowerString}.TempMean`, tempMean, true);
502
+ this.setState(
503
+ `Diagnosis${ObjTowerString}.TempGt150DefVar`,
504
+ towerAttributes[t].hvsBatteryTempperCell.filter(v => v > tempMean + tempDefDeviation * 1.5)
505
+ .length,
506
+ true,
507
+ );
508
+ this.setState(
509
+ `Diagnosis${ObjTowerString}.TempLt150DefVar`,
510
+ towerAttributes[t].hvsBatteryTempperCell
511
+ .filter(v => v > 0)
512
+ .filter(v => v < tempMean - tempDefDeviation * 1.5).length,
513
+ true,
514
+ );
515
+
516
+ this.log.silly(`Tower_${t + 1} hvsMaxmVolt >${towerAttributes[t].hvsMaxmVolt}<`);
517
+ this.log.silly(`Tower_${t + 1} hvsMinmVolt >${towerAttributes[t].hvsMinmVolt}<`);
518
+ this.log.silly(`Tower_${t + 1} hvsMaxmVoltCell >${towerAttributes[t].hvsMaxmVoltCell}<`);
519
+ this.log.silly(`Tower_${t + 1} hvsMinmVoltCell >${towerAttributes[t].hvsMinmVoltCell}<`);
520
+ this.log.silly(`Tower_${t + 1} hvsMaxTempCell >${towerAttributes[t].hvsMaxTempCell}<`);
521
+ this.log.silly(`Tower_${t + 1} hvsMinTempCell >${towerAttributes[t].hvsMinTempCell}<`);
522
+ this.log.silly(`Tower_${t + 1} hvsSOC (Diag) >${towerAttributes[t].hvsSOCDiagnosis}<`);
523
+ }
524
+ } catch (err) {
525
+ if (err instanceof Error) {
526
+ this.log.error(`Cant read in Tower ${t} with ${err.message}`);
527
+ } else {
528
+ this.log.error(`Cant read in Tower ${t} with unknown error`);
529
+ }
530
+ }
531
+ }
532
+ }
533
+ }
534
+
535
+ startQuery() {
536
+ //erster Start sofort (500ms), dann entsprechend der Config - dann muss man nicht beim Entwickeln warten bis der erste Timer durch ist.
537
+ _firstRun = true;
538
+
539
+ const runPoll = () => this.pollQuery();
540
+
541
+ // Start direkt nach 500ms
542
+ setTimeout(runPoll, 500);
543
+
544
+ // Danach zyklisch gemäß Konfiguration
545
+ idInterval1 = setInterval(runPoll, confBatPollTime * 1000);
546
+ this.log.info(`gestartet pollTime :${confBatPollTime} intervalId :${idInterval1}`);
547
+ }
548
+
549
+ async setupIPClientHandlers() {
550
+ const waitTime = 8000;
551
+ const timeout = 2000;
552
+
553
+ this.log.debug('Starte Datenabfrage via TCP-Client...');
554
+
555
+ return new Promise(resolve => {
556
+ const cleanup = () => {
557
+ this.log.debug('Schließe Socket-Verbindung und setze State zurück');
558
+ socket.destroy();
559
+ myState = 0;
560
+ };
561
+
562
+ const sendRequest = (requestIndex, nextState, delay = 200) => {
563
+ return new Promise(res => {
564
+ setTimeout(() => {
565
+ this.log.debug(`→ Sende Request [${requestIndex}] und wechsle in State ${nextState}`);
566
+ myState = nextState;
567
+ socket.write(myRequests[requestIndex]);
568
+ res();
569
+ }, delay);
570
+ });
571
+ };
572
+
573
+ const waitForData = () => {
574
+ return new Promise((res, rej) => {
575
+ socket.once('data', data => {
576
+ this.log.debug(`← Daten empfangen (Länge: ${data.length}) in State ${myState}`);
577
+ res(data);
578
+ });
579
+ socket.once('timeout', () => rej(new Error('Socket Timeout')));
580
+ socket.once('error', err => rej(err));
581
+ });
582
+ };
583
+
584
+ socket.setTimeout(timeout);
585
+
586
+ try {
587
+ socket.connect(8080, this.config.ConfIPAdress, async () => {
588
+ this.log.debug(`Socket verbunden mit ${this.config.ConfIPAdress}:8080`);
589
+ try {
590
+ myState = 2;
591
+ await sendRequest(0, 2);
592
+
593
+ while (true) {
594
+ const data = await waitForData();
595
+
596
+ if (!this.checkPacket(data)) {
597
+ this.log.warn(`⚠️ Ungültiges Paket empfangen in State ${myState}`);
598
+ this.setStateChanged('info.connection', { val: false, ack: true });
599
+ cleanup();
600
+ return resolve(false);
601
+ }
602
+
603
+ this.setStateChanged('info.connection', { val: true, ack: true });
604
+
605
+ switch (myState) {
606
+ case 2:
607
+ this.log.debug('➡️ State 2: Verarbeite Paket 0');
608
+ this.decodePacket0(data);
609
+ socket.setTimeout(timeout);
610
+ await sendRequest(1, 3);
611
+ break;
612
+
613
+ case 3:
614
+ this.log.debug('➡️ State 3: Verarbeite Paket 1');
615
+ this.decodePacket1(data);
616
+ socket.setTimeout(timeout);
617
+ await sendRequest(2, 4);
618
+ break;
619
+
620
+ case 4:
621
+ this.log.debug('➡️ State 4: Verarbeite Paket 2');
622
+ this.decodePacket2(data);
623
+ if (myNumberforDetails < ConfBatDetailshowoften || !ConfBatDetails) {
624
+ this.log.debug('⚡ Details nicht notwendig – beende Zyklus');
625
+ this.setStates();
626
+ cleanup();
627
+ return resolve(true);
628
+ }
629
+ myNumberforDetails = 0;
630
+ socket.setTimeout(timeout);
631
+ await sendRequest(3, 5);
632
+ break;
633
+
634
+ case 5:
635
+ this.log.debug('➡️ State 5: NOP + Wartezeit');
636
+ this.decodePacketNOP(data);
637
+ socket.setTimeout(waitTime + timeout);
638
+ await new Promise(res => setTimeout(res, waitTime));
639
+ await sendRequest(4, 6, 0);
640
+ break;
641
+
642
+ case 6:
643
+ this.log.debug('➡️ State 6: NOP');
644
+ this.decodePacketNOP(data);
645
+ await sendRequest(5, 7);
646
+ break;
647
+
648
+ case 7:
649
+ this.log.debug('➡️ State 7: Paket 5');
650
+ this.decodePacket5(data);
651
+ await sendRequest(6, 8);
652
+ break;
653
+
654
+ case 8:
655
+ this.log.debug('➡️ State 8: Paket 6');
656
+ this.decodePacket6(data);
657
+ await sendRequest(7, 9);
658
+ break;
659
+
660
+ case 9:
661
+ this.log.debug('➡️ State 9: Paket 7');
662
+ this.decodePacket7(data);
663
+ await sendRequest(8, 10);
664
+ break;
665
+
666
+ case 10:
667
+ this.log.debug('➡️ State 10: Paket 8');
668
+ this.decodePacket8(data);
669
+ if (hvsNumCells > 128) {
670
+ this.log.debug('Mehr als 128 Zellen erkannt → sende weiteres Paket');
671
+ await sendRequest(9, 11);
672
+ } else if (ConfBydTowerCount > 1) {
673
+ this.log.debug('Mehrere Tower erkannt Wechsel zu State 16');
674
+ await sendRequest(16, 16);
675
+ } else {
676
+ this.log.debug('Alle Daten empfangen, beende...');
677
+ this.setStates();
678
+ cleanup();
679
+ return resolve(true);
680
+ }
681
+ break;
682
+
683
+ case 11:
684
+ this.log.debug('➡️ State 11: NOP');
685
+ this.decodePacketNOP(data);
686
+ await sendRequest(10, 12);
687
+ break;
688
+
689
+ case 12:
690
+ this.log.debug('➡️ State 12: NOP + Wartezeit');
691
+ this.decodePacketNOP(data);
692
+ socket.setTimeout(8000);
693
+ await new Promise(res => setTimeout(res, 3000));
694
+ await sendRequest(11, 13, 0);
695
+ break;
696
+
697
+ case 13:
698
+ this.log.debug('➡️ State 13: NOP');
699
+ this.decodePacketNOP(data);
700
+ await sendRequest(12, 14);
701
+ break;
702
+
703
+ case 14:
704
+ this.log.debug('➡️ State 14: Response12');
705
+ this.decodeResponse12(data);
706
+ await sendRequest(13, 15);
707
+ break;
708
+
709
+ case 15:
710
+ this.log.debug('➡️ State 15: Response13');
711
+ this.decodeResponse13(data);
712
+ if (ConfBydTowerCount > 1) {
713
+ await sendRequest(16, 16);
714
+ } else {
715
+ this.log.debug('Beende nach Response13');
716
+ this.setStates();
717
+ cleanup();
718
+ return resolve(true);
719
+ }
720
+ break;
721
+
722
+ case 16:
723
+ this.log.debug('➡️ State 16: Zweiter Tower – NOP + Wartezeit');
724
+ this.decodePacketNOP(data);
725
+ socket.setTimeout(waitTime + timeout);
726
+ await new Promise(res => setTimeout(res, waitTime));
727
+ await sendRequest(4, 17, 0);
728
+ break;
729
+
730
+ case 17:
731
+ this.log.debug('➡️ State 17: NOP');
732
+ this.decodePacketNOP(data);
733
+ await sendRequest(5, 18);
734
+ break;
735
+
736
+ case 18:
737
+ this.log.debug('➡️ State 18: Paket 5 (Tower 2)');
738
+ this.decodePacket5(data, 1);
739
+ await sendRequest(6, 19);
740
+ break;
741
+
742
+ case 19:
743
+ this.log.debug('➡️ State 19: Paket 6 (Tower 2)');
744
+ this.decodePacket6(data, 1);
745
+ await sendRequest(7, 20);
746
+ break;
747
+
748
+ case 20:
749
+ this.log.debug('➡️ State 20: Paket 7 (Tower 2)');
750
+ this.decodePacket7(data, 1);
751
+ await sendRequest(8, 22);
752
+ break;
753
+
754
+ case 22:
755
+ this.log.debug('➡️ State 22: Paket 8 (Tower 2)');
756
+ this.decodePacket8(data, 1);
757
+ this.log.debug('✅ Alle Daten erfolgreich verarbeitet');
758
+ this.setStates();
759
+ cleanup();
760
+ return resolve(true);
761
+
762
+ default:
763
+ this.log.warn(`❓ Unerwarteter Zustand: ${myState}`);
764
+ cleanup();
765
+ return resolve(false);
766
+ }
767
+ }
768
+ } catch (err) {
769
+ this.log.error(`❌ Fehler im Ablauf: ${err.message}`);
770
+ this.log.debug(err.stack);
771
+ this.setStateChanged('info.connection', { val: false, ack: true });
772
+ cleanup();
773
+ return resolve(false);
774
+ }
775
+ });
776
+ } catch (err) {
777
+ this.log.error(`❌ Socket konnte nicht verbunden werden: ${err.message}`);
778
+ this.log.debug(err.stack);
779
+ this.setStateChanged('info.connection', { val: false, ack: true });
780
+ cleanup();
781
+ return resolve(false);
782
+ }
783
+ });
784
+ }
785
+
786
+ async pollQuery() {
787
+ if (myState > 0) {
788
+ return;
789
+ }
790
+
791
+ // Prüfe und ggf. setze neues Poll-Intervall
792
+ if (ConfOverridePollInterval !== 0) {
793
+ const state = await this.getState('System.OverridePoll');
794
+ const newPollTime = state?.val ?? 60;
795
+
796
+ if (confBatPollTime !== newPollTime) {
797
+ confBatPollTime = newPollTime;
798
+ clearInterval(idInterval1);
799
+ idInterval1 = setInterval(() => this.startPoll(), confBatPollTime * 1000);
800
+ this.log.info(`Poll-Intervall aktualisiert: ${confBatPollTime}s, Interval-ID: ${idInterval1}`);
801
+ }
802
+ }
803
+
804
+ myState = 1;
805
+ myNumberforDetails++;
806
+
807
+ this.log.debug(`myNumberforDetails: ${myNumberforDetails}`);
808
+ this.log.debug(`Starte Polling an IP: ${this.config.ConfIPAdress}`);
809
+
810
+ // Tower-Attribute initialisieren
811
+ towerAttributes = Array.from({ length: ConfBydTowerCount }, (_, i) => {
812
+ this.log.debug(`Initialisiere Tower ${i}`);
813
+ return {
814
+ hvsBatteryVoltsperCell: [],
815
+ hvsBatteryTempperCell: [],
816
+ };
817
+ });
818
+
819
+ const socketConnection = await this.setupIPClientHandlers();
820
+ await this.setStateAsync('info.socketConnection', socketConnection, true);
821
+ }
822
+
823
+ /*
824
+ * Calculate default deviation / Standardabweichung
825
+ */
826
+ calcDeviation(array) {
827
+ if (!Array.isArray(array) || array.length === 0) {
828
+ return 0;
829
+ }
830
+
831
+ const n = array.length;
832
+ const mean = array.reduce((sum, val) => sum + val, 0) / n;
833
+ const variance = array.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0) / (n > 1 ? n - 1 : 1);
834
+
835
+ return Math.sqrt(variance);
836
+ }
837
+
838
+ /*
839
+ * Calculate the average / mean
840
+ */
841
+ calcAverage(array) {
842
+ if (!Array.isArray(array) || array.length === 0) {
843
+ return 0;
844
+ }
845
+ return array.reduce((a, b) => a + b, 0) / array.length;
846
+ }
847
+
848
+ decodePacket2(data) {
849
+ if (this.config.ConfStoreRawMessages) {
850
+ this.setState('System.Raw_02', data.toString('hex'), true);
851
+ }
852
+ const byteArray = new Uint8Array(data);
853
+ hvsBattType = byteArray[5];
854
+ hvsInvType = byteArray[3];
855
+ hvsNumCells = 0;
856
+ hvsNumTemps = 0;
857
+ switch (hvsBattType) {
858
+ case 0: //HVL -> unknown specification, so 0 cells and 0 temps
859
+ //hvsNumCells = 0;
860
+ //hvsNumTemps = 0;
861
+ //see above, is default
862
+ break;
863
+ case 1: //HVM 16 Cells per module
864
+ hvsNumCells = hvsModules * 16;
865
+ hvsNumTemps = hvsModules * 8;
866
+ break;
867
+ //crosscheck
868
+ // 5 modules, 80 voltages, 40 temps
869
+ case 2: //HVS 32 cells per module
870
+ hvsNumCells = hvsModules * 32;
871
+ hvsNumTemps = hvsModules * 12;
872
+ break;
873
+ //crosscheck:
874
+ //Counts from real data:
875
+ //mine: 2 modules, 64 voltages, 24 temps
876
+ //4 modules, 128 voltages, 48 temps
877
+ }
878
+ //leider hässlich dazugestrickt, wollte in die andere Logik nicht eingreifen
879
+ if (hvsBattType_fromSerial == 'LVS') {
880
+ hvsBattType = 'LVS';
881
+ hvsNumCells = hvsModules * 7;
882
+ hvsNumTemps = 0;
883
+ }
884
+ if (hvsBattType_fromSerial == 'LVS') {
885
+ //unterschiedliche WR-Tabelle je nach Batt-Typ
886
+ hvsInvType_String = myINVsLVS[hvsInvType];
887
+ } else {
888
+ hvsInvType_String = myINVs[hvsInvType];
889
+ }
890
+ if (hvsInvType_String == undefined) {
891
+ hvsInvType_String = 'undefined';
892
+ }
893
+
894
+ if (hvsNumCells > 160) {
895
+ hvsNumCells = 160;
896
+ }
897
+ if (hvsNumTemps > 64) {
898
+ hvsNumTemps = 64;
899
+ }
900
+ if (ConfBatDetails && _firstRun) {
901
+ _firstRun = false;
902
+ this.setObjectsCells();
903
+ }
904
+ this.log.debug(`NumCells: ${hvsNumCells} Numtemps: ${hvsNumTemps} Modules: ${hvsModules}`);
905
+ }
906
+
907
+ setObjectsCells() {
908
+ //Diagnose-data only if necessary.
909
+ let myObjects = [];
910
+ let ObjTowerString = '';
911
+
912
+ for (let towerNumber = 0; towerNumber < ConfBydTowerCount; towerNumber++) {
913
+ if (ConfBydTowerCount > 1) {
914
+ ObjTowerString = `.Tower_${towerNumber + 1}`;
915
+ }
916
+ myObjects = [
917
+ [
918
+ `Diagnosis${ObjTowerString}.mVoltMax`,
919
+ 'state',
920
+ 'Max Cell Voltage (mv)',
921
+ 'number',
922
+ 'value.voltage',
923
+ true,
924
+ false,
925
+ 'mV',
926
+ ],
927
+ [
928
+ `Diagnosis${ObjTowerString}.mVoltMin`,
929
+ 'state',
930
+ 'Min Cell Voltage (mv)',
931
+ 'number',
932
+ 'value.voltage',
933
+ true,
934
+ false,
935
+ 'mV',
936
+ ],
937
+ [
938
+ `Diagnosis${ObjTowerString}.mVoltMaxCell`,
939
+ 'state',
940
+ 'Max Cell Volt (Cellnr)',
941
+ 'number',
942
+ 'value.voltage',
943
+ true,
944
+ false,
945
+ '',
946
+ ],
947
+ [
948
+ `Diagnosis${ObjTowerString}.mVoltMinCell`,
949
+ 'state',
950
+ 'Min Cell Volt (Cellnr)',
951
+ 'number',
952
+ 'value.voltage',
953
+ true,
954
+ false,
955
+ '',
956
+ ],
957
+ [
958
+ `Diagnosis${ObjTowerString}.TempMax`,
959
+ 'state',
960
+ 'Max Cell Temperature',
961
+ 'number',
962
+ 'value.temperature',
963
+ true,
964
+ false,
965
+ '°C',
966
+ ],
967
+ [
968
+ `Diagnosis${ObjTowerString}.TempMin`,
969
+ 'state',
970
+ 'Min Cell Temperature',
971
+ 'number',
972
+ 'value.temperature',
973
+ true,
974
+ false,
975
+ '°C',
976
+ ],
977
+ [
978
+ `Diagnosis${ObjTowerString}.TempMaxCell`,
979
+ 'state',
980
+ 'Max Cell Temp (Cellnr)',
981
+ 'number',
982
+ 'value.temperature',
983
+ true,
984
+ false,
985
+ '',
986
+ ],
987
+ [
988
+ `Diagnosis${ObjTowerString}.TempMinCell`,
989
+ 'state',
990
+ 'Min Cell Temp(Cellnr)',
991
+ 'number',
992
+ 'value.temperature',
993
+ true,
994
+ false,
995
+ '',
996
+ ],
997
+
998
+ [
999
+ `Diagnosis${ObjTowerString}.mVoltDefDeviation`,
1000
+ 'state',
1001
+ 'voltage std-dev of the cells',
1002
+ 'number',
1003
+ 'value.voltage',
1004
+ true,
1005
+ false,
1006
+ 'mV',
1007
+ ],
1008
+ [
1009
+ `Diagnosis${ObjTowerString}.TempDefDeviation`,
1010
+ 'state',
1011
+ 'temperature std-dev of the cells',
1012
+ 'number',
1013
+ 'value.temperature',
1014
+ true,
1015
+ false,
1016
+ '°C',
1017
+ ],
1018
+ [
1019
+ `Diagnosis${ObjTowerString}.mVoltMean`,
1020
+ 'state',
1021
+ 'mean voltage of the cells',
1022
+ 'number',
1023
+ 'value.voltage',
1024
+ true,
1025
+ false,
1026
+ 'mV',
1027
+ ],
1028
+ [
1029
+ `Diagnosis${ObjTowerString}.TempMean`,
1030
+ 'state',
1031
+ 'mean temperature of the cells',
1032
+ 'number',
1033
+ 'value.temperature',
1034
+ true,
1035
+ false,
1036
+ '°C',
1037
+ ],
1038
+ [
1039
+ `Diagnosis${ObjTowerString}.mVoltGt150DefVar`,
1040
+ 'state',
1041
+ '#cells voltage above 150% std-dev',
1042
+ 'number',
1043
+ 'value.voltage',
1044
+ true,
1045
+ false,
1046
+ '',
1047
+ ],
1048
+ [
1049
+ `Diagnosis${ObjTowerString}.mVoltLt150DefVar`,
1050
+ 'state',
1051
+ '#cells voltage below 150% std-dev',
1052
+ 'number',
1053
+ 'value.voltage',
1054
+ true,
1055
+ false,
1056
+ '',
1057
+ ],
1058
+ [
1059
+ `Diagnosis${ObjTowerString}.TempGt150DefVar`,
1060
+ 'state',
1061
+ '#cells temperature above 150% std-dev',
1062
+ 'number',
1063
+ 'value.temperature',
1064
+ true,
1065
+ false,
1066
+ '',
1067
+ ],
1068
+ [
1069
+ `Diagnosis${ObjTowerString}.TempLt150DefVar`,
1070
+ 'state',
1071
+ '#cells temperature below 150% std-dev',
1072
+ 'number',
1073
+ 'value.temperature',
1074
+ true,
1075
+ false,
1076
+ '',
1077
+ ],
1078
+
1079
+ [
1080
+ `Diagnosis${ObjTowerString}.ChargeTotal`,
1081
+ 'state',
1082
+ 'Total Charge in that tower',
1083
+ 'number',
1084
+ 'value.watt',
1085
+ true,
1086
+ false,
1087
+ '',
1088
+ ],
1089
+ [
1090
+ `Diagnosis${ObjTowerString}.DischargeTotal`,
1091
+ 'state',
1092
+ 'Total Discharge in that tower',
1093
+ 'number',
1094
+ 'value.watt',
1095
+ true,
1096
+ false,
1097
+ '',
1098
+ ],
1099
+ [`Diagnosis${ObjTowerString}.ETA`, 'state', 'ETA of that tower', 'number', 'value', true, false, ''],
1100
+ [
1101
+ `Diagnosis${ObjTowerString}.BatteryVolt`,
1102
+ 'state',
1103
+ 'Voltage of that tower',
1104
+ 'number',
1105
+ 'value',
1106
+ true,
1107
+ false,
1108
+ '',
1109
+ ],
1110
+ [`Diagnosis${ObjTowerString}.OutVolt`, 'state', 'Output voltage', 'number', 'value', true, false, ''],
1111
+ [
1112
+ `Diagnosis${ObjTowerString}.SOC`,
1113
+ 'state',
1114
+ 'SOC (Diagnosis)',
1115
+ 'number',
1116
+ 'value.battery',
1117
+ true,
1118
+ false,
1119
+ '%',
1120
+ ],
1121
+
1122
+ [`Diagnosis${ObjTowerString}.SOH`, 'state', 'State of Health', 'number', 'value', true, false, ''],
1123
+ [`Diagnosis${ObjTowerString}.State`, 'state', 'tower state', 'string', 'value', true, false, ''],
1124
+ [
1125
+ `Diagnosis${ObjTowerString}.BalancingCells`,
1126
+ 'state',
1127
+ 'bitmask of balanced cells',
1128
+ 'string',
1129
+ 'value',
1130
+ true,
1131
+ false,
1132
+ '',
1133
+ ],
1134
+ [
1135
+ `Diagnosis${ObjTowerString}.BalancingCellsCount`,
1136
+ 'state',
1137
+ 'number of currently balanced cells',
1138
+ 'number',
1139
+ 'value',
1140
+ true,
1141
+ false,
1142
+ '',
1143
+ ],
1144
+ [`Diagnosis${ObjTowerString}.BalancingOne`, 'state', 'tower state', 'string', 'value', true, false, ''],
1145
+ [`Diagnosis${ObjTowerString}.BalancingTwo`, 'state', 'tower state', 'string', 'value', true, false, ''],
1146
+ [
1147
+ `Diagnosis${ObjTowerString}.BalancingCountOne`,
1148
+ 'state',
1149
+ 'tower state',
1150
+ 'number',
1151
+ 'value',
1152
+ true,
1153
+ false,
1154
+ '',
1155
+ ],
1156
+ [
1157
+ `Diagnosis${ObjTowerString}.BalancingCountTwo`,
1158
+ 'state',
1159
+ 'tower state',
1160
+ 'number',
1161
+ 'value',
1162
+ true,
1163
+ false,
1164
+ '',
1165
+ ],
1166
+ ];
1167
+
1168
+ for (let i = 0; i < myObjects.length; i++) {
1169
+ this.setObjectNotExists(myObjects[i][0], {
1170
+ type: myObjects[i][1],
1171
+ common: {
1172
+ name: myObjects[i][2],
1173
+ type: myObjects[i][3],
1174
+ role: myObjects[i][4],
1175
+ read: myObjects[i][5],
1176
+ write: myObjects[i][6],
1177
+ unit: myObjects[i][7],
1178
+ },
1179
+ native: {},
1180
+ });
1181
+ }
1182
+ for (let i = 0; i < myObjects.length; i++) {
1183
+ //console.log("****extend " + i + " " + myObjects[i][0] + " " + myObjects[i][7]);
1184
+ this.checkandrepairUnit(myObjects[i][0], myObjects[i][7], myObjects[i][5], myObjects[i][2]);
1185
+ }
1186
+
1187
+ for (let i = 1; i <= hvsNumCells; i++) {
1188
+ this.setObjectNotExists(`CellDetails${ObjTowerString}.CellVolt${this.pad(i, 3)}`, {
1189
+ type: 'state',
1190
+ common: {
1191
+ name: `Voltage Cell: ${this.pad(i, 3)}`,
1192
+ type: 'number',
1193
+ role: 'value.voltage',
1194
+ read: true,
1195
+ write: false,
1196
+ unit: 'mV',
1197
+ },
1198
+ native: {},
1199
+ });
1200
+ this.checkandrepairUnit(
1201
+ `CellDetails${ObjTowerString}.CellVolt${this.pad(i, 3)}`,
1202
+ 'mV',
1203
+ 'value.voltage',
1204
+ ); //repair forgotten units in first version
1205
+
1206
+ for (let i = 1; i <= hvsNumTemps; i++) {
1207
+ this.setObjectNotExists(`CellDetails${ObjTowerString}.CellTemp${this.pad(i, 3)}`, {
1208
+ type: 'state',
1209
+ common: {
1210
+ name: `Temp Cell: ${this.pad(i, 3)}`,
1211
+ type: 'number',
1212
+ role: 'value.temperature',
1213
+ read: true,
1214
+ write: false,
1215
+ unit: '°C',
1216
+ },
1217
+ native: {},
1218
+ });
1219
+ this.checkandrepairUnit(
1220
+ `CellDetails${ObjTowerString}.CellTemp${this.pad(i, 3)}`,
1221
+ '°C',
1222
+ 'value.temperature',
1223
+ ); //repair forgotten units in first version
1224
+ }
1225
+ }
1226
+ }
1227
+ }
1228
+
1229
+ decodePacket5(data, towerNumber = 0) {
1230
+ const byteArray = new Uint8Array(data);
1231
+ towerAttributes[towerNumber].hvsMaxmVolt = this.buf2int16SI(byteArray, 5);
1232
+ towerAttributes[towerNumber].hvsMinmVolt = this.buf2int16SI(byteArray, 7);
1233
+ towerAttributes[towerNumber].hvsMaxmVoltCell = byteArray[9];
1234
+ towerAttributes[towerNumber].hvsMinmVoltCell = byteArray[10];
1235
+ towerAttributes[towerNumber].hvsMaxmTemp = this.buf2int16SI(byteArray, 11);
1236
+ towerAttributes[towerNumber].hvsMinmTemp = this.buf2int16SI(byteArray, 13);
1237
+ towerAttributes[towerNumber].hvsMaxTempCell = byteArray[15];
1238
+ towerAttributes[towerNumber].hvsMinTempCell = byteArray[16];
1239
+
1240
+ //starting with byte 101, ending with 131, Cell voltage 1-16
1241
+ const MaxCells = 16;
1242
+ for (let i = 0; i < MaxCells; i++) {
1243
+ this.log.silly(`Battery Voltage-${this.pad(i + 1, 3)} :${this.buf2int16SI(byteArray, i * 2 + 101)}`);
1244
+ towerAttributes[towerNumber].hvsBatteryVoltsperCell[i + 1] = this.buf2int16SI(byteArray, i * 2 + 101);
1245
+ }
1246
+
1247
+ // Balancing Flags
1248
+ // 17 bis 32
1249
+ towerAttributes[towerNumber].balancing = data.slice(17, 33).toString('hex');
1250
+ towerAttributes[towerNumber].balancingcount = this.countSetBits(data.slice(17, 33).toString('hex'));
1251
+
1252
+ towerAttributes[towerNumber].chargeTotal = this.buf2int32US(byteArray, 33);
1253
+ towerAttributes[towerNumber].dischargeTotal = this.buf2int32US(byteArray, 37);
1254
+ towerAttributes[towerNumber].eta =
1255
+ towerAttributes[towerNumber].dischargeTotal / towerAttributes[towerNumber].chargeTotal;
1256
+ towerAttributes[towerNumber].batteryVolt = this.buf2int16SI(byteArray, 45);
1257
+ towerAttributes[towerNumber].outVolt = this.buf2int16SI(byteArray, 51);
1258
+ towerAttributes[towerNumber].hvsSOCDiagnosis = parseFloat(
1259
+ ((this.buf2int16SI(byteArray, 53) * 1.0) / 10.0).toFixed(1),
1260
+ );
1261
+ towerAttributes[towerNumber].soh = parseFloat((this.buf2int16SI(byteArray, 55) * 1.0).toFixed(1));
1262
+ towerAttributes[towerNumber].state = byteArray[59].toString(16) + byteArray[60].toString(16);
1263
+ }
1264
+
1265
+ decodePacket6(data, towerNumber = 0) {
1266
+ const byteArray = new Uint8Array(data);
1267
+ // e.g. hvsNumCells = 80
1268
+ // first Voltage in byte 5+6
1269
+ // Count = 80-17 --> 63
1270
+ let MaxCells = hvsNumCells - 16; //0 to n-1 is the same like 1 to n
1271
+ if (MaxCells > 64) {
1272
+ MaxCells = 64;
1273
+ }
1274
+ for (let i = 0; i < MaxCells; i++) {
1275
+ this.log.silly(`Battery Voltage-${this.pad(i + 17, 3)} :${this.buf2int16SI(byteArray, i * 2 + 5)}`);
1276
+ towerAttributes[towerNumber].hvsBatteryVoltsperCell[i + 17] = this.buf2int16SI(byteArray, i * 2 + 5);
1277
+ }
1278
+ }
1279
+
1280
+ decodePacket7(data, towerNumber = 0) {
1281
+ const byteArray = new Uint8Array(data);
1282
+ //starting with byte 5, ending 101, voltage for cell 81 to 128
1283
+ //starting with byte 103, ending 132, temp for cell 1 to 30
1284
+
1285
+ // e.g. hvsNumCells = 128
1286
+ // first Voltage in byte 5+6
1287
+ // Count = 128-80 --> 48
1288
+ let MaxCells = hvsNumCells - 80; //0 to n-1 is the same like 1 to n
1289
+ if (MaxCells > 48) {
1290
+ MaxCells = 48;
1291
+ }
1292
+ this.log.silly(`hvsModules =${hvsModules} maxCells= ${MaxCells}`);
1293
+ for (let i = 0; i < MaxCells; i++) {
1294
+ this.log.silly(`Battery Voltage-${this.pad(i + 81, 3)} :${this.buf2int16SI(byteArray, i * 2 + 5)}`);
1295
+ towerAttributes[towerNumber].hvsBatteryVoltsperCell[i + 81] = this.buf2int16SI(byteArray, i * 2 + 5);
1296
+ }
1297
+
1298
+ let MaxTemps = hvsNumTemps - 0; //0 to n-1 is the same like 1 to n
1299
+ if (MaxTemps > 30) {
1300
+ MaxTemps = 30;
1301
+ }
1302
+ this.log.silly(`hvsModules =${hvsModules} MaxTemps= ${MaxTemps}`);
1303
+ for (let i = 0; i < MaxTemps; i++) {
1304
+ this.log.silly(`Battery Temp ${this.pad(i + 1, 3)} :${byteArray[i + 103]}`);
1305
+ towerAttributes[towerNumber].hvsBatteryTempperCell[i + 1] = byteArray[i + 103];
1306
+ }
1307
+ }
1308
+
1309
+ decodePacket8(data, towerNumber = 0) {
1310
+ const byteArray = new Uint8Array(data);
1311
+ let MaxTemps = hvsNumTemps - 30; //0 to n-1 is the same like 1 to n
1312
+ if (MaxTemps > 34) {
1313
+ MaxTemps = 34;
1314
+ }
1315
+ this.log.silly(`hvsModules =${hvsModules} MaxTemps= ${MaxTemps}`);
1316
+ for (let i = 0; i < MaxTemps; i++) {
1317
+ this.log.silly(`Battery Temp ${this.pad(i + 31, 3)} :${byteArray[i + 5]}`);
1318
+ towerAttributes[towerNumber].hvsBatteryTempperCell[i + 31] = byteArray[i + 5];
1319
+ }
1320
+ }
1321
+
1322
+ /*
1323
+ * decode response to request[12]
1324
+ * @see #decodePacket5()
1325
+ */
1326
+ decodeResponse12(data, towerNumber = 0) {
1327
+ const byteArray = new Uint8Array(data);
1328
+ //starting with byte 101, ending with 131, Cell voltage 129-144
1329
+
1330
+ // Balancing Flags
1331
+ towerAttributes[towerNumber].balancing = data.slice(17, 33).toString('hex');
1332
+ towerAttributes[towerNumber].balancingcount = this.countSetBits(data.slice(17, 33).toString('hex'));
1333
+
1334
+ const MaxCells = 16;
1335
+ for (let i = 0; i < MaxCells; i++) {
1336
+ this.log.silly(`Battery Voltage-${this.pad(i + 1 + 128, 3)} :${this.buf2int16SI(byteArray, i * 2 + 101)}`);
1337
+ towerAttributes[towerNumber].hvsBatteryVoltsperCell[i + 1 + 128] = this.buf2int16SI(byteArray, i * 2 + 101);
1338
+ }
1339
+ }
1340
+
1341
+ /*
1342
+ * decode response to request[13]
1343
+ * @see #decodePacket6()
1344
+ */
1345
+ decodeResponse13(data, towerNumber = 0) {
1346
+ const byteArray = new Uint8Array(data);
1347
+ let MaxCells = hvsNumCells - 128 - 16; // The first round measured up to 128 cells, request[12] then get another 16
1348
+ if (MaxCells > 16) {
1349
+ MaxCells = 16;
1350
+ } // With 5 HVS Modules, only 16 cells are remaining
1351
+ for (let i = 0; i < MaxCells; i++) {
1352
+ this.log.silly(
1353
+ `Battery Voltage-${this.pad(i + 1 + 16 + 128, 3)} :${this.buf2int16SI(byteArray, i * 2 + 5)}`,
1354
+ );
1355
+ towerAttributes[towerNumber].hvsBatteryVoltsperCell[i + 1 + 16 + 128] = this.buf2int16SI(
1356
+ byteArray,
1357
+ i * 2 + 5,
1358
+ );
1359
+ }
1360
+ }
1361
+
1362
+ decodePacket0(data) {
1363
+ if (this.config.ConfStoreRawMessages) {
1364
+ this.setState('System.Raw_00', data.toString('hex'), true);
1365
+ }
1366
+ const byteArray = new Uint8Array(data);
1367
+
1368
+ // Serialnumber
1369
+ hvsSerial = '';
1370
+ for (let i = 3; i < 22; i++) {
1371
+ hvsSerial += String.fromCharCode(byteArray[i]);
1372
+ }
1373
+
1374
+ // Hardwaretype
1375
+ //leider dazugestrickt, wollte in die andere Logik nicht eingreifen
1376
+ if (byteArray[5] == 51) {
1377
+ hvsBattType_fromSerial = 'HVS';
1378
+ }
1379
+ if (byteArray[5] == 50) {
1380
+ hvsBattType_fromSerial = 'LVS';
1381
+ }
1382
+ if (byteArray[5] == 49) {
1383
+ hvsBattType_fromSerial = 'LVS';
1384
+ }
1385
+
1386
+ // Firmwareversion
1387
+ hvsBMUA = `V${byteArray[27].toString()}.${byteArray[28].toString()}`;
1388
+ hvsBMUB = `V${byteArray[29].toString()}.${byteArray[30].toString()}`;
1389
+ if (byteArray[33] === 0) {
1390
+ hvsBMU = `${hvsBMUA}-A`;
1391
+ } else {
1392
+ hvsBMU = `${hvsBMUB}-B`;
1393
+ }
1394
+ hvsBMS = `V${byteArray[31].toString()}.${byteArray[32].toString()}-${String.fromCharCode(byteArray[34] + 65)}`;
1395
+
1396
+ // Amount of towers
1397
+ // 1st Byte - Count of towers
1398
+ // 2nd Byte - Amount of Modules (per Tower)
1399
+ hvsModules = parseInt((byteArray[36] % 16).toString());
1400
+ hvsTowers = parseInt(Math.floor(byteArray[36] / 16).toString());
1401
+
1402
+ // Architecture type
1403
+ if (byteArray[38] === 0) {
1404
+ hvsGrid = 'OffGrid';
1405
+ }
1406
+ if (byteArray[38] === 1) {
1407
+ hvsGrid = 'OnGrid';
1408
+ }
1409
+ if (byteArray[38] === 2) {
1410
+ hvsGrid = 'Backup';
1411
+ }
1412
+ }
1413
+
1414
+ decodePacket1(data) {
1415
+ if (this.config.ConfStoreRawMessages) {
1416
+ this.setState('System.Raw_01', data.toString('hex'), true);
1417
+ }
1418
+ const byteArray = new Uint8Array(data);
1419
+ hvsSOC = this.buf2int16SI(byteArray, 3);
1420
+ hvsMaxVolt = parseFloat(((this.buf2int16SI(byteArray, 5) * 1.0) / 100.0).toFixed(2));
1421
+ hvsMinVolt = parseFloat(((this.buf2int16SI(byteArray, 7) * 1.0) / 100.0).toFixed(2));
1422
+ hvsSOH = this.buf2int16SI(byteArray, 9);
1423
+ hvsA = parseFloat(((this.buf2int16SI(byteArray, 11) * 1.0) / 10.0).toFixed(1));
1424
+ hvsBattVolt = parseFloat(((this.buf2int16US(byteArray, 13) * 1.0) / 100.0).toFixed(1));
1425
+ hvsMaxTemp = this.buf2int16SI(byteArray, 15);
1426
+ hvsMinTemp = this.buf2int16SI(byteArray, 17);
1427
+ hvsBatTemp = this.buf2int16SI(byteArray, 19);
1428
+ hvsError = this.buf2int16SI(byteArray, 29);
1429
+ hvsParamT = `${byteArray[31].toString()}.${byteArray[32].toString()}`;
1430
+ hvsOutVolt = parseFloat(((this.buf2int16US(byteArray, 35) * 1.0) / 100.0).toFixed(1));
1431
+ hvsPower = Math.round(hvsA * hvsOutVolt * 100) / 100;
1432
+ hvsDiffVolt = Math.round((hvsMaxVolt - hvsMinVolt) * 100) / 100;
1433
+ hvsErrorString = '';
1434
+ // hvsError = 65535;
1435
+ for (let j = 0; j < 16; j++) {
1436
+ if (((1 << j) & hvsError) !== 0) {
1437
+ if (hvsErrorString.length > 0) {
1438
+ hvsErrorString += '; ';
1439
+ }
1440
+ hvsErrorString += this.myErrors[j];
1441
+ }
1442
+ }
1443
+ if (hvsErrorString.length === 0) {
1444
+ hvsErrorString = 'no Error';
1445
+ }
1446
+
1447
+ hvsChargeTotal = this.buf2int32US(byteArray, 37) / 10;
1448
+ hvsDischargeTotal = this.buf2int32US(byteArray, 41) / 10;
1449
+ hvsETA = hvsDischargeTotal / hvsChargeTotal;
1450
+ }
1451
+ }
1452
+
1453
+ if (module.parent) {
1454
+ /**
1455
+ * @param [options]
1456
+ */
1457
+ module.exports = options => new bydhvsControll(options);
1458
+ } else {
1459
+ // otherwise start the instance directly
1460
+ new bydhvsControll();
1461
+ }