iobroker.sigenergy 2.2.3 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -25
- package/io-package.json +13 -13
- package/lib/modbus.js +20 -0
- package/lib/registers.js +34 -24
- package/main.js +256 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -153,6 +153,14 @@ Status and power readings for the DC charger.
|
|
|
153
153
|
|
|
154
154
|
## Changelog
|
|
155
155
|
|
|
156
|
+
### 2.2.6 (2026-06-10)
|
|
157
|
+
- (ssbingo) feat: V2.9 register audit — add missing register 30279 (current control command value), move DC Charger PV registers 31509/31511 to dcCharger namespace, fix ESS Preheating TOU time gain (null→1)
|
|
158
|
+
- (ssbingo) feat: implement control write-back for plant.control.*, plant.gridCode.*, inverter.control.*, dcCharger.control.* (FC06/FC10); read RW holding registers on startup
|
|
159
|
+
- (ssbingo) fix: suppress repeated ESS Preheating warn after device reports unsupported registers; downgrade control register startup read errors to debug
|
|
160
|
+
|
|
161
|
+
### 2.2.4 (2026-06-10)
|
|
162
|
+
- (ssbingo) fix: implement ESS Preheating TOU polling (FC03, 50000–50183, 94 registers) and write-back via onStateChange; add encodeValue to ModbusConnection
|
|
163
|
+
|
|
156
164
|
### 2.2.3 (2026-06-10)
|
|
157
165
|
- (ssbingo) fix: add 25 missing admin i18n keys for PSS, PID, ESS Preheating, Extended Registers across all 11 languages
|
|
158
166
|
|
|
@@ -215,31 +223,6 @@ Status and power readings for the DC charger.
|
|
|
215
223
|
- (ssbingo) feat: added calculated states plant.pv1Power, plant.pv2Power, plant.pv3Power
|
|
216
224
|
|
|
217
225
|
|
|
218
|
-
### 1.9.6 (2026-04-16)
|
|
219
|
-
- (ssbingo) feat: added calculated states plant.pv1Power, plant.pv2Power, plant.pv3Power
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
### 1.9.5 (2026-04-08)
|
|
223
|
-
- (ssbingo) fix: removed unused common.schedule from io-package.json
|
|
224
|
-
|
|
225
|
-
### 1.9.4 (2026-04-08)
|
|
226
|
-
- (ssbingo) fix: Changelog / adding CHANGELOG_OLD.md
|
|
227
|
-
|
|
228
|
-
### 1.9.3 (2026-04-08)
|
|
229
|
-
- (ssbingo) fix remove admin/index.html
|
|
230
|
-
|
|
231
|
-
### 1.9.2 (2026-04-08)
|
|
232
|
-
- (ssbingo) fixes
|
|
233
|
-
|
|
234
|
-
### 1.9.1 (2026-04-08)
|
|
235
|
-
- (ssbingo) Fixed admin UI: removed legacy index.html/index_m.html/words.js; fixed jsonData type in jsonConfig sendTo buttons
|
|
236
|
-
|
|
237
|
-
### 1.9.0 (2026-03-26)
|
|
238
|
-
- (ssbingo) Test abgeschlossen
|
|
239
|
-
|
|
240
|
-
### 1.8.23 (2026-03-26)
|
|
241
|
-
- (ssbingo) Fixed copyright year to 2026 in LICENSE and README; technical corrections: CI/CD workflow, linting, tests
|
|
242
|
-
|
|
243
226
|
---
|
|
244
227
|
|
|
245
228
|
[Older changelogs can be found there](CHANGELOG_OLD.md)
|
package/io-package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "sigenergy",
|
|
4
|
-
"version": "2.2.
|
|
4
|
+
"version": "2.2.6",
|
|
5
5
|
"news": {
|
|
6
|
-
"2.2.
|
|
7
|
-
"en": "(ssbingo)
|
|
8
|
-
"de": "(ssbingo)
|
|
9
|
-
"ru": "(ssbingo)
|
|
10
|
-
"pt": "(ssbingo)
|
|
11
|
-
"nl": "(ssbingo)
|
|
12
|
-
"fr": "(ssbingo)
|
|
13
|
-
"it": "(ssbingo)
|
|
14
|
-
"es": "(ssbingo)
|
|
15
|
-
"pl": "(ssbingo)
|
|
16
|
-
"uk": "(ssbingo)
|
|
17
|
-
"zh-cn": "(ssbingo)
|
|
6
|
+
"2.2.6": {
|
|
7
|
+
"en": "(ssbingo) feat: V2.9 register audit — add register 30279, move DC Charger PV regs 31509/31511 to dcCharger, implement control write-back (FC06/FC10); fix: suppress ESS Preheating log flood; downgrade control startup read errors to debug",
|
|
8
|
+
"de": "(ssbingo) feat: V2.9 Register-Audit — Register 30279 ergänzt, DC-Charger PV-Register 31509/31511 in dcCharger-Namespace; Steuerungs-Rückschreiben (FC06/FC10); fix: ESS-Vorheizungs-Warn-Flooding unterdrückt; Startup-Lesefehler auf Debug herabgestuft",
|
|
9
|
+
"ru": "(ssbingo) feat: аудит регистров V2.9 — регистр 30279, PV-регистры DC 31509/31511 в dcCharger, обратная запись управляющих регистров; fix: подавление повторяющихся предупреждений ESS; ошибки чтения при старте понижены до debug",
|
|
10
|
+
"pt": "(ssbingo) feat: auditoria V2.9 — registo 30279, registos PV DC Charger 31509/31511 para dcCharger, escrita de controlo (FC06/FC10); fix: avisos ESS Preheating suprimidos; erros de leitura ao arranque em debug",
|
|
11
|
+
"nl": "(ssbingo) feat: V2.9 register-audit — register 30279, PV-registers DC Charger 31509/31511 naar dcCharger, terugschrijven besturingsregisters; fix: ESS-voorverwarmingswaarschuwingen onderdrukt; opstartleesfouten naar debug",
|
|
12
|
+
"fr": "(ssbingo) feat: audit V2.9 — registre 30279, registres PV DC 31509/31511 vers dcCharger, rétro-écriture contrôle (FC06/FC10) ; fix: avertissements ESS Preheating supprimés ; erreurs lecture démarrage en debug",
|
|
13
|
+
"it": "(ssbingo) feat: audit V2.9 — registro 30279, registri PV DC 31509/31511 in dcCharger, scrittura controllo (FC06/FC10); fix: avvisi ESS Preheating soppressi; errori lettura avvio in debug",
|
|
14
|
+
"es": "(ssbingo) feat: auditoría V2.9 — registro 30279, registros PV DC 31509/31511 a dcCharger, escritura control (FC06/FC10); fix: avisos ESS Preheating suprimidos; errores lectura inicio en debug",
|
|
15
|
+
"pl": "(ssbingo) feat: audyt V2.9 — rejestr 30279, rejestry PV DC 31509/31511 do dcCharger, zapis sterujący (FC06/FC10); fix: ostrzeżenia ESS Preheating wyciszone; błędy odczytu przy starcie w debug",
|
|
16
|
+
"uk": "(ssbingo) feat: аудит V2.9 — регістр 30279, PV-регістри DC 31509/31511 у dcCharger, зворотний запис керуючих; fix: попередження ESS Preheating придушені; помилки читання при запуску знижені до debug",
|
|
17
|
+
"zh-cn": "(ssbingo) feat: V2.9 寄存器审计 — 寄存器 30279,DC 充电机 PV 31509/31511 至 dcCharger,控制寄存器回写;fix: 抑制 ESS 预热重复警告;控制启动读取错误降级为 debug"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"titleLang": {
|
package/lib/modbus.js
CHANGED
|
@@ -244,6 +244,26 @@ class ModbusConnection {
|
|
|
244
244
|
* @param {number|null} gain - Scaling factor
|
|
245
245
|
* @returns {number|string} Decoded value
|
|
246
246
|
*/
|
|
247
|
+
static encodeValue(value, type, gain) {
|
|
248
|
+
const raw = gain && gain !== 1 ? Math.round(value * gain) : Math.round(value);
|
|
249
|
+
switch (type) {
|
|
250
|
+
case 'U16':
|
|
251
|
+
return [raw & 0xffff];
|
|
252
|
+
case 'S16':
|
|
253
|
+
return [raw < 0 ? (raw + 65536) & 0xffff : raw & 0xffff];
|
|
254
|
+
case 'U32': {
|
|
255
|
+
const u = raw >>> 0;
|
|
256
|
+
return [(u >>> 16) & 0xffff, u & 0xffff];
|
|
257
|
+
}
|
|
258
|
+
case 'S32': {
|
|
259
|
+
const u32 = (raw < 0 ? raw + 4294967296 : raw) >>> 0;
|
|
260
|
+
return [(u32 >>> 16) & 0xffff, u32 & 0xffff];
|
|
261
|
+
}
|
|
262
|
+
default:
|
|
263
|
+
return [raw & 0xffff];
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
247
267
|
static parseValue(rawData, type, gain) {
|
|
248
268
|
let raw;
|
|
249
269
|
|
package/lib/registers.js
CHANGED
|
@@ -1294,6 +1294,16 @@ const PLANT_READ_REGISTERS = [
|
|
|
1294
1294
|
role: 'value.voltage',
|
|
1295
1295
|
desc: 'Grid code rated voltage (V2.9)',
|
|
1296
1296
|
},
|
|
1297
|
+
{
|
|
1298
|
+
addr: 30279,
|
|
1299
|
+
qty: 1,
|
|
1300
|
+
name: 'plant.currentCtrlCmdValue',
|
|
1301
|
+
type: 'U16',
|
|
1302
|
+
gain: 100,
|
|
1303
|
+
unit: '%',
|
|
1304
|
+
role: 'value',
|
|
1305
|
+
desc: 'Current control command value (V2.8)',
|
|
1306
|
+
},
|
|
1297
1307
|
{
|
|
1298
1308
|
addr: 30280,
|
|
1299
1309
|
qty: 1,
|
|
@@ -2529,27 +2539,6 @@ const INVERTER_READ_REGISTERS = [
|
|
|
2529
2539
|
role: 'value',
|
|
2530
2540
|
desc: 'Power factor adjustment feedback (V2.6)',
|
|
2531
2541
|
},
|
|
2532
|
-
// V2.6 — separate FC04 group (gap > 4 from 31041)
|
|
2533
|
-
{
|
|
2534
|
-
addr: 31509,
|
|
2535
|
-
qty: 2,
|
|
2536
|
-
name: 'inverter.pvDailyGeneration',
|
|
2537
|
-
type: 'U32',
|
|
2538
|
-
gain: 100,
|
|
2539
|
-
unit: 'kWh',
|
|
2540
|
-
role: 'value.energy',
|
|
2541
|
-
desc: 'PV daily generation (V2.6)',
|
|
2542
|
-
},
|
|
2543
|
-
{
|
|
2544
|
-
addr: 31511,
|
|
2545
|
-
qty: 2,
|
|
2546
|
-
name: 'inverter.pvTotalGeneration',
|
|
2547
|
-
type: 'U32',
|
|
2548
|
-
gain: 100,
|
|
2549
|
-
unit: 'kWh',
|
|
2550
|
-
role: 'value.energy',
|
|
2551
|
-
desc: 'PV total generation (V2.6)',
|
|
2552
|
-
},
|
|
2553
2542
|
];
|
|
2554
2543
|
|
|
2555
2544
|
// ============================================================
|
|
@@ -2849,7 +2838,28 @@ const DC_CHARGER_READ_REGISTERS = [
|
|
|
2849
2838
|
role: 'value.time',
|
|
2850
2839
|
desc: 'Current charging duration (single session)',
|
|
2851
2840
|
},
|
|
2852
|
-
// V2.
|
|
2841
|
+
// V2.6 — PV generation (DC Charger only, gap of 1 from 31508)
|
|
2842
|
+
{
|
|
2843
|
+
addr: 31509,
|
|
2844
|
+
qty: 2,
|
|
2845
|
+
name: 'dcCharger.pvDailyGeneration',
|
|
2846
|
+
type: 'U32',
|
|
2847
|
+
gain: 100,
|
|
2848
|
+
unit: 'kWh',
|
|
2849
|
+
role: 'value.energy',
|
|
2850
|
+
desc: '[DC Charger] PV daily generation (V2.6)',
|
|
2851
|
+
},
|
|
2852
|
+
{
|
|
2853
|
+
addr: 31511,
|
|
2854
|
+
qty: 2,
|
|
2855
|
+
name: 'dcCharger.pvTotalGeneration',
|
|
2856
|
+
type: 'U32',
|
|
2857
|
+
gain: 100,
|
|
2858
|
+
unit: 'kWh',
|
|
2859
|
+
role: 'value.energy',
|
|
2860
|
+
desc: '[DC Charger] PV total generation (V2.6)',
|
|
2861
|
+
},
|
|
2862
|
+
// V2.8/V2.9 — separate FC04 group (gap of 1 from 31512)
|
|
2853
2863
|
{
|
|
2854
2864
|
addr: 31513,
|
|
2855
2865
|
qty: 1,
|
|
@@ -4559,7 +4569,7 @@ const ESS_PREHEATING_WRITE_REGISTERS = (() => {
|
|
|
4559
4569
|
qty: 2,
|
|
4560
4570
|
name: `plant.essPreheating.tou${n}.startTime`,
|
|
4561
4571
|
type: 'U32',
|
|
4562
|
-
gain:
|
|
4572
|
+
gain: 1,
|
|
4563
4573
|
unit: 's',
|
|
4564
4574
|
perm: 'RW',
|
|
4565
4575
|
desc: `TOU ${n} start time`,
|
|
@@ -4569,7 +4579,7 @@ const ESS_PREHEATING_WRITE_REGISTERS = (() => {
|
|
|
4569
4579
|
qty: 2,
|
|
4570
4580
|
name: `plant.essPreheating.tou${n}.endTime`,
|
|
4571
4581
|
type: 'U32',
|
|
4572
|
-
gain:
|
|
4582
|
+
gain: 1,
|
|
4573
4583
|
unit: 's',
|
|
4574
4584
|
perm: 'RW',
|
|
4575
4585
|
desc: `TOU ${n} end time`,
|
package/main.js
CHANGED
|
@@ -16,6 +16,10 @@ const {
|
|
|
16
16
|
AC_CHARGER_READ_REGISTERS,
|
|
17
17
|
PSS_READ_REGISTERS,
|
|
18
18
|
PID_READ_REGISTERS,
|
|
19
|
+
ESS_PREHEATING_WRITE_REGISTERS,
|
|
20
|
+
PLANT_WRITE_REGISTERS,
|
|
21
|
+
INVERTER_WRITE_REGISTERS,
|
|
22
|
+
DC_CHARGER_WRITE_REGISTERS,
|
|
19
23
|
} = require('./lib/registers');
|
|
20
24
|
const SigenMicroScanner = require('./lib/scanner');
|
|
21
25
|
const { getRegistersForDevice } = require('./lib/sigenMicroRegisters');
|
|
@@ -41,8 +45,11 @@ class Sigenergy extends utils.Adapter {
|
|
|
41
45
|
// (modbus reads, scans, setTimeout-chains) can abort gracefully instead of
|
|
42
46
|
// calling setStateAsync on a stopped adapter.
|
|
43
47
|
this._stopped = false;
|
|
48
|
+
this._controlRegistersRead = false;
|
|
49
|
+
this._essPreheatingUnsupported = false;
|
|
44
50
|
|
|
45
51
|
this.on('ready', this.onReady.bind(this));
|
|
52
|
+
this.on('stateChange', this.onStateChange.bind(this));
|
|
46
53
|
this.on('message', this.onMessage.bind(this));
|
|
47
54
|
this.on('unload', this.onUnload.bind(this));
|
|
48
55
|
}
|
|
@@ -222,6 +229,11 @@ class Sigenergy extends utils.Adapter {
|
|
|
222
229
|
this.log.debug('[poll] cycle started');
|
|
223
230
|
|
|
224
231
|
try {
|
|
232
|
+
if (!this._controlRegistersRead) {
|
|
233
|
+
await this._readControlRegisters();
|
|
234
|
+
this._controlRegistersRead = true;
|
|
235
|
+
}
|
|
236
|
+
|
|
225
237
|
const _t0 = Date.now();
|
|
226
238
|
await this._readPlant();
|
|
227
239
|
if (this._stopped) {
|
|
@@ -263,6 +275,12 @@ class Sigenergy extends utils.Adapter {
|
|
|
263
275
|
return;
|
|
264
276
|
}
|
|
265
277
|
}
|
|
278
|
+
if (this.config.enableEssPreheating) {
|
|
279
|
+
await this._readEssPreheating();
|
|
280
|
+
if (this._stopped) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
266
284
|
|
|
267
285
|
await this._updateStatistics();
|
|
268
286
|
await this._updatePvStringPowers();
|
|
@@ -433,6 +451,98 @@ class Sigenergy extends utils.Adapter {
|
|
|
433
451
|
}
|
|
434
452
|
}
|
|
435
453
|
|
|
454
|
+
async _readEssPreheating() {
|
|
455
|
+
if (this._essPreheatingUnsupported) {
|
|
456
|
+
this.log.debug(
|
|
457
|
+
'[essPreheating] Skipping — registers not supported by device (disable in adapter settings)',
|
|
458
|
+
);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const plantId = this.config.plantId || 247;
|
|
462
|
+
const batchSize = 50;
|
|
463
|
+
const groups = this._buildReadGroups(ESS_PREHEATING_WRITE_REGISTERS, batchSize);
|
|
464
|
+
this.log.debug(`Reading ESS Preheating (slaveId=${plantId}): ${groups.length} group(s)`);
|
|
465
|
+
|
|
466
|
+
let failCount = 0;
|
|
467
|
+
for (const group of groups) {
|
|
468
|
+
if (this._stopped) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
this.log.debug(`[essPreheating] FC03 addr=${group.startAddr} qty=${group.totalQty}`);
|
|
473
|
+
const raw = await this.modbus.readHoldingRegisters(plantId, group.startAddr, group.totalQty);
|
|
474
|
+
await this._processReadGroup(group, raw, 'essPreheating');
|
|
475
|
+
await this._sleep(100);
|
|
476
|
+
} catch (err) {
|
|
477
|
+
failCount++;
|
|
478
|
+
this.log.debug(`[essPreheating] read error at ${group.startAddr}: ${err.message}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if (failCount === groups.length && groups.length > 0) {
|
|
482
|
+
this._essPreheatingUnsupported = true;
|
|
483
|
+
this.log.warn(
|
|
484
|
+
'ESS Preheating: all register groups failed (Modbus exception 2 — device does not support these registers). ' +
|
|
485
|
+
'Polling disabled for this session. Disable "ESS Preheating" in adapter settings to suppress this message.',
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async _readControlRegisters() {
|
|
491
|
+
const plantId = this.config.plantId || 247;
|
|
492
|
+
const inverterId = this.config.inverterId || 1;
|
|
493
|
+
this.log.debug('[controlRegs] Reading RW holding registers (one-time on startup)');
|
|
494
|
+
|
|
495
|
+
const plantRw = PLANT_WRITE_REGISTERS.filter(r => r.perm === 'RW' && (!r.feature || this.config[r.feature]));
|
|
496
|
+
if (plantRw.length > 0) {
|
|
497
|
+
for (const group of this._buildReadGroups(plantRw, 50)) {
|
|
498
|
+
if (this._stopped) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
try {
|
|
502
|
+
const raw = await this.modbus.readHoldingRegisters(plantId, group.startAddr, group.totalQty);
|
|
503
|
+
await this._processReadGroup(group, raw, 'control');
|
|
504
|
+
await this._sleep(100);
|
|
505
|
+
} catch (err) {
|
|
506
|
+
this.log.debug(`[controlRegs] plant addr=${group.startAddr}: ${err.message}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const invRw = INVERTER_WRITE_REGISTERS.filter(r => r.perm === 'RW');
|
|
512
|
+
if (invRw.length > 0) {
|
|
513
|
+
for (const group of this._buildReadGroups(invRw, 50)) {
|
|
514
|
+
if (this._stopped) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
try {
|
|
518
|
+
const raw = await this.modbus.readHoldingRegisters(inverterId, group.startAddr, group.totalQty);
|
|
519
|
+
await this._processReadGroup(group, raw, 'control');
|
|
520
|
+
await this._sleep(100);
|
|
521
|
+
} catch (err) {
|
|
522
|
+
this.log.debug(`[controlRegs] inverter addr=${group.startAddr}: ${err.message}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (this.config.hasDcCharger) {
|
|
528
|
+
const dcRw = DC_CHARGER_WRITE_REGISTERS.filter(r => r.perm === 'RW');
|
|
529
|
+
if (dcRw.length > 0) {
|
|
530
|
+
for (const group of this._buildReadGroups(dcRw, 50)) {
|
|
531
|
+
if (this._stopped) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
try {
|
|
535
|
+
const raw = await this.modbus.readHoldingRegisters(inverterId, group.startAddr, group.totalQty);
|
|
536
|
+
await this._processReadGroup(group, raw, 'control');
|
|
537
|
+
await this._sleep(100);
|
|
538
|
+
} catch (err) {
|
|
539
|
+
this.log.debug(`[controlRegs] dcCharger addr=${group.startAddr}: ${err.message}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
436
546
|
/**
|
|
437
547
|
* Read all active SigenMicro devices
|
|
438
548
|
*/
|
|
@@ -664,6 +774,25 @@ class Sigenergy extends utils.Adapter {
|
|
|
664
774
|
await this._createStateFromRegister(reg);
|
|
665
775
|
}
|
|
666
776
|
|
|
777
|
+
// Plant control write states
|
|
778
|
+
await this._createChannel('plant.control', 'Plant Control');
|
|
779
|
+
if (this.config.enableGridCode) {
|
|
780
|
+
await this._createChannel('plant.gridCode', 'Grid Code Parameters');
|
|
781
|
+
}
|
|
782
|
+
for (const reg of PLANT_WRITE_REGISTERS) {
|
|
783
|
+
if (this._stopped) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
if (reg.feature && !this.config[reg.feature]) {
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
await this._createWriteStateFromRegister(reg);
|
|
790
|
+
}
|
|
791
|
+
this.subscribeStates('plant.control.*');
|
|
792
|
+
if (this.config.enableGridCode) {
|
|
793
|
+
this.subscribeStates('plant.gridCode.*');
|
|
794
|
+
}
|
|
795
|
+
|
|
667
796
|
// Virtual PV string power states (calculated from voltage × current)
|
|
668
797
|
for (const pvState of [
|
|
669
798
|
{ id: 'plant.pv1Power', desc: 'PV string 1 power' },
|
|
@@ -694,6 +823,16 @@ class Sigenergy extends utils.Adapter {
|
|
|
694
823
|
await this._createStateFromRegister(reg);
|
|
695
824
|
}
|
|
696
825
|
|
|
826
|
+
// Inverter control write states
|
|
827
|
+
await this._createChannel('inverter.control', 'Inverter Control');
|
|
828
|
+
for (const reg of INVERTER_WRITE_REGISTERS) {
|
|
829
|
+
if (this._stopped) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
await this._createWriteStateFromRegister(reg);
|
|
833
|
+
}
|
|
834
|
+
this.subscribeStates('inverter.control.*');
|
|
835
|
+
|
|
697
836
|
if (this.config.hasAcCharger) {
|
|
698
837
|
this.log.debug('Creating AC charger objects');
|
|
699
838
|
await this._createChannel('acCharger', 'AC Charger');
|
|
@@ -714,6 +853,14 @@ class Sigenergy extends utils.Adapter {
|
|
|
714
853
|
}
|
|
715
854
|
await this._createStateFromRegister(reg);
|
|
716
855
|
}
|
|
856
|
+
await this._createChannel('dcCharger.control', 'DC Charger Control');
|
|
857
|
+
for (const reg of DC_CHARGER_WRITE_REGISTERS) {
|
|
858
|
+
if (this._stopped) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
await this._createWriteStateFromRegister(reg);
|
|
862
|
+
}
|
|
863
|
+
this.subscribeStates('dcCharger.control.*');
|
|
717
864
|
}
|
|
718
865
|
|
|
719
866
|
if (this.config.hasSigenMicro) {
|
|
@@ -754,6 +901,21 @@ class Sigenergy extends utils.Adapter {
|
|
|
754
901
|
for (let n = 1; n <= 30; n++) {
|
|
755
902
|
await this._createChannel(`plant.essPreheating.tou${n}`, `TOU Window ${n}`);
|
|
756
903
|
}
|
|
904
|
+
for (const reg of ESS_PREHEATING_WRITE_REGISTERS) {
|
|
905
|
+
await this.setObjectNotExistsAsync(reg.name, {
|
|
906
|
+
type: 'state',
|
|
907
|
+
common: {
|
|
908
|
+
name: reg.desc,
|
|
909
|
+
type: 'number',
|
|
910
|
+
role: 'value',
|
|
911
|
+
unit: reg.unit || '',
|
|
912
|
+
read: true,
|
|
913
|
+
write: true,
|
|
914
|
+
},
|
|
915
|
+
native: { addr: reg.addr, qty: reg.qty, dataType: reg.type, gain: reg.gain },
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
this.subscribeStates('plant.essPreheating.*');
|
|
757
919
|
}
|
|
758
920
|
|
|
759
921
|
await this._createStatisticsStates();
|
|
@@ -817,6 +979,27 @@ class Sigenergy extends utils.Adapter {
|
|
|
817
979
|
});
|
|
818
980
|
}
|
|
819
981
|
|
|
982
|
+
async _createWriteStateFromRegister(reg) {
|
|
983
|
+
const type = reg.type === 'STRING' ? 'string' : 'number';
|
|
984
|
+
await this.setObjectNotExistsAsync(reg.name, {
|
|
985
|
+
type: 'state',
|
|
986
|
+
common: {
|
|
987
|
+
name: reg.desc,
|
|
988
|
+
type,
|
|
989
|
+
role: 'value',
|
|
990
|
+
unit: reg.unit || '',
|
|
991
|
+
read: reg.perm === 'RW',
|
|
992
|
+
write: true,
|
|
993
|
+
},
|
|
994
|
+
native: {
|
|
995
|
+
addr: reg.addr,
|
|
996
|
+
qty: reg.qty,
|
|
997
|
+
dataType: reg.type,
|
|
998
|
+
gain: reg.gain,
|
|
999
|
+
},
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
|
|
820
1003
|
/**
|
|
821
1004
|
* Create ioBroker objects for all active SigenMicro devices
|
|
822
1005
|
*/
|
|
@@ -928,7 +1111,79 @@ class Sigenergy extends utils.Adapter {
|
|
|
928
1111
|
/**
|
|
929
1112
|
* Handle messages from admin (connection test, etc.)
|
|
930
1113
|
*
|
|
931
|
-
* @param {
|
|
1114
|
+
* @param {ioBroker.Object} obj - Message object
|
|
1115
|
+
*/
|
|
1116
|
+
async onStateChange(id, state) {
|
|
1117
|
+
if (!state || state.ack) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
if (!this.modbus || !this.modbus.connected) {
|
|
1121
|
+
this.log.warn(`Cannot write state ${id}: Modbus not connected`);
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
const localId = id.replace(/^[^.]+\.\d+\./, '');
|
|
1125
|
+
|
|
1126
|
+
// ESS Preheating writes (FC03/FC10)
|
|
1127
|
+
if (localId.startsWith('plant.essPreheating.')) {
|
|
1128
|
+
const reg = ESS_PREHEATING_WRITE_REGISTERS.find(r => r.name === localId);
|
|
1129
|
+
if (!reg) {
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
const plantId = this.config.plantId || 247;
|
|
1133
|
+
const raw = ModbusConnection.encodeValue(state.val, reg.type, reg.gain);
|
|
1134
|
+
try {
|
|
1135
|
+
if (raw.length === 1) {
|
|
1136
|
+
await this.modbus.writeSingleRegister(plantId, reg.addr, raw[0]);
|
|
1137
|
+
} else {
|
|
1138
|
+
await this.modbus.writeMultipleRegisters(plantId, reg.addr, raw);
|
|
1139
|
+
}
|
|
1140
|
+
await this.setStateAsync(id, { val: state.val, ack: true });
|
|
1141
|
+
this.log.debug(`[essPreheating] wrote ${localId} = ${state.val} → addr ${reg.addr}`);
|
|
1142
|
+
} catch (err) {
|
|
1143
|
+
this.log.error(`ESS Preheating write error for ${localId}: ${err.message}`);
|
|
1144
|
+
}
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// Plant / Inverter / DC Charger control register writes
|
|
1149
|
+
let reg = null;
|
|
1150
|
+
let slaveId = null;
|
|
1151
|
+
let category = '';
|
|
1152
|
+
|
|
1153
|
+
if (localId.startsWith('plant.control.') || localId.startsWith('plant.gridCode.')) {
|
|
1154
|
+
reg = PLANT_WRITE_REGISTERS.find(r => r.name === localId);
|
|
1155
|
+
slaveId = this.config.plantId || 247;
|
|
1156
|
+
category = 'plant';
|
|
1157
|
+
} else if (localId.startsWith('inverter.control.')) {
|
|
1158
|
+
reg = INVERTER_WRITE_REGISTERS.find(r => r.name === localId);
|
|
1159
|
+
slaveId = this.config.inverterId || 1;
|
|
1160
|
+
category = 'inverter';
|
|
1161
|
+
} else if (localId.startsWith('dcCharger.control.')) {
|
|
1162
|
+
reg = DC_CHARGER_WRITE_REGISTERS.find(r => r.name === localId);
|
|
1163
|
+
slaveId = this.config.inverterId || 1;
|
|
1164
|
+
category = 'dcCharger';
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
if (!reg) {
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
const raw = ModbusConnection.encodeValue(state.val, reg.type, reg.gain);
|
|
1172
|
+
try {
|
|
1173
|
+
if (raw.length === 1) {
|
|
1174
|
+
await this.modbus.writeSingleRegister(slaveId, reg.addr, raw[0]);
|
|
1175
|
+
} else {
|
|
1176
|
+
await this.modbus.writeMultipleRegisters(slaveId, reg.addr, raw);
|
|
1177
|
+
}
|
|
1178
|
+
await this.setStateAsync(id, { val: state.val, ack: true });
|
|
1179
|
+
this.log.debug(`[${category}] wrote ${localId} = ${state.val} → addr ${reg.addr} slave ${slaveId}`);
|
|
1180
|
+
} catch (err) {
|
|
1181
|
+
this.log.error(`Control register write error for ${localId}: ${err.message}`);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/**
|
|
1186
|
+
* @param {ioBroker.Object} obj - Message object
|
|
932
1187
|
*/
|
|
933
1188
|
async onMessage(obj) {
|
|
934
1189
|
if (!obj || !obj.command) {
|