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 +1 -1
- package/README.md +7 -3
- package/admin/i18n/de/translations.json +3 -3
- package/admin/i18n/en/translations.json +1 -1
- package/io-package.json +14 -14
- package/lib/controls/service_queue.js +32 -13
- package/lib/drivers/driver_inverter.js +6 -1
- package/lib/modbus/modbus_server.js +9 -1
- package/main.js +1 -1
- package/package.json +6 -6
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
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)
|
|
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
|
|
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 ,": "
|
|
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
|
|
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 ," : "
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
55
|
-
"globals": "^15.
|
|
56
|
-
"mocha": "^
|
|
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.
|
|
60
|
+
"typescript": "~5.7.2"
|
|
61
61
|
},
|
|
62
62
|
"main": "main.js",
|
|
63
63
|
"files": [
|