iobroker.sun2000 0.1.3 → 0.2.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 +20 -8
- package/io-package.json +56 -70
- package/lib/modbus_connect.js +13 -18
- package/lib/register.js +92 -65
- package/lib/tools.js +79 -0
- package/main.js +52 -44
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
|
|
15
15
|
Read register data from Huawei SUN2000 inverter and LUNA2000 battery using Modbus TCP.
|
|
16
16
|
|
|
17
|
+
Feel free to follow the discussions in the german [iobroker forum](https://forum.iobroker.net/topic/71768/test-adapter-sun2000-v0-1-x-huawei-wechselrichter)
|
|
18
|
+
|
|
17
19
|
Modbus interface definition (Issue 5, 2023-02-16):
|
|
18
20
|
https://forum.iobroker.net/assets/uploads/files/1699119419919-solar-inverter-modbus-interface-definitions-v5.pdf
|
|
19
21
|
|
|
20
|
-
The development of this adapter was inspired by discussions from the forum thread https://forum.iobroker.net/topic/53005/huawei-sun2000-iobroker-via-js-script-funktioniert and the iobroker javascript https://github.com/ChrisBCH/SunLuna2000_iobroker.
|
|
21
|
-
|
|
22
22
|
## Supported hardware
|
|
23
23
|
|
|
24
24
|
* HUAWEI Inverter (SUN2000 Serie) M1
|
|
@@ -31,10 +31,17 @@ The development of this adapter was inspired by discussions from the forum threa
|
|
|
31
31
|
## Feature list
|
|
32
32
|
|
|
33
33
|
* Maximum 5 inverters (master/slave) can be processed, each with a battery module (max. 30kWh).
|
|
34
|
-
*
|
|
34
|
+
* Real-time values such as input power, output power, charging/discharging power and the grid consumption are read out at a fixed interval.
|
|
35
35
|
* States are only written for changed data from the inverter. This relieves the burden on the iobroker instance.
|
|
36
36
|
* The states “inputPower” or “activePower” in the “collected” path can be monitored with a “was updated” trigger element. Because these states are always written within the set interval.
|
|
37
37
|
|
|
38
|
+
## Settings
|
|
39
|
+
|
|
40
|
+
* `address`: Inverter IP address
|
|
41
|
+
* `port`: Inverter modbus port (default: 502)
|
|
42
|
+
* `modbusIds`: inverter IDs, separated with "," (default: 1, max. 5 inverters)
|
|
43
|
+
* `updateInterval`: Fast update interval (default: 20 sec, smallest 5 seconds per inverter)
|
|
44
|
+
|
|
38
45
|
## Configure inverters
|
|
39
46
|
|
|
40
47
|
In order to use the Modbus connection, all Huawei devices must use the latest firmware
|
|
@@ -50,12 +57,10 @@ If you use two inverters, then connect to the second inverter and read the commu
|
|
|
50
57
|
|
|
51
58
|
[How activate 'Modbus TCP' - from huawei forum](https://forum.huawei.com/enterprise/en/modbus-tcp-guide/thread/789585-100027)
|
|
52
59
|
|
|
53
|
-
##
|
|
60
|
+
## Inspiration
|
|
61
|
+
|
|
62
|
+
The development of this adapter was inspired by discussions from the forum thread https://forum.iobroker.net/topic/53005/huawei-sun2000-iobroker-via-js-script-funktioniert and the iobroker javascript https://github.com/ChrisBCH/SunLuna2000_iobroker.
|
|
54
63
|
|
|
55
|
-
* `address`: Inverter IP address
|
|
56
|
-
* `port`: Inverter modbus port (default: 502)
|
|
57
|
-
* `modbusIds`: inverter IDs, separated with "," (default: 1, max. 5 inverters)
|
|
58
|
-
* `updateInterval`: Fast update interval (default: 20 sec, smallest 5 seconds per inverter)
|
|
59
64
|
|
|
60
65
|
## Changelog
|
|
61
66
|
|
|
@@ -63,6 +68,13 @@ If you use two inverters, then connect to the second inverter and read the commu
|
|
|
63
68
|
Placeholder for the next version (at the beginning of the line):
|
|
64
69
|
### **WORK IN PROGRESS**
|
|
65
70
|
-->
|
|
71
|
+
### 0.2.0 (2024-01-24)
|
|
72
|
+
* [Add sun2000 to latest](https://github.com/ioBroker/ioBroker.repositories/pull/3219)
|
|
73
|
+
* improve error handling (#34)
|
|
74
|
+
* add simple optimizer info
|
|
75
|
+
* Riemann sum of input power with energy loss for new state `dailySolarYield`
|
|
76
|
+
* try to recreate the `yield today` from the fusion portal
|
|
77
|
+
|
|
66
78
|
### 0.1.3 (2024-01-17)
|
|
67
79
|
* display the data from PV strings (#27)
|
|
68
80
|
* optimize the timing of interval loop
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "sun2000",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.2.0": {
|
|
7
|
+
"en": "improve error handling (#34)\nadd simple optimizer info \nRiemann sum of input power with energy loss for new state `dailySolarYield`\ntry to recreate the `yield today` from the fusion portal",
|
|
8
|
+
"de": "fehlerbehandlung verbessern (#34)\neinfache optimierer-info hinzufügen\nRiemann Summe der Eingangsleistung mit Energieverlust für neuen Zustand `dailySolarYield `\nversuchen, den `yield heute` aus dem fusionsportal wiederherzustellen",
|
|
9
|
+
"ru": "улучшить обработку ошибок (#34)\nдобавить простую информацию о оптимизации\nRiemann Сумма входной мощности с потерей энергии для нового состояния `dailySolarYield \"\nпопытаться воссоздать «урожай сегодня» с портала синтеза",
|
|
10
|
+
"pt": "melhorar o manuseio de erros (#34)\nadicionar informações de otimização simples\nRiemann soma de energia de entrada com perda de energia para novo estado `dailySolarYield \"\ntentar recriar o `yield hoje` do portal de fusão",
|
|
11
|
+
"nl": "verbetering van de foutafhandeling (#34)\nvoeg eenvoudige optimalisatie info toe\nRiemann-som van het ingangsvermogen met energieverlies voor nieuwe toestand Wat\nproberen om de te recreëren van vandaag van de fusie portal",
|
|
12
|
+
"fr": "améliorer la gestion des erreurs (#34)\najouter des informations d'optimisation simples\nRiemann somme de puissance d'entrée avec perte d'énergie pour le nouvel état `dailySolarYield \"\nessayer de recréer le « rendement aujourd'hui » du portail de fusion",
|
|
13
|
+
"it": "migliorare la gestione degli errori (#34)\naggiungere semplici informazioni di ottimizzazione\nRiemann somma di potenza di ingresso con perdita di energia per nuovo stato `dailySolarYield #\ncercare di ricreare il `yield oggi` dal portale fusion",
|
|
14
|
+
"es": "mejorar el manejo de errores (#34)\nañadir información de optimización simple\nRiemann suma de poder de entrada con pérdida de energía para el nuevo estado `dailySolarYield `\ntratar de recrear el `yield hoy' del portal de fusión",
|
|
15
|
+
"pl": "poprawić obsługę błędów (# 34)\ndodaj proste informacje o optymalizacji\nRiemann suma mocy wejściowej z utratą energii dla nowego stanu \"dailySolarYeld '\nspróbuj odtworzyć \"wydajność dzisiaj\" z portalu syntezy jądrowej",
|
|
16
|
+
"uk": "поліпшення обробки помилок (#34)\nдодати просту оптимізацію\nСума вхідної енергії з втратою енергії для нового стану `dailySolarYield й\nнамагатися відтворити `yield сьогодні` з порталу fusion",
|
|
17
|
+
"zh-cn": "改进错误处理(# 34)\n添加简单的优化信息\nRiemann 输入功率总和, `\n尝试从聚变门户重现“今天”"
|
|
18
|
+
},
|
|
6
19
|
"0.1.3": {
|
|
7
20
|
"en": "display the data from PV strings (#27)\noptimize the timing of interval loop\nimproved handling of read timeouts from more then 2 inverters",
|
|
8
21
|
"de": "die Daten von PV-Strings anzeigen (#27)\noptimieren sie das timing der intervallschleife\nverbesserte handhabung von lesezeitausgängen von mehr als 2 wechselrichtern",
|
|
@@ -29,79 +42,36 @@
|
|
|
29
42
|
"uk": "виправити: немає даних, якщо інтервал менше 20 сек (#24)\nпідготовка зібраних значень точно\nрозширити до 5 інверторів #18\nвиправити проблеми з декількома інверторами",
|
|
30
43
|
"zh-cn": "固定: 如果间隔小于20秒(# 24) 则无数据\n更准确地编制收集的数值\n扩展至5个反转器 # 18\n解决多个反转器的问题"
|
|
31
44
|
},
|
|
32
|
-
"0.1.2-alpha.3": {
|
|
33
|
-
"en": "fix: wrong deploying date",
|
|
34
|
-
"de": "fix: falsches bereitstellungsdatum",
|
|
35
|
-
"ru": "исправить: неправильная дата развертывания",
|
|
36
|
-
"pt": "correção: data de implantação errada",
|
|
37
|
-
"nl": "fix: verkeerde datum voor ingebruikname",
|
|
38
|
-
"fr": "correction : mauvaise date de déploiement",
|
|
39
|
-
"it": "fix: la data di distribuzione sbagliata",
|
|
40
|
-
"es": "fijado: fecha de despliegue incorrecta",
|
|
41
|
-
"pl": "fix: nieprawidłowa data rozmieszczenia",
|
|
42
|
-
"uk": "виправити: неправильна дата розгортання",
|
|
43
|
-
"zh-cn": "固定: 部署日期错误"
|
|
44
|
-
},
|
|
45
|
-
"0.1.2-alpha.2": {
|
|
46
|
-
"en": "fix: no Data if interval less 20 sec (#24)",
|
|
47
|
-
"de": "fix: keine Daten, wenn Intervall weniger 20 sec (#24)",
|
|
48
|
-
"ru": "исправление: нет данных, если интервал менее 20 секунд (#24)",
|
|
49
|
-
"pt": "correção: não Dados se intervalo menos 20 segundos (#24)",
|
|
50
|
-
"nl": "fix: geen Gegevens indien interval minder 20 sec (#24)",
|
|
51
|
-
"fr": "correction : pas de données si l'intervalle est inférieur à 20 secondes (#24)",
|
|
52
|
-
"it": "correzione: no Dati se intervallo meno 20 sec (#24)",
|
|
53
|
-
"es": "fijado: no Datos si intervalo menos 20 segundos (#24)",
|
|
54
|
-
"pl": "fix: no Data if interval less 20 sec (# 24)",
|
|
55
|
-
"uk": "виправити: немає даних, якщо інтервал менше 20 сек (#24)",
|
|
56
|
-
"zh-cn": "固定: 如果间隔小于20秒(# 24) 则无数据"
|
|
57
|
-
},
|
|
58
|
-
"0.1.2-alpha.1": {
|
|
59
|
-
"en": "deploy npm package",
|
|
60
|
-
"de": "npm paket bereitstellen",
|
|
61
|
-
"ru": "развернуть пакет npm",
|
|
62
|
-
"pt": "implementar pacote npm",
|
|
63
|
-
"nl": "npm-pakket implementeren",
|
|
64
|
-
"fr": "déployer le paquet npm",
|
|
65
|
-
"it": "distribuire il pacchetto npm",
|
|
66
|
-
"es": "paquete npm",
|
|
67
|
-
"pl": "rozmieszczenie pakietu npm",
|
|
68
|
-
"uk": "пакет npm",
|
|
69
|
-
"zh-cn": "部署 npm 软件包"
|
|
70
|
-
},
|
|
71
|
-
"0.1.2-alpha.0": {
|
|
72
|
-
"en": "prepare collected values more precisely\nexpand up to 5 inverters #18\nfix problems with multiple inverters",
|
|
73
|
-
"de": "die gesammelten werte genauer\nbis zu 5 wechselrichtern #18 erweitern\nprobleme mit mehreren wechselrichtern beheben",
|
|
74
|
-
"ru": "готовить собранные значения точнее\n#18\nисправить проблемы с несколькими инверторами",
|
|
75
|
-
"pt": "preparar valores coletados mais precisamente\nexpandir até 5 inversores #18\ncorrigir problemas com vários inversores",
|
|
76
|
-
"nl": "verzamelde waarden nauwkeuriger voorbereiden\nuit te breiden tot 5 omvormers #18\nproblemen oplossen met meerdere inverters",
|
|
77
|
-
"fr": "préparer plus précisément les valeurs collectées\nétendre jusqu'à 5 onduleurs #18\nrésoudre les problèmes avec plusieurs onduleurs",
|
|
78
|
-
"it": "preparare i valori raccolti più precisamente\nespandere fino a 5 inverter #18\nrisolvere problemi con più inverter",
|
|
79
|
-
"es": "preparar los valores recogidos con mayor precisión\nampliar hasta 5 inversores #18\nsolucionar problemas con múltiples inversores",
|
|
80
|
-
"pl": "dokładniej przygotować zebrane wartości\nrozszerzyć do 5 inwerterów # 18\nrozwiązać problemy z wieloma inwerterami",
|
|
81
|
-
"uk": "підготовка зібраних значень точно\nрозширити до 5 інверторів #18\nвиправити проблеми з декількома інверторами",
|
|
82
|
-
"zh-cn": "更准确地编制收集的数值\n扩展至5个反转器 # 18\n解决多个反转器的问题"
|
|
83
|
-
},
|
|
84
45
|
"0.1.1": {
|
|
85
46
|
"en": "fix some collected values",
|
|
86
|
-
"de": "Korrektur einiger der gesammelten Werte (collected values)"
|
|
47
|
+
"de": "Korrektur einiger der gesammelten Werte (collected values)",
|
|
48
|
+
"ru": "исправить некоторые собранные значения",
|
|
49
|
+
"pt": "corrigir alguns valores coletados",
|
|
50
|
+
"nl": "sommige verzamelde waarden vast te stellen",
|
|
51
|
+
"fr": "fixer certaines valeurs collectées",
|
|
52
|
+
"it": "correggere alcuni valori raccolti",
|
|
53
|
+
"es": "fijar algunos valores recogidos",
|
|
54
|
+
"pl": "naprawić niektóre zebrane wartości",
|
|
55
|
+
"uk": "виправити деякі зібрані значення",
|
|
56
|
+
"zh-cn": "固定一些收集的值"
|
|
87
57
|
}
|
|
88
58
|
},
|
|
89
59
|
"titleLang": {
|
|
90
|
-
"en": "sun2000",
|
|
91
|
-
"de": "sun2000",
|
|
92
|
-
"ru": "
|
|
93
|
-
"pt": "
|
|
94
|
-
"nl": "
|
|
95
|
-
"fr": "
|
|
96
|
-
"it": "
|
|
97
|
-
"es": "
|
|
98
|
-
"pl": "
|
|
99
|
-
"uk": "sun2000",
|
|
100
|
-
"zh-cn": "
|
|
60
|
+
"en": "Huawei sun2000 inverters",
|
|
61
|
+
"de": "Huawei sun2000 Wechselrichtern",
|
|
62
|
+
"ru": "Huawei sun2000 inverters",
|
|
63
|
+
"pt": "Huawei sun2000 inverters",
|
|
64
|
+
"nl": "Huawei sun2000 inverters",
|
|
65
|
+
"fr": "Huawei sun2000 inverters",
|
|
66
|
+
"it": "Huawei sun2000 inverters",
|
|
67
|
+
"es": "Huawei sun2000 inverters",
|
|
68
|
+
"pl": "Huawei sun2000 inverters",
|
|
69
|
+
"uk": "Huawei sun2000 inverters",
|
|
70
|
+
"zh-cn": "Huawei sun2000 inverters"
|
|
101
71
|
},
|
|
102
72
|
"desc": {
|
|
103
|
-
"en": "Read data from Huawei SUN2000
|
|
104
|
-
"de": "
|
|
73
|
+
"en": "Read data from Huawei SUN2000 inverters and LUNA2000 battery using Modbus TCP\n",
|
|
74
|
+
"de": "Auslesen von Huawei SUN2000 Wechselrichtern und LUNA2000 Akkus über Modbus TCP\n",
|
|
105
75
|
"ru": "Прочитайте данные от Huawei SUN2000 inverter и LUNA2000 батареи с помощью Modbus TCP\n",
|
|
106
76
|
"pt": "Leia dados do inversor Huawei SUN2000 e da bateria LUNA2000 usando Modbus TCP\n",
|
|
107
77
|
"nl": "Lees gegevens van Huawei SUN2000 inverter en LUNA2000 batterij met Modbus TCP\n",
|
|
@@ -199,7 +169,7 @@
|
|
|
199
169
|
"common": {
|
|
200
170
|
"name": {
|
|
201
171
|
"en": "Inverter IP",
|
|
202
|
-
"de": "
|
|
172
|
+
"de": "Wechselrichter IP"
|
|
203
173
|
},
|
|
204
174
|
"type": "string",
|
|
205
175
|
"role": "indicator.ip",
|
|
@@ -228,8 +198,8 @@
|
|
|
228
198
|
"type": "state",
|
|
229
199
|
"common": {
|
|
230
200
|
"name": {
|
|
231
|
-
"en": "
|
|
232
|
-
"de": "Modbus
|
|
201
|
+
"en": "Modbus IDs of inverters",
|
|
202
|
+
"de": "Modbus IDs der Wechselrichters"
|
|
233
203
|
},
|
|
234
204
|
"type": "string",
|
|
235
205
|
"role": "indicator.id",
|
|
@@ -253,6 +223,22 @@
|
|
|
253
223
|
"desc": "modbus update interval",
|
|
254
224
|
"unit": "sec"
|
|
255
225
|
}
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"_id": "info.JSONhealth",
|
|
229
|
+
"type": "state",
|
|
230
|
+
"common": {
|
|
231
|
+
"name": {
|
|
232
|
+
"en": "health information",
|
|
233
|
+
"de": "Gesundheitsinformation"
|
|
234
|
+
},
|
|
235
|
+
"type": "string",
|
|
236
|
+
"role": "indicator.alarm.health",
|
|
237
|
+
"read": true,
|
|
238
|
+
"write": false,
|
|
239
|
+
"desc": "",
|
|
240
|
+
"unit": ""
|
|
241
|
+
}
|
|
256
242
|
}
|
|
257
243
|
]
|
|
258
244
|
}
|
package/lib/modbus_connect.js
CHANGED
|
@@ -34,15 +34,11 @@ class ModbusConnect extends DeviceInterface {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
close() {
|
|
37
|
-
return new Promise((resolve
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (err) reject(err);
|
|
41
|
-
resolve(data);
|
|
42
|
-
} );
|
|
43
|
-
} else {
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
this.client.close(() => {
|
|
39
|
+
//if (err) reject(err);
|
|
44
40
|
resolve({});
|
|
45
|
-
}
|
|
41
|
+
} );
|
|
46
42
|
});
|
|
47
43
|
}
|
|
48
44
|
|
|
@@ -69,22 +65,22 @@ class ModbusConnect extends DeviceInterface {
|
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
return new Promise((resolve
|
|
74
|
-
this.client.destroy((
|
|
75
|
-
if (err) reject(err);
|
|
76
|
-
resolve(
|
|
68
|
+
_destroy() {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
this.client.destroy(() => {
|
|
71
|
+
//if (err) reject(err);
|
|
72
|
+
resolve({});
|
|
77
73
|
});
|
|
78
74
|
});
|
|
79
75
|
}
|
|
80
76
|
|
|
81
77
|
async _checkError(err) {
|
|
78
|
+
this.adapter.log.debug('modbus client error: '+JSON.stringify(err));
|
|
79
|
+
await this.close();
|
|
82
80
|
if (err.modbusCode == null) {
|
|
83
|
-
this.adapter.log.debug('
|
|
84
|
-
await this.close();
|
|
81
|
+
this.adapter.log.debug('modbus client destroyed');
|
|
85
82
|
//https://github.com/yaacov/node-modbus-serial/issues/96
|
|
86
83
|
//await this._destroy();
|
|
87
|
-
//this.adapter.log.debug('Client destroy!');
|
|
88
84
|
await this._create();
|
|
89
85
|
}
|
|
90
86
|
}
|
|
@@ -93,7 +89,7 @@ class ModbusConnect extends DeviceInterface {
|
|
|
93
89
|
async connect(repeatCounter = 0) {
|
|
94
90
|
try {
|
|
95
91
|
this.adapter.log.info('Open Connection...');
|
|
96
|
-
await this.close();
|
|
92
|
+
this.isOpen() && await this.close();
|
|
97
93
|
await this.client.setTimeout(5000);
|
|
98
94
|
await this.client.connectTcpRTUBuffered(this.ipAddress, { port: this.port} ); //hang
|
|
99
95
|
await this.delay(1000);
|
|
@@ -113,7 +109,6 @@ class ModbusConnect extends DeviceInterface {
|
|
|
113
109
|
async readHoldingRegisters(address, length) {
|
|
114
110
|
try {
|
|
115
111
|
await this.open();
|
|
116
|
-
//this.adapter.log.debug('client.setID: '+this._id);
|
|
117
112
|
await this.client.setID(this._id);
|
|
118
113
|
const data = await this.client.readHoldingRegisters(address, length);
|
|
119
114
|
return data.data;
|
package/lib/register.js
CHANGED
|
@@ -1,39 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
const {deviceType,batteryStatus,dataRefreshRate,dataType} = require(__dirname + '/types.js');
|
|
3
|
-
|
|
4
|
-
class StateMap {
|
|
5
|
-
constructor () {
|
|
6
|
-
this.stateMap = new Map();
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
get(id) {
|
|
10
|
-
return this.stateMap.get(id);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
set(id, value, options) {
|
|
14
|
-
if (options?.type == 'number' && isNaN(value)) return;
|
|
15
|
-
if (value !== null) {
|
|
16
|
-
if (options?.renew || this.get(id)?.value !== value) {
|
|
17
|
-
if (options?.stored ) {
|
|
18
|
-
this.stateMap.set(id, {id: id, value: value, stored: options.stored});
|
|
19
|
-
} else {
|
|
20
|
-
this.stateMap.set(id, {id: id, value: value});
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
values () {
|
|
27
|
-
return this.stateMap.values();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
}
|
|
3
|
+
const {StateMap,RiemannSum} = require(__dirname + '/tools.js');
|
|
31
4
|
|
|
32
5
|
class Registers {
|
|
33
6
|
constructor(adapterInstance) {
|
|
34
7
|
this.adapter = adapterInstance;
|
|
35
8
|
this.stateCache = new StateMap();
|
|
36
|
-
|
|
9
|
+
for (const inverter of this.adapter.inverters) {
|
|
10
|
+
inverter.solarSum = new RiemannSum();
|
|
11
|
+
}
|
|
37
12
|
this._loadStates();
|
|
38
13
|
|
|
39
14
|
//https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/stateroles.md
|
|
@@ -75,6 +50,9 @@ class Registers {
|
|
|
75
50
|
},
|
|
76
51
|
{
|
|
77
52
|
state: {id: 'derived.inputPowerWithEfficiencyLoss', name: 'input power with efficiency loss', type: 'number', unit: 'kW', role: 'value.power', desc: ''}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
state: {id: 'derived.dailySolarYield', name: 'Solar Yield Today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'Riemann sum of input power with efficiency loss'}
|
|
78
56
|
}
|
|
79
57
|
],
|
|
80
58
|
postHook: (path) => {
|
|
@@ -95,6 +73,9 @@ class Registers {
|
|
|
95
73
|
}
|
|
96
74
|
//inputPower_with_efficiency_loss
|
|
97
75
|
this.stateCache.set(path+'derived.inputPowerWithEfficiencyLoss', inPowerEff, {type: 'number'});
|
|
76
|
+
|
|
77
|
+
this.inverterInfo.solarSum.add(inPowerEff); //riemannSum
|
|
78
|
+
this.stateCache.set(path+'derived.dailySolarYield', this.inverterInfo.solarSum.sum, {type: 'number'});
|
|
98
79
|
}
|
|
99
80
|
},
|
|
100
81
|
{
|
|
@@ -372,7 +353,7 @@ class Registers {
|
|
|
372
353
|
},
|
|
373
354
|
{
|
|
374
355
|
address : 37100,
|
|
375
|
-
length :
|
|
356
|
+
length : 36,
|
|
376
357
|
info : 'meter info',
|
|
377
358
|
refresh : dataRefreshRate.low,
|
|
378
359
|
type : deviceType.meter,
|
|
@@ -451,6 +432,25 @@ class Registers {
|
|
|
451
432
|
{
|
|
452
433
|
state: {id: 'meter.activePowerL3', name: 'Active Power L3', type: 'number', unit: 'A', role: 'value.current'},
|
|
453
434
|
register: {reg: 37136, type: dataType.int32}
|
|
435
|
+
}
|
|
436
|
+
]
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
address : 37200,
|
|
440
|
+
length : 3,
|
|
441
|
+
info : 'optimizer info (static info)',
|
|
442
|
+
type : deviceType.inverter,
|
|
443
|
+
states: [{
|
|
444
|
+
state: {id: 'optimizer.optimizerTotalNumber', name: 'Optimizer Total Number', type: 'number', unit: '', role: 'value'},
|
|
445
|
+
register: {reg: 37200, type: dataType.int16}
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
state: {id: 'optimizer.optimizerOnlineNumber', name: 'Optimizer Online Number', type: 'number', unit: '', role: 'value'},
|
|
449
|
+
register: {reg: 37201, type: dataType.int16}
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
state: {id: 'optimizer.optimizerFeatureData', name: 'Optimizer Feature Data', type: 'number', unit: '', role: 'value'},
|
|
453
|
+
register: {reg: 37202, type: dataType.int16}
|
|
454
454
|
}]
|
|
455
455
|
},
|
|
456
456
|
{
|
|
@@ -519,11 +519,14 @@ class Registers {
|
|
|
519
519
|
this.postUpdateHooks = [
|
|
520
520
|
{
|
|
521
521
|
refresh : dataRefreshRate.low,
|
|
522
|
-
state: {id: 'derived.dailyInputYield', name: '
|
|
522
|
+
state: {id: 'derived.dailyInputYield', name: 'Portal Yield Today', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'Try to recreate the yield from the portal'},
|
|
523
523
|
fn : (path) => {
|
|
524
524
|
const disCharge = this.stateCache.get(path+'battery.currentDayDischargeCapacity')?.value;
|
|
525
525
|
const charge = this.stateCache.get(path+'battery.currentDayChargeCapacity')?.value;
|
|
526
|
-
|
|
526
|
+
let inputYield = Math.round((this.stateCache.get(path+'dailyEnergyYield')?.value + charge - disCharge)*100)/100;
|
|
527
|
+
const energyLoss = new Date().getHours()*this.inverterInfo.energyLoss;//+disCharge*0.05;
|
|
528
|
+
inputYield -= energyLoss;
|
|
529
|
+
if (inputYield < 0) inputYield=0;
|
|
527
530
|
this.stateCache.set(path+'derived.dailyInputYield', inputYield, {type: 'number'});
|
|
528
531
|
}
|
|
529
532
|
}
|
|
@@ -563,7 +566,8 @@ class Registers {
|
|
|
563
566
|
refresh : dataRefreshRate.low,
|
|
564
567
|
states : [
|
|
565
568
|
{id: 'collected.dailyEnergyYield', name: 'Daily Energy Yield', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'daily output yield of the inverters'},
|
|
566
|
-
{id: 'collected.dailyInputYield', name: '
|
|
569
|
+
{id: 'collected.dailyInputYield', name: 'Daily Portal Yield', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'Try to recreate the yield from the portal'},
|
|
570
|
+
{id: 'collected.dailySolarYield', name: 'Daily Solar Yield', type: 'number', unit: 'kWh', role: 'value.power.consumption', desc: 'Riemann sum of input power with efficiency loss'},
|
|
567
571
|
{id: 'collected.accumulatedEnergyYield', name: 'Accumulated Energy Yield', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
|
|
568
572
|
{id: 'collected.consumptionSum', name: 'Consumption Sum', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
|
|
569
573
|
{id: 'collected.gridExportStart', name: 'Grid Export Start Today', type: 'number', unit: 'kWh', role: 'value.power.consumption'},
|
|
@@ -580,7 +584,8 @@ class Registers {
|
|
|
580
584
|
{id: 'collected.ratedCapacity', name: 'Rated of battery Capacity', type: 'number', unit: 'Wh', role: 'value.capacity'}
|
|
581
585
|
],
|
|
582
586
|
fn : (inverters) => {
|
|
583
|
-
let inYield = 0;
|
|
587
|
+
let inYield = 0; //deprecated
|
|
588
|
+
let solarYield = 0;
|
|
584
589
|
let outYield = 0;
|
|
585
590
|
let enYield = 0;
|
|
586
591
|
let charge = 0;
|
|
@@ -591,7 +596,8 @@ class Registers {
|
|
|
591
596
|
let load = 0;
|
|
592
597
|
for (const inverter of inverters) {
|
|
593
598
|
outYield += this.stateCache.get(inverter.path+'.dailyEnergyYield')?.value;
|
|
594
|
-
inYield += this.stateCache.get(inverter.path+'.derived.dailyInputYield')?.value;
|
|
599
|
+
inYield += this.stateCache.get(inverter.path+'.derived.dailyInputYield')?.value; //deprecated
|
|
600
|
+
solarYield += this.stateCache.get(inverter.path+'.derived.dailySolarYield')?.value;
|
|
595
601
|
enYield += this.stateCache.get(inverter.path+'.accumulatedEnergyYield')?.value;
|
|
596
602
|
charge += this.stateCache.get(inverter.path+'.battery.currentDayChargeCapacity')?.value;
|
|
597
603
|
disCharge += this.stateCache.get(inverter.path+'.battery.currentDayDischargeCapacity')?.value;
|
|
@@ -603,7 +609,8 @@ class Registers {
|
|
|
603
609
|
}
|
|
604
610
|
}
|
|
605
611
|
this.stateCache.set('collected.dailyEnergyYield',outYield, {type: 'number'});
|
|
606
|
-
this.stateCache.set('collected.dailyInputYield',inYield, {type: 'number'});
|
|
612
|
+
this.stateCache.set('collected.dailyInputYield',inYield, {type: 'number'}); //deprecated
|
|
613
|
+
this.stateCache.set('collected.dailySolarYield',solarYield, {type: 'number'});
|
|
607
614
|
this.stateCache.set('collected.accumulatedEnergyYield',enYield, {type: 'number'});
|
|
608
615
|
const conSum = enYield + this.stateCache.get('meter.reverseActiveEnergy')?.value - this.stateCache.get('meter.positiveActiveEnergy')?.value;
|
|
609
616
|
this.stateCache.set('collected.consumptionSum',conSum, {type: 'number'});
|
|
@@ -731,8 +738,8 @@ class Registers {
|
|
|
731
738
|
if ( refreshRate !== dataRefreshRate.high) {
|
|
732
739
|
if (lastread) {
|
|
733
740
|
if (!reg.refresh) continue;
|
|
734
|
-
if ((start - lastread) <
|
|
735
|
-
this.adapter.log.debug('Last
|
|
741
|
+
if ((start - lastread) < this.adapter.settings.lowIntervall) {
|
|
742
|
+
this.adapter.log.debug('Last read :'+(start - lastread));
|
|
736
743
|
continue;
|
|
737
744
|
}
|
|
738
745
|
}
|
|
@@ -766,12 +773,6 @@ class Registers {
|
|
|
766
773
|
if (dataRefreshRate.compare(refreshRate,hook.refresh)) {
|
|
767
774
|
const state = hook.state;
|
|
768
775
|
if (!hook['initState'+this.inverterInfo.index]) {
|
|
769
|
-
/*
|
|
770
|
-
if (state.id == 'derived.dailyInputYield' ) {
|
|
771
|
-
this.adapter.log.debug('*** [runPostUpdateHooks modbusID '+JSON.stringify(this.inverterInfo));
|
|
772
|
-
this.adapter.log.debug('*** [runPostUpdateHooks] path '+path+state.id+' modbusID ');
|
|
773
|
-
}
|
|
774
|
-
*/
|
|
775
776
|
await this._initState(path,state);
|
|
776
777
|
}
|
|
777
778
|
hook.fn(path);
|
|
@@ -780,7 +781,6 @@ class Registers {
|
|
|
780
781
|
}
|
|
781
782
|
}
|
|
782
783
|
|
|
783
|
-
|
|
784
784
|
async runProcessHooks(refreshRate) {
|
|
785
785
|
for (const hook of this.postProcessHooks) {
|
|
786
786
|
if (dataRefreshRate.compare(refreshRate,hook.refresh)) {
|
|
@@ -796,28 +796,55 @@ class Registers {
|
|
|
796
796
|
this.storeStates(); //fire and forget
|
|
797
797
|
}
|
|
798
798
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
} );
|
|
799
|
+
async _loadStates() {
|
|
800
|
+
let state = await this.adapter.getStateAsync('collected.gridExportStart');
|
|
801
|
+
this.stateCache.set('collected.gridExportStart',state?.val, {type : 'number', stored : true });
|
|
802
|
+
state = await this.adapter.getStateAsync('collected.gridImportStart');
|
|
803
|
+
this.stateCache.set('collected.gridImportStart',state?.val, {type : 'number', stored : true });
|
|
804
|
+
state = await this.adapter.getStateAsync('collected.consumptionStart');
|
|
805
|
+
this.stateCache.set('collected.consumptionStart',state?.val, {type : 'number', stored : true });
|
|
806
|
+
for (const inverter of this.adapter.inverters) {
|
|
807
|
+
state = await this.adapter.getStateAsync(inverter.path+'.derived.dailySolarYield');
|
|
808
|
+
state?.val && inverter.solarSum.setStart(state.val,state.ts);
|
|
809
|
+
}
|
|
811
810
|
}
|
|
812
|
-
*/
|
|
813
811
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
this.
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
812
|
+
ChechReadError(timeShift) {
|
|
813
|
+
const now = new Date();
|
|
814
|
+
for (const inverter of this.adapter.inverters) {
|
|
815
|
+
for (const [i, reg] of this.registerFields.entries()) {
|
|
816
|
+
if (reg.type == deviceType.meter && inverter.meter == false) continue; //not meter
|
|
817
|
+
if (reg.states && reg.refresh) {
|
|
818
|
+
const lastread = reg['lastread'+inverter.index];
|
|
819
|
+
const ret = {
|
|
820
|
+
errno : 0,
|
|
821
|
+
address : reg.address,
|
|
822
|
+
info : reg.info,
|
|
823
|
+
inverter : inverter.index,
|
|
824
|
+
modbusID : inverter.modbusId,
|
|
825
|
+
tc : now.getTime
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
if (lastread) {
|
|
829
|
+
ret.lastread = lastread;
|
|
830
|
+
} else {
|
|
831
|
+
ret.lastread = 0;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
if (now.getTime()-ret.lastread > timeShift) {
|
|
835
|
+
if (reg.lastread == 0 && i == 0) {
|
|
836
|
+
ret.errno = 101;
|
|
837
|
+
ret.message = 'Can\'t read data from inverter! Please check the configuration.';
|
|
838
|
+
} else {
|
|
839
|
+
ret.errno = 102;
|
|
840
|
+
ret.message = 'Not all data can be read! Please inspect the sun2000 logs.';
|
|
841
|
+
}
|
|
842
|
+
return ret ;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return {message: 'No problems detected'};
|
|
821
848
|
}
|
|
822
849
|
|
|
823
850
|
// one minute before midnight - perform housekeeping actions
|
package/lib/tools.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class StateMap {
|
|
4
|
+
constructor () {
|
|
5
|
+
this.stateMap = new Map();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
get(id) {
|
|
9
|
+
return this.stateMap.get(id);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
set(id, value, options) {
|
|
13
|
+
if (options?.type == 'number' && isNaN(value)) return;
|
|
14
|
+
if (value !== null) {
|
|
15
|
+
if (options?.renew || this.get(id)?.value !== value) {
|
|
16
|
+
if (options?.type == 'number') {
|
|
17
|
+
value = Math.round((value + Number.EPSILON) * 1000) / 1000; //3rd behind
|
|
18
|
+
}
|
|
19
|
+
if (options?.stored ) {
|
|
20
|
+
this.stateMap.set(id, {id: id, value: value, stored: options.stored});
|
|
21
|
+
} else {
|
|
22
|
+
this.stateMap.set(id, {id: id, value: value});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
values () {
|
|
29
|
+
return this.stateMap.values();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class RiemannSum {
|
|
35
|
+
constructor (autoResetAtMitnight = true) {
|
|
36
|
+
this._resetAtMitnight = autoResetAtMitnight;
|
|
37
|
+
this.reset();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
add (newValue) {
|
|
41
|
+
//Obersumme bilden
|
|
42
|
+
const now = new Date();
|
|
43
|
+
if (this._resetAtMitnight) {
|
|
44
|
+
const night = new Date(
|
|
45
|
+
now.getFullYear(),
|
|
46
|
+
now.getMonth(),
|
|
47
|
+
now.getDate(), // today, ...
|
|
48
|
+
0, 0, 0 // ...at 00:00:00 hours
|
|
49
|
+
);
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
if (this._lastDate.getTime() <= night.getTime()) {
|
|
52
|
+
this.reset();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
this._sum += newValue * (now.getTime() - this._lastDate.getTime())/3600/1000; //Hour/Sekunden
|
|
57
|
+
this._lastDate = now;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
reset() {
|
|
61
|
+
this._sum = 0;
|
|
62
|
+
this._lastDate = new Date();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get sum() {
|
|
66
|
+
return this._sum;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setStart(sum, ts) {
|
|
70
|
+
this._sum = sum;
|
|
71
|
+
this._lastDate = new Date(ts);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
module.exports = {
|
|
77
|
+
StateMap,
|
|
78
|
+
RiemannSum
|
|
79
|
+
};
|
package/main.js
CHANGED
|
@@ -25,15 +25,16 @@ class Sun2000 extends utils.Adapter {
|
|
|
25
25
|
name: 'sun2000',
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
this.lastTimeUpdated =
|
|
28
|
+
this.lastTimeUpdated = new Date().getTime();
|
|
29
29
|
this.lastStateUpdatedHigh = 0;
|
|
30
30
|
this.lastStateUpdatedLow = 0;
|
|
31
31
|
this.isConnected = false;
|
|
32
32
|
this.inverters = [];
|
|
33
33
|
this.settings = {
|
|
34
|
-
|
|
34
|
+
highIntervall : 20000,
|
|
35
|
+
lowIntervall : 60000,
|
|
35
36
|
address : '',
|
|
36
|
-
port : 520
|
|
37
|
+
port : 520
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
this.on('ready', this.onReady.bind(this));
|
|
@@ -43,14 +44,6 @@ class Sun2000 extends utils.Adapter {
|
|
|
43
44
|
this.on('unload', this.onUnload.bind(this));
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
getInverterInfo(id) {
|
|
47
|
-
/*
|
|
48
|
-
const inverter = this.inverters.find((item) => item.modbusId == id);
|
|
49
|
-
return inverter;
|
|
50
|
-
*/
|
|
51
|
-
return this.inverters[id];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
47
|
async initPath() {
|
|
55
48
|
await this.extendObjectAsync('info', {
|
|
56
49
|
type: 'channel',
|
|
@@ -151,12 +144,18 @@ class Sun2000 extends utils.Adapter {
|
|
|
151
144
|
native: {}
|
|
152
145
|
});
|
|
153
146
|
|
|
147
|
+
await this.extendObjectAsync(path+'.optimizer', {
|
|
148
|
+
type: 'channel',
|
|
149
|
+
common: {
|
|
150
|
+
name: 'channel optimizer'
|
|
151
|
+
},
|
|
152
|
+
native: {}
|
|
153
|
+
});
|
|
154
|
+
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
async InitProcess() {
|
|
158
|
-
this.state = new Registers(this);
|
|
159
|
-
this.modbusClient = new ModbusConnect(this,this.settings.address,this.settings.port);
|
|
160
159
|
try {
|
|
161
160
|
await this.initPath();
|
|
162
161
|
/*
|
|
@@ -165,6 +164,8 @@ class Sun2000 extends utils.Adapter {
|
|
|
165
164
|
} catch (err) {
|
|
166
165
|
this.log.warn(err);
|
|
167
166
|
}
|
|
167
|
+
this.modbusClient = new ModbusConnect(this,this.settings.address,this.settings.port);
|
|
168
|
+
this.state = new Registers(this);
|
|
168
169
|
this.dataPolling();
|
|
169
170
|
this.runWatchDog();
|
|
170
171
|
this.atMidnight();
|
|
@@ -221,29 +222,33 @@ class Sun2000 extends utils.Adapter {
|
|
|
221
222
|
runWatchDog() {
|
|
222
223
|
this.watchDogHandle && this.clearInterval(this.watchDogHandle);
|
|
223
224
|
this.watchDogHandle = this.setInterval( () => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
225
|
+
const sinceLastUpdate = new Date().getTime() - this.lastTimeUpdated; //ms
|
|
226
|
+
this.log.debug('Watchdog: time of last update '+sinceLastUpdate/1000+' sec');
|
|
227
|
+
const lastIsConnected = this.isConnected;
|
|
228
|
+
this.isConnected = this.lastStateUpdatedHigh > 0 && sinceLastUpdate < this.settings.highIntervall*3;
|
|
229
|
+
if (this.isConnected !== lastIsConnected ) this.setState('info.connection', this.isConnected, true);
|
|
230
|
+
if (!this.isConnected) {
|
|
231
|
+
this.setStateAsync('info.JSONhealth', {val: '{errno:1, message: "Can\'t connect to inverter"}', ack: true});
|
|
232
|
+
} else {
|
|
233
|
+
if (this.alreadyRunWatchDog) {
|
|
234
|
+
const ret = this.state.ChechReadError(this.settings.lowIntervall*2);
|
|
235
|
+
if (ret.errno) this.log.warn(ret.message);
|
|
236
|
+
this.setStateAsync('info.JSONhealth', {val: JSON.stringify(ret), ack: true});
|
|
237
|
+
} else {
|
|
238
|
+
this.alreadyRunWatchDog = true;
|
|
236
239
|
}
|
|
237
|
-
|
|
238
|
-
this.lastStateUpdatedLow = 0;
|
|
239
|
-
this.lastStateUpdatedHigh = 0;
|
|
240
|
+
}
|
|
240
241
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
this.lastStateUpdatedLow = 0;
|
|
243
|
+
this.lastStateUpdatedHigh = 0;
|
|
244
|
+
|
|
245
|
+
if (sinceLastUpdate > this.settings.highIntervall*10) {
|
|
246
|
+
this.setStateAsync('info.JSONhealth', {val: '{errno:2, message: "Internal loop error"}', ack: true});
|
|
247
|
+
this.log.warn('watchdog: restart Adapter...');
|
|
248
|
+
this.restart();
|
|
245
249
|
}
|
|
246
|
-
|
|
250
|
+
|
|
251
|
+
},this.settings.lowIntervall);
|
|
247
252
|
}
|
|
248
253
|
|
|
249
254
|
|
|
@@ -256,21 +261,25 @@ class Sun2000 extends utils.Adapter {
|
|
|
256
261
|
await this.setStateAsync('info.ip', {val: this.config.address, ack: true});
|
|
257
262
|
await this.setStateAsync('info.port', {val: this.config.port, ack: true});
|
|
258
263
|
await this.setStateAsync('info.modbusIds', {val: this.config.modbusIds, ack: true});
|
|
264
|
+
await this.setStateAsync('info.JSONhealth', {val: '{message : "Information is collected"}', ack: true});
|
|
259
265
|
|
|
260
266
|
// Load user settings
|
|
261
267
|
if (this.config.address !== '' || this.config.port > 0 || this.config.updateInterval > 0 ) {
|
|
262
|
-
this.settings.
|
|
268
|
+
this.settings.highIntervall = this.config.updateInterval*1000; //ms
|
|
263
269
|
this.settings.address = this.config.address;
|
|
264
270
|
this.settings.port = this.config.port;
|
|
265
271
|
this.settings.modbusIds = this.config.modbusIds.split(',').map((n) => {return Number(n);});
|
|
266
272
|
|
|
267
273
|
if (this.settings.modbusIds.length > 0 && this.settings.modbusIds.length < 6) {
|
|
268
|
-
if (this.settings.
|
|
269
|
-
this.settings.
|
|
274
|
+
if (this.settings.highIntervall < 5000*this.settings.modbusIds.length ) {
|
|
275
|
+
this.settings.highIntervall = 5000*this.settings.modbusIds.length;
|
|
276
|
+
}
|
|
277
|
+
if (this.settings.highIntervall >= this.settings.lowIntervall) {
|
|
278
|
+
this.settings.lowIntervall = this.settings.highIntervall;
|
|
270
279
|
}
|
|
271
|
-
await this.setStateAsync('info.modbusUpdateInterval', {val: this.settings.
|
|
280
|
+
await this.setStateAsync('info.modbusUpdateInterval', {val: this.settings.highIntervall/1000, ack: true});
|
|
272
281
|
for (const [i,id] of this.settings.modbusIds.entries()) {
|
|
273
|
-
this.inverters.push({index: i, modbusId: id, meter: (i==0)});
|
|
282
|
+
this.inverters.push({index: i, modbusId: id, energyLoss: 0.008, meter: (i==0)}); //own energy consumption of inverter 8 W
|
|
274
283
|
}
|
|
275
284
|
await this.InitProcess();
|
|
276
285
|
} else {
|
|
@@ -293,35 +302,34 @@ class Sun2000 extends utils.Adapter {
|
|
|
293
302
|
|
|
294
303
|
const start = new Date().getTime();
|
|
295
304
|
this.log.debug('### DataPolling START '+ Math.round((start-this.lastTimeUpdated)/1000)+' sec ###');
|
|
296
|
-
if (this.lastTimeUpdated > 0 && (start-this.lastTimeUpdated)/1000 > this.settings.
|
|
297
|
-
this.log.
|
|
305
|
+
if (this.lastTimeUpdated > 0 && (start-this.lastTimeUpdated)/1000 > this.settings.highIntervall/1000 + 1) {
|
|
306
|
+
this.log.info('time intervall '+(start-this.lastTimeUpdated)/1000+' sec');
|
|
298
307
|
}
|
|
299
308
|
this.lastTimeUpdated = start;
|
|
300
|
-
const nextLoop = this.settings.
|
|
309
|
+
const nextLoop = this.settings.highIntervall - start % (this.settings.highIntervall) + start;
|
|
301
310
|
|
|
302
311
|
//High Loop
|
|
303
312
|
for (const item of this.inverters) {
|
|
304
313
|
this.modbusClient.setID(item.modbusId);
|
|
305
314
|
this.lastStateUpdatedHigh += await this.state.updateStates(item,this.modbusClient,dataRefreshRate.high,timeLeft(nextLoop));
|
|
306
315
|
}
|
|
316
|
+
await this.state.runProcessHooks(dataRefreshRate.high);
|
|
307
317
|
|
|
308
318
|
if (timeLeft(nextLoop) > 500) {
|
|
309
|
-
await this.state.runProcessHooks(dataRefreshRate.high);
|
|
310
319
|
//Low Loop
|
|
311
320
|
for (const [i,item] of this.inverters.entries()) {
|
|
312
321
|
this.modbusClient.setID(item.modbusId);
|
|
313
322
|
//this.log.debug('+++++ Loop: '+i+' Left Time: '+timeLeft(nextLoop,(i+1)/this.inverters.length)+' Faktor '+((i+1)/this.inverters.length));
|
|
314
323
|
this.lastStateUpdatedLow += await this.state.updateStates(item,this.modbusClient,dataRefreshRate.low,timeLeft(nextLoop,(i+1)/this.inverters.length));
|
|
315
324
|
}
|
|
316
|
-
await this.state.runProcessHooks(dataRefreshRate.low);
|
|
317
325
|
}
|
|
326
|
+
await this.state.runProcessHooks(dataRefreshRate.low);
|
|
318
327
|
|
|
319
328
|
if (this.pollingTimer) this.clearTimeout(this.pollingTimer);
|
|
320
329
|
this.pollingTimer = this.setTimeout(() => {
|
|
321
330
|
this.dataPolling(); //recursiv
|
|
322
331
|
}, timeLeft(nextLoop));
|
|
323
332
|
this.log.debug('### DataPolling STOP ###');
|
|
324
|
-
//this.state.mitnightProcess();
|
|
325
333
|
}
|
|
326
334
|
|
|
327
335
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.sun2000",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "sun2000",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "bolliy",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@iobroker/adapter-core": "^3.0.4",
|
|
26
|
-
"modbus-serial": "^8.0.
|
|
26
|
+
"modbus-serial": "^8.0.16"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@alcalzone/release-script": "^3.6.0",
|