iobroker.sun2000 0.18.0 → 0.18.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 bolliy <stephan@mante.info>
3
+ Copyright (c) 2025 bolliy <stephan@mante.info>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -57,14 +57,18 @@ browse in the [wiki](https://github.com/bolliy/ioBroker.sun2000/wiki)
57
57
  * [`Export Control`](https://github.com/bolliy/ioBroker.sun2000/wiki/Begrenzung-Netzeinspeisung-(Export-Control)): The excess PV energy is fed into the power grid, but not all countries allow users to sell electricity. Some countries have introduced regulations to restrict the sale of electricity to the grid.
58
58
  * [`modbus-proxy`](https://github.com/bolliy/ioBroker.sun2000/wiki/Modbus-Proxy): Third party device such as wallbox, energy manager etc. can receive data even if the modbus interface of inverter is already in use. In addition you can mirror the sun2000 data to another IoBroker instance.
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
- * Huawei [`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.
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
 
62
62
  ## Changelog
63
-
64
63
  <!--
65
64
  Placeholder for the next version (at the beginning of the line):
66
65
  ### **WORK IN PROGRESS**
67
66
  -->
67
+ ### 0.18.1 (2025-01-08)
68
+ * dependency and configuration updates
69
+ * control: if the battery is not running, events related to the battery are discarded
70
+ * modbus-proxy: adjusted advanced logging
71
+
68
72
  ### 0.18.0 (2024-12-11)
69
73
  * dependency and configuration updates
70
74
  * modbus-proxy: the modbus ID 250 is mapped to ID 0
@@ -225,7 +229,7 @@ initial release
225
229
  ## License
226
230
  MIT License
227
231
 
228
- Copyright (c) 2024 bolliy <stephan@mante.info>
232
+ Copyright (c) 2025 bolliy <stephan@mante.info>
229
233
 
230
234
  Permission is hereby granted, free of charge, to any person obtaining a copy
231
235
  of this software and associated documentation files (the "Software"), to deal
@@ -1,12 +1,12 @@
1
1
  {
2
- "donateTxt": "Wenn dir dieser Adapter gefällt, würde ich mich über eine Spende freuen.",
2
+ "donateTxt": "Wenn Ihnen dieser Adapter gefällt, denken Sie bitte über eine Spende nach, um meine Arbeit zu unterstützen.",
3
3
  "address": "Geräte-IP-Adresse",
4
4
  "port": "Modbus TCP-Port",
5
5
  "Modbus inverter IDs": "Modbus-Wechselrichter-IDs",
6
6
  "Update interval (sec)": "Aktualisierungsintervall [Sek.]",
7
7
  "The device ip address": "Die IP-Adresse des Geräts",
8
8
  "The modbus TCP port": "Der Modbus-TCP-Port",
9
- "The modbus inverter IDs, separated with character ,": "Die Modbus-Wechselrichter-IDs, getrennt durch das Zeichen ,",
9
+ "The modbus inverter IDs, separated with character ,": "Wechselrichter-IDs, der Master muss zuerst eingegeben werden, getrennt durch ,",
10
10
  "Update interval to update the values from the inverters": "Aktualisierungsintervall zur Aktualisierung der Werte von den Wechselrichtern",
11
11
  "modbus connection timeout": "Zeitüberschreitung der Modbus-Verbindung",
12
12
  "delay between modbus requests": "Verzögerung zwischen Modbus-Anfragen",
@@ -18,7 +18,7 @@
18
18
  "If want to listen only at localhost use 127.0.0.1": "Wenn Sie nur auf localhost zuhören möchten, verwenden Sie 127.0.0.1",
19
19
  "ms.port": "Modbus-Proxy-TCP-Port",
20
20
  "The Modbus-proxy TCP port": "Der Modbus-Proxy-TCP-Port",
21
- "The SDongle modbus ID": "Die SDongle-Modbus-ID, normalerweise 100",
21
+ "The SDongle modbus ID": "Die SDongle-Modbus-ID normalerweise 100",
22
22
  "ms.log": "Erweiterte Protokollierung",
23
23
  "Advanced logging": "Erweiterte Protokollierung von Modbus-Transaktionen",
24
24
  "sl.meterId": "Modbus-ID des SmartLogger-Messgeräts",
@@ -6,7 +6,7 @@
6
6
  "Update interval (sec)": "Update interval [sec]",
7
7
  "The device ip address": "The device ip address",
8
8
  "The modbus TCP port" : "The modbus TCP port",
9
- "The modbus inverter IDs, separated with character ," : "The modbus inverter IDs, separated with character ,",
9
+ "The modbus inverter IDs, separated with character ," : "Inverter IDs, the master must be entered first, separated by ,",
10
10
  "Update interval to update the values from the inverters" : "Update interval to update the values from the inverters",
11
11
  "modbus connection timeout" : "modbus connection timeout",
12
12
  "delay between modbus requests" : "delay between modbus requests",
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "sun2000",
4
- "version": "0.18.0",
4
+ "version": "0.18.1",
5
5
  "news": {
6
+ "0.18.1": {
7
+ "en": "dependency and configuration updates\ncontrol: if the battery is not running, events related to the battery are discarded\nmodbus-proxy: adjusted advanced logging",
8
+ "de": "abhängigkeits- und konfigurationsupdates\nsteuerung: wenn die batterie nicht läuft, werden ereignisse im zusammenhang mit der batterie verworfen\nmodbus-proxy: angepasste erweiterte protokollierung",
9
+ "ru": "обновления зависимости и конфигурации\nуправление: если батарея не работает, события, связанные с батареей, отбрасываются\nmodbus-proxy: скорректированные передовые лесозаготовки",
10
+ "pt": "atualizações de dependência e configuração\ncontrole: se a bateria não estiver funcionando, os eventos relacionados à bateria são descartados\nmodbus-proxy: log avançado ajustado",
11
+ "nl": "afhankelijkheid en configuratie-updates\ncontrole: als de batterij niet draait, worden gebeurtenissen in verband met de batterij weggegooid\nmodbus-proxy: aangepaste geavanceerde logging",
12
+ "fr": "mises à jour de la dépendance et de la configuration\ncontrôle: si la batterie ne fonctionne pas, les événements liés à la batterie sont jetés\nmodbus-proxy: enregistrement avancé ajusté",
13
+ "it": "aggiornamenti di dipendenza e configurazione\ncontrollo: se la batteria non è in esecuzione, gli eventi relativi alla batteria vengono scartati\nmodbus-proxy: registrazione avanzata regolata",
14
+ "es": "actualizaciones de dependencia y configuración\ncontrol: si la batería no funciona, los eventos relacionados con la batería son descartados\nmodbus-proxy: registro avanzado ajustado",
15
+ "pl": "aktualizacje zależności i konfiguracji\nsterowanie: jeśli akumulator nie jest uruchomiony, zdarzenia związane z akumulatorem są odrzucane\nmodbus- proxy: dostosowane zaawansowane logowanie",
16
+ "uk": "оновлення залежності та конфігурації\nконтроль: якщо акумулятор не працює, події, пов'язані з акумулятором, відкидаються\nmodbus-proxy: регульований розширений журналювання",
17
+ "zh-cn": "依赖和配置更新\n控件: 如果电池没有运行, 与电池相关的事件会被丢弃\nmodbus-代用:经过调整的高级记录"
18
+ },
6
19
  "0.18.0": {
7
20
  "en": "dependency and configuration updates\nmodbus-proxy: the modbus ID 250 is mapped to ID 0",
8
21
  "de": "abhängigkeits- und konfigurationsupdates\nmodbus-proxy: die modbus ID 250 wird auf ID 0 abgebildet",
@@ -80,19 +93,6 @@
80
93
  "pl": "Dane SDongle nie zostały zapisane jako stany obiektu\nustawić ustawienia adaptera",
81
94
  "uk": "Дані SDongle не були написані як об'єкти\nналаштування адаптера",
82
95
  "zh-cn": "SDongle 数据没有作为对象状态写入\n调整适配器设置"
83
- },
84
- "0.15.0": {
85
- "en": "dependency and configuration updates\ndisplay a clearly legible table bar #121\nmodbus-proxy write data also to the read cache #119",
86
- "de": "abhängigkeits- und konfigurationsupdates\neine deutlich lesbare tischleiste #121 anzeigen\nmodbus-proxy schreibdaten auch zum lesespeicher #119",
87
- "ru": "обновления зависимости и конфигурации\n#121\nmodbus-proxy пишут данные также кэшу #119",
88
- "pt": "atualizações de dependência e configuração\nexibir uma barra de mesa claramente legível #121\nmodbus-proxy escrever dados também para o cache de leitura #119",
89
- "nl": "afhankelijkheid en configuratie-updates\neen duidelijk leesbare tafelbalk #121 tonen\nmodbus-proxy schrijf gegevens ook naar de leescache #119",
90
- "fr": "mises à jour de la dépendance et de la configuration\nafficher une barre de table clairement lisible #121\nmodbus-proxy écrire les données aussi au cache lecture #119",
91
- "it": "aggiornamenti di dipendenza e configurazione\nvisualizza una barra da tavolo chiaramente leggibile #121\nmodbus-proxy scrivere i dati anche alla cache di lettura #119",
92
- "es": "actualizaciones de dependencia y configuración\nmostrar una barra de mesa claramente legible #121\nmodbus-proxy escribe datos también al caché de lectura #119",
93
- "pl": "aktualizacje zależności i konfiguracji\nwyświetla czytelny pasek tabeli # 121\nmodbus- proxy write data also to the read cache # 119",
94
- "uk": "оновлення залежності та конфігурації\nдисплей чітко нижня білизна #121\nmodbus-proxy пише дані також для читання кеш #119",
95
- "zh-cn": "依赖和配置更新\n显示清晰可见的表栏 # 121\nmodbus- 代理写入数据到读缓存 # 119"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -139,12 +139,6 @@ class ServiceQueueMap {
139
139
  return await this._writeRegisters(47249,dataType.numToArray(event.value,dataType.uint32));
140
140
  }
141
141
  },
142
- /*
143
- {
144
- state: { id: 'battery.targetSOC ', name: 'Target SOC', type: 'number', unit: '%', role: 'level', desc: 'reg: 47101 , len: 1'},
145
- type : deviceType.gridPowerControl
146
- },
147
- */
148
142
  {
149
143
  state: { id: 'battery.targetSOC', name: 'Target SOC', type: 'number', unit: '%', role: 'level', desc: 'reg: 47101 , len: 1'},
150
144
  type : deviceType.battery,
@@ -242,7 +236,7 @@ class ServiceQueueMap {
242
236
  if (this.inverterInfo.instance) {
243
237
  for (const item of this.serviceFields) {
244
238
  //no battery - no controls
245
- if (item.type == deviceType.battery && this.inverterInfo.instance.numberBatteryUnits() === 0) continue;
239
+ //if (item.type == deviceType.battery && this.inverterInfo.instance.numberBatteryUnits() === 0) continue;
246
240
  if (item.type == deviceType.meter && !this.inverterInfo?.meter) continue;
247
241
  if (item.type == deviceType.gridPowerControl && !this.inverterInfo?.meter) continue;
248
242
  if (item?.state) {
@@ -259,7 +253,7 @@ class ServiceQueueMap {
259
253
  this.set(entry.state.id,state);
260
254
  }
261
255
  }
262
-
256
+ //upgrade
263
257
  const tSOC = await this.adapter.getState(this.inverterInfo.path+'.control.battery.targetSOC ');
264
258
  if (tSOC) {
265
259
  await this.adapter.delObject(this.inverterInfo.path+'.control.battery.targetSOC ',{ recursive: false });
@@ -286,10 +280,14 @@ class ServiceQueueMap {
286
280
  tCDP[3] = 383 - Working mode settings - self-consumption (discharge)
287
281
  */
288
282
  const tCDP = [1,0,1440,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
289
- if (await this._writeRegisters(47255,tCDP)) this.adapter.logger.info('Control: The default TOU setting are transferred');
283
+ if (await this._writeRegisters(47255,tCDP)) this.log.info('Control: The default TOU setting are transferred');
290
284
  }
291
285
  }
292
286
  }
287
+
288
+ if (this._initialized) {
289
+ this.log.info('Control: Service queue initialized');
290
+ }
293
291
  }
294
292
 
295
293
  get(id) {
@@ -316,8 +314,12 @@ class ServiceQueueMap {
316
314
  return this._map.values();
317
315
  }
318
316
 
317
+ /**
318
+ * Processes the events in the eventMap. For each event, it calls the associated function in the serviceMap.
319
+ * @param {ModbusClient} modbusClient - The modbus client to use for writing the states.
320
+ */
319
321
  async process(modbusClient) {
320
- if (!this.inverterInfo.instance.modbusAllowed) return;
322
+ //if (!this.inverterInfo.instance.modbusAllowed) return;
321
323
  this._modbusClient = modbusClient;
322
324
 
323
325
  if (this._initialized) {
@@ -326,8 +328,24 @@ class ServiceQueueMap {
326
328
  if (event.ack) continue; //allready done
327
329
  const service = this._serviceMap.get(event.id);
328
330
  if(!service.errorCount) service.errorCount = 0;
329
-
330
331
  if (event.value !== null && service.fn) {
332
+ //check if battery is present and running
333
+ if (service.type == deviceType.battery) {
334
+ if (this.inverterInfo.instance.numberBatteryUnits() === 0) {
335
+ this.log.warn(`Control: Event is discarded because no battery has been detected. `);
336
+ if (!this.adapter.isReady) {
337
+ this.log.warn('Control: The Adapter is not ready! Please check the value in the state sun2000.x.info.JSONhealth and the Log output.');
338
+ }
339
+ this._eventMap.delete(event.id); //forget the event
340
+ continue;
341
+ }
342
+ const BatStatus = this.inverterInfo.instance.stateCache.get(this.inverterInfo.path+'.battery.runningStatus')?.value;
343
+ if (BatStatus !== 'RUNNING' && BatStatus !== 'STANDBY' && BatStatus !== '') {
344
+ this.log.warn(`Control: Event is discarded because battery is not running. State: ${this.inverterInfo.path}.battery.runningStatus = ${BatStatus}. `);
345
+ this._eventMap.delete(event.id); //forget the event
346
+ continue;
347
+ }
348
+ }
331
349
  count ++;
332
350
  if (await service.fn(event)) {
333
351
  service.errorCount = 0;
@@ -347,10 +365,11 @@ class ServiceQueueMap {
347
365
  }
348
366
  }
349
367
  }
350
- if (count > 1) break; //max 2
368
+ if (count > 1) break; //max 2 Events
351
369
  }
352
370
  }
353
- if (!this._initialized && this.adapter.isReady) await this._init();
371
+ //if (!this._initialized && this.adapter.isReady) await this._init();
372
+ if (!this._initialized && this.adapter.isConnected) await this._init();
354
373
  }
355
374
 
356
375
  async _writeRegisters(address,data) {
@@ -969,9 +969,14 @@ class InverterSun2000 extends DriverBase{
969
969
  }
970
970
  }
971
971
 
972
- //overload
972
+ /**
973
+ * #overload#
974
+ * Get the number of battery units.
975
+ * @returns {number} The number of battery units
976
+ */
973
977
  numberBatteryUnits() {
974
978
  let units = 0;
979
+ //Check if the first battery unit exists
975
980
  const state1 = this.stateCache.get(`${this.deviceInfo.path}.battery.unit.1.SN`);
976
981
  if (state1 && state1.value) units = 1;
977
982
  const state2 = this.stateCache.get(`${this.deviceInfo.path}.battery.unit.2.SN`);
@@ -153,6 +153,10 @@ class ModbusServer {
153
153
  if (device) {
154
154
  //this.adapter.log.debug('Device Info '+JSON.stringify(device?.info));
155
155
  const values = device.getHoldingRegisters(startAddr,length);
156
+ if (this.adapter.settings.ms.log) {
157
+ this.log.info('Modbus-proxy: read data from id/address/data ' + unitId + '/' + startAddr +'/'+ values);
158
+ }
159
+
156
160
  if (!this.adapter.isConnected) {
157
161
  //this._addInfoStat('#WaitForConnected',startAddr, length, unitId);
158
162
  await this.wait(500);
@@ -180,8 +184,12 @@ class ModbusServer {
180
184
  }
181
185
 
182
186
  async _handleSetReg(fnName,address, data, unitId, callback) {
183
- try {
187
+ if (this.adapter.settings.ms.log) {
188
+ this.log.info('Modbus-proxy: Try to write data to id/address ' + unitId + '/' + address +'/'+ data);
189
+ } else {
184
190
  this.log.debug('Modbus-proxy: Try to write data to id/address ' + unitId + '/' + address +'/'+ data);
191
+ }
192
+ try {
185
193
  const device = this.getDeviceInstance(unitId);
186
194
  if (!device) {
187
195
  await this.wait(500);
package/main.js CHANGED
@@ -279,7 +279,7 @@ class Sun2000 extends utils.Adapter {
279
279
  this.settings.highInterval = 10000*this.settings.modbusIds.length;
280
280
  } else {
281
281
  let minInterval = this.settings.modbusIds.length*this.settings.modbusDelay*2.5; //len*5*delay/2
282
- if (this.settings.integration > 0) { //SmartLogger
282
+ if (this.settings.integration > 0) { //SmartLogger, Emma
283
283
  minInterval += 5000;
284
284
  } else {
285
285
  for (const device of this.devices) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.sun2000",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "description": "sun2000",
5
5
  "author": {
6
6
  "name": "bolliy",
@@ -45,19 +45,19 @@
45
45
  "@types/chai": "^4.3.20",
46
46
  "@types/chai-as-promised": "^7.1.8",
47
47
  "@types/mocha": "^10.0.10",
48
- "@types/node": "^22.10.1",
48
+ "@types/node": "^22.10.5",
49
49
  "@types/proxyquire": "^1.3.31",
50
50
  "@types/sinon": "^17.0.3",
51
51
  "@types/sinon-chai": "^3.2.12",
52
52
  "chai": "^4.5.0",
53
53
  "chai-as-promised": "^7.1.2",
54
- "eslint": "^9.16.0",
55
- "globals": "^15.13.0",
56
- "mocha": "^10.8.2",
54
+ "eslint": "^9.17.0",
55
+ "globals": "^15.14.0",
56
+ "mocha": "^11.0.1",
57
57
  "proxyquire": "^2.1.3",
58
58
  "sinon": "^19.0.2",
59
59
  "sinon-chai": "^3.7.0",
60
- "typescript": "~5.6.3"
60
+ "typescript": "~5.7.2"
61
61
  },
62
62
  "main": "main.js",
63
63
  "files": [