iobroker.sun2000 2.5.0 → 2.5.1

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
@@ -69,8 +69,18 @@ The sun2000 adapter calculates how much of your self-generated solar energy is a
69
69
  Placeholder for the next version (at the beginning of the line):
70
70
  ### **WORK IN PROGRESS**
71
71
  -->
72
+ ### 2.5.1 (2026-06-29)
73
+ - (bolliy) fix: update service queue logic ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283))
74
+ - (bolliy) statistics fix: adjust reset handling logic to treat significant drops in value as potential resets
75
+
72
76
  ### 2.5.0 (2026-06-09)
73
- * statistics: added live power chart (statistics.jsonLive)
77
+ * (bolliy) statistics: added live power chart (statistics.jsonLive)
78
+ * (bolliy) statistics: consumption breakdown — breakdown values are now subtracted from the total `consumption` entry so the lower chart panel shows the remainder separately from the breakdown series
79
+ * (bolliy) statistics: `xAxisFormatter` for the live chart only labels full-hour ticks to avoid clutter
80
+ * (bolliy) statistics: tooltip formatter refactored — `formatTooltipValue(unit, negative, decimals)` helper used consistently across all series
81
+ * (bolliy) statistics: if no battery is present, the charts are generated without battery information (SOC, charge, discharge).
82
+ * (bolliy) fix emma: update register addresses of meter.activePowerL1-L3 ([#282](https://github.com/bolliy/ioBroker.sun2000/issues/282))
83
+ * (bolliy) requires node.js >= 22
74
84
 
75
85
  ### 2.4.5 (2026-05-14)
76
86
  * statistics fix: return weekly range up to current Monday
@@ -86,14 +96,6 @@ The sun2000 adapter calculates how much of your self-generated solar energy is a
86
96
  * statistics: computed values `selfSufficiency` and `selfConsumption` calculated automatically in all time-series states
87
97
  * statistics: data placeholders (`%%solarYield%%`, `%%selfSufficiency%%` etc.) and negated variants (`%%gridExportNeg%%` etc.) for mirrored chart layouts
88
98
 
89
- ### 2.4.2 (2026-04-04)
90
- * fix test-and-release: deploy with 24.x
91
- * statistics: flexcharts integration — built-in Apache ECharts configuration with bar and line chart support
92
- * statistics: day-break visualization with alternating shaded areas for hourly charts
93
- * statistics: per chart-type templates (`statistics.flexCharts.template.hourly` etc.) for full ECharts customization including functions
94
- * statistics: data placeholders (`%%solarYield%%`, `%%gridExport%%` etc.) allow complete chart layout control via template states
95
- * statistics: chart output states (`statistics.flexCharts.jsonOutput.hourly` etc.) updated automatically each hour
96
-
97
99
  [Older changelogs can be found there](CHANGELOG_OLD.md)
98
100
 
99
101
  ## License
@@ -389,19 +389,6 @@
389
389
  "type": "panel",
390
390
  "label": "Statistics",
391
391
  "items": {
392
- "staticLink1": {
393
- "type": "staticLink",
394
- "text": "Information and settings regarding the statistics explained in the wiki",
395
- "href": "https://github.com/bolliy/ioBroker.sun2000/wiki/Statistk-(statistics)",
396
- "button": true,
397
- "icon": "info",
398
- "newLine": true,
399
- "xs": 12,
400
- "sm": 12,
401
- "md": 6,
402
- "lg": 3,
403
- "xl": 3
404
- },
405
392
  "stat_liveInterval": {
406
393
  "type": "number",
407
394
  "label": "Live chart interval (min)",
@@ -414,6 +401,19 @@
414
401
  "md": 6,
415
402
  "lg": 4,
416
403
  "xl": 4
404
+ },
405
+ "staticLink1": {
406
+ "type": "staticLink",
407
+ "text": "Information and settings regarding the statistics explained in the wiki",
408
+ "href": "https://github.com/bolliy/ioBroker.sun2000/wiki/Statistk-(statistics)",
409
+ "button": true,
410
+ "icon": "info",
411
+ "newLine": true,
412
+ "xs": 12,
413
+ "sm": 12,
414
+ "md": 6,
415
+ "lg": 3,
416
+ "xl": 3
417
417
  }
418
418
  }
419
419
 
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "sun2000",
4
- "version": "2.5.0",
4
+ "version": "2.5.1",
5
5
  "news": {
6
+ "2.5.1": {
7
+ "en": "fix: update service queue logic ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283))\nstatistics fix: adjust reset handling logic to treat significant drops in value as potential resets",
8
+ "de": "fix: update service queue logic ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283))\nstatistics fix: anpassung der reset-handling-logik, um signifikante wertverluste als potenzielle resets zu behandeln",
9
+ "ru": "исправить: обновить логику очереди обслуживания ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283))\nисправление статистики: настройте логику обработки сбросов, чтобы рассматривать значительное снижение стоимости как потенциальные сбросы",
10
+ "pt": "fix: update service fila logic ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283))\nstatistics fix: ajuste a lógica de manipulação de reset para tratar quedas significativas no valor como resets potenciais",
11
+ "nl": "fix: update service wachtrij logica ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283))\nstatistieken fix: aanpassen reset handling logica om significante dalingen in waarde te behandelen als potentiële ressets",
12
+ "fr": "correction: mise à jour de la logique de la file d'attente de service ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283))\ncorrection statistique: ajuster la logique de réinitialisation pour traiter les baisses significatives de valeur comme des réinitialisations potentielles",
13
+ "it": "correzione: aggiornamento della coda di servizio logica ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283))\nstatistiche fix: regolare la gestione del reset logica per trattare gocce significative di valore come potenziali reset",
14
+ "es": "fix: update service queue logic ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283))\nestadística fijado: ajuste la lógica de manejo de reajuste para tratar gotas significativas en valor como posibles reajustes",
15
+ "pl": "fix: update service queue logic ([# 283] (https: / / github.com / bolliy / ioBroker.sun2000 / discussions / 283))\nstatystyki fix: dostosować logikę obsługi reset w celu traktowania znaczących spadku wartości jako potencjalnych reset",
16
+ "uk": "виправити: оновити логіку черги ([#283](https://github.com/bolliy/ioBroker.sun2000/discussions/283)))\nвиправлення статистичних даних: регулюйте логіку обробки скидання для обробки значних крапель у значення як потенційні скидання",
17
+ "zh-cn": "fix:更新服务队列逻辑([#283] (https://github.com/bolliy/ioBroker.sun2000/discussions/283))\n统计修补:调整重置处理逻辑,将价值大幅下降视为潜在重置"
18
+ },
6
19
  "2.5.0": {
7
20
  "en": "statistics: added live power chart (statistics.jsonLive)\nrequires node.js >= 22",
8
21
  "de": "statistiken: Live Power Chart hinzugefügt (statistics.jsonLive)\nrequires node.js >= 22",
@@ -80,19 +93,6 @@
80
93
  "pl": "fix: kolejność przypisywania bitów poprawionych alarmów JSON\nw języku angielskim:\nstatystyki: Zagregowane historyczne zbierane punkty danych w skróty czasowe (np. godzinowe, dzienne, miesięczne, roczne). Dane są przechowywane w ścieżce \"statystyki\" jako JSON.",
81
94
  "uk": "фіксувати: порядок відведення біту виправлено тривоги Сонце\nновий стан `inverter.x.emma.activeAlarmSN` і `inverter.x.emma.HistoricalAlarmSN` : emma тривоги [#226](https://github.com/bolliy/ioBroker.sun2000/products/226)\nстатистика: Агрегати історичних зібраних точок даних на часові суми (наприклад, час, щоденно, щомісяця, рік). Дані зберігаються на шляху `statistics` як JSON.",
82
95
  "zh-cn": "固定: 修改提醒的位任务顺序 贾森\n新状态`inverter.x.emma.active AlarmSN ' 和`inverter.x.emma. Historical AlarmSN ' : emma警报[# 226](https://github.com/bolliy/ioBroker.sun2000/issues/226)\n统计: 将历史收集的数据点汇总为时间摘要(如小时、每日、每月、每年)。 数据作为JSON储存在路径`统计'中."
83
- },
84
- "2.3.7": {
85
- "en": "deleted deprecated state `collected.usableSurplusPower`",
86
- "de": "gelöschter deprecated state `collect.usableSurplusPower `",
87
- "ru": "удаленное устаревшее состояние 'collected.usableSurplusPower пункт",
88
- "pt": "estado desactualizado excluído `colected.usableSurplusPower `",
89
- "nl": "verwijderde verouderde staat Wat",
90
- "fr": "état obsolète supprimé `collected.usableSurplusPower \"",
91
- "it": "cancellato stato deprecato `colletto.usableSurplusPower #",
92
- "es": "borrado estado deprecatado `collected.usableSurplusPower `",
93
- "pl": "usunięty stan zdepregatowany \"collected.usableSurplusPower '",
94
- "uk": "вилучена депресована держава `collected.usableSurplusPower й",
95
- "zh-cn": "删除已贬值状态“ 已收集。 可使用 SurplusPower `"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -286,7 +286,7 @@
286
286
  "de": "Gerät angeschlossen"
287
287
  },
288
288
  "type": "boolean",
289
- "role": "indicator.connected",
289
+ "role": "sensor",
290
290
  "read": true,
291
291
  "write": false,
292
292
  "desc": "Is the device connected?"
@@ -301,7 +301,7 @@
301
301
  "de": "Geräte IP"
302
302
  },
303
303
  "type": "string",
304
- "role": "indicator.ip",
304
+ "role": "text",
305
305
  "read": true,
306
306
  "write": false,
307
307
  "desc": "Device IP address"
@@ -316,7 +316,7 @@
316
316
  "de": "Modbusport"
317
317
  },
318
318
  "type": "number",
319
- "role": "indicator.port",
319
+ "role": "value",
320
320
  "read": true,
321
321
  "write": false,
322
322
  "desc": "Device modbus port"
@@ -331,7 +331,7 @@
331
331
  "de": "Modbus IDs der Geräte"
332
332
  },
333
333
  "type": "string",
334
- "role": "indicator.id",
334
+ "role": "text",
335
335
  "read": true,
336
336
  "write": false,
337
337
  "desc": "device modbus IDs"
@@ -346,7 +346,7 @@
346
346
  "de": "modbus Update Intervall"
347
347
  },
348
348
  "type": "number",
349
- "role": "value",
349
+ "role": "time.interval",
350
350
  "read": true,
351
351
  "write": false,
352
352
  "desc": "modbus update interval",
@@ -362,7 +362,7 @@
362
362
  "de": "modbus timeout"
363
363
  },
364
364
  "type": "number",
365
- "role": "value",
365
+ "role": "time.timeout",
366
366
  "read": true,
367
367
  "write": false,
368
368
  "desc": "modbus timeout",
@@ -378,7 +378,7 @@
378
378
  "de": "modbus delay"
379
379
  },
380
380
  "type": "number",
381
- "role": "value",
381
+ "role": "time.span",
382
382
  "read": true,
383
383
  "write": false,
384
384
  "desc": "delay between modbus requests",
@@ -394,7 +394,7 @@
394
394
  "de": "modbus connect delay"
395
395
  },
396
396
  "type": "number",
397
- "role": "value",
397
+ "role": "time.span",
398
398
  "read": true,
399
399
  "write": false,
400
400
  "desc": "delay after modbus connected",
@@ -410,7 +410,7 @@
410
410
  "de": "Gesundheitsinformation"
411
411
  },
412
412
  "type": "string",
413
- "role": "indicator.alarm.health",
413
+ "role": "json",
414
414
  "read": true,
415
415
  "write": false,
416
416
  "desc": "",
@@ -426,7 +426,7 @@
426
426
  "de": "modbus Server aktiv"
427
427
  },
428
428
  "type": "boolean",
429
- "role": "value",
429
+ "role": "sensor",
430
430
  "read": true,
431
431
  "write": false,
432
432
  "desc": "",
@@ -12,7 +12,7 @@ class ServiceQueueMap {
12
12
  this._eventMap = new Map();
13
13
  this._initialized = false;
14
14
  this._name = 'inverter control';
15
- this._emmaAvailable = this.adapter.devices.find(d => d.driverClass == driverClasses.emma);
15
+ //this._emmaAvailable = this.adapter.devices.find(d => d.driverClass == driverClasses.emma);
16
16
 
17
17
  this.serviceFields = [
18
18
  {
@@ -9,6 +9,7 @@ class DriverBase {
9
9
  this.adapter = stateInstance.adapter;
10
10
  this.stateCache = stateInstance.stateCache;
11
11
  this.deviceInfo = device;
12
+
12
13
  this._modbusClient = null; //NEW!!
13
14
  //https://wiki.selfhtml.org/wiki/JavaScript/Operatoren/Nullish_Coalescing_Operator
14
15
  //https://stackoverflow.com/questions/2851404/what-does-options-options-mean-in-javascript
@@ -77,7 +78,7 @@ class DriverBase {
77
78
  * @returns {boolean} True if the device is in test mode, false otherwise
78
79
  */
79
80
  isTestMode() {
80
- return false;
81
+ return this.deviceInfo.testMode ?? false;
81
82
  }
82
83
 
83
84
  logHoldingRegisters(startAddr, length) {
@@ -10,6 +10,10 @@ class Emma extends DriverBase {
10
10
  ...options,
11
11
  });
12
12
 
13
+ if (this.isTestMode()) {
14
+ this._modbusId = 1;
15
+ }
16
+
13
17
  this.control = new ServiceQueueMap(this.adapter, this.deviceInfo); //emma service queue
14
18
 
15
19
  //https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/stateroles.md
@@ -590,6 +594,10 @@ class Emma extends DriverBase {
590
594
  this.log.debug(`### PostHook indentification for Inverter Sun2000 - ret: ${JSON.stringify(ret)}`);
591
595
  //check if inverter sun2000 already exists
592
596
  const inverterExist = this.adapter.devices.findIndex(x => x.driverClass === driverClasses.inverter);
597
+ //TestMode
598
+ if (this.isTestMode() && ret.length === 0) {
599
+ ret.push({ slave_id: 1 });
600
+ }
593
601
  for (const [i, inverter] of ret.entries()) {
594
602
  this.log.info(`${this._name} identify subdevice an inverter sun2000: OID=${inverter.obj_id}, modbus id: ${inverter.slave_id}`);
595
603
  //unless inverters are configured, add them
@@ -804,16 +812,6 @@ class Emma extends DriverBase {
804
812
 
805
813
  this.registerFields.push.apply(this.registerFields, newFields);
806
814
  }
807
-
808
- // Override
809
- /**
810
- * Checks if the device is in test mode
811
- * @returns {boolean} True if the device is in test mode, false otherwise
812
- * A device is considered in test mode if its modbus ID is not equal to 0.
813
- */
814
- isTestMode() {
815
- return this.modbusId !== 0;
816
- }
817
815
  }
818
816
 
819
817
  class EmmaCharger extends DriverBase {
@@ -103,7 +103,7 @@ class InverterSun2000 extends DriverBase {
103
103
  this.activePowerSum.setStart(state?.val, state?.ts);
104
104
  }
105
105
  });
106
-
106
+ /*
107
107
  this.chargeSumHelper = new RiemannSum();
108
108
  this.adapter.getState(`${this.deviceInfo.path}.battery.currentDayChargeCapacity`, (err, state) => {
109
109
  if (!err && state) {
@@ -116,6 +116,7 @@ class InverterSun2000 extends DriverBase {
116
116
  this.dischargeSumHelper.setStart(state?.val, state?.ts, false);
117
117
  }
118
118
  });
119
+ */
119
120
 
120
121
  this.control = new ServiceQueueMap(this.adapter, this.deviceInfo);
121
122
 
@@ -235,6 +236,7 @@ class InverterSun2000 extends DriverBase {
235
236
  ],
236
237
  //Check if the address field is active
237
238
  checkIfActive: () => this._batteryExists(),
239
+ /*
238
240
  postHook: path => {
239
241
  //calculate daily charge and discharge capacity
240
242
  const chargeDischargePower = this.stateCache.get(`${path}battery.chargeDischargePower`)?.value ?? 0;
@@ -264,6 +266,7 @@ class InverterSun2000 extends DriverBase {
264
266
  this.stateCache.set(`${path}battery.statistics.currentDayChargeCapacity`, this.chargeSumHelper.sum, { type: 'number', stored: true }); //stored for statistics
265
267
  this.stateCache.set(`${path}battery.statistics.currentDayDischargeCapacity`, this.dischargeSumHelper.sum, { type: 'number', stored: true }); //stored for statistics
266
268
  },
269
+ */
267
270
  },
268
271
  {
269
272
  address: 32064,
@@ -280,7 +280,7 @@ class ModbusServer {
280
280
  }
281
281
 
282
282
  wait(ms) {
283
- return new Promise(resolve => setTimeout(resolve, ms));
283
+ return new Promise(resolve => this.adapter.setTimeout(resolve, ms));
284
284
  }
285
285
  }
286
286
 
package/lib/register.js CHANGED
@@ -22,6 +22,7 @@ class Registers {
22
22
 
23
23
  this.statistics = new statistics(adapterInstance, this.stateCache);
24
24
 
25
+ /*
25
26
  for (const device of this.adapter.devices) {
26
27
  //DriverInfo Instance or Sdongle
27
28
  const handler = getDriverHandler(device.driverClass);
@@ -29,6 +30,7 @@ class Registers {
29
30
  device.instance = new handler(this, device);
30
31
  }
31
32
  }
33
+ */
32
34
 
33
35
  //Upgrade to v2.3.7 - deleted deprecated states
34
36
  if (
@@ -363,16 +365,6 @@ class Registers {
363
365
  role: 'value.power.produced',
364
366
  desc: 'Riemann sum of external power',
365
367
  },
366
- /*
367
- {
368
- id: 'collected.dailyActiveEnergy',
369
- name: 'Active Energy today',
370
- type: 'number',
371
- unit: 'kWh',
372
- role: 'value.power.produced',
373
- desc: 'Amount of Riemann sum of sum of active power',
374
- },
375
- */
376
368
  ],
377
369
  fn: inverters => {
378
370
  let inYield = 0;
@@ -510,15 +502,14 @@ class Registers {
510
502
 
511
503
  async storeStates() {
512
504
  for (const stateEntry of this.stateCache.values()) {
513
- //if (stateEntry?.storeType === storeType.never) continue;
514
505
  if (stateEntry.stored) {
515
506
  continue;
516
507
  }
517
- //if (stateEntry?.storeType !== storeType.always && stateEntry.stored) continue;
518
508
  if (stateEntry.value !== null) {
519
509
  try {
520
510
  stateEntry.stored = true;
521
- await this.adapter.setState(stateEntry.id, { val: stateEntry.value, ack: true });
511
+ const valueToStore = typeof stateEntry.value === 'number' ? this.stateCache.round(stateEntry.value, 3) : stateEntry.value;
512
+ await this.adapter.setState(stateEntry.id, { val: valueToStore, ack: true });
522
513
  this.adapter.logger.debug(`Fetched ${stateEntry.id}, val=${stateEntry.value}`);
523
514
  } catch (err) {
524
515
  stateEntry.stored = false;
@@ -543,7 +534,7 @@ class Registers {
543
534
  * @returns {Promise<number>} - Returns a promise that resolves to the number of states updated.
544
535
  */
545
536
  async updateStates(device, modbusClient, refreshRate, duration) {
546
- //this.adapter.log.debug('### DeviceInfo: '+device.index+' '+JSON.stringify(device.instance.info));
537
+ //this.adapter.log.debug(`### DeviceInfo: ${device.index} ${JSON.stringify(device.instance?.info)}`);
547
538
  if (!device.instance) {
548
539
  const handler = getDriverHandler(device.driverClass);
549
540
  if (handler) {
@@ -170,7 +170,7 @@ class ConsumptionBreakdown {
170
170
  if (state && state?.val != null) {
171
171
  const raw = Number(state.val);
172
172
  if (!isNaN(raw)) {
173
- this.stateCache.set(cacheKey, raw / bd.gain || 1, { type: 'number', stored: true });
173
+ this.stateCache.set(cacheKey, raw / (bd.gain || 1), { type: 'number', stored: true });
174
174
  } else {
175
175
  this.adapter.logger.debug(`consumptionBreakdown: '${bd.targetPath}' value is not a number: ${state.val}`);
176
176
  }
@@ -317,29 +317,33 @@ class statistics {
317
317
  for (const stat of effectiveStats) {
318
318
  if (stat.type === statisticsType.computed) continue;
319
319
 
320
- const source = Math.round((Number(this.stateCache.get(stat.sourceId)?.value ?? 0) + Number.EPSILON) * 1000) / 1000;
320
+ //const source = Math.round((Number(this.stateCache.get(stat.sourceId)?.value ?? 0) + Number.EPSILON) * 1000) / 1000;
321
+ const source = this.stateCache.get(stat.sourceId)?.value;
321
322
  if (source === null || source === undefined) {
322
323
  if (!stat._isBreakdown) this.adapter.logger.debug(`Source state ${stat.sourceId} not found statistic hook`);
323
324
  continue;
324
325
  }
325
326
  let value = source;
326
327
  if (stat.type === statisticsType.delta || stat.type === statisticsType.deltaReset) {
327
- const lastTotal = Number(last[stat.targetPath]?.total ?? 0);
328
- if (stat.type === statisticsType.deltaReset) {
329
- if (fromDate.getTime() !== periodStart.getTime()) value -= lastTotal;
328
+ if (last[stat.targetPath]?.total === undefined) {
329
+ this.adapter.logger.debug(`No total value for ${stat.targetPath} in last entry, delta set to 0`);
330
+ value = 0;
330
331
  } else {
331
- if (last[stat.targetPath]?.total === undefined) {
332
- this.adapter.logger.debug(`No total value for ${stat.targetPath} in last entry, delta set to 0`);
333
- value = 0;
332
+ const lastTotal = Number(last[stat.targetPath]?.total ?? 0);
333
+ if (stat.type === statisticsType.deltaReset) {
334
+ // Handle resets: If the new value is significantly lower than the last total value, a reset has likely occurred. Otherwise, the difference relative to the last total value is treated as consumption.
335
+ if (value > lastTotal * 0.8) {
336
+ value -= lastTotal;
337
+ }
334
338
  } else {
335
339
  value -= lastTotal;
336
340
  }
337
341
  }
338
342
  }
339
- //value = Math.round((Number(value) + Number.EPSILON) * 1000) / 1000;
343
+ value = Math.round((Number(value) + Number.EPSILON) * 1000) / 1000;
340
344
  entry[stat.targetPath] = { value: Number(value.toFixed(3)) };
341
345
  if (stat.type === statisticsType.delta || stat.type === statisticsType.deltaReset) {
342
- entry[stat.targetPath].total = Number(source.toFixed(3));
346
+ entry[stat.targetPath].total = Number(source.toFixed(5));
343
347
  }
344
348
  entry[stat.targetPath].unit = stat.unit || 'kWh';
345
349
  }
@@ -585,7 +589,6 @@ class statistics {
585
589
  for (const stat of this._effectiveLiveStats()) {
586
590
  // Only convert energy (kWh) deltas — leave SOC (%) and computed (%) as-is
587
591
  if (stat.type !== statisticsType.deltaReset && stat.type !== statisticsType.delta) continue;
588
- //if ((stat.unit || 'kWh') !== 'kWh') continue; //??
589
592
  if (last[stat.targetPath] === undefined) continue;
590
593
 
591
594
  const kw = Math.round(last[stat.targetPath].value * convFactor * 1000) / 1000;
@@ -843,7 +846,9 @@ class statistics {
843
846
  await this._initializeLiveTimer();
844
847
  this._initializeTask();
845
848
 
846
- this.adapter.subscribeStates(`${this._path}.*`);
849
+ this.adapter.subscribeStates(`${this._path}.flexCharts.template*`);
850
+ this.adapter.subscribeStates(`${this._path}.dataDef*`);
851
+
847
852
  this._initialized = true;
848
853
  }
849
854
 
package/lib/tools.js CHANGED
@@ -57,6 +57,7 @@ class Logging {
57
57
  class StateMap {
58
58
  constructor() {
59
59
  this.stateMap = new Map();
60
+ this._mapDecimalPlaces = 10; //internal
60
61
  }
61
62
 
62
63
  round(num, decimalPlaces = 0) {
@@ -65,7 +66,12 @@ class StateMap {
65
66
  return Math.round(n) / p;
66
67
  }
67
68
 
68
- get(id) {
69
+ get(id, decimalPlaces = -1) {
70
+ const mapItem = this.stateMap.get(id);
71
+ if (decimalPlaces >= 0 && mapItem?.type === 'number') {
72
+ return { ...mapItem, value: this.round(mapItem.value, decimalPlaces) };
73
+ }
74
+
69
75
  return this.stateMap.get(id);
70
76
  }
71
77
 
@@ -78,7 +84,7 @@ class StateMap {
78
84
  if (value !== null) {
79
85
  if (options?.type == 'number') {
80
86
  //value = Math.round((value + Number.EPSILON) * 1000) / 1000; //3rd behind
81
- value = this.round(value, 3);
87
+ value = this.round(Number(value), this._mapDecimalPlaces);
82
88
  }
83
89
 
84
90
  const existing = this.get(id); //existing entry
@@ -86,6 +92,7 @@ class StateMap {
86
92
  const mapOptions = {
87
93
  id: id,
88
94
  value: value,
95
+ type: options?.type ? options.type : existing?.type ? existing.type : typeof value,
89
96
  stored: existing?.stored ? existing.stored : false,
90
97
  };
91
98
  if (options?.renew || existing?.value !== value) {
package/main.js CHANGED
@@ -556,9 +556,9 @@ class Sun2000 extends utils.Adapter {
556
556
  this.devices.push({
557
557
  index: 0,
558
558
  duration: 0,
559
- //modbusId: 1, --> testMode
560
559
  modbusId: 0,
561
560
  meter: true,
561
+ //testMode: true,
562
562
  driverClass: driverClasses.emma,
563
563
  });
564
564
  }
@@ -685,10 +685,10 @@ class Sun2000 extends utils.Adapter {
685
685
  this.pollingTimer && this.clearTimeout(this.pollingTimer);
686
686
  this.mitnightTimer && this.clearTimeout(this.mitnightTimer);
687
687
  this.watchDogHandle && this.clearInterval(this.watchDogHandle);
688
- this.modbusClient && this.modbusClient.close();
689
688
  if (typeof this.state.destroy === 'function') {
690
689
  this.state.destroy();
691
690
  }
691
+ this.modbusClient && this.modbusClient.close();
692
692
  this.setState('info.connection', false, true);
693
693
  callback();
694
694
  } catch {
@@ -707,19 +707,20 @@ class Sun2000 extends utils.Adapter {
707
707
  if (state) {
708
708
  // The state was changed
709
709
  if (state.ack) {
710
- //this.logger.info(`state ${id} was changed but the ack flag was set. Therefore, no processing takes place!`);
710
+ //this.logger.warn(`state ${id} was changed but the ack flag was set. Therefore, no processing takes place!`);
711
711
  return;
712
712
  }
713
713
  const idArray = id.split('.');
714
714
  // sun2000.0.inverter.0.control
715
715
  if (idArray[2] == 'inverter') {
716
- const control = this.devices[Number(idArray[3])].instance.control;
717
- if (control) {
716
+ const inverter = this.devices.find(d => d.driverClass == driverClasses.inverter && d.index == Number(idArray[3]));
717
+ //const control = this.devices[Number(idArray[3])].instance.control;
718
+ if (inverter && inverter.instance.control) {
718
719
  let serviceId = idArray[5];
719
720
  for (let i = 6; i < idArray.length; i++) {
720
721
  serviceId += `.${idArray[i]}`;
721
722
  }
722
- control.set(serviceId, state);
723
+ inverter.instance.control.set(serviceId, state);
723
724
  }
724
725
  //this.log.info(`### state ${id} changed: ${state.val} (ack = ${state.ack})`);
725
726
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.sun2000",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "sun2000",
5
5
  "author": {
6
6
  "name": "bolliy",
@@ -27,11 +27,11 @@
27
27
  "node": ">= 22"
28
28
  },
29
29
  "dependencies": {
30
- "@iobroker/adapter-core": "^3.3.2",
30
+ "@iobroker/adapter-core": "^3.4.1",
31
+ "javascript-stringify": "^2.1.0",
31
32
  "modbus-serial": "^8.0.25",
32
33
  "suncalc2": "^1.8.1",
33
- "tcp-port-used": "^1.0.2",
34
- "javascript-stringify": "^2.1.0"
34
+ "tcp-port-used": "^1.0.3"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@alcalzone/release-script": "^5.2.1",
@@ -42,7 +42,7 @@
42
42
  "@iobroker/eslint-config": "^2.3.4",
43
43
  "@iobroker/testing": "^5.2.2",
44
44
  "@tsconfig/node22": "^22.0.5",
45
- "@types/node": "^22.19.19",
45
+ "@types/node": "^22.19.21",
46
46
  "typescript": "~6.0.3"
47
47
  },
48
48
  "main": "main.js",