iobroker.sun2000 2.3.6 → 2.4.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
@@ -30,7 +30,7 @@ Feel free to follow the discussions in the german [iobroker forum](https://forum
30
30
  ## Requirements
31
31
  * Node.js 20 or higher
32
32
  * ioBroker host (js-controller) 6.0.11 or higher
33
- * ioBroker admin 7.6.17 or higher
33
+ * ioBroker admin 7.6.20 or higher
34
34
 
35
35
  ## Documentation
36
36
 
@@ -59,12 +59,24 @@ browse in the [wiki](https://github.com/bolliy/ioBroker.sun2000/wiki)
59
59
  * Huawei [`SmartLogger`](https://github.com/bolliy/ioBroker.sun2000/wiki/SmartLogger) integration: Monitors and manages the PV power system. The adapter saves the collected data in the same way as it does when read out the inverter directly.
60
60
  * Huawei [`Emma`](https://github.com/bolliy/ioBroker.sun2000/wiki/Emma) integration: The Modbus access, network connectivity (WiFi and Ethernet) and the DDSU/DTSU-666H smart meter functions are integrated in one unit - the use of the Sdongle becomes redundant. In addition Huawei EV chargers and load shedding/control (via selected Shelly devices) are supported and "intelligent" controlled.
61
61
  * Huawei [`Charger`](https://github.com/bolliy/ioBroker.sun2000/issues/171) via Emma integration: The chargers are automatically recognized and the data is saved in their own path.
62
+ * Statistics: Aggregates historical collected datapoints into time-based summaries (e.g. hourly, daily, monthly, yearly).
63
+ In the medium term, these statistics should be able to be visualized in ioBroker VIS using the flexcharts adapter to create interactive diagrams for inverter performance and energy production.
64
+
65
+
62
66
 
63
67
  ## Changelog
64
68
  <!--
65
69
  Placeholder for the next version (at the beginning of the line):
66
70
  ### **WORK IN PROGRESS**
67
71
  -->
72
+ ### 2.4.0 (2026-03-14)
73
+ * fix: the order of bit assignment corrected of alarmsJSON
74
+ * new state `inverter.x.emma.activeAlarmSN` and `inverter.x.emma.HistoricalAlarmSN` : emma alarms [#226](https://github.com/bolliy/ioBroker.sun2000/issues/226)
75
+ * statistics: Aggregates historical collected datapoints into time-based summaries (e.g. hourly, daily, monthly, yearly). The data is stored in the path `statistics` as JSON.
76
+
77
+ ### 2.3.7 (2026-02-01)
78
+ * deleted deprecated state `collected.usableSurplusPower`
79
+
68
80
  ### 2.3.6 (2026-01-29)
69
81
  * dependency and configuration updates
70
82
  * new state `inverter.x.derived.alarmsJSON` : json array with intverter alarms (id, name, level) [#226](https://github.com/bolliy/ioBroker.sun2000/issues/226)
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "sun2000",
4
- "version": "2.3.6",
4
+ "version": "2.4.0",
5
5
  "news": {
6
+ "2.4.0": {
7
+ "en": "fix: the order of bit assignment corrected of alarmsJSON\nnew state `inverter.x.emma.activeAlarmSN` and `inverter.x.emma.HistoricalAlarmSN` : emma alarms [#226](https://github.com/bolliy/ioBroker.sun2000/issues/226)\nstatistics: Aggregates historical collected datapoints into time-based summaries (e.g. hourly, daily, monthly, yearly). The data is stored in the path `statistics` as JSON.",
8
+ "de": "fix: die Reihenfolge der Bitzuweisung korrigiert von Alarmen JSON\nneuer Zustand `inverter.x.emma.activeAlarmSN` und `inverter.x.emma.HistoricalAlarmSN`: Emma alarms [#226](https://github.com/bolliy/ioBroker.sun2000/issues/226)\nstatistik: Aggregate historische gesammelte Datenpunkte in zeitbasierte Zusammenfassungen (z.B. stündlich, täglich, monatlich, jährlich). Die Daten werden im Pfad `statistics` als JSON gespeichert.",
9
+ "ru": "исправление: порядок назначения битов исправлен сигнализацией Джон\nновое состояние 'inverter.x.emma.activeAlarmSN' и 'inverter.x.emma.HistoricalAlarmSN': emma alarms [#226] (https://github.com/bolliy/ioBroker.sun2000/issues/226)\nстатистика: Собирает исторические собранные точки данных в основанные на времени резюме (например, почасовые, ежедневные, ежемесячные, годовые). Данные хранятся в «статистике» пути JSON.",
10
+ "pt": "corrigir: a ordem de atribuição de bits corrigida dos alarmes JSON\nnovo estado `inverter.x.emma.activeAlarmSN` e `inverter.x.emma.HistoricAlarmSN` : alarmes emma [#226](https://github.com/bolliy/ioBroker.sun2000/issues/226)\nestatísticas: Agrega pontos de dados históricos recolhidos em resumos baseados no tempo (por exemplo, horários, diários, mensais, anuais). Os dados são armazenados no caminho `statistics` como JSON.",
11
+ "nl": "fix: de volgorde van bittoewijzing gecorrigeerd van alarmen JSON\nnieuwe staat \nstatistieken: Verzamelt historische verzamelde datapunten in tijdgebaseerde samenvattingen (bv. uur, dag, maand, jaar). De gegevens worden opgeslagen in het pad .",
12
+ "fr": "correction : l'ordre d'attribution des bits corrigé des alarmes JSON\nnouvel état `inverter.x.emma.activeAlarmSN` et `inverter.x.emma.HistoricalAlarmSN`: emma alarms [#226](https://github.com/bolliy/ioBroker.sun2000/issues/226)\nstatistiques : Agrége les points de données historiques collectés en résumés chronologiques (p. ex. horaires, quotidiens, mensuels, annuels). Les données sont stockées dans le chemin `statistique` comme JSON.",
13
+ "it": "fix: l'ordine di bit assegnazione corretto di allarmi JSON\nnuovo stato `inverter.x.emma.activeAlarmSN` e `inverter.x.emma.HistoricalAlarmSN` : allarme emma [#226](https://github.com/bolliy/ioBroker.sun2000/problems/226)\nstatistiche: Aggrega i datapoint storici raccolti in riassunti basati sul tempo (es. orario, giornaliero, mensile, annuale). I dati vengono memorizzati nel percorso `statistics` come JSON.",
14
+ "es": "fijación: el orden de asignación de bits corregido de alarmas JSON\nnuevo estado `inverter.x.emma.activeAlarmSN` e `inverter.x.emma.HistoricalAlarmSN` : emma alarms [#226](https://github.com/bolliy/ioBroker.sun2000/issues/226)\nestadística: Aggregates historical collected datapoints into time-based summaries (e.g. hourly, daily, monthly, yearly). Los datos se almacenan en el camino 'estadística' como JSON.",
15
+ "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.",
16
+ "uk": "фіксувати: порядок відведення біту виправлено тривоги Сонце\nновий стан `inverter.x.emma.activeAlarmSN` і `inverter.x.emma.HistoricalAlarmSN` : emma тривоги [#226](https://github.com/bolliy/ioBroker.sun2000/products/226)\nстатистика: Агрегати історичних зібраних точок даних на часові суми (наприклад, час, щоденно, щомісяця, рік). Дані зберігаються на шляху `statistics` як JSON.",
17
+ "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储存在路径`统计'中."
18
+ },
19
+ "2.3.7": {
20
+ "en": "deleted deprecated state `collected.usableSurplusPower`",
21
+ "de": "gelöschter deprecated state `collect.usableSurplusPower `",
22
+ "ru": "удаленное устаревшее состояние 'collected.usableSurplusPower пункт",
23
+ "pt": "estado desactualizado excluído `colected.usableSurplusPower `",
24
+ "nl": "verwijderde verouderde staat Wat",
25
+ "fr": "état obsolète supprimé `collected.usableSurplusPower \"",
26
+ "it": "cancellato stato deprecato `colletto.usableSurplusPower #",
27
+ "es": "borrado estado deprecatado `collected.usableSurplusPower `",
28
+ "pl": "usunięty stan zdepregatowany \"collected.usableSurplusPower '",
29
+ "uk": "вилучена депресована держава `collected.usableSurplusPower й",
30
+ "zh-cn": "删除已贬值状态“ 已收集。 可使用 SurplusPower `"
31
+ },
6
32
  "2.3.6": {
7
33
  "en": "dependency and configuration updates\nnew state `inverter.x.derived.alarmsJSON` : json array with intverter alarms (id, name, level) [#226](https://github.com/bolliy/ioBroker.sun2000/issues/226)\nadd ChargeDischargePower for Battery units [#234](https://github.com/bolliy/ioBroker.sun2000/issues/234)\nadd minimum and maximum temperature for battery packs [#236](https://github.com/bolliy/ioBroker.sun2000/issues/236)",
8
34
  "de": "abhängigkeits- und konfigurationsupdates\nneuer Zustand `inverter.x.derived.alarmsJSON`: json array with intverter alarms (id, name, level) [#226](https://github.com/bolliy/ioBroker.sun2000/issues/226)\nadd ChargeDischargePower for Battery Units [#234](https://github.com/bolliy/ioBroker.sun2000/issues/234)\nmindest- und Höchsttemperatur für Akkupacks [#236](https://github.com/bolliy/ioBroker.sun2000/issues/236)",
@@ -67,32 +93,6 @@
67
93
  "pl": "pozwala ponownie 'control.battery.chargeFromGridFunction' podczas korzystania z Emmy",
68
94
  "uk": "дозволяє знову `control.battery.chargeЗ альбомуGridFunction` при використанні Емма",
69
95
  "zh-cn": "允许在使用 Emma 时再次使用 \" control.battery. charge from GridFunction \" "
70
- },
71
- "2.3.1": {
72
- "en": "fix: handle potential null values in set method of RegisterMap",
73
- "de": "fix: griff potenzielle Nullwerte in der eingestellten Methode des Registrierens Landkarte",
74
- "ru": "исправление: обработка потенциальных нулевых значений в установленном методе Регистра Карта",
75
- "pt": "corrigir: manusear valores nulos potenciais no método definido de Registro Mapa",
76
- "nl": "fix: handle potentiële nulwaarden in set methode van Register Kaart",
77
- "fr": "fix: gérer les valeurs null potentielles dans la méthode définie de Register Carte",
78
- "it": "fix: gestire potenziali valori null nel metodo impostato di Registrazione Mappa",
79
- "es": "fijación: manejar valores nulos potenciales en el método set de Registro Mapa",
80
- "pl": "fix: obsługi potencjalnych wartości null w ustawieniu metody rejestru Mapa",
81
- "uk": "виправити: обробляти потенціал null значення в встановленому методі Реєстру Мапа",
82
- "zh-cn": "固定值:在设定的登记方法中处理潜在的无效值 地图"
83
- },
84
- "2.3.0": {
85
- "en": "new release for npm migrates to Trusted Publishing",
86
- "de": "neue Veröffentlichung für npm migriert Trusted Publishing",
87
- "ru": "новый релиз для npm перенесен в Trusted Publishing",
88
- "pt": "nova versão para npm migra para a publicação confiável",
89
- "nl": "nieuwe release voor npm-migraties naar Trusted Publishing",
90
- "fr": "nouvelle version pour npm migre vers Trusted Publishing",
91
- "it": "nuovo rilascio per npm migra a Trusted Publishing",
92
- "es": "nueva versión para npm migrates a Trusted Publishing",
93
- "pl": "nowa wersja dla migratów npm do zaufanej publikacji",
94
- "uk": "новий реліз для npm migrats для Trusted Publishing",
95
- "zh-cn": "npm 的新版本迁移到信任出版"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -150,6 +150,7 @@
150
150
  "compact": true,
151
151
  "connectionType": "local",
152
152
  "dataSource": "poll",
153
+ "messagebox": true,
153
154
  "adminUI": {
154
155
  "config": "json"
155
156
  },
@@ -221,7 +222,7 @@
221
222
  ],
222
223
  "globalDependencies": [
223
224
  {
224
- "admin": ">=7.6.17"
225
+ "admin": ">=7.6.20"
225
226
  }
226
227
  ],
227
228
  "plugins": {
@@ -247,6 +248,8 @@
247
248
  "ms_log": false,
248
249
  "sl_meterId": 11,
249
250
  "ds_bu": true,
251
+ "ds_bp": false,
252
+ "cb_tou": false,
250
253
  "integration": 0
251
254
  },
252
255
  "objects": [],
package/lib/alarms.js CHANGED
@@ -58,11 +58,11 @@ const inverterAlarms3 = new Map()
58
58
  .set('14', { id: 2093, name: 'DC Switch Abnormal', level: alarmLevel.Minor })
59
59
  .set('15', { id: 2094, name: 'Allowable discharge capacity of the battery is low', level: alarmLevel.Warning });
60
60
 
61
- function textsFromBitfield(alarmString, lot) {
61
+ function fromBitfield(alarmString, lot) {
62
62
  const result = [];
63
- for (const [i, char] of Object.entries(alarmString)) {
64
- if (char === '1') {
65
- const alarmText = lot.get(i);
63
+ for (let i = 0; i < alarmString.length; i++) {
64
+ if (alarmString[alarmString.length - 1 - i] === '1') {
65
+ const alarmText = lot.get(String(i));
66
66
  if (alarmText) {
67
67
  result.push(alarmText);
68
68
  }
@@ -76,11 +76,11 @@ function getAlarmInfo(alarmCode, alarmNo) {
76
76
  const alarmString = (alarmCode >>> 0).toString(2).padStart(16, '0');
77
77
  switch (alarmNo) {
78
78
  case 1:
79
- return textsFromBitfield(alarmString, inverterAlarms1);
79
+ return fromBitfield(alarmString, inverterAlarms1);
80
80
  case 2:
81
- return textsFromBitfield(alarmString, inverterAlarms2);
81
+ return fromBitfield(alarmString, inverterAlarms2);
82
82
  case 3:
83
- return textsFromBitfield(alarmString, inverterAlarms3);
83
+ return fromBitfield(alarmString, inverterAlarms3);
84
84
  }
85
85
  }
86
86
  return [];
@@ -191,7 +191,7 @@ class DriverBase {
191
191
 
192
192
  /**
193
193
  * Read the device list for a given modbusId.
194
- * @param {ModbusClient} modbusClient - The modbus client to use.
194
+ * @param {object} modbusClient - The modbus client to use.
195
195
  * @param {number} [modbusId] - The modbus ID to query.
196
196
  * @returns {Promise<[number, { [key: string]: string }]>}
197
197
  * The first element of the array is the number of devices,
@@ -209,6 +209,7 @@ class DriverBase {
209
209
  throw new Error(`readDeviceList: No answer for OID=0x${objectId.toString(16).toUpperCase()}: ${e.message}`);
210
210
  }
211
211
  const numDevices = parseInt(JSON.stringify(allInfo['135'] || '').replace(/[^0-9]/g, ''));
212
+ // @ts-expect-error - we know that the value is a string, but the type definition is incorrect
212
213
  return [numDevices, allInfo];
213
214
  }
214
215
 
@@ -293,7 +294,7 @@ class DriverBase {
293
294
  * If the error is a connection error, it stops the update loop.
294
295
  * Finally, it calls the _runPostUpdateHooks function to run any post update hooks and stores the states.
295
296
  *
296
- * @param {ModbusClient} modbusClient - The Modbus client to use for communication.
297
+ * @param {object} modbusClient - The Modbus client to use for communication.
297
298
  * @param {dataRefreshRate} refreshRate - The refresh rate to use for updating the states.
298
299
  * @param {number} [duration] - The duration in milliseconds for which the states should be updated.
299
300
  * @returns {Promise<number>} - A promise that resolves to the number of registers read.
@@ -784,6 +784,22 @@ class Emma extends DriverBase {
784
784
  this.stateCache.set(`${path}emma.derived.systemTime`, fixTime(systemTime), { type: 'number' });
785
785
  },
786
786
  },
787
+ {
788
+ address: 65500,
789
+ length: 4,
790
+ info: 'Public Register Definitions',
791
+ refresh: dataRefreshRate.low,
792
+ states: [
793
+ {
794
+ state: { id: 'emma.activeAlarmSN', name: 'Active alarm SN', type: 'number', role: 'value', desc: 'reg:65500, len:2' },
795
+ register: { reg: 65500, type: dataType.uint32 },
796
+ },
797
+ {
798
+ state: { id: 'emma.HistoricalAlarmSN', name: 'Historical alarm SN', type: 'number', role: 'value', desc: 'reg:65502, len:2' },
799
+ register: { reg: 65502, type: dataType.uint32 },
800
+ },
801
+ ],
802
+ },
787
803
  ];
788
804
 
789
805
  this.registerFields.push.apply(this.registerFields, newFields);
@@ -1056,9 +1056,10 @@ class InverterSun2000 extends DriverBase {
1056
1056
  },
1057
1057
  ],
1058
1058
  postHook: path => {
1059
- //const alarm1Info = getAlarmInfo(128, 1);
1060
1059
  const alarm1Info = getAlarmInfo(this.stateCache.get(`${path}alarm1`)?.value, 1);
1060
+ //const alarm1Info = getAlarmInfo(128, 1);
1061
1061
  const alarm2Info = getAlarmInfo(this.stateCache.get(`${path}alarm2`)?.value, 2);
1062
+ //const alarm3Info = getAlarmInfo(32, 3);
1062
1063
  const alarm3Info = getAlarmInfo(this.stateCache.get(`${path}alarm3`)?.value, 3);
1063
1064
  const alarmInfos = alarm1Info.concat(alarm2Info, alarm3Info);
1064
1065
 
@@ -1740,36 +1741,40 @@ class InverterSun2000 extends DriverBase {
1740
1741
  //overload
1741
1742
  get modbusAllowed() {
1742
1743
  //if the modbus-device offline we cannot read or write anythink!
1743
- let modbusAllowed = true;
1744
- if (this.deviceInfo.index > 0) {
1744
+ let allowModbus = true; //allowed = true;
1745
+ //if integration = sDongle and only a slave inverter has not a meter.
1746
+ if (this.adapter.settings.integration === 0 && !this.deviceInfo.meter) {
1745
1747
  //I am a slave inverter
1746
- if (this.adapter.devices[0].driverClass === driverClasses.inverter && this.adapter.devices[0].instance) {
1747
- modbusAllowed = this.adapter.devices[0].instance.modbusAllowed; //first ask the master
1748
+ //If the master inverter has a meter, ask the master if modbus is allowed.
1749
+ if (this.adapter.devices[0].instance && this.adapter.devices[0].instance !== this.deviceInfo.instance) {
1750
+ if (this.adapter.devices[0].driverClass === driverClasses.inverter && this.adapter.devices[0].meter) {
1751
+ allowModbus = this.adapter.devices[0].instance.modbusAllowed; //first ask the master
1752
+ }
1748
1753
  }
1749
1754
  }
1750
- if (modbusAllowed) {
1755
+ if (allowModbus) {
1751
1756
  //430 = SUN2000-8KTL-M2
1752
1757
  if (this.deviceStatus === 0x0002) {
1753
- if (this.deviceInfo.index > 0 && this._modelId < 430) modbusAllowed = false;
1758
+ if (this.deviceInfo.index > 0 && this._modelId < 430) allowModbus = false;
1754
1759
  } //standby
1755
1760
  if (this.deviceStatus >= 0x0300 && this.deviceStatus <= 0x0307) {
1756
- modbusAllowed = false;
1761
+ allowModbus = false;
1757
1762
  } //shutdown
1758
1763
  if (this._errorCount > 3) {
1759
- modbusAllowed = false;
1764
+ allowModbus = false;
1760
1765
  }
1761
1766
  }
1762
1767
 
1763
- if (!modbusAllowed && !this.log.quiet) {
1768
+ if (!allowModbus && !this.log.quiet) {
1764
1769
  this.log.info(`The inverter with modbus ID ${this._modbusId} is no longer accessible. That is why the logs are minimized.`);
1765
1770
  this.log.beQuiet(true);
1766
1771
  }
1767
- if (modbusAllowed && this.log.quiet) {
1772
+ if (allowModbus && this.log.quiet) {
1768
1773
  this.log.beQuiet(false);
1769
1774
  this.log.info(`The inverter with modbus ID ${this._modbusId} is accessible again.`);
1770
1775
  //this._errorCount = 0;
1771
1776
  }
1772
- this._modbusAllowed = modbusAllowed;
1777
+ this._modbusAllowed = allowModbus;
1773
1778
  return this._modbusAllowed;
1774
1779
  }
1775
1780
 
package/lib/register.js CHANGED
@@ -3,6 +3,8 @@
3
3
  const { deviceType, driverClasses, dataRefreshRate } = require(`${__dirname}/types.js`);
4
4
  const { RiemannSum, StateMap } = require(`${__dirname}/tools.js`);
5
5
  const getDriverHandler = require(`${__dirname}/drivers/index.js`);
6
+ const tools = require(`${__dirname}/tools.js`);
7
+ const statistics = require(`${__dirname}/statistics.js`);
6
8
 
7
9
  class Registers {
8
10
  constructor(adapterInstance) {
@@ -10,6 +12,7 @@ class Registers {
10
12
  this.stateCache = new StateMap();
11
13
 
12
14
  this.externalSum = new RiemannSum();
15
+ this.statistics = new statistics(adapterInstance, this.stateCache);
13
16
 
14
17
  for (const device of this.adapter.devices) {
15
18
  //DriverInfo Instance or Sdongle
@@ -19,8 +22,21 @@ class Registers {
19
22
  }
20
23
  }
21
24
 
22
- this.postProcessHooks = [];
23
- this.inverterPostProcessHooks = [
25
+ //Upgrade to v2.3.7 - deleted deprecated states
26
+ if (
27
+ tools.existsState(this.adapter, `collected.usableSurplusPower`, (err, exists) => {
28
+ if (!err && exists) {
29
+ tools.deleteState(this.adapter, `collected.usableSurplusPower`, (err, deleted) => {
30
+ if (!err && deleted) {
31
+ this.adapter.logger.debug('Deleted deprecated state collected.usableSurplusPower');
32
+ }
33
+ });
34
+ }
35
+ })
36
+ );
37
+
38
+ //this.postProcessHooks = [];
39
+ this.postProcessHooks = [
24
40
  {
25
41
  refresh: dataRefreshRate.high,
26
42
  states: [
@@ -43,14 +59,6 @@ class Registers {
43
59
  unit: 'kW',
44
60
  role: 'value.power',
45
61
  },
46
- {
47
- id: 'collected.usableSurplusPower',
48
- name: 'usable surplus power',
49
- type: 'number',
50
- unit: 'kW',
51
- role: 'value.power',
52
- desc: 'depreciated: Please use collected.surplus.usablePower instead',
53
- },
54
62
  {
55
63
  id: 'collected.surplus.power',
56
64
  name: 'surplus power',
@@ -138,7 +146,7 @@ class Registers {
138
146
  if (usableSurplusPower < 0.01) usableSurplusPower = 0;
139
147
  }
140
148
 
141
- this.adapter.log.debug(
149
+ this.adapter.logger.debug(
142
150
  `### Caculate usableSurplus power ${surplusPower} bufferOn ${this.bufferOn} soc ${soc} minSoc ${minSoc} bufferSoc ${bufferSoc} threshold ${hysterese / 2}`,
143
151
  );
144
152
  }
@@ -175,16 +183,12 @@ class Registers {
175
183
  this.stateCache.set('collected.activePower', actPower, { type: 'number', renew: true });
176
184
  this.stateCache.set('collected.houseConsumption', houseConsum, { type: 'number' });
177
185
  this.stateCache.set('collected.chargeDischargePower', chargeDischarge, { type: 'number' });
178
- this.stateCache.set('collected.usableSurplusPower', surplusArray[1], {
179
- type: 'number',
180
- });
181
186
  this.stateCache.set('collected.surplus.power', surplusArray[0], {
182
187
  type: 'number',
183
188
  });
184
189
  this.stateCache.set('collected.surplus.usablePower', surplusArray[1], {
185
190
  type: 'number',
186
191
  });
187
-
188
192
  this.stateCache.set('collected.externalPower', extPower, { type: 'number' });
189
193
  },
190
194
  },
@@ -312,7 +316,17 @@ class Registers {
312
316
  feedinEnergy = this.stateCache.get('meter.positiveActiveEnergy')?.value ?? 0;
313
317
  supplyFromGrid = this.stateCache.get('meter.reverseActiveEnergy')?.value ?? 0;
314
318
  }
315
- //stimmt leider nicht genau - bleibt aber erstmal bestehen
319
+ //NEU - Initialisierung
320
+ const gridExportStart = this.stateCache.get('collected.gridExportStart')?.value;
321
+ if (!gridExportStart && feedinEnergy !== 0) {
322
+ this.stateCache.set('collected.gridExportStart', feedinEnergy, { type: 'number' });
323
+ }
324
+ const gridImportStart = this.stateCache.get('collected.gridImportStart')?.value;
325
+ if (!gridImportStart && supplyFromGrid !== 0) {
326
+ this.stateCache.set('collected.gridImportStart', supplyFromGrid, { type: 'number' });
327
+ }
328
+
329
+ //stimmt wahrscheinlich nicht genau - bleibt aber erstmal bestehen
316
330
  const conSum = enYield + supplyFromGrid - feedinEnergy;
317
331
  this.stateCache.set('collected.consumptionSum', conSum, { type: 'number' });
318
332
  // compute export and import today
@@ -322,14 +336,7 @@ class Registers {
322
336
  this.stateCache.set('collected.gridImportToday', supplyFromGrid - this.stateCache.get('collected.gridImportStart')?.value, {
323
337
  type: 'number',
324
338
  });
325
- /* old computation of consumption today
326
- this.stateCache.set(
327
- 'collected.consumptionToday',
328
- this.stateCache.get('collected.consumptionSum')?.value - this.stateCache.get('collected.consumptionStart')?.value,
329
- { type: 'number' },
330
- );
331
- */
332
- //new computation of consumption today
339
+ //consumption today
333
340
  this.stateCache.set(
334
341
  'collected.consumptionToday',
335
342
  activeEnergy +
@@ -350,7 +357,9 @@ class Registers {
350
357
  ];
351
358
 
352
359
  //only Inverter
353
- this.postProcessHooks.push.apply(this.postProcessHooks, this.inverterPostProcessHooks);
360
+ //this.postProcessHooks.push.apply(this.postProcessHooks, this.inverterPostProcessHooks);
361
+ this.postProcessHooks.push.apply(this.postProcessHooks, this.statistics.processHooks);
362
+
354
363
  this._loadStates();
355
364
  }
356
365
 
@@ -370,6 +379,17 @@ class Registers {
370
379
  },
371
380
  native: {},
372
381
  });
382
+
383
+ if (state.initVal) {
384
+ const ret = await this.adapter.getState(state.id);
385
+ if (!ret || ret.val === null) {
386
+ try {
387
+ await this.adapter.setState(state.id, { val: state.initVal, ack: true });
388
+ } catch (err) {
389
+ this.adapter.log.warn(`Error while initializing ${state.id}, val=${state.initVal} err=${err.message}`);
390
+ }
391
+ }
392
+ }
373
393
  }
374
394
 
375
395
  async storeStates() {
@@ -401,7 +421,7 @@ class Registers {
401
421
  * its states. Logs an error if no device instance has been initialized.
402
422
  *
403
423
  * @param {object} device - The device object containing the instance and driverClass.
404
- * @param {ModbusClient} modbusClient - The Modbus client used for communication.
424
+ * @param {object} modbusClient - The Modbus client used for communication.
405
425
  * @param {string} refreshRate - The rate at which data should be refreshed.
406
426
  * @param {number} duration - The duration for which the states should be updated.
407
427
  * @returns {Promise<number>} - Returns a promise that resolves to the number of states updated.
@@ -439,7 +459,7 @@ class Registers {
439
459
  }
440
460
  }
441
461
  hook.initState = true;
442
- hook.fn(this.adapter.devices);
462
+ hook.fn && hook.fn(this.adapter.devices);
443
463
  }
444
464
  }
445
465
  this.storeStates(); //fire and forget
@@ -449,6 +469,7 @@ class Registers {
449
469
  async _loadStates() {
450
470
  let state = await this.adapter.getState('collected.gridExportStart');
451
471
  this.stateCache.set('collected.gridExportStart', state?.val, { type: 'number', stored: true });
472
+ this.stateCache.set('collected.gridImportStart', state?.val, { type: 'number', stored: true });
452
473
  state = await this.adapter.getState('collected.gridImportStart');
453
474
  this.stateCache.set('collected.gridImportStart', state?.val, { type: 'number', stored: true });
454
475
  state = await this.adapter.getState('collected.consumptionStart');
@@ -515,6 +536,7 @@ class Registers {
515
536
  // one minute before midnight - perform housekeeping actions
516
537
  //state
517
538
  async mitnightProcess() {
539
+ this.statistics.mitNightProcess();
518
540
  // copy current export/import kWh - used to compute daily import/export in kWh
519
541
  const sign = this.stateCache.get('meter.derived.signConventionForPowerFeed-in')?.value ?? 1;
520
542
  if (sign === -1) {
@@ -533,7 +555,7 @@ class Registers {
533
555
  await device.instance.mitnightProcess();
534
556
  }
535
557
  }
536
- this.storeStates(); //fire and forget
558
+ this.storeStates();
537
559
  }
538
560
  }
539
561