iobroker.sun2000 0.6.1 → 0.7.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 +18 -2
- package/admin/sun2000.png +0 -0
- package/io-package.json +93 -42
- package/lib/controls/service_queue.js +2 -3
- package/lib/drivers/driver_base.js +12 -12
- package/lib/drivers/driver_inverter.js +102 -91
- package/lib/drivers/driver_sdongle.js +11 -38
- package/lib/drivers/driver_slogger.js +2 -2
- package/lib/modbus/modbus_connect.js +0 -4
- package/lib/register.js +11 -17
- package/lib/tools.js +38 -2
- package/main.js +16 -7
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|

|
|
7
7
|

|
|
8
8
|
[](./docs/README.md)
|
|
9
|
+
[](https://www.paypal.com/donate/?hosted_button_id=ZTX3VP9LZBDCG)
|
|
10
|
+
[](https://github.com/sponsors/bolliy)
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
[](https://nodei.co/npm/iobroker.sun2000/)
|
|
11
14
|
|
|
@@ -17,12 +20,16 @@ Sentry reporting is used starting with js-controller 3.0.
|
|
|
17
20
|
|
|
18
21
|
## sun2000 adapter for ioBroker
|
|
19
22
|
|
|
20
|
-
Read register data from Huawei SUN2000 inverter and LUNA2000 battery using Modbus TCP.
|
|
23
|
+
Read and write register data from Huawei SUN2000 inverter and LUNA2000 battery using Modbus TCP. Third-party devices can be integrated via the modbus proxy. Even a Huawei SmartLogger can be integrated.
|
|
21
24
|
|
|
22
25
|
[Huawei product information](https://solar.huawei.com/en/professionals/all-products?residential-smart-pv)
|
|
23
26
|
|
|
24
27
|
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)
|
|
25
28
|
|
|
29
|
+
## Required
|
|
30
|
+
* Node.js 18.x or higher
|
|
31
|
+
* ioBroker host (js-controller) 5.x or higher
|
|
32
|
+
|
|
26
33
|
## Documentation
|
|
27
34
|
|
|
28
35
|
See the [documentation page](./docs/README.md) or
|
|
@@ -52,6 +59,15 @@ browse in the [wiki](https://github.com/bolliy/ioBroker.sun2000/wiki)
|
|
|
52
59
|
Placeholder for the next version (at the beginning of the line):
|
|
53
60
|
### **WORK IN PROGRESS**
|
|
54
61
|
-->
|
|
62
|
+
### 0.7.0 (2024-04-03)
|
|
63
|
+
* breaking changes
|
|
64
|
+
- Node.js 18.x or higher required
|
|
65
|
+
- ioBroker host (js-controller) 5.x or higher
|
|
66
|
+
|
|
67
|
+
### 0.6.2 (2024-03-31)
|
|
68
|
+
* standby detection adjusted
|
|
69
|
+
* improvement of logs
|
|
70
|
+
|
|
55
71
|
### 0.6.1 (2024-03-23)
|
|
56
72
|
* Battery control: After the second failed attempt, the control event is discarded
|
|
57
73
|
* Battery control: Adjust the battery maxCharge and Discharge to the actual values
|
|
@@ -61,7 +77,7 @@ browse in the [wiki](https://github.com/bolliy/ioBroker.sun2000/wiki)
|
|
|
61
77
|
* fix the standby detection #60
|
|
62
78
|
|
|
63
79
|
### 0.5.1 (2024-03-11)
|
|
64
|
-
*
|
|
80
|
+
* config page restructured
|
|
65
81
|
* read only the required string data
|
|
66
82
|
* fix interval medium
|
|
67
83
|
|
package/admin/sun2000.png
CHANGED
|
Binary file
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "sun2000",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.7.0": {
|
|
7
|
+
"en": "breaking changes\nNode.js 18.x or higher required\nioBroker host (js-controller) 5.x or higher",
|
|
8
|
+
"de": "änderungen\nNode.js 18.x oder höher erforderlich\nioBroker Host (js-controller) 5.x oder höher",
|
|
9
|
+
"ru": "изменения\nNode.js 18.x или выше требуется\nioBroker host (js-controller) 5.x или выше",
|
|
10
|
+
"pt": "alterações de ruptura\nNode.js 18.x ou superior requerido\nhospedeiro ioBroker (js-controller) 5.x ou superior",
|
|
11
|
+
"nl": "wijzigingen breken\nNode.js 18.x of hoger vereist\nioBroker host (js-controller) 5.x of hoger",
|
|
12
|
+
"fr": "casser les changements\nNode.js 18.x ou plus requis\nioBroker host (js-controller) 5.x ou plus",
|
|
13
|
+
"it": "cambiamenti di rottura\nNode.js 18.x o superiore richiesto\nioBroker host (js-controller) 5.x o superiore",
|
|
14
|
+
"es": "cambios de ruptura\nNode.js 18.x o superior requerido\nioBroker host (js-controller) 5.x o superior",
|
|
15
|
+
"pl": "łamanie zmian\nNode.js 18,x lub wyższa wymagana\njoBroker host (kontroler js-) 5.x lub wyższy",
|
|
16
|
+
"uk": "поломка змін\nNode.js 18.x або вище потрібно\nioBroker host (js-controller) 5.x або вище",
|
|
17
|
+
"zh-cn": "断开更改\n所需节点.js 18.x或更高\nioBroker 主机(js-controller) 5.x或更高"
|
|
18
|
+
},
|
|
19
|
+
"0.6.2": {
|
|
20
|
+
"en": "standby detection adjusted\nImprovement of logs",
|
|
21
|
+
"de": "standby-erkennung angepasst\nVerbesserung der Protokolle",
|
|
22
|
+
"ru": "резервное обнаружение с поправкой\nСовершенствование журналов",
|
|
23
|
+
"pt": "detecção de espera ajustada\nMelhoria dos logs",
|
|
24
|
+
"nl": "stand-by detectie aangepast\nVerbetering van de logs",
|
|
25
|
+
"fr": "réglage de la détection en attente\nAmélioration des registres",
|
|
26
|
+
"it": "rilevamento standby regolato\nMiglioramento dei registri",
|
|
27
|
+
"es": "detección de reserva ajustada\nMejora de los registros",
|
|
28
|
+
"pl": "korekta detekcji czuwania\nPoprawa dzienników",
|
|
29
|
+
"uk": "автономне виявлення\nУдосконалення колод",
|
|
30
|
+
"zh-cn": "经调整的备用检测\n改进日志"
|
|
31
|
+
},
|
|
6
32
|
"0.6.1": {
|
|
7
33
|
"en": "Battery control: After the second failed attempt, the control event is discarded\nBattery control: Adjust the battery maxCharge and Discharge to the actual values",
|
|
8
34
|
"de": "Batteriesteuerung: Nach dem zweiten gescheiterten Versuch wird das Kontrollereignis verworfen\nBatteriesteuerung: Einstellen der Batterie maxCharge und Entladen auf die tatsächlichen Werte",
|
|
@@ -67,32 +93,6 @@
|
|
|
67
93
|
"pl": "odczyt danych strun fotowoltaicznych wolniej (interwał średni)",
|
|
68
94
|
"uk": "читати повільніші дані ПВ (середній інтервал)",
|
|
69
95
|
"zh-cn": "读取较慢的光电字符串数据( 中间隔)"
|
|
70
|
-
},
|
|
71
|
-
"0.4.0": {
|
|
72
|
-
"en": "detect standby mode of inverters (#34)\ndevices in standby often give incorrect values. These are assigned \"0\" (#40)\nthe modbus register and the length are stored in the description of the states\nimplemented modbus-proxy (read-only cache)\nread register data from SDongleA \nadditional loop interval medium (SDongle data)\nIntegration of [NRGkick Wallbox](https://www.nrgkick.com)\nread string data faster (high interval)",
|
|
73
|
-
"de": "standby-modus von wechselrichtern erkennen (#34)\ngeräte im Standby geben oft falsche Werte. Diese werden \"0\" zugeordnet (#40)\ndas modbusregister und die länge werden in der beschreibung der zustände gespeichert\nimplementiert modbus-proxy (nur lesen cache)\nregisterdaten von SDongleA lesen\nzusätzliches Loop-Intervallmedium (SDongle-Daten)\nIntegration von [NRGkick Wallbox](https://www.nrgkick.com)\nstringdaten schneller lesen (hohes intervall)",
|
|
74
|
-
"ru": "#34\nустройства в режиме ожидания часто дают неправильные значения. Назначены \"0\" (#40)\nрегистр modbus и длина хранятся в описании состояний\nреализованный modbus-proxy (только для чтения)\nчитать данные реестра SDongleA\nдополнительная интерактивная среда (данные SDongle)\nИнтеграция [NRGkick Wallbox] (https://www.nrgkick.com)\nчитать строковые данные быстрее (высокий интервал)",
|
|
75
|
-
"pt": "detectar modo de espera de inversores (#34)\ndispositivos em espera muitas vezes dão valores incorretos. Estes são atribuídos \"0\" (#40)\no registro do modbus e o comprimento são armazenados na descrição dos estados\nimplementado modbus-proxy (apenas leitura cache)\nler dados de registro de SDongleA\nmeio de intervalo de loop adicional (dados de SDongle)\nIntegração de [NRGkick Wallbox](https://www.nrgkick.com)\nler dados de cadeia mais rápido (alto intervalo)",
|
|
76
|
-
"nl": "de standbymodus van de inverters detecteren (#34)\napparaten in stand-by geven vaak onjuiste waarden. Deze worden toegewezen aan \"0\" (#40)\nde modbus register en de lengte worden opgeslagen in de beschrijving van de staten\ngeïmplementeerd modbus-proxy (alleen-lezen cache)\nlees registergegevens van SDongleA\nextra loopintervalmedium (SDongle-gegevens)\nIntegratie van [NRGkick Wallbox](https://www.nrgkick.com)\nlees tekstgegevens sneller (hoog interval)",
|
|
77
|
-
"fr": "détecter le mode veille des onduleurs (#34)\nles dispositifs en attente donnent souvent des valeurs incorrectes. Ils sont assignés « 0 » (#40)\nle registre modbus et la longueur sont stockés dans la description des états\nmis en œuvre modbus-proxy (cache de lecture seule)\nlire les données du registre de SDongleA\nmilieu d'intervalle de boucle supplémentaire (données SDongle)\nIntégration de [NRGkick Wallbox](https://www.nrgkick.com)\nlire les données de chaîne plus rapidement (intervalle élevé)",
|
|
78
|
-
"it": "rilevare modalità standby di inverter (#34)\ni dispositivi in standby spesso danno valori errati. Questi sono assegnati \"0\" (#40)\nil registro modbus e la lunghezza sono memorizzate nella descrizione degli stati\nimplementato modbus-proxy ( cache di sola lettura)\nleggere i dati del registro da SDongleA\nintervallo di loop aggiuntivo medio (dati del triangolo)\nIntegrazione [NRGkick Wallbox](https://www.nrgkick.com)\nleggere i dati della stringa più velocemente (alto intervallo)",
|
|
79
|
-
"es": "detectar el modo de espera de los inversores (#34)\ndispositivos en standby a menudo dan valores incorrectos. Estos son asignados \"0\" (#40)\nel registro del modbus y la longitud se almacenan en la descripción de los estados\naplicado modbus-proxy (caché de sólo lectura)\nread register data from SDongleA\nintervalo de bucle adicional medio (datos de SDongle)\nIntegración de [NRGkick Wallbox](https://www.nrgkick.com)\nleer datos de cadena más rápido (intervalo alto)",
|
|
80
|
-
"pl": "wykrywanie trybu czuwania inwerterów (# 34)\nurządzenia w trybie czuwania często dają nieprawidłowe wartości. Są one przypisane \"0\" (# 40)\nrejestr modbus i długość są przechowywane w opisie stanów\nzaimplementowany modbus- proxy (tylko read- cache)\nczytaj dane rejestru z SDongleA\ndodatkowe medium interwałowe pętli (dane SDongle)\nW związku z tym Komisja uznała, że nie ma podstaw, aby stwierdzić, że w odniesieniu do niektórych rodzajów działalności, które nie są objęte zakresem rozporządzenia (WE) nr 659 / 1999, nie można uznać, że działalność ta nie jest zgodna z rynkiem wewnętrznym\nodczyt danych łańcuchowych szybciej (wysoki interwał)",
|
|
81
|
-
"uk": "виявити режим очікування інверторів (#34)\nпристрої в режимі очікування часто дають неправильні значення. Присвоюється \"0\" (#40)\nреєстр модбусів і довжина зберігаються в описі станів\nреалізований модбус-проксі (тільки кеш)\nчитати реєстраційні дані з SDongleA\nдодатковий інтервал петлі середній (SDongle data)\nІнтеграція [NRGkick Wallbox](https://www.nrgkick.com)\nчитати дані рядка швидше (високий інтервал)",
|
|
82
|
-
"zh-cn": "检测反转器的备用模式 (# 34)\n正在待命的设备往往给出错误的值。 这些被分配为\"0\" (# 40)\nmodbus 寄存器和长度存储在状态描述中\n已执行的 modbus- 代理( 只读缓存)\n从 SDongleA 读取寄存器数据\n额外的循环间隔介质( SDongle 数据)\n整合[NRGkick Wallbox](https://www.nrgkick.com)\n更快地读取字符串数据( 高间隔)"
|
|
83
|
-
},
|
|
84
|
-
"0.3.1": {
|
|
85
|
-
"en": "state `sun2000.0.collected.chargeDischargePowercharge` is not always refreshed #47",
|
|
86
|
-
"de": "state `sun2000.0.collected.chargeDischargePowercharge` ist nicht immer erfrischt #47",
|
|
87
|
-
"ru": "состояние `sun2000.0.collected.chargeDischargePowercharge' не всегда обновляется #47",
|
|
88
|
-
"pt": "estado `sun2000.0.collected.chargeDischargePowercharge` nem sempre é atualizado #47",
|
|
89
|
-
"nl": "staat ",
|
|
90
|
-
"fr": "état `sun2000.0.collected.chargeDéchargePowercharge` n'est pas toujours rafraîchi #47",
|
|
91
|
-
"it": "stato `sun2000.0.colletto.chargeDischargePowercharge` non è sempre aggiornato #47",
|
|
92
|
-
"es": "estado `sun2000.0.collected.chargeDischargePowercharge` no siempre se actualiza #47",
|
|
93
|
-
"pl": "state 'sun2000.0.0 collected.chargeDischargePowercharge' nie zawsze jest odświeżona # 47",
|
|
94
|
-
"uk": "#47",
|
|
95
|
-
"zh-cn": "state `sun 2000.00.colled. 充电电源 ' 并不总是刷新47"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
|
@@ -109,17 +109,17 @@
|
|
|
109
109
|
"zh-cn": "Huawei sun2000 inverter"
|
|
110
110
|
},
|
|
111
111
|
"desc": {
|
|
112
|
-
"en": "
|
|
113
|
-
"de": "Daten von bis zu 5 Huawei SUN2000 Wechselrichtern und LUNA2000 Batterien
|
|
114
|
-
"ru": "
|
|
115
|
-
"pt": "
|
|
116
|
-
"nl": "
|
|
117
|
-
"fr": "
|
|
118
|
-
"it": "
|
|
119
|
-
"es": "
|
|
120
|
-
"pl": "
|
|
121
|
-
"uk": "
|
|
122
|
-
"zh-cn": "
|
|
112
|
+
"en": "To read and write data from up to 5 Huawei SUN2000 inverters and LUNA2000 batteries via Modbus TCP. Third-party devices can be integrated via the modbus proxy.",
|
|
113
|
+
"de": "Zum Lesen und Schreiben von Daten von bis zu 5 Huawei SUN2000 Wechselrichtern und LUNA2000 Batterien über Modbus TCP. Drittgeräte können über den Modbus Proxy integriert werden.",
|
|
114
|
+
"ru": "Прочитать и писать данные до 5 инверторов Huawei SUN2000 и батарей LUNA2000 через Modbus TCP. Сторонние устройства могут быть интегрированы через modbus proxy.",
|
|
115
|
+
"pt": "Para ler e escrever dados de até 5 inversores Huawei SUN2000 e baterias LUNA2000 via Modbus TCP. Dispositivos de terceiros podem ser integrados através do proxy modbus.",
|
|
116
|
+
"nl": "Gegevens lezen en schrijven van maximaal 5 Huawei SUN2000 omvormers en LUNA2000 batterijen via Modbus TCP. De apparaten van derden kunnen worden geïntegreerd via de modbus proxy.",
|
|
117
|
+
"fr": "Pour lire et écrire des données de jusqu'à 5 onduleurs Huawei SUN2000 et batteries LUNA2000 via Modbus TCP. Les périphériques tiers peuvent être intégrés via le proxy modbus.",
|
|
118
|
+
"it": "Per leggere e scrivere dati da un massimo di 5 inverter Huawei SUN2000 e batterie LUNA2000 tramite Modbus TCP. I dispositivi di terze parti possono essere integrati tramite il proxy modbus.",
|
|
119
|
+
"es": "Para leer y escribir datos de hasta 5 inversores Huawei SUN2000 y baterías LUNA2000 a través de Modbus TCP. Los dispositivos de terceros se pueden integrar a través del modbus proxy.",
|
|
120
|
+
"pl": "Do odczytu i zapisu danych z do 5 Huawei SUN2000 inwerterów i akumulatorów LUNA2000 za pośrednictwem Modbus TCP. Urządzenia trójstronne mogą być zintegrowane za pośrednictwem proxy modbus.",
|
|
121
|
+
"uk": "Щоб читати та писати дані від 5 інверторів Huawei SUN2000 та батарей LUNA2000 через Modbus TCP. Сторонні пристрої можуть бути інтегровані через проксі модбуса.",
|
|
122
|
+
"zh-cn": "通过Modbus TCP读取和写入最多5个Huawei SUN2000反转器和LUNA2000电池的数据。 第三方设备可以通过modbus代理集成."
|
|
123
123
|
},
|
|
124
124
|
"authors": [
|
|
125
125
|
"bolliy <stephan@mante.info>"
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"sun2000",
|
|
131
131
|
"luna2000",
|
|
132
132
|
"inverter",
|
|
133
|
-
"
|
|
133
|
+
"smartcharger",
|
|
134
134
|
"sdongle"
|
|
135
135
|
],
|
|
136
136
|
"tier": 2,
|
|
@@ -154,9 +154,60 @@
|
|
|
154
154
|
"adminUI": {
|
|
155
155
|
"config": "json"
|
|
156
156
|
},
|
|
157
|
+
"docs": {
|
|
158
|
+
"en": [
|
|
159
|
+
"docs/README.md"
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
"messages": [
|
|
163
|
+
{
|
|
164
|
+
"condition": {
|
|
165
|
+
"operand": "and",
|
|
166
|
+
"rules": [
|
|
167
|
+
"oldVersion<=0.6.2",
|
|
168
|
+
"newVersion>0.6.2"
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
"title": {
|
|
172
|
+
"en": "node.js 18.x or higher required",
|
|
173
|
+
"de": "node.js 18.x oder höher erforderlich",
|
|
174
|
+
"ru": "node.js 18.x или выше требуется",
|
|
175
|
+
"pt": "node.js 18.x ou superior requerido",
|
|
176
|
+
"nl": "node.js 18.x of hoger vereist",
|
|
177
|
+
"fr": "node.js 18.x ou plus requis",
|
|
178
|
+
"it": "node.js 18.x o superiore richiesto",
|
|
179
|
+
"es": "node.js 18.x o superior requerido",
|
|
180
|
+
"pl": "node.js 18,x lub wyższa wymagana",
|
|
181
|
+
"uk": "node.js 18.x або вище потрібно",
|
|
182
|
+
"zh-cn": "节点.js 18.x或更高"
|
|
183
|
+
},
|
|
184
|
+
"text": {
|
|
185
|
+
"en": "Please try to stay current with your Node.js version because the support is limited in time. As of now all Node.js versions below 18.x are no longer supported. (End Of Life).",
|
|
186
|
+
"de": "Bitte versuchen Sie die Node.js-Version aktuell zu halten, da die Unterstützung zeitlich begrenzt ist. Ab sofort werden alle Node.js Versionen unter 18.x nicht mehr unterstützt. (End Of Life).",
|
|
187
|
+
"ru": "Пожалуйста, попытайтесь оставаться в курсе своей версии Node.js, потому что поддержка ограничена во времени. На данный момент все версии Node.js ниже 18.x больше не поддерживаются. (Конец жизни).",
|
|
188
|
+
"pt": "Por favor, tente manter-se atual com sua versão Node.js porque o suporte é limitado a tempo. A partir de agora todas as versões do Node.js abaixo de 18.x não são mais suportadas. (End Of Life).",
|
|
189
|
+
"nl": "Probeer om actueel te blijven met uw Node.js-versie omdat de ondersteuning beperkt is in de tijd. Vanaf nu worden alle Node.js versies onder 18.x niet meer ondersteund. (Einde van het leven).",
|
|
190
|
+
"fr": "Veuillez essayer de rester à jour avec votre version Node.js car le support est limité dans le temps. Toutes les versions de Node.js sous 18.x ne sont plus prises en charge. (Fin de vie).",
|
|
191
|
+
"it": "Si prega di cercare di rimanere corrente con la versione Node.js perché il supporto è limitato nel tempo. A partire da ora tutte le versioni Node.js inferiori a 18.x non sono più supportate. (End Of Life).",
|
|
192
|
+
"es": "Por favor, trate de mantenerse actualizado con su versión Node.js porque el soporte es limitado en el tiempo. A partir de ahora todas las versiones Node.js debajo de 18.x ya no están soportadas. (End Of Life).",
|
|
193
|
+
"pl": "Proszę spróbować pozostać na bieżąco z wersją Node.js ponieważ wsparcie jest ograniczone w czasie. Od teraz wszystkie wersje Node.js poniżej 18.x nie są już obsługiwane. (Koniec życia).",
|
|
194
|
+
"uk": "Будь-ласка, будь ласка, намагайтеся постійно триматися з версією Node.js, оскільки підтримка обмежена в часі. Зараз всі версії Node.js нижче 18.x не підтримуються. (Для життя).",
|
|
195
|
+
"zh-cn": "请尝试保留您的 Node.js 版本, 因为支持时间有限 。 到目前为止,18x以下的所有Node.js版本不再被支持. (寿终."
|
|
196
|
+
},
|
|
197
|
+
"level": "info",
|
|
198
|
+
"buttons": [
|
|
199
|
+
"agree",
|
|
200
|
+
"cancel"
|
|
201
|
+
],
|
|
202
|
+
"link": "https://github.com/bolliy/ioBroker.sun2000/wiki/Update-node.js",
|
|
203
|
+
"linkText": {
|
|
204
|
+
"en": "The node.js update procedure is described here."
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
],
|
|
157
208
|
"dependencies": [
|
|
158
209
|
{
|
|
159
|
-
"js-controller": ">=
|
|
210
|
+
"js-controller": ">=5.0.0"
|
|
160
211
|
}
|
|
161
212
|
],
|
|
162
213
|
"globalDependencies": [
|
|
@@ -253,7 +304,7 @@
|
|
|
253
304
|
"role": "indicator.port",
|
|
254
305
|
"read": true,
|
|
255
306
|
"write": false,
|
|
256
|
-
"desc": "
|
|
307
|
+
"desc": "Device modbus port"
|
|
257
308
|
}
|
|
258
309
|
},
|
|
259
310
|
{
|
|
@@ -268,7 +319,7 @@
|
|
|
268
319
|
"role": "indicator.id",
|
|
269
320
|
"read": true,
|
|
270
321
|
"write": false,
|
|
271
|
-
"desc": "
|
|
322
|
+
"desc": "device modbus IDs"
|
|
272
323
|
}
|
|
273
324
|
},
|
|
274
325
|
{
|
|
@@ -106,7 +106,6 @@ class ServiceQueueMap {
|
|
|
106
106
|
if (item.type == deviceType.battery && this.inverterInfo?.numberBatteryUnits == 0) continue;
|
|
107
107
|
|
|
108
108
|
if (item?.state) {
|
|
109
|
-
//this._map.set(item.state.id, { field : item , value : null, ack : false });
|
|
110
109
|
this._serviceMap.set(item.state.id, item);
|
|
111
110
|
}
|
|
112
111
|
}
|
|
@@ -134,7 +133,7 @@ class ServiceQueueMap {
|
|
|
134
133
|
tCDP[3] = 383 - Working mode settings - self-consumption (discharge)
|
|
135
134
|
*/
|
|
136
135
|
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];
|
|
137
|
-
if (await this._writeRegisters(47255,tCDP)) this.adapter.logger.info('Control: The default TOU
|
|
136
|
+
if (await this._writeRegisters(47255,tCDP)) this.adapter.logger.info('Control: The default TOU setting are transferred');
|
|
138
137
|
}
|
|
139
138
|
}
|
|
140
139
|
}
|
|
@@ -191,7 +190,7 @@ class ServiceQueueMap {
|
|
|
191
190
|
service.errorCount ++;
|
|
192
191
|
if (service.errorCount > 1) {
|
|
193
192
|
this._eventMap.delete(event.id); //forget it
|
|
194
|
-
this.log.
|
|
193
|
+
this.log.info('Control: Event is discarded because it could not be processed. '+this.inverterInfo.path+'.control.'+event.id);
|
|
195
194
|
}
|
|
196
195
|
}
|
|
197
196
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {deviceType,storeType,dataRefreshRate,dataType} = require(__dirname + '/../types.js');
|
|
4
|
-
const {Logging,RegisterMap
|
|
4
|
+
const {Logging,RegisterMap} = require(__dirname + '/../tools.js');
|
|
5
5
|
|
|
6
6
|
class DriverBase {
|
|
7
7
|
constructor(stateInstance,device, options) {
|
|
@@ -17,12 +17,13 @@ class DriverBase {
|
|
|
17
17
|
this._driverClass = options?.driverClass,
|
|
18
18
|
this._name = options?.name;
|
|
19
19
|
|
|
20
|
+
this._errorCount = 0;
|
|
20
21
|
this._modbusAllowed = true; //modbus request is allowed
|
|
21
22
|
this._deviceStatus = -1; //device shutdown or standby
|
|
22
23
|
this._regMap = new RegisterMap();
|
|
23
24
|
|
|
24
25
|
//v0.6.x
|
|
25
|
-
this.control =
|
|
26
|
+
this.control = null; //Battery Charge control
|
|
26
27
|
this.log = new Logging(this.adapter); //my own Logger
|
|
27
28
|
|
|
28
29
|
this.registerFields = [];
|
|
@@ -46,8 +47,6 @@ class DriverBase {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
get modbusAllowed () {
|
|
49
|
-
//v0.6.0
|
|
50
|
-
if (!this._modbusAllowed) this._modbusAllowed = isSunshine(this.adapter);
|
|
51
50
|
return this._modbusAllowed;
|
|
52
51
|
}
|
|
53
52
|
|
|
@@ -92,8 +91,6 @@ class DriverBase {
|
|
|
92
91
|
if (reg.states) {
|
|
93
92
|
for(const field of reg.states) {
|
|
94
93
|
const state = field.state;
|
|
95
|
-
//v0.4.0
|
|
96
|
-
//if (field.store !== storeType.never && !reg.initState) {
|
|
97
94
|
if (field.store !== storeType.never && !state.initState) {
|
|
98
95
|
await this.state.initState(path,state);
|
|
99
96
|
state.initState = true;
|
|
@@ -107,10 +104,13 @@ class DriverBase {
|
|
|
107
104
|
if (field.mapper) {
|
|
108
105
|
value = await field.mapper(value);
|
|
109
106
|
}
|
|
107
|
+
|
|
110
108
|
this.stateCache.set(path+state.id, value, {
|
|
111
109
|
renew : field?.store === storeType.always,
|
|
112
110
|
stored : field?.store === storeType.never
|
|
113
|
-
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -124,7 +124,7 @@ class DriverBase {
|
|
|
124
124
|
async updateStates(modbusClient,refreshRate,duration) {
|
|
125
125
|
//if the device is down or standby we cannot read or write anythink?!
|
|
126
126
|
//new since 0.4.x
|
|
127
|
-
if (!this.modbusAllowed) return 0;
|
|
127
|
+
//if (!this.modbusAllowed) return 0;
|
|
128
128
|
if (this._modbusId >= 0) modbusClient.setID(this._modbusId);
|
|
129
129
|
|
|
130
130
|
const start = this._newNowTime();
|
|
@@ -142,10 +142,7 @@ class DriverBase {
|
|
|
142
142
|
break;
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
-
//v0.
|
|
146
|
-
//it is ok, because needs data for new modbus server!
|
|
147
|
-
//if (!reg.states || reg.states.length == 0) continue; //no states ?!
|
|
148
|
-
|
|
145
|
+
if (!this.modbusAllowed && reg.standby !== true) continue; //standby - v0.6.x
|
|
149
146
|
if (!dataRefreshRate.compare(refreshRate,reg.refresh)) continue; //refreshrate unequal
|
|
150
147
|
if (reg.type == deviceType.meter && this.deviceInfo?.meter == false) continue; //meter
|
|
151
148
|
if (reg.type == deviceType.battery && this.deviceInfo?.numberBatteryUnits == 0) continue; //battery
|
|
@@ -170,12 +167,15 @@ class DriverBase {
|
|
|
170
167
|
reg.lastread = this._newNowTime();
|
|
171
168
|
await this._processRegister(reg,data);
|
|
172
169
|
readRegisters++;
|
|
170
|
+
this._errorCount = 0;
|
|
173
171
|
} catch (err) {
|
|
172
|
+
if (err.modbusCode === undefined) this._errorCount ++;
|
|
174
173
|
if (!reg.readErrorHook || !reg.readErrorHook(err,reg)) {
|
|
175
174
|
this.log.warn(`Error while reading from ${modbusClient.ipAddress} [Reg: ${reg.address}, Len: ${reg.length}, modbusID: ${modbusClient.id}] with: ${err.message}`);
|
|
176
175
|
if (err.code == 'EHOSTUNREACH' || err.modbusCode == 6) break; // modbus is busy : 6
|
|
177
176
|
}
|
|
178
177
|
}
|
|
178
|
+
|
|
179
179
|
}
|
|
180
180
|
//Einschubfunktionen
|
|
181
181
|
await this._runPostUpdateHooks(refreshRate);
|
|
@@ -11,8 +11,7 @@ class InverterInfo extends DriverBase {
|
|
|
11
11
|
super(stateInstance,device,{
|
|
12
12
|
name: 'Huawei DriverInfo'
|
|
13
13
|
});
|
|
14
|
-
this._newInstance =
|
|
15
|
-
this._errorCount = 0;
|
|
14
|
+
this._newInstance = null;
|
|
16
15
|
|
|
17
16
|
//https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/stateroles.md
|
|
18
17
|
const newFields = [
|
|
@@ -33,32 +32,16 @@ class InverterInfo extends DriverBase {
|
|
|
33
32
|
state: {id: 'info.modelID', name: 'Model ID', type: 'number', role: 'info.hardware', desc: 'reg:30070, len:1' },
|
|
34
33
|
register: {reg: 30070, type: dataType.uint16}
|
|
35
34
|
}],
|
|
36
|
-
readErrorHook: (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.log.error('Modbus Error 2: Huawei inverter could not be identified for modbus ID '+this._modbusId+'!');
|
|
41
|
-
return true; //self handle
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
this.log.warn('Can not connect to Huawei inverter for modbus ID '+this._modbusId+'!');
|
|
45
|
-
if (err.modbusCode === undefined) {
|
|
46
|
-
if (!isSunshine(this.adapter)) {
|
|
47
|
-
this._errorCount ++;
|
|
48
|
-
if (this._errorCount > 2) {
|
|
49
|
-
this.log.warn('It will be tried again when the sun rises :-)');
|
|
50
|
-
this._modbusAllowed = false;
|
|
51
|
-
return true;
|
|
52
|
-
//this.log.beQuiet(!sunShine);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
this._errorCount = 0;
|
|
57
|
-
}
|
|
35
|
+
readErrorHook: () => { //err,reg
|
|
36
|
+
this.log.error('Can not connect to Huawei inverter for modbus ID '+this._modbusId+'!');
|
|
37
|
+
//reg.lastread = this._newNowTime(); //try it once
|
|
38
|
+
//return true;
|
|
58
39
|
},
|
|
59
40
|
postHook: (path) => {
|
|
60
41
|
const detectedModelId = this.stateCache.get(path+'info.modelID')?.value;
|
|
61
42
|
if (detectedModelId) {
|
|
43
|
+
const model = this.stateCache.get(path+'info.model')?.value;
|
|
44
|
+
this.log.info('Identified a Huawei '+model+' model '+detectedModelId+' for modbus ID '+this._modbusId);
|
|
62
45
|
const model_sun2000M1 = [424,425,426,427,428,429,463,142];
|
|
63
46
|
if(model_sun2000M1.includes(detectedModelId) || detectedModelId >= 430) {
|
|
64
47
|
this._newInstance = new InverterSun2000_M1(this.state,device, { modelId : detectedModelId });
|
|
@@ -73,29 +56,27 @@ class InverterInfo extends DriverBase {
|
|
|
73
56
|
];
|
|
74
57
|
|
|
75
58
|
this.registerFields.push.apply(this.registerFields,newFields);
|
|
76
|
-
|
|
77
|
-
/*
|
|
78
|
-
const newHooks = [
|
|
79
|
-
{
|
|
80
|
-
fn : () => {
|
|
81
|
-
if (!this._newInstance) {
|
|
82
|
-
this.log.error('Can not connect to Huawei inverter for modbus ID '+this._modbusId+'!');
|
|
83
|
-
if (!isSunshine(this.adapter)) {
|
|
84
|
-
this.log.info('Try again when the sun is shining.');
|
|
85
|
-
this._modbusAllowed = false;
|
|
86
|
-
//this.log.beQuiet(!sunShine);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
];
|
|
92
|
-
this.postUpdateHooks.push.apply(this.postUpdateHooks,newHooks);
|
|
93
|
-
*/
|
|
94
59
|
}
|
|
95
60
|
|
|
96
61
|
get newInstance () {
|
|
97
62
|
return this._newInstance;
|
|
98
63
|
}
|
|
64
|
+
|
|
65
|
+
//overload
|
|
66
|
+
get modbusAllowed () {
|
|
67
|
+
if (isSunshine(this.adapter)) {
|
|
68
|
+
if (!this._modbusAllowed) {
|
|
69
|
+
this._modbusAllowed = true;
|
|
70
|
+
this._errorCount = 0;
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
if (this._errorCount > 3 && this._modbusAllowed) {
|
|
74
|
+
this.log.warn('It will try again when the sun rises for modbus id '+this._modbusId+' :-)');
|
|
75
|
+
this._modbusAllowed = false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return this._modbusAllowed;
|
|
79
|
+
}
|
|
99
80
|
}
|
|
100
81
|
|
|
101
82
|
|
|
@@ -107,11 +88,14 @@ class InverterSun2000 extends DriverBase{
|
|
|
107
88
|
driverClass : driverClasses.inverter,
|
|
108
89
|
...options,
|
|
109
90
|
});
|
|
91
|
+
//TestMode
|
|
92
|
+
this._testMode = this.adapter.settings.address === '192.168.2.54';
|
|
93
|
+
this.log.debug('### TestMode (#60) '+this._testMode+ ' '+this.adapter.settings.address);
|
|
94
|
+
this._testMode = false;
|
|
110
95
|
|
|
111
96
|
this.solarSum = new RiemannSum();
|
|
112
|
-
//v0.6.0
|
|
113
97
|
this.adapter.getState(this.deviceInfo.path+'.derived.dailySolarYield',(err, state) => {
|
|
114
|
-
if (
|
|
98
|
+
if (!err && state) this.solarSum.setStart(state.val,state.ts);
|
|
115
99
|
});
|
|
116
100
|
|
|
117
101
|
this.control = new ServiceQueueMap(this.adapter,this.deviceInfo);
|
|
@@ -151,17 +135,6 @@ class InverterSun2000 extends DriverBase{
|
|
|
151
135
|
register: {reg: 30073, type: dataType.int32, gain:1000}
|
|
152
136
|
}],
|
|
153
137
|
},
|
|
154
|
-
{
|
|
155
|
-
address : 37765,
|
|
156
|
-
length : 2,
|
|
157
|
-
info : 'Battery Charge And Discharge Power',
|
|
158
|
-
refresh : dataRefreshRate.high,
|
|
159
|
-
type : deviceType.battery,
|
|
160
|
-
states : [{
|
|
161
|
-
state: {id: 'battery.chargeDischargePower', name: 'Charge/Discharge power', desc: 'reg:37765, len:2 (>0 charging, <0 discharging)', type: 'number', unit: 'kW', role: 'value.power'},
|
|
162
|
-
register: {reg: 37765, type: dataType.int32, gain:1000}
|
|
163
|
-
}]
|
|
164
|
-
},
|
|
165
138
|
{
|
|
166
139
|
address : 32080,
|
|
167
140
|
length : 2,
|
|
@@ -174,6 +147,17 @@ class InverterSun2000 extends DriverBase{
|
|
|
174
147
|
store : storeType.always
|
|
175
148
|
}]
|
|
176
149
|
},
|
|
150
|
+
{
|
|
151
|
+
address : 37765,
|
|
152
|
+
length : 2,
|
|
153
|
+
info : 'Battery Charge And Discharge Power',
|
|
154
|
+
refresh : dataRefreshRate.high,
|
|
155
|
+
type : deviceType.battery,
|
|
156
|
+
states : [{
|
|
157
|
+
state: {id: 'battery.chargeDischargePower', name: 'Charge/Discharge power', desc: 'reg:37765, len:2 (>0 charging, <0 discharging)', type: 'number', unit: 'kW', role: 'value.power'},
|
|
158
|
+
register: {reg: 37765, type: dataType.int32, gain:1000}
|
|
159
|
+
}]
|
|
160
|
+
},
|
|
177
161
|
{
|
|
178
162
|
address : 32064,
|
|
179
163
|
length : 2,
|
|
@@ -215,11 +199,11 @@ class InverterSun2000 extends DriverBase{
|
|
|
215
199
|
info : 'meter activePower',
|
|
216
200
|
refresh : dataRefreshRate.high,
|
|
217
201
|
type : deviceType.meter,
|
|
202
|
+
//standby : true,
|
|
218
203
|
states: [{
|
|
219
204
|
state: {id: 'meter.activePower', name: 'ActivePower', type: 'number', unit: 'kW', role: 'value.power.active', desc: 'reg:37113, len:2 (>0: feed-in to grid. <0: supply from grid.)' },
|
|
220
205
|
register: { reg: 37113, type: dataType.int32, gain:1000 }
|
|
221
|
-
}
|
|
222
|
-
]
|
|
206
|
+
}]
|
|
223
207
|
},
|
|
224
208
|
{
|
|
225
209
|
address : 37052,
|
|
@@ -400,6 +384,41 @@ class InverterSun2000 extends DriverBase{
|
|
|
400
384
|
}
|
|
401
385
|
}
|
|
402
386
|
},
|
|
387
|
+
{
|
|
388
|
+
//read also standby
|
|
389
|
+
address : 32089,
|
|
390
|
+
length : 1,
|
|
391
|
+
info : 'inverter deviceStatus',
|
|
392
|
+
refresh : dataRefreshRate.low,
|
|
393
|
+
standby : true,
|
|
394
|
+
type : deviceType.inverter,
|
|
395
|
+
states: [{
|
|
396
|
+
state: {id: 'deviceStatus', name: 'Device Status', type: 'number', unit: '', role: 'value', desc: 'reg:32089, len:1'},
|
|
397
|
+
register: {reg: 32089, type: dataType.uint16},
|
|
398
|
+
mapper: async value => {
|
|
399
|
+
if (this._testMode) {
|
|
400
|
+
this.log.info('testMode: Die Sonne scheint? '+isSunshine(this.adapter));
|
|
401
|
+
if (!isSunshine(this.adapter)) return 2;
|
|
402
|
+
//return 2;
|
|
403
|
+
}
|
|
404
|
+
return value;
|
|
405
|
+
},
|
|
406
|
+
},{
|
|
407
|
+
state: {id: 'derived.deviceStatus', name: 'Device Status Information', type: 'string', unit: '', role: 'value'}
|
|
408
|
+
}
|
|
409
|
+
],
|
|
410
|
+
readErrorHook: (err,reg) => { //(err,reg)
|
|
411
|
+
if (err.modbusCode === undefined) {
|
|
412
|
+
reg.lastread = this._newNowTime();
|
|
413
|
+
//return true; //Error has been handled
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
postHook: (path) => {
|
|
417
|
+
//DeviceStatus
|
|
418
|
+
const deviceStatus = this.stateCache.get(path+'deviceStatus')?.value;
|
|
419
|
+
this.stateCache.set(path+'derived.deviceStatus',getDeviceStatusInfo(deviceStatus));
|
|
420
|
+
}
|
|
421
|
+
},
|
|
403
422
|
{
|
|
404
423
|
address : 32066,
|
|
405
424
|
length : 50,
|
|
@@ -473,13 +492,6 @@ class InverterSun2000 extends DriverBase{
|
|
|
473
492
|
state: {id: 'isulationResistance', name: 'Isulation Resistance', type: 'number', unit: 'MOhm', role: 'value', desc: 'reg:32088, len:1'},
|
|
474
493
|
register: {reg: 32088, type: dataType.uint16, gain: 1000}
|
|
475
494
|
},
|
|
476
|
-
{
|
|
477
|
-
state: {id: 'deviceStatus', name: 'Device Status', type: 'number', unit: '', role: 'value', desc: 'reg:32089, len:1'},
|
|
478
|
-
register: {reg: 32089, type: dataType.uint16}
|
|
479
|
-
},
|
|
480
|
-
{
|
|
481
|
-
state: {id: 'derived.deviceStatus', name: 'Device Status Information', type: 'string', unit: '', role: 'value'}
|
|
482
|
-
},
|
|
483
495
|
{
|
|
484
496
|
state: {id: 'faultCode', name: 'Fault Code', type: 'number', unit: '', role: 'value', desc: 'reg:32090, len:1'},
|
|
485
497
|
register: {reg: 32090, type: dataType.uint16}
|
|
@@ -500,14 +512,7 @@ class InverterSun2000 extends DriverBase{
|
|
|
500
512
|
state: {id: 'dailyEnergyYield', name: 'Daily Energy Yield', type: 'number', unit: 'kWh', role: 'value.power.produced', desc: 'reg:32114, len:2'},
|
|
501
513
|
register: {reg: 32114, type: dataType.uint32, gain: 100}
|
|
502
514
|
}
|
|
503
|
-
|
|
504
|
-
],
|
|
505
|
-
postHook: (path) => {
|
|
506
|
-
//DeviceStatus
|
|
507
|
-
const deviceStatus = this.stateCache.get(path+'deviceStatus')?.value;
|
|
508
|
-
//this.deviceInfo.deviceStatus = deviceStatus;
|
|
509
|
-
this.stateCache.set(path+'derived.deviceStatus',getDeviceStatusInfo(deviceStatus));
|
|
510
|
-
}
|
|
515
|
+
]
|
|
511
516
|
},
|
|
512
517
|
{
|
|
513
518
|
address : 37100,
|
|
@@ -703,39 +708,45 @@ class InverterSun2000 extends DriverBase{
|
|
|
703
708
|
//overload
|
|
704
709
|
get modbusAllowed () {
|
|
705
710
|
//if the modbus-device offline we cannot read or write anythink!
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
this.adapter.log.debug('### modbusAllowed '+allow+' deviceStatus: '+this.deviceStatus);
|
|
711
|
-
*/
|
|
712
|
-
|
|
713
|
-
if (this.deviceStatus == 0x0002) { //detecting irradiation
|
|
714
|
-
if (this.adapter.settings.sunrise) {
|
|
715
|
-
const timeAfterSunrise = this._newNowTime() - this.adapter.settings.sunrise?.getTime();
|
|
716
|
-
this._modbusAllowed = timeAfterSunrise > 0 && timeAfterSunrise < 60*60*1000; //60 Minutes after sunrise
|
|
717
|
-
} else {
|
|
718
|
-
//im Zweifel immer wake up
|
|
719
|
-
this._modbusAllowed = true;
|
|
711
|
+
let modbusAllowed = true;
|
|
712
|
+
if (this.deviceInfo.index > 0) { //I am a slave inverter
|
|
713
|
+
if (this.adapter.devices[0].driverClass === driverClasses.inverter && this.adapter.devices[0].instance) {
|
|
714
|
+
modbusAllowed = this.adapter.devices[0].instance.modbusAllowed; //first ask the master
|
|
720
715
|
}
|
|
721
|
-
} else {
|
|
722
|
-
this._modbusAllowed = true;
|
|
723
716
|
}
|
|
717
|
+
if (modbusAllowed) {
|
|
718
|
+
modbusAllowed = this.deviceStatus !== 0x0002;
|
|
719
|
+
if (this._errorCount > 3) modbusAllowed = false;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (!modbusAllowed && !this.log.quiet) {
|
|
723
|
+
this.log.info(`The inverter with modbus ID ${this._modbusId} is no longer accessible. That is why the logs are minimized.`);
|
|
724
|
+
this.log.beQuiet(true);
|
|
725
|
+
}
|
|
726
|
+
if (modbusAllowed && this.log.quiet) {
|
|
727
|
+
this.log.beQuiet(false);
|
|
728
|
+
this.log.info(`The inverter with modbus ID ${this._modbusId} is accessible again.`);
|
|
729
|
+
this._errorCount = 0;
|
|
730
|
+
}
|
|
731
|
+
this._modbusAllowed = modbusAllowed;
|
|
724
732
|
return this._modbusAllowed;
|
|
725
733
|
}
|
|
726
734
|
|
|
727
735
|
//overload
|
|
728
736
|
get deviceStatus() {
|
|
729
|
-
const status = this.stateCache.get(this.
|
|
737
|
+
const status = this.stateCache.get(this._getStatePath()+'deviceStatus')?.value;
|
|
730
738
|
if (status) {
|
|
731
|
-
//this.adapter.log.debug('### deviceStatus '+status);
|
|
732
739
|
if (status !== this._deviceStatus && this._deviceStatus >= 0) {
|
|
733
|
-
this.log.info(`The Inverter ${this.
|
|
740
|
+
this.log.info(`The Inverter with modbus ID ${this._modbusId} switches to ${this.stateCache.get(this.deviceInfo.path+'.derived.deviceStatus')?.value} mode.`);
|
|
734
741
|
}
|
|
735
742
|
this._deviceStatus = status;
|
|
736
743
|
}
|
|
737
744
|
return this._deviceStatus;
|
|
738
745
|
}
|
|
746
|
+
|
|
747
|
+
async mitnightProcess() {
|
|
748
|
+
this.solarSum.reset();
|
|
749
|
+
}
|
|
739
750
|
}
|
|
740
751
|
|
|
741
752
|
class InverterSun2000_M1 extends InverterSun2000{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const {driverClasses,dataRefreshRate,dataType} = require(__dirname + '/../types.js');
|
|
2
|
-
const {isSunshine} = require(__dirname + '/../tools.js');
|
|
2
|
+
//const {isSunshine} = require(__dirname + '/../tools.js');
|
|
3
3
|
const DriverBase = require(__dirname + '/driver_base.js');
|
|
4
4
|
|
|
5
5
|
class Sdongle extends DriverBase{
|
|
@@ -11,8 +11,6 @@ class Sdongle extends DriverBase{
|
|
|
11
11
|
...options,
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
this._errorCount = 0;
|
|
15
|
-
|
|
16
14
|
const newFields = [
|
|
17
15
|
{
|
|
18
16
|
address : 30015,
|
|
@@ -29,27 +27,7 @@ class Sdongle extends DriverBase{
|
|
|
29
27
|
{
|
|
30
28
|
state: {id: 'sdongle.protokolVersion', name: 'Protokol Version', type: 'number', unit: '', role: 'value',desc: 'reg:30068, len:2'},
|
|
31
29
|
register: {reg: 30068, type: dataType.uint32}
|
|
32
|
-
}]
|
|
33
|
-
readErrorHook: (err) => {
|
|
34
|
-
/*
|
|
35
|
-
if (err.code == 'EHOSTUNREACH') {
|
|
36
|
-
this._modbusAllowed = isSunshine(this.adapter);
|
|
37
|
-
}
|
|
38
|
-
*/
|
|
39
|
-
if (err.modbusCode === undefined) {
|
|
40
|
-
if (!isSunshine(this.adapter)) {
|
|
41
|
-
this._errorCount ++;
|
|
42
|
-
if (this._errorCount > 2) {
|
|
43
|
-
this._modbusAllowed = false;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
this._errorCount = 0;
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
postHook: () => {
|
|
51
|
-
this._errorCount = 0;
|
|
52
|
-
}
|
|
30
|
+
}]
|
|
53
31
|
},
|
|
54
32
|
{
|
|
55
33
|
address : 37410,
|
|
@@ -102,20 +80,15 @@ class Sdongle extends DriverBase{
|
|
|
102
80
|
|
|
103
81
|
this.registerFields.push.apply(this.registerFields,newFields);
|
|
104
82
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
];
|
|
117
|
-
this.postUpdateHooks.push.apply(this.postUpdateHooks,newHooks);
|
|
118
|
-
*/
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//overload
|
|
86
|
+
get modbusAllowed () {
|
|
87
|
+
// the first device is the master
|
|
88
|
+
if (this.adapter.devices[0].driverClass === driverClasses.inverter && this.adapter.devices[0].instance) {
|
|
89
|
+
return this.adapter.devices[0].instance.modbusAllowed; //ask the master
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
119
92
|
}
|
|
120
93
|
}
|
|
121
94
|
|
|
@@ -64,8 +64,8 @@ class SmartLoggerMeter extends DriverBase{
|
|
|
64
64
|
...options,
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
this._testMode = (this._modbusId == 1);
|
|
68
|
-
|
|
67
|
+
//this._testMode = (this._modbusId == 1);
|
|
68
|
+
this._testMode = false;
|
|
69
69
|
|
|
70
70
|
//https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/stateroles.md
|
|
71
71
|
const newFields = [
|
|
@@ -153,10 +153,6 @@ class ModbusConnect extends DeviceInterface {
|
|
|
153
153
|
this._adjustDelay(err,false);
|
|
154
154
|
await this._create();
|
|
155
155
|
}
|
|
156
|
-
/*
|
|
157
|
-
if (err.modbusCode === 6 ) this._adjustDelay(err,false); //Slave device busy
|
|
158
|
-
if (err.modbusCode === 5 ) await this._create();
|
|
159
|
-
*/
|
|
160
156
|
}
|
|
161
157
|
}
|
|
162
158
|
|
package/lib/register.js
CHANGED
|
@@ -151,15 +151,17 @@ class Registers {
|
|
|
151
151
|
|
|
152
152
|
async storeStates() {
|
|
153
153
|
for (const stateEntry of this.stateCache.values()) {
|
|
154
|
+
//if (stateEntry?.storeType === storeType.never) continue;
|
|
154
155
|
if (stateEntry.stored) continue;
|
|
156
|
+
//if (stateEntry?.storeType !== storeType.always && stateEntry.stored) continue;
|
|
155
157
|
if (stateEntry.value !== null) {
|
|
156
158
|
try {
|
|
157
159
|
stateEntry.stored = true;
|
|
158
160
|
await this.adapter.setStateAsync(stateEntry.id, {val: stateEntry.value , ack: true});
|
|
159
|
-
|
|
161
|
+
this.adapter.logger.debug(`Fetched ${stateEntry.id}, val=${stateEntry.value}`);
|
|
160
162
|
} catch (err) {
|
|
161
163
|
stateEntry.stored = false;
|
|
162
|
-
this.adapter.
|
|
164
|
+
this.adapter.logger.warn(`Error while fetching ${stateEntry.id}, val=${stateEntry.value} err=${err.message}`);
|
|
163
165
|
}
|
|
164
166
|
}
|
|
165
167
|
}
|
|
@@ -170,17 +172,14 @@ class Registers {
|
|
|
170
172
|
async updateStates(device,modbusClient,refreshRate,duration) {
|
|
171
173
|
//this.adapter.log.debug('### DeviceInfo: '+device.index+' '+JSON.stringify(device.instance.info));
|
|
172
174
|
if (device.instance) {
|
|
173
|
-
//const buff = device.instance.getHoldingRegisters(37115,2);
|
|
174
|
-
//this.adapter.log.debug('### buff '+buff);
|
|
175
|
-
//new Instance?
|
|
176
175
|
if (device.instance.newInstance) {
|
|
177
|
-
this.adapter.
|
|
176
|
+
this.adapter.logger.debug('DeviceInfo: '+device.index+' '+JSON.stringify(device.instance.info));
|
|
178
177
|
device.instance = device.instance.newInstance;
|
|
179
|
-
this.adapter.
|
|
178
|
+
this.adapter.logger.debug('Device: '+device.index+' '+JSON.stringify(device.instance.info));
|
|
180
179
|
}
|
|
181
180
|
return device.instance.updateStates(modbusClient,refreshRate,duration);
|
|
182
181
|
} else {
|
|
183
|
-
this.adapter.
|
|
182
|
+
this.adapter.logger.error('No device instance has been initialized!');
|
|
184
183
|
return 0;
|
|
185
184
|
}
|
|
186
185
|
}
|
|
@@ -210,22 +209,14 @@ class Registers {
|
|
|
210
209
|
this.stateCache.set('collected.gridImportStart',state?.val, {type : 'number', stored : true });
|
|
211
210
|
state = await this.adapter.getStateAsync('collected.consumptionStart');
|
|
212
211
|
this.stateCache.set('collected.consumptionStart',state?.val, {type : 'number', stored : true });
|
|
213
|
-
//v0.6.0
|
|
214
|
-
/*
|
|
215
|
-
for (const device of this.adapter.devices) {
|
|
216
|
-
state = await this.adapter.getStateAsync(device.path+'.derived.dailySolarYield');
|
|
217
|
-
state?.val && device.instance.solarSum && device.instance.solarSum.setStart(state.val,state.ts);
|
|
218
|
-
}
|
|
219
|
-
*/
|
|
220
212
|
}
|
|
221
213
|
|
|
222
214
|
//state
|
|
223
215
|
CheckReadError(timeShift) {
|
|
224
216
|
const now = new Date();
|
|
225
217
|
for (const device of this.adapter.devices) {
|
|
226
|
-
//v0.4.x
|
|
227
|
-
if (!device.instance.info.online) continue;
|
|
228
218
|
for (const [i, reg] of device.instance.registerFields.entries()) {
|
|
219
|
+
if (!device.instance.modbusAllowed) continue; //standby
|
|
229
220
|
if (reg.type == deviceType.meter && device.meter == false) continue; //not meter
|
|
230
221
|
if (reg.type == deviceType.battery && device?.numberBatteryUnits == 0) continue; //battery
|
|
231
222
|
if (reg.type == deviceType.batteryUnit2 && device?.numberBatteryUnits < 2) continue; //battery Unit2
|
|
@@ -270,6 +261,9 @@ class Registers {
|
|
|
270
261
|
this.stateCache.set('collected.gridImportStart',this.stateCache.get('meter.reverseActiveEnergy')?.value, {type : 'number'});
|
|
271
262
|
// copy consumption Sum to Start for the next day
|
|
272
263
|
this.stateCache.set('collected.consumptionStart',this.stateCache.get('collected.consumptionSum')?.value, {type : 'number'});
|
|
264
|
+
for (const device of this.adapter.devices) {
|
|
265
|
+
if (device.instance.mitnightProcess) await device.instance.mitnightProcess();
|
|
266
|
+
}
|
|
273
267
|
this.storeStates(); //fire and forget
|
|
274
268
|
}
|
|
275
269
|
|
package/lib/tools.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
|
|
2
3
|
const suncalc = require('suncalc2');
|
|
4
|
+
//const {storeType} = require(__dirname + '/types.js');
|
|
3
5
|
|
|
4
6
|
|
|
5
7
|
class Logging {
|
|
@@ -51,13 +53,29 @@ class StateMap {
|
|
|
51
53
|
if (options?.type == 'number') {
|
|
52
54
|
value = Math.round((value + Number.EPSILON) * 1000) / 1000; //3rd behind
|
|
53
55
|
}
|
|
54
|
-
|
|
56
|
+
|
|
57
|
+
const existing = this.get(id); //existing entry
|
|
58
|
+
|
|
59
|
+
const mapOptions = {
|
|
60
|
+
id: id,
|
|
61
|
+
value: value,
|
|
62
|
+
stored : existing?.stored ? existing.stored : false
|
|
63
|
+
};
|
|
64
|
+
if (options?.renew || existing?.value !== value) mapOptions.stored = false;
|
|
65
|
+
if (options?.stored) mapOptions.stored = options.stored;
|
|
66
|
+
|
|
67
|
+
this.stateMap.set(id, mapOptions);
|
|
68
|
+
|
|
69
|
+
/* the old code
|
|
70
|
+
if (options?.renew || existing?.value !== value) {
|
|
55
71
|
if (options?.stored ) {
|
|
56
72
|
this.stateMap.set(id, {id: id, value: value, stored: options.stored});
|
|
57
73
|
} else {
|
|
58
74
|
this.stateMap.set(id, {id: id, value: value});
|
|
59
75
|
}
|
|
60
76
|
}
|
|
77
|
+
*/
|
|
78
|
+
|
|
61
79
|
}
|
|
62
80
|
}
|
|
63
81
|
|
|
@@ -210,7 +228,25 @@ function getAstroDate (adapter,pattern, date, offsetMinutes) {
|
|
|
210
228
|
function isSunshine(adapter) {
|
|
211
229
|
if (adapter.settings.sunrise && adapter.settings.sunset) {
|
|
212
230
|
const now = new Date();
|
|
213
|
-
|
|
231
|
+
/*
|
|
232
|
+
const sunrise = new Date(
|
|
233
|
+
now.getFullYear(),
|
|
234
|
+
now.getMonth(),
|
|
235
|
+
now.getDate(), // today, ...
|
|
236
|
+
14, 18, 0 // ...at 00:00:00 hours
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const sunset = new Date(
|
|
240
|
+
now.getFullYear(),
|
|
241
|
+
now.getMonth(),
|
|
242
|
+
now.getDate(), // today, ...
|
|
243
|
+
14, 16, 0 // ...at 00:00:00 hours
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
return (now.getTime() > sunrise.getTime() || now.getTime() < sunset.getTime());
|
|
247
|
+
*/
|
|
248
|
+
|
|
249
|
+
return (now.getTime() > adapter.settings.sunrise.getTime() && now.getTime() < adapter.settings.sunset.getTime());
|
|
214
250
|
}
|
|
215
251
|
return true;
|
|
216
252
|
}
|
package/main.js
CHANGED
|
@@ -185,9 +185,15 @@ class Sun2000 extends utils.Adapter {
|
|
|
185
185
|
await this.initPath();
|
|
186
186
|
this.state = new Registers(this);
|
|
187
187
|
await this.atMidnight();
|
|
188
|
+
/*
|
|
189
|
+
if (!this.settings.sunrise || !this.settings.sunset) {
|
|
190
|
+
this.adapterDisable('*** Adapter deactivated, Latitude and longitude must be set! ***');
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
*/
|
|
188
194
|
if (this.settings.modbusAdjust) {
|
|
189
195
|
this.settings.modbusAdjust = isSunshine(this);
|
|
190
|
-
this.logger.debug('Sunshine: '+this.settings.modbusAdjust);
|
|
196
|
+
//this.logger.debug('Sunshine: '+this.settings.modbusAdjust);
|
|
191
197
|
}
|
|
192
198
|
this.modbusClient = new ModbusConnect(this,this.settings);
|
|
193
199
|
this.modbusClient.setCallback(this.endOfmodbusAdjust.bind(this));
|
|
@@ -351,7 +357,7 @@ class Sun2000 extends utils.Adapter {
|
|
|
351
357
|
numberBatteryUnits : 0
|
|
352
358
|
});
|
|
353
359
|
}
|
|
354
|
-
//
|
|
360
|
+
//SmartLogger
|
|
355
361
|
if (this.settings.sl.active) {
|
|
356
362
|
this.devices.push({
|
|
357
363
|
index: 0,
|
|
@@ -381,12 +387,10 @@ class Sun2000 extends utils.Adapter {
|
|
|
381
387
|
await this.adjustInverval();
|
|
382
388
|
await this.StartProcess();
|
|
383
389
|
} else {
|
|
384
|
-
this.
|
|
385
|
-
this.setForeignState('system.adapter.' + this.namespace + '.alive', false);
|
|
390
|
+
this.adapterDisable('*** Adapter deactivated, can\'t parse modbusIds! ***');
|
|
386
391
|
}
|
|
387
392
|
} else {
|
|
388
|
-
this.
|
|
389
|
-
this.setForeignState('system.adapter.' + this.namespace + '.alive', false);
|
|
393
|
+
this.adapterDisable('*** Adapter deactivated, Adapter Settings incomplete! ***');
|
|
390
394
|
}
|
|
391
395
|
}
|
|
392
396
|
|
|
@@ -400,7 +404,7 @@ class Sun2000 extends utils.Adapter {
|
|
|
400
404
|
const start = new Date().getTime();
|
|
401
405
|
this.logger.debug('### DataPolling START '+ Math.round((start-this.lastTimeUpdated)/1000)+' sec ###');
|
|
402
406
|
if (this.lastTimeUpdated > 0 && (start-this.lastTimeUpdated)/1000 > this.settings.highInterval/1000 + 1) {
|
|
403
|
-
this.logger.
|
|
407
|
+
this.logger.debug('Interval '+(start-this.lastTimeUpdated)/1000+' sec');
|
|
404
408
|
}
|
|
405
409
|
this.lastTimeUpdated = start;
|
|
406
410
|
const nextLoop = this.settings.highInterval - start % (this.settings.highInterval) + start;
|
|
@@ -471,6 +475,11 @@ class Sun2000 extends utils.Adapter {
|
|
|
471
475
|
},this.settings.lowInterval);
|
|
472
476
|
}
|
|
473
477
|
|
|
478
|
+
adapterDisable(errMsg) {
|
|
479
|
+
this.logger.error(errMsg);
|
|
480
|
+
this.setForeignState('system.adapter.' + this.namespace + '.alive', false);
|
|
481
|
+
}
|
|
482
|
+
|
|
474
483
|
/**
|
|
475
484
|
* Is called when adapter shuts down - callback has to be called under any circumstances!
|
|
476
485
|
* @param {() => void} callback
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.sun2000",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "sun2000",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "bolliy",
|
|
@@ -19,28 +19,28 @@
|
|
|
19
19
|
"url": "https://github.com/bolliy/ioBroker.sun2000.git"
|
|
20
20
|
},
|
|
21
21
|
"engines": {
|
|
22
|
-
"node": ">=
|
|
22
|
+
"node": ">= 18"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@iobroker/adapter-core": "^3.0.
|
|
26
|
-
"suncalc2": "^1.8.1",
|
|
25
|
+
"@iobroker/adapter-core": "^3.0.6",
|
|
27
26
|
"modbus-serial": "^8.0.16",
|
|
27
|
+
"suncalc2": "^1.8.1",
|
|
28
28
|
"tcp-port-used": "^1.0.2"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@alcalzone/release-script": "^3.
|
|
32
|
-
"@alcalzone/release-script-plugin-iobroker": "^3.
|
|
33
|
-
"@alcalzone/release-script-plugin-license": "^3.
|
|
34
|
-
"@alcalzone/release-script-plugin-manual-review": "^3.
|
|
35
|
-
"@iobroker/adapter-dev": "^1.
|
|
36
|
-
"@iobroker/testing": "^4.1.
|
|
37
|
-
"@tsconfig/node16": "^16.1.
|
|
38
|
-
"@types/chai": "^4.3.
|
|
31
|
+
"@alcalzone/release-script": "^3.7.0",
|
|
32
|
+
"@alcalzone/release-script-plugin-iobroker": "^3.7.0",
|
|
33
|
+
"@alcalzone/release-script-plugin-license": "^3.7.0",
|
|
34
|
+
"@alcalzone/release-script-plugin-manual-review": "^3.7.0",
|
|
35
|
+
"@iobroker/adapter-dev": "^1.3.0",
|
|
36
|
+
"@iobroker/testing": "^4.1.1",
|
|
37
|
+
"@tsconfig/node16": "^16.1.3",
|
|
38
|
+
"@types/chai": "^4.3.14",
|
|
39
39
|
"@types/chai-as-promised": "^7.1.8",
|
|
40
40
|
"@types/mocha": "^10.0.4",
|
|
41
|
-
"@types/node": "^20.
|
|
41
|
+
"@types/node": "^20.12.2",
|
|
42
42
|
"@types/proxyquire": "^1.3.31",
|
|
43
|
-
"@types/sinon": "^17.0.
|
|
43
|
+
"@types/sinon": "^17.0.3",
|
|
44
44
|
"@types/sinon-chai": "^3.2.12",
|
|
45
45
|
"chai": "^4.3.10",
|
|
46
46
|
"chai-as-promised": "^7.1.1",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"proxyquire": "^2.1.3",
|
|
50
50
|
"sinon": "^17.0.1",
|
|
51
51
|
"sinon-chai": "^3.7.0",
|
|
52
|
-
"typescript": "~5.
|
|
52
|
+
"typescript": "~5.4.3"
|
|
53
53
|
},
|
|
54
54
|
"main": "main.js",
|
|
55
55
|
"files": [
|