iobroker.sigenergy 1.6.2 → 1.7.0

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
@@ -156,6 +156,12 @@ MIT License — Copyright (c) 2025 ioBroker Community
156
156
  ---
157
157
 
158
158
  ## Changelog
159
+ ### 1.7.0 (2026-03-16)
160
+ * Replace HTML adminTab iframe with native jsonConfig tab (sigenmicro-tab.json); use type:state to show live scan progress (info.scanProgress state) directly in admin 7 React UI without any custom JavaScript
161
+
162
+ ### 1.6.3 (2026-03-16)
163
+ * Fix scan progress display: replace all CSS-variable-dependent elements with a single always-visible log box using hardcoded colors; each chunk callback updates text immediately
164
+
159
165
  ### 1.6.2 (2026-03-16)
160
166
  * Fix scan: restore proven chunked sendTo approach (3 IDs per call); add dual progress display: direct text update per chunk + getState polling from info.scanProgress state
161
167
 
package/admin/index.html CHANGED
@@ -925,9 +925,21 @@
925
925
  </div>
926
926
 
927
927
  <!-- Scan status line: always visible, updated as text only -->
928
- <div id="scanStatusLine" style="margin-top:14px; font-size:0.9rem; color:var(--text-light); min-height:1.4em;"></div>
929
-
930
- <div class="scan-result-msg" id="scanResultMsg" style="display:none"></div>
928
+ <!-- Scan log box: hardcoded styles, always visible, never hidden -->
929
+ <div id="scanLogBox" style="
930
+ margin-top: 16px;
931
+ padding: 12px 14px;
932
+ background: #f0f4f8;
933
+ border: 1px solid #c8d0d8;
934
+ border-radius: 8px;
935
+ font-size: 0.88rem;
936
+ font-family: monospace;
937
+ color: #333;
938
+ min-height: 48px;
939
+ line-height: 1.6;
940
+ white-space: pre-wrap;
941
+ word-break: break-all;
942
+ ">Ready. Set ID range above and click Start Scan.</div>
931
943
  </div>
932
944
 
933
945
  <!-- Card 2: Device list -->
@@ -1165,112 +1177,86 @@ function updateReservedIdsNote() {
1165
1177
  $note.html('<span>Reserved IDs:</span> ' + badges + '<span style="margin-left:4px;">' + warn + '</span>');
1166
1178
  }
1167
1179
 
1180
+ function _log(msg) {
1181
+ // Always-visible log box with hardcoded colors — no CSS vars
1182
+ var box = document.getElementById('scanLogBox');
1183
+ if (!box) return;
1184
+ box.style.color = '#333';
1185
+ box.style.background = '#f0f4f8';
1186
+ box.style.border = '1px solid #c8d0d8';
1187
+ box.textContent = msg;
1188
+ }
1189
+
1168
1190
  function startSigenMicroScan() {
1169
1191
  var from = parseInt($('#sigenMicroScanFrom').val(), 10) || 10;
1170
1192
  var to = parseInt($('#sigenMicroScanTo').val(), 10) || 30;
1171
1193
 
1172
1194
  if (from < 1 || to > 246 || from > to) {
1173
- _showScanMsg('error', 'Invalid scan range. First ID must be 1-246 and <= last ID.');
1195
+ _log('ERROR: Invalid scan range. First ID must be 1-246 and <= last ID.');
1174
1196
  return;
1175
1197
  }
1176
1198
 
1177
- var reserved = _getReservedIds();
1178
- var allFound = [];
1179
- var scannedIds = 0;
1180
- var totalIds = to - from + 1;
1181
- var stat = document.getElementById('scanStatusLine');
1182
- var msgEl = document.getElementById('scanResultMsg');
1183
- var btn = document.getElementById('btnScan');
1199
+ var reserved = _getReservedIds();
1200
+ var allFound = [];
1201
+ var totalIds = to - from + 1;
1202
+ var btn = document.getElementById('btnScan');
1184
1203
 
1185
1204
  btn.disabled = true;
1186
1205
  btn.classList.add('scanning');
1187
1206
  btn.querySelector('.scan-btn-text').textContent = 'Scanning...';
1188
1207
  document.getElementById('btnSaveMicro').disabled = true;
1189
- msgEl.style.display = 'none';
1190
- msgEl.className = 'scan-result-msg';
1191
- msgEl.textContent = '';
1192
1208
 
1193
1209
  var skipArr = Array.from(reserved).sort(function(a, b) { return a - b; });
1194
- stat.style.color = 'var(--text-light)';
1195
- stat.textContent = 'Starting... IDs ' + from + '-' + to +
1196
- (skipArr.length ? ' (skip: ' + skipArr.join(',') + ')' : '');
1197
-
1198
- // ── Progress: poll info.scanProgress state every 600ms ─────────────────
1199
- var ns = _getAdapterNs();
1200
- var progressId = ns + '.info.scanProgress';
1201
- var pollTimer = setInterval(function() {
1202
- var sock = null;
1203
- try { if (typeof socket !== 'undefined') sock = socket; } catch(e) {}
1204
- if (!sock) try { sock = window.socket; } catch(e) {}
1205
- if (!sock) return;
1206
- function onState(err, st) {
1207
- if (!err && st && st.val) {
1208
- stat.style.color = 'var(--text)';
1209
- stat.textContent = st.val;
1210
- }
1211
- }
1212
- if (typeof sock.getState === 'function') {
1213
- try { sock.getState(progressId, onState); return; } catch(e) {}
1214
- }
1215
- if (typeof sock.emit === 'function') {
1216
- try { sock.emit('getState', progressId, onState); } catch(e) {}
1217
- }
1218
- }, 600);
1210
+ _log('Starting scan: IDs ' + from + '-' + to +
1211
+ (skipArr.length ? ' (skip: ' + skipArr.join(', ') + ')' : ''));
1219
1212
 
1220
- // ── Chunked scan: 3 IDs per sendTo (this approach is confirmed working) ──
1221
- var CHUNK = 3;
1222
- var chunks = [];
1213
+ // Chunked scan: 3 IDs per sendTo call
1214
+ var CHUNK = 3;
1215
+ var chunks = [];
1223
1216
  for (var cs = from; cs <= to; cs += CHUNK) {
1224
1217
  chunks.push({ from: cs, to: Math.min(cs + CHUNK - 1, to) });
1225
1218
  }
1226
- var chunkIdx = 0;
1227
- var scanDone_ = false;
1219
+ var idx = 0;
1220
+ var finished = false;
1228
1221
 
1229
1222
  function nextChunk() {
1230
- if (scanDone_ || chunkIdx >= chunks.length) {
1231
- if (!scanDone_) {
1232
- scanDone_ = true;
1233
- clearInterval(pollTimer);
1234
- _scanDone(allFound, true);
1223
+ if (finished || idx >= chunks.length) {
1224
+ if (!finished) {
1225
+ finished = true;
1226
+ _scanFinish(allFound);
1235
1227
  }
1236
1228
  return;
1237
1229
  }
1238
- var chunk = chunks[chunkIdx++];
1239
- // Immediate text update so user sees movement even before getState polling
1240
- var pct = Math.min(99, Math.round(((chunkIdx - 1) / chunks.length) * 100));
1241
- stat.style.color = 'var(--text)';
1242
- stat.textContent = pct + '% — probing IDs ' + chunk.from + '-' + chunk.to + '...';
1243
-
1244
- _sendToAdapter('scanSigenMicro', { scanFrom: chunk.from, scanTo: chunk.to }, function(res) {
1245
- if (scanDone_) return;
1246
- if (res && res.devices && res.devices.length) {
1247
- allFound = allFound.concat(res.devices);
1230
+ var chunk = chunks[idx++];
1231
+ var pct = Math.min(99, Math.round(((idx - 1) / chunks.length) * 100));
1232
+ _log(pct + '% probing IDs ' + chunk.from + '-' + chunk.to + ' ...');
1233
+
1234
+ _sendToAdapter('scanSigenMicro', { scanFrom: chunk.from, scanTo: chunk.to },
1235
+ function(res) {
1236
+ if (finished) return;
1237
+ if (res && res.devices && res.devices.length) {
1238
+ allFound = allFound.concat(res.devices);
1239
+ _log(pct + '% IDs ' + chunk.from + '-' + chunk.to +
1240
+ ' → found ' + res.devices.length +
1241
+ ' device(s) [total: ' + allFound.length + ']');
1242
+ } else {
1243
+ _log(pct + '% IDs ' + chunk.from + '-' + chunk.to + ' → no devices');
1244
+ }
1245
+ setTimeout(nextChunk, 0);
1248
1246
  }
1249
- scannedIds += (chunk.to - chunk.from + 1);
1250
- setTimeout(nextChunk, 0);
1251
- });
1247
+ );
1252
1248
  }
1253
1249
 
1254
1250
  nextChunk();
1255
1251
  }
1256
1252
 
1257
- function _scanDone(devices, success) {
1258
- var btn = document.getElementById('btnScan');
1259
- var stat = document.getElementById('scanStatusLine');
1260
-
1253
+ function _scanFinish(devices) {
1254
+ var btn = document.getElementById('btnScan');
1261
1255
  btn.disabled = false;
1262
1256
  btn.classList.remove('scanning');
1263
1257
  btn.querySelector('.scan-btn-text').textContent = 'Start Scan';
1264
1258
 
1265
- if (!success) {
1266
- stat.style.color = 'var(--error, #c0392b)';
1267
- stat.textContent = 'Scan failed or aborted.';
1268
- _showScanMsg('error', 'Scan failed. Check the adapter log.');
1269
- return;
1270
- }
1271
-
1272
1259
  if (!devices) devices = [];
1273
-
1274
1260
  var found = devices.map(function(dev) {
1275
1261
  var prev = _microDevices.find(function(e) { return e.slaveId === dev.slaveId; });
1276
1262
  return Object.assign({}, dev, { active: prev ? !!prev.active : false });
@@ -1282,23 +1268,27 @@ function _scanDone(devices, success) {
1282
1268
  var n = found.length;
1283
1269
  var fromV = document.getElementById('sigenMicroScanFrom').value;
1284
1270
  var toV = document.getElementById('sigenMicroScanTo').value;
1271
+ var box = document.getElementById('scanLogBox');
1285
1272
 
1286
1273
  if (n > 0) {
1287
- stat.style.color = 'var(--success, #27ae60)';
1288
- stat.textContent = '100% — Scan complete: ' + n + ' device' + (n !== 1 ? 's' : '') + ' found.';
1289
- _showScanMsg('success', 'Scan complete: ' + n + ' SigenMicro device' +
1290
- (n !== 1 ? 's' : '') + ' found. Enable devices, then click Save selection.');
1274
+ box.style.background = '#d4edda';
1275
+ box.style.border = '1px solid #c3e6cb';
1276
+ box.style.color = '#155724';
1277
+ box.textContent = 'Scan complete: ' + n + ' SigenMicro device' +
1278
+ (n !== 1 ? 's' : '') + ' found. Enable devices, then click Save selection.';
1291
1279
  } else {
1292
- stat.style.color = 'var(--text-light)';
1293
- stat.textContent = '100% — Scan complete: no devices found in range ' + fromV + '-' + toV + '.';
1294
- _showScanMsg('info', 'Scan complete: no SigenMicro devices found in range ' +
1295
- fromV + '-' + toV + '. Verify Modbus connection and ID range.');
1280
+ box.style.background = '#d1ecf1';
1281
+ box.style.border = '1px solid #bee5eb';
1282
+ box.style.color = '#0c5460';
1283
+ box.textContent = 'Scan complete: no SigenMicro devices found in range ' +
1284
+ fromV + '-' + toV + '. Verify Modbus connection and ID range.';
1296
1285
  }
1297
1286
  document.getElementById('btnSaveMicro').disabled = (n === 0);
1298
1287
  document.getElementById('deviceCountBadge').textContent =
1299
1288
  n + ' device' + (n !== 1 ? 's' : '') + ' found';
1300
1289
  }
1301
1290
 
1291
+
1302
1292
  function _renderDeviceTable(devices) {
1303
1293
  var $container = $('#deviceListContainer');
1304
1294
  if (!devices || devices.length === 0) {
@@ -1360,10 +1350,23 @@ function saveSigenMicroDevices() {
1360
1350
  }
1361
1351
 
1362
1352
  function _showScanMsg(type, text) {
1363
- var el = document.getElementById('scanResultMsg');
1364
- el.className = 'scan-result-msg ' + type;
1365
- el.textContent = text;
1366
- el.style.display = 'block';
1353
+ // Redirect to _log box with appropriate color
1354
+ var box = document.getElementById('scanLogBox');
1355
+ if (!box) return;
1356
+ if (type === 'success') {
1357
+ box.style.background = '#d4edda';
1358
+ box.style.border = '1px solid #c3e6cb';
1359
+ box.style.color = '#155724';
1360
+ } else if (type === 'error') {
1361
+ box.style.background = '#f8d7da';
1362
+ box.style.border = '1px solid #f5c6cb';
1363
+ box.style.color = '#721c24';
1364
+ } else {
1365
+ box.style.background = '#d1ecf1';
1366
+ box.style.border = '1px solid #bee5eb';
1367
+ box.style.color = '#0c5460';
1368
+ }
1369
+ box.textContent = text;
1367
1370
  }
1368
1371
 
1369
1372
  function _esc(s) {
@@ -427,6 +427,43 @@
427
427
  "md": 6,
428
428
  "lg": 5,
429
429
  "xl": 4
430
+ },
431
+ "_divider_progress": {
432
+ "type": "divider",
433
+ "sm": 12,
434
+ "xs": 12,
435
+ "md": 12,
436
+ "lg": 12,
437
+ "xl": 12
438
+ },
439
+ "_progressLabel": {
440
+ "type": "staticText",
441
+ "text": "Live scan progress (updates while scan is running):",
442
+ "sm": 12,
443
+ "xs": 12,
444
+ "md": 12,
445
+ "lg": 12,
446
+ "xl": 12
447
+ },
448
+ "_scanProgress": {
449
+ "type": "state",
450
+ "label": "Progress",
451
+ "oid": "info.scanProgress",
452
+ "sm": 12,
453
+ "xs": 12,
454
+ "md": 12,
455
+ "lg": 12,
456
+ "xl": 12
457
+ },
458
+ "_scanStatus": {
459
+ "type": "state",
460
+ "label": "Status",
461
+ "oid": "info.scanStatus",
462
+ "sm": 12,
463
+ "xs": 12,
464
+ "md": 6,
465
+ "lg": 6,
466
+ "xl": 6
430
467
  }
431
468
  }
432
469
  },
@@ -0,0 +1,86 @@
1
+ {
2
+ "type": "panel",
3
+ "i18n": false,
4
+ "items": {
5
+ "_header": {
6
+ "type": "header",
7
+ "size": 4,
8
+ "text": "SigenMicro — Device Scanner",
9
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
10
+ },
11
+ "_desc": {
12
+ "type": "staticText",
13
+ "text": "Scan the Modbus bus for SigenMicro micro-inverters. The scan uses the existing Modbus connection and pauses polling temporarily. IDs used by Plant, Inverter, and Chargers are skipped automatically.",
14
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
15
+ },
16
+ "_divider1": {
17
+ "type": "divider",
18
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
19
+ },
20
+ "sigenMicroScanFrom": {
21
+ "type": "number",
22
+ "label": "Scan — first Modbus ID",
23
+ "min": 1, "max": 246,
24
+ "sm": 12, "xs": 12, "md": 3, "lg": 3, "xl": 3
25
+ },
26
+ "sigenMicroScanTo": {
27
+ "type": "number",
28
+ "label": "Scan — last Modbus ID",
29
+ "min": 1, "max": 246,
30
+ "sm": 12, "xs": 12, "md": 3, "lg": 3, "xl": 3
31
+ },
32
+ "_divider2": {
33
+ "type": "divider",
34
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
35
+ },
36
+ "scan_btn": {
37
+ "type": "sendTo",
38
+ "label": "🔍 Start Scan",
39
+ "command": "scanSigenMicro",
40
+ "jsonData": true,
41
+ "variant": "contained",
42
+ "color": "primary",
43
+ "icon": "search",
44
+ "sm": 12, "xs": 12, "md": 4, "lg": 3, "xl": 3
45
+ },
46
+ "_divider3": {
47
+ "type": "divider",
48
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
49
+ },
50
+ "_progressTitle": {
51
+ "type": "staticText",
52
+ "text": "Scan progress (live — updates automatically during scan):",
53
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
54
+ },
55
+ "_scanProgress": {
56
+ "type": "state",
57
+ "label": "Current step",
58
+ "oid": "info.scanProgress",
59
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
60
+ },
61
+ "_scanStatus": {
62
+ "type": "state",
63
+ "label": "Status",
64
+ "oid": "info.scanStatus",
65
+ "sm": 12, "xs": 12, "md": 6, "lg": 4, "xl": 4
66
+ },
67
+ "_divider4": {
68
+ "type": "divider",
69
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
70
+ },
71
+ "_devicesTitle": {
72
+ "type": "staticText",
73
+ "text": "Found devices (stored after scan — use the adapter config to enable/disable individual devices):",
74
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
75
+ },
76
+ "sigenMicroDevices": {
77
+ "type": "text",
78
+ "label": "Discovered devices (JSON)",
79
+ "readOnly": true,
80
+ "minRows": 4,
81
+ "validateJson": true,
82
+ "allowEmpty": true,
83
+ "sm": 12, "xs": 12, "md": 12, "lg": 12, "xl": 12
84
+ }
85
+ }
86
+ }
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "sigenergy",
4
- "version": "1.6.2",
4
+ "version": "1.7.0",
5
5
  "news": {
6
+ "1.7.0": {
7
+ "en": "Replace HTML adminTab iframe with native jsonConfig tab (sigenmicro-tab.json); use type:state to show live scan progress (info.scanProgress state) directly in admin 7 React UI without any custom JavaScript",
8
+ "de": "HTML-adminTab-iframe durch nativen jsonConfig-Tab ersetzt (sigenmicro-tab.json); type:state zeigt live Scan-Fortschritt (info.scanProgress) direkt in Admin-7-React-UI ohne eigenes JavaScript",
9
+ "ru": "HTML-iframe заменён нативным jsonConfig-вкладкой; type:state показывает прогресс сканирования (info.scanProgress) в реальном времени",
10
+ "pt": "iframe HTML substituido por aba jsonConfig nativa; type:state mostra progresso em tempo real sem JavaScript",
11
+ "nl": "HTML-iframe vervangen door native jsonConfig-tab; type:state toont live scanvoortgang zonder custom JS",
12
+ "fr": "iframe HTML remplace par onglet jsonConfig natif; type:state montre la progression en temps reel sans JS",
13
+ "it": "iframe HTML sostituito da tab jsonConfig nativo; type:state mostra avanzamento in tempo reale senza JS",
14
+ "es": "iframe HTML reemplazado por pestaña jsonConfig nativa; type:state muestra progreso en tiempo real sin JS",
15
+ "pl": "iframe HTML zastapiony natywna karta jsonConfig; type:state pokazuje postep w czasie rzeczywistym bez JS",
16
+ "uk": "HTML-iframe замiнено нативною jsonConfig-вкладкою; type:state показує прогрес в реальному часi без JS",
17
+ "zh-cn": "将HTML adminTab iframe替换为原生jsonConfig标签页;type:state直接在Admin 7 React UI中显示实时扫描进度"
18
+ },
19
+ "1.6.3": {
20
+ "en": "Fix scan progress display: replace all CSS-variable-dependent elements with a single always-visible log box using hardcoded colors; each chunk callback updates text immediately",
21
+ "de": "Scan-Fortschritt: alle CSS-Variablen-abhaengigen Elemente durch eine immer sichtbare Log-Box mit hartcodierten Farben ersetzt; jeder Chunk-Callback aktualisiert Text sofort",
22
+ "ru": "Прогресс сканирования: все элементы с CSS-переменными заменены постоянно видимым логом с жёсткими цветами",
23
+ "pt": "Progresso scan: todos elementos dependentes de CSS-vars substituidos por caixa de log sempre visivel com cores fixas",
24
+ "nl": "Scan voortgang: alle CSS-var-afhankelijke elementen vervangen door altijd zichtbare logbox met vaste kleuren",
25
+ "fr": "Progression scan: tous les elements CSS-var remplacés par une boite de log toujours visible avec couleurs fixes",
26
+ "it": "Avanzamento scan: tutti gli elementi CSS-var sostituiti da una log box sempre visibile con colori fissi",
27
+ "es": "Progreso scan: todos los elementos CSS-var reemplazados por caja de log siempre visible con colores fijos",
28
+ "pl": "Postep skanowania: wszystkie elementy CSS-var zastapione zawsze widoczna skrzynka logu ze stalymi kolorami",
29
+ "uk": "Прогрес сканування: усi CSS-var елементи замiнено постiйно видимим лог-боксом з фiксованими кольорами",
30
+ "zh-cn": "修复扫描进度显示:将所有CSS变量依赖元素替换为使用硬编码颜色的始终可见日志框"
31
+ },
6
32
  "1.6.2": {
7
33
  "en": "Fix scan: restore proven chunked sendTo approach (3 IDs per call); add dual progress display: direct text update per chunk + getState polling from info.scanProgress state",
8
34
  "de": "Scan: bewaehrten Chunked-sendTo-Ansatz wiederhergestellt (3 IDs pro Aufruf); duale Fortschrittsanzeige: direktes Text-Update pro Chunk + getState-Polling aus info.scanProgress",
@@ -213,10 +239,10 @@
213
239
  "adminTab": {
214
240
  "singleton": true,
215
241
  "name": {
216
- "en": "Sigenergy",
217
- "de": "Sigenergy"
242
+ "en": "SigenMicro",
243
+ "de": "SigenMicro"
218
244
  },
219
- "link": "%web_protocol%://%ip%:%web_port%/adapter/sigenergy/index.html"
245
+ "link": "sigenmicro-tab.json"
220
246
  },
221
247
  "supportCustoms": false,
222
248
  "schedule": "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.sigenergy",
3
- "version": "1.6.2",
3
+ "version": "1.7.0",
4
4
  "description": "ioBroker adapter for Sigenergy solar energy systems via Modbus TCP/RTU",
5
5
  "author": {
6
6
  "name": "ssbingo",