iobroker.sigenergy 2.3.3 → 2.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -153,6 +153,9 @@ Status and power readings for the DC charger.
153
153
 
154
154
  ## Changelog
155
155
 
156
+ ### 2.3.4 (2026-06-12)
157
+ - (ssbingo) fix: correct protocol level detection — use proper register quantities for probes, descend from V2.9 to V2.6, distinguish transport errors from device exceptions to avoid false pre-V2.6 report
158
+
156
159
  ### 2.3.3 (2026-06-11)
157
160
  - (ssbingo) fix: suppress repeated register read warnings after first occurrence for plant/inverter/acCharger/dcCharger/pss/pid; subsequent failures log at debug level only
158
161
 
package/io-package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "sigenergy",
4
- "version": "2.3.3",
4
+ "version": "2.3.4",
5
5
  "news": {
6
- "2.3.3": {
7
- "en": "(ssbingo) fix: suppress repeated register read warnings after first occurrence for plant/inverter/acCharger/dcCharger/pss/pid; subsequent failures log at debug level only",
8
- "de": "(ssbingo) fix: wiederholte Register-Lesewarnungen nach erstem Auftreten für plant/inverter/acCharger/dcCharger/pss/pid unterdrückt; weitere Fehler nur noch auf Debug-Ebene protokolliert",
9
- "ru": "(ssbingo) fix: подавление повторяющихся предупреждений о чтении регистров после первого возникновения для plant/inverter/acCharger/dcCharger/pss/pid; последующие ошибки логируются только на уровне debug",
10
- "pt": "(ssbingo) fix: suprimir avisos repetidos de leitura de registos após primeira ocorrência para plant/inverter/acCharger/dcCharger/pss/pid; falhas subsequentes registadas apenas ao nível debug",
11
- "nl": "(ssbingo) fix: herhaalde register-leeswaarschuwingen na eerste voorkomen onderdrukken voor plant/inverter/acCharger/dcCharger/pss/pid; volgende fouten alleen op debug-niveau gelogd",
12
- "fr": "(ssbingo) fix: supprimer les avertissements répétés de lecture de registres après la première occurrence pour plant/inverter/acCharger/dcCharger/pss/pid ; les échecs suivants sont journalisés au niveau debug uniquement",
13
- "it": "(ssbingo) fix: sopprimere gli avvisi ripetuti di lettura registro dopo la prima occorrenza per plant/inverter/acCharger/dcCharger/pss/pid; i guasti successivi vengono registrati solo a livello debug",
14
- "es": "(ssbingo) fix: suprimir advertencias repetidas de lectura de registros tras la primera aparición para plant/inverter/acCharger/dcCharger/pss/pid; los fallos posteriores solo se registran a nivel debug",
15
- "pl": "(ssbingo) fix: tłumienie powtarzających się ostrzeżeń o odczycie rejestrów po pierwszym wystąpieniu dla plant/inverter/acCharger/dcCharger/pss/pid; kolejne błędy rejestrowane tylko na poziomie debug",
16
- "uk": "(ssbingo) fix: придушення повторюваних попереджень про зчитування регістрів після першого виникнення для plant/inverter/acCharger/dcCharger/pss/pid; наступні помилки журналюються лише на рівні debug",
17
- "zh-cn": "(ssbingo) fix: plant/inverter/acCharger/dcCharger/pss/pid 寄存器读取警告在首次出现后不再重复;后续失败仅在 debug 级别记录"
6
+ "2.3.4": {
7
+ "en": "(ssbingo) fix: correct protocol level detection use proper register quantities for probes, descend from V2.9 to V2.6, distinguish transport errors from device exceptions to avoid false pre-V2.6 report",
8
+ "de": "(ssbingo) fix: Protokollversionserkennung korrigiert korrekte Registermengen für Probes, absteigende Reihenfolge V2.9→V2.6, Transportfehler von Geräteantworten unterschieden um fehlerhafte pre-V2.6-Meldung zu vermeiden",
9
+ "ru": "(ssbingo) fix: исправлено определение уровня протокола правильное количество регистров для проб, порядок убывания V2.9→V2.6, разграничение транспортных ошибок и ответов устройства для исключения ложного сообщения pre-V2.6",
10
+ "pt": "(ssbingo) fix: correção da deteção do nível de protocolo — quantidades corretas de registos para sondas, ordem decrescente V2.9→V2.6, distinção entre erros de transporte e exceções do dispositivo para evitar falso relatório pre-V2.6",
11
+ "nl": "(ssbingo) fix: correcte protocolniveaudetectie juiste registerhoeveelheden voor probes, aflopende volgorde V2.9→V2.6, onderscheid transportfouten van apparaatreacties om vals pre-V2.6-rapport te voorkomen",
12
+ "fr": "(ssbingo) fix: correction de la détection du niveau de protocole — quantités de registres correctes pour les sondes, ordre décroissant V2.9→V2.6, distinction des erreurs de transport des exceptions du périphérique pour éviter un rapport pre-V2.6 erroné",
13
+ "it": "(ssbingo) fix: correzione del rilevamento del livello di protocollo quantità di registri corrette per le probe, ordine decrescente V2.9→V2.6, distinzione errori di trasporto da eccezioni del dispositivo per evitare falso rapporto pre-V2.6",
14
+ "es": "(ssbingo) fix: corrección de la detección del nivel de protocolo — cantidades correctas de registros para sondas, orden descendente V2.9→V2.6, distinción de errores de transporte de excepciones del dispositivo para evitar informe falso pre-V2.6",
15
+ "pl": "(ssbingo) fix: poprawka wykrywania poziomu protokołu właściwe ilości rejestrów dla sond, kolejność malejąca V2.9→V2.6, rozróżnienie błędów transportu od wyjątków urządzenia w celu uniknięcia fałszywego raportu pre-V2.6",
16
+ "uk": "(ssbingo) fix: виправлення визначення рівня протоколу правильна кількість регістрів для проб, порядок спадання V2.9→V2.6, розрізнення транспортних помилок від винятків пристрою для уникнення хибного звіту pre-V2.6",
17
+ "zh-cn": "(ssbingo) fix: 修正协议版本检测 使用正确的寄存器数量探测,从 V2.9 降序到 V2.6,区分传输错误和设备异常,避免误报 pre-V2.6"
18
18
  }
19
19
  },
20
20
  "titleLang": {
package/lib/registers.js CHANGED
@@ -1252,7 +1252,7 @@ const PLANT_READ_REGISTERS = [
1252
1252
  desc: 'Total oil generator energy (V2.8)',
1253
1253
  feature: 'enableCumulativeEnergy',
1254
1254
  },
1255
- // V2.8 — separate FC04 group (gap > 4 from 30087)
1255
+ // V2.9 — separate FC04 group (gap > 4 from 30087)
1256
1256
  {
1257
1257
  addr: 30272,
1258
1258
  qty: 2,
@@ -1261,7 +1261,7 @@ const PLANT_READ_REGISTERS = [
1261
1261
  gain: 100,
1262
1262
  unit: 'kWh',
1263
1263
  role: 'value.energy',
1264
- desc: 'PV total daily generation (V2.8)',
1264
+ desc: 'PV total daily generation (V2.9)',
1265
1265
  },
1266
1266
  {
1267
1267
  addr: 30274,
@@ -1271,9 +1271,9 @@ const PLANT_READ_REGISTERS = [
1271
1271
  gain: 100,
1272
1272
  unit: 'kWh',
1273
1273
  role: 'value.energy',
1274
- desc: 'PV total generation previous day (V2.8)',
1274
+ desc: 'PV total generation previous day (V2.9)',
1275
1275
  },
1276
- // V2.9
1276
+ // V2.8
1277
1277
  {
1278
1278
  addr: 30276,
1279
1279
  qty: 1,
@@ -1282,7 +1282,7 @@ const PLANT_READ_REGISTERS = [
1282
1282
  gain: 100,
1283
1283
  unit: 'Hz',
1284
1284
  role: 'value.frequency',
1285
- desc: 'Grid code rated frequency (V2.9)',
1285
+ desc: 'Grid code rated frequency (V2.8)',
1286
1286
  },
1287
1287
  {
1288
1288
  addr: 30277,
@@ -1292,7 +1292,7 @@ const PLANT_READ_REGISTERS = [
1292
1292
  gain: 100,
1293
1293
  unit: 'V',
1294
1294
  role: 'value.voltage',
1295
- desc: 'Grid code rated voltage (V2.9)',
1295
+ desc: 'Grid code rated voltage (V2.8)',
1296
1296
  },
1297
1297
  {
1298
1298
  addr: 30279,
@@ -1312,7 +1312,7 @@ const PLANT_READ_REGISTERS = [
1312
1312
  gain: null,
1313
1313
  unit: '',
1314
1314
  role: 'value',
1315
- desc: 'Merged alarm 6 (V2.9)',
1315
+ desc: 'Merged alarm 6 (V2.8)',
1316
1316
  },
1317
1317
  {
1318
1318
  addr: 30281,
@@ -1322,7 +1322,7 @@ const PLANT_READ_REGISTERS = [
1322
1322
  gain: null,
1323
1323
  unit: '',
1324
1324
  role: 'value',
1325
- desc: 'Merged alarm 7 (V2.9)',
1325
+ desc: 'Merged alarm 7 (V2.8)',
1326
1326
  },
1327
1327
  {
1328
1328
  addr: 30282,
@@ -1332,7 +1332,7 @@ const PLANT_READ_REGISTERS = [
1332
1332
  gain: 1000,
1333
1333
  unit: 'kW',
1334
1334
  role: 'value.power',
1335
- desc: 'General load power (V2.9)',
1335
+ desc: 'General load power (V2.8)',
1336
1336
  },
1337
1337
  {
1338
1338
  addr: 30284,
@@ -1342,7 +1342,7 @@ const PLANT_READ_REGISTERS = [
1342
1342
  gain: 1000,
1343
1343
  unit: 'kW',
1344
1344
  role: 'value.power',
1345
- desc: 'Total load power (V2.9)',
1345
+ desc: 'Total load power (V2.8)',
1346
1346
  },
1347
1347
  {
1348
1348
  addr: 30286,
package/main.js CHANGED
@@ -255,8 +255,9 @@ class Sigenergy extends utils.Adapter {
255
255
  this._controlRegistersRead = true;
256
256
  }
257
257
  if (!this._protocolDetected) {
258
- await this._detectProtocolLevel();
259
- this._protocolDetected = true;
258
+ // Only latch the flag if detection was conclusive (no transport
259
+ // errors). Otherwise retry on the next poll cycle.
260
+ this._protocolDetected = await this._detectProtocolLevel();
260
261
  }
261
262
 
262
263
  const _t0 = Date.now();
@@ -311,13 +312,13 @@ class Sigenergy extends utils.Adapter {
311
312
  await this._updatePvStringPowers();
312
313
 
313
314
  await this.setStateAsync('info.connection', { val: true, ack: true });
314
- // Successful cycle reset dedup flags so next outage logs at ERROR level again.
315
+ // Successful cycle reset dedup flags so next outage logs at ERROR level again.
315
316
  this._pollErrorKey = null;
316
317
  this._pollWarnLogged = false;
317
318
  this.log.debug(`[poll] cycle completed in ${Date.now() - _pollStart} ms`);
318
319
  } catch (err) {
319
- // Deduplicate: first occurrence ERROR (visible + Sentry-worthy).
320
- // Identical subsequent errors DEBUG, so a broken network does not flood the log.
320
+ // Deduplicate: first occurrence ERROR (visible + Sentry-worthy).
321
+ // Identical subsequent errors DEBUG, so a broken network does not flood the log.
321
322
  const key = `poll:${err.code || err.message}`;
322
323
  if (this._pollErrorKey !== key) {
323
324
  this.log.error(`Poll error: ${err.message}`);
@@ -542,26 +543,61 @@ class Sigenergy extends utils.Adapter {
542
543
  }
543
544
  }
544
545
 
546
+ /**
547
+ * Detect the Modbus protocol level supported by the device.
548
+ *
549
+ * Design notes (root cause of the former "pre-V2.6" misdetection):
550
+ * 1. Sigenergy firmware rejects reads that do not cover a complete data
551
+ * point. Probing addr 30088 (U64, qty 4) with qty=1 returns Modbus
552
+ * exception 0x02 (ILLEGAL DATA ADDRESS, "combination of reference
553
+ * number and transfer length is invalid"). Every probe therefore
554
+ * reads the FULL quantity of its data point.
555
+ * 2. Probes run in DESCENDING order and stop at the first success, so a
556
+ * single optional/legacy register can no longer cap the result.
557
+ * 3. Probe registers are valid for both "Hybrid Inv." and "PV Inv."
558
+ * (per protocol doc V2.9, table 5-1) and match the official version
559
+ * history: 30272 added in V2.9, 30276 in V2.8, 30194 in V2.7,
560
+ * 30088 in V2.6.
561
+ * 4. A Modbus exception (1/2) means "register not implemented" — that is
562
+ * a conclusive "no". A transport error/timeout is INCONCLUSIVE: it is
563
+ * retried once, and if detection stays inconclusive the whole run is
564
+ * repeated on the next poll cycle instead of reporting pre-V2.6.
565
+ *
566
+ * @returns {Promise<boolean>} true if detection was conclusive
567
+ */
545
568
  async _detectProtocolLevel() {
546
569
  const plantId = this.config.plantId || 247;
547
570
  const inverterId = this.config.inverterId || 1;
548
571
 
549
572
  const probes = [
550
- { addr: 30088, level: 'V2.6' },
551
- { addr: 30200, level: 'V2.7' },
552
- { addr: 30228, level: 'V2.8' },
553
- { addr: 30286, level: 'V2.9' },
573
+ { addr: 30272, qty: 2, level: 'V2.9' }, // PV total daily generation (U32)
574
+ { addr: 30276, qty: 1, level: 'V2.8' }, // [Grid code] Rated frequency (U16)
575
+ { addr: 30194, qty: 2, level: 'V2.7' }, // Third party inverter active power (S32)
576
+ { addr: 30088, qty: 4, level: 'V2.6' }, // Plant PV total generation (U64)
554
577
  ];
555
578
 
556
579
  let detectedLevel = null;
580
+ let inconclusive = false;
557
581
  for (const probe of probes) {
558
- try {
559
- await this.modbus.readInputRegisters(plantId, probe.addr, 1);
582
+ if (this._stopped) {
583
+ return false;
584
+ }
585
+ const result = await this._probeRegister(plantId, probe);
586
+ if (result === 'supported') {
560
587
  detectedLevel = probe.level;
561
- } catch (err) {
562
- this.log.debug(`[protocolProbe] addr=${probe.addr} (${probe.level}): ${err.message}`);
563
588
  break;
564
589
  }
590
+ if (result === 'error') {
591
+ inconclusive = true;
592
+ }
593
+ await this._sleep(100);
594
+ }
595
+
596
+ if (!detectedLevel && inconclusive) {
597
+ this.log.warn(
598
+ 'Protocol level detection inconclusive (communication errors during probing) — will retry on next poll cycle',
599
+ );
600
+ return false;
565
601
  }
566
602
 
567
603
  let firmwareVersion = 'unknown';
@@ -575,6 +611,48 @@ class Sigenergy extends utils.Adapter {
575
611
  const levelStr = detectedLevel ? `>=${detectedLevel}` : 'pre-V2.6';
576
612
  this.log.info(`Detected protocol level: ${levelStr} (firmware: ${firmwareVersion})`);
577
613
  await this.setStateAsync('info.protocolLevel', { val: levelStr, ack: true });
614
+ return true;
615
+ }
616
+
617
+ /**
618
+ * Probe a single register (full data point) on the device.
619
+ *
620
+ * @param {number} slaveId - Modbus slave id
621
+ * @param {{addr: number, qty: number, level: string}} probe - probe definition
622
+ * @returns {Promise<'supported'|'unsupported'|'error'>} probe result
623
+ */
624
+ async _probeRegister(slaveId, probe) {
625
+ for (let attempt = 1; attempt <= 2; attempt++) {
626
+ if (this._stopped) {
627
+ return 'error';
628
+ }
629
+ try {
630
+ await this.modbus.readInputRegisters(slaveId, probe.addr, probe.qty);
631
+ this.log.debug(`[protocolProbe] addr=${probe.addr} qty=${probe.qty} (${probe.level}): supported`);
632
+ return 'supported';
633
+ } catch (err) {
634
+ // modbus-serial sets err.modbusCode on Modbus exception responses
635
+ // (1 = ILLEGAL FUNCTION, 2 = ILLEGAL DATA ADDRESS, ...). An
636
+ // exception is an answer FROM the device => register not
637
+ // implemented on this firmware. Anything else (timeout, socket
638
+ // error) is a transport problem and proves nothing.
639
+ if (err && err.modbusCode !== undefined) {
640
+ this.log.debug(
641
+ `[protocolProbe] addr=${probe.addr} qty=${probe.qty} (${probe.level}): ` +
642
+ `not supported (Modbus exception ${err.modbusCode})`,
643
+ );
644
+ return 'unsupported';
645
+ }
646
+ this.log.debug(
647
+ `[protocolProbe] addr=${probe.addr} qty=${probe.qty} (${probe.level}): ` +
648
+ `transport error on attempt ${attempt}/2: ${err.message}`,
649
+ );
650
+ if (attempt < 2) {
651
+ await this._sleep(300);
652
+ }
653
+ }
654
+ }
655
+ return 'error';
578
656
  }
579
657
 
580
658
  async _readControlRegisters() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.sigenergy",
3
- "version": "2.3.3",
3
+ "version": "2.3.4",
4
4
  "description": "ioBroker adapter for Sigenergy solar energy systems via Modbus TCP/RTU",
5
5
  "author": {
6
6
  "name": "ssbingo",