@atelieruldetehnologii/node-red-custome-solutions 0.62.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/NOTICE +56 -0
  2. package/PROPRIETARY-LICENSE.txt +157 -0
  3. package/README.md +105 -0
  4. package/TERMS-AND-CONDITIONS.md +311 -0
  5. package/advantech-read/advantech-read.html +775 -0
  6. package/advantech-read/advantech-read.js +449 -0
  7. package/advantech-write/advantech-write.html +413 -0
  8. package/advantech-write/advantech-write.js +338 -0
  9. package/bess-control/bess-control.html +569 -0
  10. package/bess-control/bess-control.js +541 -0
  11. package/bess-control/lib/allocation.js +149 -0
  12. package/bess-control/lib/batteries.js +86 -0
  13. package/bess-control/lib/helpers.js +157 -0
  14. package/bess-control/lib/messages.js +131 -0
  15. package/bess-control/lib/runtime.js +156 -0
  16. package/carlo-gavazzi/carlo-gavazzi.html +4327 -0
  17. package/carlo-gavazzi/carlo-gavazzi.js +3081 -0
  18. package/control-export/control-export.html +3644 -0
  19. package/control-export/control-export.js +2192 -0
  20. package/control-reactive/control-reactive.html +3136 -0
  21. package/control-reactive/control-reactive.js +2124 -0
  22. package/fronius-emulator/README.md +258 -0
  23. package/fronius-emulator/fronius-emulator.html +170 -0
  24. package/fronius-emulator/fronius-emulator.js +520 -0
  25. package/fronius-emulator/lib/binary-codec.js +65 -0
  26. package/fronius-emulator/lib/fronius-register-map.js +185 -0
  27. package/fronius-emulator/lib/modbus-parser.js +61 -0
  28. package/fronius-emulator/lib/modbus-tcp-server.js +224 -0
  29. package/fronius-emulator/lib/validation.js +206 -0
  30. package/fronius-emulator/lib/virtual-meter.js +132 -0
  31. package/fronius-read/fronius-read.html +3944 -0
  32. package/fronius-read/fronius-read.js +1466 -0
  33. package/fronius-read/lib/inverter-profiles.js +711 -0
  34. package/fronius-read/lib/sunspec-core.js +303 -0
  35. package/fronius-read/lib/sunspec-model-parsers.js +845 -0
  36. package/fronius-write/fronius-write.html +1789 -0
  37. package/fronius-write/fronius-write.js +2531 -0
  38. package/influxdb/influxdb.html +1077 -0
  39. package/influxdb/influxdb.js +1487 -0
  40. package/janitza/janitza.html +2562 -0
  41. package/janitza/janitza.js +1500 -0
  42. package/janitza/lib/janitza-profiles.js +371 -0
  43. package/lib/a3-test-server.js +199 -0
  44. package/lib/adam-6000-profiles.js +173 -0
  45. package/lib/licensing/constants.js +103 -0
  46. package/lib/licensing/device-id.js +251 -0
  47. package/lib/licensing/errors.js +21 -0
  48. package/lib/licensing/license-client.js +377 -0
  49. package/lib/licensing/license-state.js +50 -0
  50. package/lib/licensing/node-guard.js +150 -0
  51. package/lib/licensing/package-integrity.js +167 -0
  52. package/lib/licensing/package-integrity.json +513 -0
  53. package/lib/licensing/public-key.pem +3 -0
  54. package/lib/licensing/public-key.pem.example +2 -0
  55. package/lib/licensing/public-keys.js +18 -0
  56. package/lib/licensing/storage.js +63 -0
  57. package/lib/licensing/token-verifier.js +84 -0
  58. package/lib/numbers.js +26 -0
  59. package/license/atelier-license.html +88 -0
  60. package/license/atelier-license.js +170 -0
  61. package/modbus-tcp-discover/modbus-tcp-discover.html +190 -0
  62. package/modbus-tcp-discover/modbus-tcp-discover.js +706 -0
  63. package/multi-battery/multi-battery.html +649 -0
  64. package/multi-battery/multi-battery.js +668 -0
  65. package/multi-grid/multi-grid.html +745 -0
  66. package/multi-grid/multi-grid.js +1392 -0
  67. package/multi-inverter/lib/engine-core.js +237 -0
  68. package/multi-inverter/multi-inverter.html +2674 -0
  69. package/multi-inverter/multi-inverter.js +1393 -0
  70. package/package.json +109 -0
  71. package/phoenix-contact/lib/eem-ma370-advanced.js +175 -0
  72. package/phoenix-contact/lib/eem-ma370.js +138 -0
  73. package/phoenix-contact/lib/integrations.js +149 -0
  74. package/phoenix-contact/lib/phoenix-contact-profile.js +35030 -0
  75. package/phoenix-contact/phoenix-contact.html +840 -0
  76. package/phoenix-contact/phoenix-contact.js +679 -0
  77. package/pv-generator/pv-generator.html +284 -0
  78. package/pv-generator/pv-generator.js +387 -0
  79. package/pylontech-read/pylontech-read.html +763 -0
  80. package/pylontech-read/pylontech-read.js +518 -0
  81. package/pylontech-write/pylontech-write.html +405 -0
  82. package/pylontech-write/pylontech-write.js +546 -0
  83. package/scada-aggregator/lib/aggregate.js +72 -0
  84. package/scada-aggregator/lib/groups.js +39 -0
  85. package/scada-aggregator/scada-aggregator.html +108 -0
  86. package/scada-aggregator/scada-aggregator.js +117 -0
  87. package/scada-server/lib/aggregator-map.js +58 -0
  88. package/scada-server/lib/encoding.js +235 -0
  89. package/scada-server/lib/modbus-server.js +492 -0
  90. package/scada-server/lib/object-path.js +18 -0
  91. package/scada-server/lib/register-map.js +288 -0
  92. package/scada-server/scada-server.html +1419 -0
  93. package/scada-server/scada-server.js +704 -0
  94. package/scripts/check-syntax.js +66 -0
  95. package/scripts/package-integrity.js +46 -0
  96. package/scripts/package-release.js +116 -0
  97. package/scripts/run-tests.js +2178 -0
  98. package/scripts/tgz-integrity.js +74 -0
  99. package/sma-read/lib/sunspec-core.js +83 -0
  100. package/sma-read/lib/sunspec-model-parsers.js +853 -0
  101. package/sma-read/sma-read.html +659 -0
  102. package/sma-read/sma-read.js +458 -0
  103. package/sma-write/sma-write.html +105 -0
  104. package/sma-write/sma-write.js +270 -0
  105. package/victron-read/lib/mqtt-state.js +176 -0
  106. package/victron-read/victron-read.html +1074 -0
  107. package/victron-read/victron-read.js +1222 -0
@@ -0,0 +1,845 @@
1
+ function toSigned16(v) {
2
+
3
+ return v > 32767 ? v - 65536 : v;
4
+
5
+ }
6
+
7
+
8
+ function round2(v) {
9
+
10
+ if (v === null || v === undefined || Number.isNaN(v)) return null;
11
+
12
+ return Math.round(v * 100) / 100;
13
+
14
+ }
15
+
16
+
17
+ function scaleValue(raw, sf) {
18
+
19
+ if (raw === null || raw === undefined || sf === null || sf === undefined) return null;
20
+
21
+ if (sf === -32768) return null;
22
+
23
+ return raw * Math.pow(10, sf);
24
+
25
+ }
26
+
27
+
28
+ function decodeSunSpecString(registers) {
29
+
30
+ let out = "";
31
+
32
+
33
+ for (const reg of registers) {
34
+
35
+ out += String.fromCharCode((reg >> 8) & 0xff);
36
+
37
+ out += String.fromCharCode(reg & 0xff);
38
+
39
+ }
40
+
41
+
42
+ return out.replace(/\0/g, "").trim();
43
+
44
+ }
45
+
46
+
47
+ function safeGet(arr, idx, def = null) {
48
+
49
+ return idx >= 0 && idx < arr.length ? arr[idx] : def;
50
+
51
+ }
52
+
53
+
54
+ function u16ToFloat(regHi, regLo) {
55
+
56
+ const buf = Buffer.allocUnsafe(4);
57
+
58
+ buf.writeUInt16BE(regHi >>> 0, 0);
59
+
60
+ buf.writeUInt16BE(regLo >>> 0, 2);
61
+
62
+ return buf.readFloatBE(0);
63
+
64
+ }
65
+
66
+ function u32(hi, lo) {
67
+
68
+ return (((hi || 0) << 16) | (lo || 0)) >>> 0;
69
+
70
+ }
71
+
72
+
73
+ function readScaledS16(d, index, sf) {
74
+
75
+ const raw = safeGet(d, index, null);
76
+
77
+ if (raw === null || raw === 0x8000 || raw === 0xFFFF) return null;
78
+
79
+ return round2(scaleValue(toSigned16(raw), sf));
80
+
81
+ }
82
+
83
+
84
+ function readScaledU32(d, index, sf) {
85
+
86
+ if (index + 1 >= d.length) return null;
87
+
88
+ const raw = u32(safeGet(d, index, 0), safeGet(d, index + 1, 0));
89
+
90
+ if (raw === 0xFFFFFFFF) return null;
91
+
92
+ return round2(scaleValue(raw, sf));
93
+
94
+ }
95
+
96
+
97
+ function readFloat32(d, index) {
98
+
99
+ if (index + 1 >= d.length) return null;
100
+
101
+ const hi = safeGet(d, index, null);
102
+
103
+ const lo = safeGet(d, index + 1, null);
104
+
105
+ if (hi === null || lo === null || (hi === 0xFFFF && lo === 0xFFFF)) return null;
106
+
107
+ const value = u16ToFloat(hi, lo);
108
+
109
+ return Number.isFinite(value) ? round2(value) : null;
110
+
111
+ }
112
+
113
+
114
+ function parseCommon(model) {
115
+
116
+ const d = model.raw;
117
+
118
+
119
+ return {
120
+
121
+ manufacturer: decodeSunSpecString(d.slice(0, 16)),
122
+
123
+ model: decodeSunSpecString(d.slice(16, 32)),
124
+
125
+ options: decodeSunSpecString(d.slice(32, 40)),
126
+
127
+ version: decodeSunSpecString(d.slice(40, 48)),
128
+
129
+ serial: decodeSunSpecString(d.slice(48, 64)),
130
+
131
+ device_address: safeGet(d, 64, null)
132
+
133
+ };
134
+
135
+ }
136
+
137
+
138
+ function parseInverterInt(model) {
139
+
140
+ const d = model.raw;
141
+
142
+
143
+ const A_SF = toSigned16(safeGet(d, 4, 0));
144
+
145
+ const V_SF = toSigned16(safeGet(d, 11, 0));
146
+
147
+ const W_SF = toSigned16(safeGet(d, 13, 0));
148
+
149
+ const HZ_SF = toSigned16(safeGet(d, 15, 0));
150
+
151
+ const VA_SF = toSigned16(safeGet(d, 17, 0));
152
+
153
+ const VAR_SF = toSigned16(safeGet(d, 19, 0));
154
+
155
+
156
+ return {
157
+
158
+ model_id: model.model_id,
159
+
160
+
161
+ current_total_a: round2(scaleValue(safeGet(d, 0, 0), A_SF)),
162
+
163
+ current_l1_a: round2(scaleValue(toSigned16(safeGet(d, 1, 0)), A_SF)),
164
+
165
+ current_l2_a: round2(scaleValue(toSigned16(safeGet(d, 2, 0)), A_SF)),
166
+
167
+ current_l3_a: round2(scaleValue(toSigned16(safeGet(d, 3, 0)), A_SF)),
168
+
169
+
170
+ voltage_ll_ab_v: round2(scaleValue(toSigned16(safeGet(d, 5, 0)), V_SF)),
171
+
172
+ voltage_ll_bc_v: round2(scaleValue(toSigned16(safeGet(d, 6, 0)), V_SF)),
173
+
174
+ voltage_ll_ca_v: round2(scaleValue(toSigned16(safeGet(d, 7, 0)), V_SF)),
175
+
176
+
177
+ voltage_l1_v: round2(scaleValue(toSigned16(safeGet(d, 8, 0)), V_SF)),
178
+
179
+ voltage_l2_v: round2(scaleValue(toSigned16(safeGet(d, 9, 0)), V_SF)),
180
+
181
+ voltage_l3_v: round2(scaleValue(toSigned16(safeGet(d, 10, 0)), V_SF)),
182
+
183
+
184
+ power_w: round2(scaleValue(toSigned16(safeGet(d, 12, 0)), W_SF)),
185
+
186
+ frequency_hz: round2(scaleValue(toSigned16(safeGet(d, 14, 0)), HZ_SF)),
187
+
188
+ apparent_power_va: round2(scaleValue(toSigned16(safeGet(d, 16, 0)), VA_SF)),
189
+
190
+ reactive_power_var: round2(scaleValue(toSigned16(safeGet(d, 18, 0)), VAR_SF)),
191
+
192
+
193
+ raw_sf: {
194
+
195
+ A_SF,
196
+
197
+ V_SF,
198
+
199
+ W_SF,
200
+
201
+ HZ_SF,
202
+
203
+ VA_SF,
204
+
205
+ VAR_SF
206
+
207
+ }
208
+
209
+ };
210
+
211
+ }
212
+
213
+
214
+ function parseInverterFloat(model) {
215
+
216
+ const d = model.raw;
217
+
218
+
219
+ function f(idx) {
220
+
221
+ if (idx + 1 >= d.length) return null;
222
+
223
+ return round2(u16ToFloat(d[idx], d[idx + 1]));
224
+
225
+ }
226
+
227
+
228
+ return {
229
+
230
+ model_id: model.model_id,
231
+
232
+
233
+ current_total_a: f(0),
234
+
235
+ current_l1_a: f(2),
236
+
237
+ current_l2_a: f(4),
238
+
239
+ current_l3_a: f(6),
240
+
241
+
242
+ voltage_ll_ab_v: f(8),
243
+
244
+ voltage_ll_bc_v: f(10),
245
+
246
+ voltage_ll_ca_v: f(12),
247
+
248
+
249
+ voltage_l1_v: f(14),
250
+
251
+ voltage_l2_v: f(16),
252
+
253
+ voltage_l3_v: f(18),
254
+
255
+
256
+ power_w: f(20),
257
+
258
+ frequency_hz: f(22),
259
+
260
+ apparent_power_va: f(24),
261
+
262
+ reactive_power_var: f(26)
263
+
264
+ };
265
+
266
+ }
267
+
268
+
269
+ function parseMeterInt(model) {
270
+
271
+ const d = model.raw;
272
+
273
+ const A_SF = toSigned16(safeGet(d, 4, 0));
274
+
275
+ const V_SF = toSigned16(safeGet(d, 13, 0));
276
+
277
+ const HZ_SF = toSigned16(safeGet(d, 15, 0));
278
+
279
+ const W_SF = toSigned16(safeGet(d, 20, 0));
280
+
281
+ const VA_SF = toSigned16(safeGet(d, 25, 0));
282
+
283
+ const VAR_SF = toSigned16(safeGet(d, 30, 0));
284
+
285
+ const PF_SF = toSigned16(safeGet(d, 35, 0));
286
+
287
+ const WH_SF = toSigned16(safeGet(d, 52, 0));
288
+
289
+ const VAH_SF = toSigned16(safeGet(d, 69, 0));
290
+
291
+ const VARH_SF = toSigned16(safeGet(d, 102, 0));
292
+
293
+
294
+ return {
295
+
296
+ model_id: model.model_id,
297
+
298
+ meter_model: getMeterModelName(model.model_id),
299
+
300
+ current_total_a: readScaledS16(d, 0, A_SF),
301
+
302
+ current_l1_a: readScaledS16(d, 1, A_SF),
303
+
304
+ current_l2_a: readScaledS16(d, 2, A_SF),
305
+
306
+ current_l3_a: readScaledS16(d, 3, A_SF),
307
+
308
+ voltage_ln_avg_v: readScaledS16(d, 5, V_SF),
309
+
310
+ voltage_l1_v: readScaledS16(d, 6, V_SF),
311
+
312
+ voltage_l2_v: readScaledS16(d, 7, V_SF),
313
+
314
+ voltage_l3_v: readScaledS16(d, 8, V_SF),
315
+
316
+ voltage_ll_avg_v: readScaledS16(d, 9, V_SF),
317
+
318
+ voltage_ll_ab_v: readScaledS16(d, 10, V_SF),
319
+
320
+ voltage_ll_bc_v: readScaledS16(d, 11, V_SF),
321
+
322
+ voltage_ll_ca_v: readScaledS16(d, 12, V_SF),
323
+
324
+ frequency_hz: readScaledS16(d, 14, HZ_SF),
325
+
326
+ power_w: readScaledS16(d, 16, W_SF),
327
+
328
+ power_l1_w: readScaledS16(d, 17, W_SF),
329
+
330
+ power_l2_w: readScaledS16(d, 18, W_SF),
331
+
332
+ power_l3_w: readScaledS16(d, 19, W_SF),
333
+
334
+ apparent_power_va: readScaledS16(d, 21, VA_SF),
335
+
336
+ apparent_power_l1_va: readScaledS16(d, 22, VA_SF),
337
+
338
+ apparent_power_l2_va: readScaledS16(d, 23, VA_SF),
339
+
340
+ apparent_power_l3_va: readScaledS16(d, 24, VA_SF),
341
+
342
+ reactive_power_var: readScaledS16(d, 26, VAR_SF),
343
+
344
+ reactive_power_l1_var: readScaledS16(d, 27, VAR_SF),
345
+
346
+ reactive_power_l2_var: readScaledS16(d, 28, VAR_SF),
347
+
348
+ reactive_power_l3_var: readScaledS16(d, 29, VAR_SF),
349
+
350
+ power_factor: readScaledS16(d, 31, PF_SF),
351
+
352
+ power_factor_l1: readScaledS16(d, 32, PF_SF),
353
+
354
+ power_factor_l2: readScaledS16(d, 33, PF_SF),
355
+
356
+ power_factor_l3: readScaledS16(d, 34, PF_SF),
357
+
358
+ energy_export_wh: readScaledU32(d, 36, WH_SF),
359
+
360
+ energy_export_l1_wh: readScaledU32(d, 38, WH_SF),
361
+
362
+ energy_export_l2_wh: readScaledU32(d, 40, WH_SF),
363
+
364
+ energy_export_l3_wh: readScaledU32(d, 42, WH_SF),
365
+
366
+ energy_import_wh: readScaledU32(d, 44, WH_SF),
367
+
368
+ energy_import_l1_wh: readScaledU32(d, 46, WH_SF),
369
+
370
+ energy_import_l2_wh: readScaledU32(d, 48, WH_SF),
371
+
372
+ energy_import_l3_wh: readScaledU32(d, 50, WH_SF),
373
+
374
+ apparent_energy_export_vah: readScaledU32(d, 53, VAH_SF),
375
+
376
+ apparent_energy_import_vah: readScaledU32(d, 61, VAH_SF),
377
+
378
+ reactive_energy_import_q1_varh: readScaledU32(d, 70, VARH_SF),
379
+
380
+ reactive_energy_import_q2_varh: readScaledU32(d, 78, VARH_SF),
381
+
382
+ reactive_energy_export_q3_varh: readScaledU32(d, 86, VARH_SF),
383
+
384
+ reactive_energy_export_q4_varh: readScaledU32(d, 94, VARH_SF),
385
+
386
+ event_flags: u32(safeGet(d, 103, 0), safeGet(d, 104, 0)),
387
+
388
+ raw_sf: {
389
+
390
+ A_SF,
391
+
392
+ V_SF,
393
+
394
+ HZ_SF,
395
+
396
+ W_SF,
397
+
398
+ VA_SF,
399
+
400
+ VAR_SF,
401
+
402
+ PF_SF,
403
+
404
+ WH_SF,
405
+
406
+ VAH_SF,
407
+
408
+ VARH_SF
409
+
410
+ }
411
+
412
+ };
413
+
414
+ }
415
+
416
+
417
+ function parseMeterFloat(model) {
418
+
419
+ const d = model.raw;
420
+
421
+ return {
422
+
423
+ model_id: model.model_id,
424
+
425
+ meter_model: getMeterModelName(model.model_id),
426
+
427
+ current_total_a: readFloat32(d, 0),
428
+
429
+ current_l1_a: readFloat32(d, 2),
430
+
431
+ current_l2_a: readFloat32(d, 4),
432
+
433
+ current_l3_a: readFloat32(d, 6),
434
+
435
+ voltage_ln_avg_v: readFloat32(d, 8),
436
+
437
+ voltage_l1_v: readFloat32(d, 10),
438
+
439
+ voltage_l2_v: readFloat32(d, 12),
440
+
441
+ voltage_l3_v: readFloat32(d, 14),
442
+
443
+ voltage_ll_avg_v: readFloat32(d, 16),
444
+
445
+ voltage_ll_ab_v: readFloat32(d, 18),
446
+
447
+ voltage_ll_bc_v: readFloat32(d, 20),
448
+
449
+ voltage_ll_ca_v: readFloat32(d, 22),
450
+
451
+ frequency_hz: readFloat32(d, 24),
452
+
453
+ power_w: readFloat32(d, 26),
454
+
455
+ power_l1_w: readFloat32(d, 28),
456
+
457
+ power_l2_w: readFloat32(d, 30),
458
+
459
+ power_l3_w: readFloat32(d, 32),
460
+
461
+ apparent_power_va: readFloat32(d, 34),
462
+
463
+ apparent_power_l1_va: readFloat32(d, 36),
464
+
465
+ apparent_power_l2_va: readFloat32(d, 38),
466
+
467
+ apparent_power_l3_va: readFloat32(d, 40),
468
+
469
+ reactive_power_var: readFloat32(d, 42),
470
+
471
+ reactive_power_l1_var: readFloat32(d, 44),
472
+
473
+ reactive_power_l2_var: readFloat32(d, 46),
474
+
475
+ reactive_power_l3_var: readFloat32(d, 48),
476
+
477
+ power_factor: readFloat32(d, 50),
478
+
479
+ power_factor_l1: readFloat32(d, 52),
480
+
481
+ power_factor_l2: readFloat32(d, 54),
482
+
483
+ power_factor_l3: readFloat32(d, 56),
484
+
485
+ energy_export_wh: readFloat32(d, 58),
486
+
487
+ energy_export_l1_wh: readFloat32(d, 60),
488
+
489
+ energy_export_l2_wh: readFloat32(d, 62),
490
+
491
+ energy_export_l3_wh: readFloat32(d, 64),
492
+
493
+ energy_import_wh: readFloat32(d, 66),
494
+
495
+ energy_import_l1_wh: readFloat32(d, 68),
496
+
497
+ energy_import_l2_wh: readFloat32(d, 70),
498
+
499
+ energy_import_l3_wh: readFloat32(d, 72),
500
+
501
+ apparent_energy_export_vah: readFloat32(d, 74),
502
+
503
+ apparent_energy_import_vah: readFloat32(d, 82),
504
+
505
+ reactive_energy_import_q1_varh: readFloat32(d, 90),
506
+
507
+ reactive_energy_import_q2_varh: readFloat32(d, 98),
508
+
509
+ reactive_energy_export_q3_varh: readFloat32(d, 106),
510
+
511
+ reactive_energy_export_q4_varh: readFloat32(d, 114),
512
+
513
+ event_flags: u32(safeGet(d, 122, 0), safeGet(d, 123, 0))
514
+
515
+ };
516
+
517
+ }
518
+
519
+
520
+ function getMeterModelName(modelId) {
521
+
522
+ if (modelId === 201 || modelId === 211) return "single_phase";
523
+
524
+ if (modelId === 202 || modelId === 212) return "split_phase";
525
+
526
+ if (modelId === 203 || modelId === 213) return "three_phase_wye";
527
+
528
+ if (modelId === 204 || modelId === 214) return "three_phase_delta";
529
+
530
+ return "unknown";
531
+
532
+ }
533
+
534
+
535
+ function parseControlRaw(model) {
536
+
537
+ return {
538
+
539
+ model_id: model.model_id,
540
+
541
+ len: model.len,
542
+
543
+ raw: model.raw
544
+
545
+ };
546
+
547
+ }
548
+
549
+
550
+ function parseMppt160(model) {
551
+
552
+ const d = model.raw;
553
+
554
+
555
+ const DCA_SF = toSigned16(safeGet(d, 0, 0));
556
+
557
+ const DCV_SF = toSigned16(safeGet(d, 1, 0));
558
+
559
+ const DCW_SF = toSigned16(safeGet(d, 2, 0));
560
+
561
+ const DCWH_SF = toSigned16(safeGet(d, 3, 0));
562
+
563
+
564
+ const global_evt =
565
+
566
+ ((safeGet(d, 4, 0) << 16) | safeGet(d, 5, 0)) >>> 0;
567
+
568
+
569
+ const moduleCount = safeGet(d, 6, 0);
570
+
571
+ const tmsPer = safeGet(d, 7, null);
572
+
573
+
574
+ const blocks = [];
575
+
576
+ const blockSize = 20;
577
+
578
+ const start = 8;
579
+
580
+
581
+ function notImplU16(v) {
582
+
583
+ return v === 0xFFFF;
584
+
585
+ }
586
+
587
+
588
+ function notImplS16(v) {
589
+
590
+ return v === 0x8000 || v === 0xFFFF;
591
+
592
+ }
593
+
594
+
595
+ function u32(hi, lo) {
596
+
597
+ return (((hi || 0) << 16) | (lo || 0)) >>> 0;
598
+
599
+ }
600
+
601
+
602
+ for (let i = 0; i < moduleCount; i++) {
603
+
604
+ const base = start + i * blockSize;
605
+
606
+ const block = d.slice(base, base + blockSize);
607
+
608
+
609
+ if (block.length < blockSize) {
610
+
611
+ break;
612
+
613
+ }
614
+
615
+
616
+ const mpptId = safeGet(block, 0, null);
617
+
618
+ const name = decodeSunSpecString(block.slice(1, 9)) || null;
619
+
620
+
621
+ const dcaRaw = safeGet(block, 9, null);
622
+
623
+ const dcvRaw = safeGet(block, 10, null);
624
+
625
+ const dcwRaw = safeGet(block, 11, null);
626
+
627
+
628
+ const dcwhRaw = u32(safeGet(block, 12, 0), safeGet(block, 13, 0));
629
+
630
+ const tmsRaw = u32(safeGet(block, 14, 0), safeGet(block, 15, 0));
631
+
632
+
633
+ const tmpRaw = safeGet(block, 16, null);
634
+
635
+ const dcStRaw = safeGet(block, 17, null);
636
+
637
+ const dcEvtRaw = u32(safeGet(block, 18, 0), safeGet(block, 19, 0));
638
+
639
+
640
+ const entry = {
641
+
642
+ mppt_id: mpptId,
643
+
644
+ name: name,
645
+
646
+ dc_current_a: notImplU16(dcaRaw) ? null : round2(scaleValue(dcaRaw, DCA_SF)),
647
+
648
+ dc_voltage_v: notImplU16(dcvRaw) ? null : round2(scaleValue(dcvRaw, DCV_SF)),
649
+
650
+ dc_power_w: notImplU16(dcwRaw) ? null : round2(scaleValue(dcwRaw, DCW_SF)),
651
+
652
+ dc_energy_wh: dcwhRaw === 0xFFFFFFFF ? null : round2(scaleValue(dcwhRaw, DCWH_SF)),
653
+
654
+ timestamp_s: tmsRaw === 0xFFFFFFFF ? null : tmsRaw,
655
+
656
+ temperature_c: notImplS16(tmpRaw) ? null : tmpRaw,
657
+
658
+ dc_state: dcStRaw === 0xFFFF ? null : dcStRaw,
659
+
660
+ dc_event: dcEvtRaw === 0xFFFFFFFF ? null : dcEvtRaw,
661
+
662
+ raw: block
663
+
664
+ };
665
+
666
+
667
+ blocks.push(entry);
668
+
669
+ }
670
+
671
+
672
+ const dcTotalPower = round2(
673
+
674
+ blocks.reduce((sum, b) => sum + (b.dc_power_w || 0), 0)
675
+
676
+ );
677
+
678
+
679
+ return {
680
+
681
+ model_id: model.model_id,
682
+
683
+ module_count: moduleCount,
684
+
685
+ tms_per: tmsPer === 0xFFFF ? null : tmsPer,
686
+
687
+ global_event: global_evt === 0xFFFFFFFF ? null : global_evt,
688
+
689
+ scale_factors: {
690
+
691
+ DCA_SF,
692
+
693
+ DCV_SF,
694
+
695
+ DCW_SF,
696
+
697
+ DCWH_SF
698
+
699
+ },
700
+
701
+ mppts: blocks,
702
+
703
+ dc_total_power_w: dcTotalPower
704
+
705
+ };
706
+
707
+ }
708
+
709
+
710
+ const MODEL_PARSERS = {
711
+
712
+ 1: parseCommon,
713
+
714
+ 101: parseInverterInt,
715
+
716
+ 102: parseInverterInt,
717
+
718
+ 103: parseInverterInt,
719
+
720
+ 111: parseInverterFloat,
721
+
722
+ 112: parseInverterFloat,
723
+
724
+ 113: parseInverterFloat,
725
+
726
+ 120: parseControlRaw,
727
+
728
+ 121: parseControlRaw,
729
+
730
+ 122: parseControlRaw,
731
+
732
+ 123: parseControlRaw,
733
+
734
+ 160: parseMppt160,
735
+
736
+ 201: parseMeterInt,
737
+
738
+ 202: parseMeterInt,
739
+
740
+ 203: parseMeterInt,
741
+
742
+ 204: parseMeterInt,
743
+
744
+ 211: parseMeterFloat,
745
+
746
+ 212: parseMeterFloat,
747
+
748
+ 213: parseMeterFloat,
749
+
750
+ 214: parseMeterFloat
751
+
752
+ };
753
+
754
+
755
+ function buildSummary(models) {
756
+
757
+ const summary = {
758
+
759
+ common: null,
760
+
761
+ ac: null,
762
+
763
+ dc: null,
764
+
765
+ meter: null,
766
+
767
+ controls: [],
768
+
769
+ unknown_models: []
770
+
771
+ };
772
+
773
+
774
+ for (const model of models) {
775
+
776
+ const parser = MODEL_PARSERS[model.model_id];
777
+
778
+
779
+ if (!parser) {
780
+
781
+ summary.unknown_models.push({
782
+
783
+ model_id: model.model_id,
784
+
785
+ addr: model.addr,
786
+
787
+ len: model.len
788
+
789
+ });
790
+
791
+ continue;
792
+
793
+ }
794
+
795
+
796
+ const parsed = parser(model);
797
+
798
+
799
+ if (model.model_id === 1) {
800
+
801
+ summary.common = parsed;
802
+
803
+ } else if ([101, 102, 103, 111, 112, 113].includes(model.model_id)) {
804
+
805
+ summary.ac = parsed;
806
+
807
+ } else if ([120, 121, 122, 123].includes(model.model_id)) {
808
+
809
+ summary.controls.push(parsed);
810
+
811
+ } else if (model.model_id === 160) {
812
+
813
+ summary.dc = parsed;
814
+
815
+ } else if ([201, 202, 203, 204, 211, 212, 213, 214].includes(model.model_id)) {
816
+
817
+ summary.meter = parsed;
818
+
819
+ }
820
+
821
+ }
822
+
823
+
824
+ return summary;
825
+
826
+ }
827
+
828
+
829
+ module.exports = {
830
+
831
+ MODEL_PARSERS,
832
+
833
+ buildSummary,
834
+
835
+ toSigned16,
836
+
837
+ round2,
838
+
839
+ scaleValue,
840
+
841
+ decodeSunSpecString,
842
+
843
+ safeGet
844
+
845
+ };