@impact0815/node-red-contrib-alphaess-modbus 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,98 @@
1
+ # Changelog
2
+
3
+ ## 0.4.0
4
+
5
+ ### Added
6
+ - **English and German**: editor labels, hints, output labels and help texts in `locales/en-US` and `locales/de`;
7
+ status texts, log and error messages are translated as well (language of the Node-RED server).
8
+ Data in `msg.payload` (field names, alarm and warning texts) stays English.
9
+ - German README (`README.de.md`)
10
+ - Disclaimer in README, README.de.md, editor help (both nodes) and editor (short note in both dialogs)
11
+ - Warning in the editor when *Allow write access* is enabled, and a log notice at start
12
+ - Test `test/i18n.test.js`: same keys and placeholders in all languages, every used key exists, help complete
13
+
14
+ ### Changed (breaking)
15
+ - Package name is now **`@impact0815/node-red-contrib-alphaess-modbus`** (scoped name as required by the Node-RED packaging
16
+ guidelines for new packages). Node types are unchanged, existing flows keep working.
17
+ Uninstall the old package `node-red-contrib-alphaess-modbus` before installing the new one.
18
+ - Help texts moved from `alphaess-modbus.html` to `locales/<language>/alphaess-modbus.html`
19
+
20
+ ### Other
21
+ - `.gitattributes` enforces LF line endings
22
+ - `update-nodered.sh` removes the old unscoped package automatically
23
+
24
+ ## 0.3.1
25
+
26
+ ### Fixed
27
+ - Stale detection compares the exact age in milliseconds; before, the age was rounded to seconds first,
28
+ so a block could be reported stale up to 0.5 s too late
29
+ - Tests always close simulator and connections, also when an assertion fails (the test run no longer hangs)
30
+
31
+ ## 0.3.0
32
+
33
+ Based on the newer *AlphaESS Household Modbus Register Parameter List*.
34
+
35
+ ### Added
36
+ - Grid meter: lifetime energy per phase (`energyConsumeFromGridPhase`, `energyFeedToGridPhase`, 0x0037–0x0042)
37
+ - Inverter: PV total power register (`pvPowerTotalRegister`, 0x0453); `pvPowerTotal` stays the sum of PV1–PV6
38
+ - Inverter warnings 1/2 decoded as text (Note 32): `warningBits`, and texts in `payload.warnings`
39
+ instead of hex values; `warning1`/`warning2` stay raw numbers
40
+ - Inverter fault extend 1 bits 17–31 and fault extend 2 bits 0–25 decoded as text (Note 27)
41
+ - Battery warnings bits 8–12 (Note 28; bit 8 is now *Software versions inconsistent* instead of *No soc calibration*)
42
+ - Device info: inverter ARM software version, EMS version suffix, WiFi serial number, battery serial numbers (new block `batteryInfo`)
43
+ - Dispatch: PV switch and para7 read back (0x0889/0x088A); dispatch mode texts for 19–25
44
+ - Dispatch mode 19 (*No Battery Charge*) can be written; test and off-grid modes (20–25) are rejected
45
+
46
+ ### Changed
47
+ - Extended blocks are read with the new length first and fall back automatically to the length of the
48
+ older register list V1.1 if the system rejects the request; `payload.blocks.<block>.registers` shows the reduced length
49
+ - Non-printable characters are removed from ASCII values (serial numbers, versions)
50
+ - Simulator: option `--legacy` simulates a firmware with the older register list
51
+ - Repository links point to github.com/impact0815; CONTRIBUTING refers to the newer register list
52
+
53
+ ## 0.2.3
54
+
55
+ ### Documentation
56
+ - Editor help rewritten: quick start, all settings explained, examples for every input command, output descriptions,
57
+ daily values, MQTT topics, link call pattern, troubleshooting
58
+ - Connection help: settings, single-connection limit of the EMS, troubleshooting for `ECONNREFUSED`, timeouts and Modbus exceptions
59
+ - README: Docker installation, quick start, PV meter guidance, SOC scale check, examples, troubleshooting table
60
+
61
+ ## 0.2.2
62
+
63
+ ### Changed
64
+ - *Daily store* is now a drop-down with the context stores configured in `settings.js` instead of a text field.
65
+ A value that is not configured stays selectable and is marked "not configured in settings.js".
66
+ If only the in-memory store exists, the editor shows a hint how to add a persistent store.
67
+
68
+ ## 0.2.1
69
+
70
+ ### Added
71
+ - Check of the selected context store ("Daily store"): if it is not configured in `settings.js`,
72
+ the node logs a warning at start, shows a yellow status and adds a warning to `payload.warnings`
73
+ (and once to the alarm output). Before, Node-RED silently fell back to the in-memory store.
74
+ - `daily.complete` / `yesterday.complete`: `true` if counting started within 15 minutes after local midnight,
75
+ i.e. the daily values cover the whole day. Also published as `<prefix>/daily/complete`.
76
+
77
+ ## 0.2.0
78
+
79
+ ### Added
80
+ - Third output **command response** for `readInfo`, `readRaw` and all write commands
81
+ - `feedIn` and `timePeriod` skip the write if the values are already set (`msg.unchanged = true`)
82
+ - Only changed registers are written (one request per contiguous run of changed registers)
83
+ - Setting **Min. interval** (default 10 s): minimum time between two actual writes of the same command; `dispatchStop` is never blocked
84
+
85
+ ### Changed (breaking)
86
+ - Responses to commands are no longer sent on output 1 but on output 3; output 1 only carries poll data
87
+ - `msg.written` is now an array of frames `[{ address, values }]` instead of a single object
88
+
89
+ ## 0.1.0
90
+
91
+ - First version
92
+ - Modbus TCP client without external dependencies
93
+ - Blocks: grid meter, PV meter, battery, inverter, system running data, system config, time period control, dispatch, device info
94
+ - Staggered polling: fast blocks every interval, slow blocks every "slow interval", device info at start and daily
95
+ - Derived values: consumption, autarky, self-consumption rate, daily energy (since local midnight) and previous day
96
+ - Alarm output (on change) and stale-data detection per block
97
+ - Direct MQTT publishing through an existing Node-RED MQTT broker config
98
+ - Optional control (disabled by default): dispatch, feed-in limit, time periods
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 impact0815
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.de.md ADDED
@@ -0,0 +1,422 @@
1
+ # @impact0815/node-red-contrib-alphaess-modbus
2
+
3
+ [English version → README.md](https://github.com/impact0815/node-red-contrib-alphaess-modbus/blob/main/README.md)
4
+
5
+ Lokaler Zugriff auf **Alpha-ESS**-Speichersysteme (SMILE-Serie, Storion) per **Modbus TCP**, ohne Cloud.
6
+
7
+ - Echtzeitdaten von Netzzähler, PV-Zähler, Batterie, Wechselrichter und System
8
+ - Abgeleitete Werte: Hausverbrauch, Autarkie, Eigenverbrauchsquote
9
+ - Tageswerte seit Mitternacht (Ortszeit) und die Werte des Vortags
10
+ - Alarme und Warnungen als Text, mit einem Ausgang, der nur bei Änderungen sendet
11
+ - Erkennung veralteter Daten pro Registerblock
12
+ - MQTT direkt über eine vorhandene MQTT-Broker-Konfiguration von Node-RED, ohne zusätzliche MQTT-Node
13
+ - Optionale Steuerung, **standardmäßig abgeschaltet**: Dispatch (Laden/Entladen), Einspeisegrenze, Lade-/Entladezeitfenster
14
+ - Schonendes Schreiben: unveränderte Werte werden nicht geschrieben, nur geänderte Register, optionaler Mindestabstand
15
+ - Funktioniert mit aktueller und älterer EMS-Firmware (automatischer Rückfall auf die ältere Registerliste)
16
+ - Editor und Hilfe auf **Englisch und Deutsch**
17
+ - Keine Laufzeitabhängigkeiten
18
+
19
+ Registeradressen und Skalierungen beruhen auf der *AlphaESS Household Modbus Register Parameter List*
20
+ (Nachfolger der *Register Parameter List V1.1*).
21
+
22
+ ## Haftungsausschluss
23
+
24
+ > **Nutzung auf eigene Gefahr. Keine Gewährleistung.**
25
+
26
+ - Dies ist ein unabhängiges Community-Projekt. Es ist **nicht mit Alpha ESS verbunden und wird von Alpha ESS weder unterstützt
27
+ noch empfohlen**. „Alpha ESS“, „SMILE“ und „Storion“ dienen nur zur Beschreibung der Kompatibilität; die Marken gehören ihren Inhabern.
28
+ - Die Software wird **„wie besehen“ und ohne jede Gewährleistung** bereitgestellt, siehe [LICENSE](LICENSE) (MIT).
29
+ Eine Haftung der Autoren für Schäden aus der Nutzung ist ausgeschlossen, soweit gesetzlich zulässig.
30
+ - Das **Lesen** von Daten verändert das System nicht. **Schreibbefehle** (Dispatch, Einspeisegrenze, Zeitfenster) verändern, wie das
31
+ System lädt, entlädt und ins Netz einspeist. Falsche Werte können zu ungewolltem Netzbezug, zu tiefer oder zu flacher Entladung,
32
+ fehlender Notstromreserve oder einer Einspeisung entgegen den Vorgaben des Netzbetreibers führen und die Herstellergarantie berühren.
33
+ - Der Schreibzugriff ist standardmäßig abgeschaltet. Aktiviere ihn nur, wenn dir die Wirkung jedes Befehls klar ist; beginne mit
34
+ kurzen Laufzeiten und prüfe das Ergebnis in der Hersteller-App.
35
+ - Die Registerangaben beruhen auf der Herstellerdokumentation und auf Tests an einzelnen Anlagen.
36
+ Dein Modell oder deine Firmware kann sich anders verhalten.
37
+
38
+ ## Installation
39
+
40
+ Über *Palette verwalten* im Node-RED-Editor (Suche nach `alphaess-modbus`) oder im Node-RED-Benutzerverzeichnis (meist `~/.node-red`):
41
+
42
+ ```
43
+ npm install @impact0815/node-red-contrib-alphaess-modbus
44
+ ```
45
+
46
+ Danach Node-RED neu starten.
47
+
48
+ Voraussetzungen: Node-RED 3.0 oder neuer, Node.js 18 oder neuer und aktiviertes Modbus TCP am Alpha-ESS-System.
49
+
50
+ ### Docker
51
+
52
+ Beim offiziellen Image `nodered/node-red` nach `/data` installieren, damit das Paket ein Update des Containers übersteht:
53
+
54
+ ```
55
+ docker exec -it node-red bash -c "cd /data && npm install @impact0815/node-red-contrib-alphaess-modbus"
56
+ docker restart node-red
57
+ ```
58
+
59
+ Nach Installation oder Update den Editor im Browser neu laden (F5), sonst fehlen neue Ausgänge und Einstellungen.
60
+
61
+ ### Umstieg vom Paket ohne Scope (Versionen vor 0.4.0)
62
+
63
+ Bis 0.3.x hieß das Paket `node-red-contrib-alphaess-modbus` (aus einer lokalen Datei installiert).
64
+ Beide Pakete liefern dieselben Node-Typen, deshalb zuerst das alte entfernen. Flows und Einstellungen bleiben erhalten:
65
+
66
+ ```
67
+ cd ~/.node-red # Docker: docker exec -it node-red bash -c "cd /data && ..."
68
+ npm uninstall node-red-contrib-alphaess-modbus
69
+ npm install @impact0815/node-red-contrib-alphaess-modbus
70
+ ```
71
+
72
+ ## Schnellstart
73
+
74
+ 1. Eine **AlphaESS-Modbus**-Node einfügen und eine Verbindung mit der IP-Adresse des Systems anlegen (Port 502, Unit-ID 85).
75
+ 2. Standardblöcke und Intervall von 15 s beibehalten. An Ausgang 1 eine Debug-Node anschließen und deployen.
76
+ 3. Nach wenigen Sekunden zeigt der Status `PV … | Netz … | SOC … | Last …`.
77
+ 4. Optional: einen dauerhaften Kontextspeicher für die [Tageswerte](#tageswerte) einrichten und einen [MQTT](#mqtt)-Broker auswählen.
78
+
79
+ Zeigt der Status `ECONNREFUSED`, siehe [Fehlersuche](#fehlersuche).
80
+
81
+ ## Sprachen
82
+
83
+ | Teil | Sprache |
84
+ |---|---|
85
+ | Editor (Beschriftungen, Hinweise) und Hilfe in der Seitenleiste | Englisch oder Deutsch, je nach Spracheinstellung des Editors (*Benutzereinstellungen → Sprache*, Standard: Browsersprache) |
86
+ | Statustexte unter der Node, Log- und Fehlermeldungen | Sprache des Node-RED-Servers |
87
+ | Daten in `msg.payload` (Feldnamen, Alarm- und Warnungstexte) und MQTT-Topics | immer Englisch, damit Flows unabhängig von der Sprache funktionieren |
88
+
89
+ Weitere Sprachen lassen sich unter `locales/` ergänzen, siehe [CONTRIBUTING.md](CONTRIBUTING.md).
90
+
91
+ ## Konfiguration
92
+
93
+ ### Verbindung (`alphaess-modbus-config`)
94
+
95
+ | Einstellung | Standard | Beschreibung |
96
+ |---|---|---|
97
+ | Host | – | IP-Adresse des Systems |
98
+ | Port | 502 | Modbus-TCP-Port |
99
+ | Unit-ID | 85 | Modbus-Slave-Adresse (0x55) |
100
+ | Timeout | 2000 ms | pro Anfrage |
101
+ | Pause | 20 ms | Pause zwischen zwei Anfragen |
102
+
103
+ Alle Nodes mit derselben Verbindung teilen sich eine TCP-Verbindung; Anfragen werden nacheinander gesendet.
104
+ Das EMS nimmt meist nur eine Modbus-TCP-Verbindung gleichzeitig an. Andere Modbus-Clients (auch ungenutzte
105
+ `modbus-client`-Konfigurationen anderer Pakete) führen zu `ECONNREFUSED`.
106
+
107
+ ### Node (`alphaess-modbus`)
108
+
109
+ | Einstellung | Standard | Beschreibung |
110
+ |---|---|---|
111
+ | Intervall | 15 s | Abfrageintervall, `0` = nur bei Eingang |
112
+ | Langsame Blöcke | 300 s | Intervall für Systemkonfiguration, Zeitfenster und Dispatch-Status |
113
+ | Veraltet nach | 180 s | ein Block gilt nach dieser Zeit ohne erfolgreiches Lesen als veraltet |
114
+ | Blöcke lesen | – | Auswahl der zu lesenden Registerblöcke |
115
+ | EMS | EMS 3.5/3.6 | wählt die Texte für Batteriefehler und -warnungen |
116
+ | PV-Zähler addieren | aus | rechnet einen AC-gekoppelten PV-Wechselrichter in PV-Leistung und Tages-PV-Energie ein, siehe [PV-Zähler](#pv-zähler-ac-gekoppelte-anlagen) |
117
+ | Tageswerte-Speicher | Standard | Kontextspeicher für die Tageswerte, Auswahl aus den in `settings.js` konfigurierten Speichern; für Dauerhaftigkeit einen dauerhaften Speicher (z. B. `file`) wählen, siehe [Tageswerte](#tageswerte) |
118
+ | MQTT-Broker | – | optional, siehe [MQTT](#mqtt) |
119
+ | Schreibzugriff erlauben | aus | nötig für alle Steuerbefehle; zeigt im Editor einen Warnhinweis und schreibt beim Start einen Hinweis ins Log |
120
+ | Max. Leistung | – | optionale Obergrenze für die Dispatch-Leistung in W |
121
+ | Mindestabstand | 10 s | Mindestzeit zwischen zwei tatsächlichen Schreibvorgängen desselben Befehls, `0` = aus |
122
+ | SOC-Skalierung | 0.1 | %/bit für die SOC-Werte der Zeitfenster, siehe [SOC-Skalierung prüfen](#soc-skalierung-prüfen) |
123
+
124
+ ### PV-Zähler (AC-gekoppelte Anlagen)
125
+
126
+ Der Block *PV-Zähler* liest einen zweiten Zähler, den es nur gibt, wenn ein zusätzlicher, externer PV-Wechselrichter ins Hausnetz
127
+ einspeist (AC-gekoppelte Anlage). Bei DC- und den meisten Hybridanlagen hängen alle Module am Alpha-ESS-Wechselrichter und sind schon
128
+ im Block *Wechselrichter* enthalten. Der Block ist deshalb standardmäßig aus: Bei Anlagen ohne PV-Zähler würde er nur Fehler (und einen
129
+ dauerhaften Alarm *Stale data*) oder Nullen liefern.
130
+
131
+ Prüfen in `payload.details.systemConfig`:
132
+
133
+ | Feld | PV-Zähler wahrscheinlich | Kein PV-Zähler |
134
+ |---|---|---|
135
+ | `systemMode.text` | `AC` oder `Hybrid` | `DC` |
136
+ | `pvCapacityGridInverter` | > 0 | 0 |
137
+ | `meterCtSelect.text` | enthält `PV meter` | enthält `PV CT` |
138
+
139
+ Ist einer vorhanden, den Block *PV-Zähler* und *PV-Zähler zu PV-Leistung / Tages-PV-Energie addieren* aktivieren.
140
+ Die Tages-PV-Energie des externen Wechselrichters kommt aus dem Block *Systemlaufdaten*, der aktiv bleiben muss.
141
+
142
+ ## Ausgänge
143
+
144
+ ### Ausgang 1 – Daten
145
+
146
+ Eine Nachricht pro Abfragezyklus und bei `read`. Die Hauptwerte heißen wie in der Cloud-Node
147
+ [node-red-contrib-alphaess](https://github.com/dehsgr/node-red-contrib-alphaess); Flows können also zwischen Cloud und lokalem Zugriff wechseln.
148
+
149
+ ```json
150
+ {
151
+ "consumption": 980, "grid": -720, "modules": 4000, "battery": -2300, "soc": 87.3,
152
+ "gridImport": 0, "gridExport": 720, "batteryCharge": 2300, "batteryDischarge": 0,
153
+ "autarky": 100, "selfConsumptionRate": 82,
154
+ "daily": {
155
+ "day": "2026-09-27", "since": "2026-09-27T00:00:05.000Z", "complete": true,
156
+ "pv": 12.4, "gridFeed": 4.1, "gridImport": 1.2, "batteryCharge": 5.0, "batteryDischarge": 3.3,
157
+ "batteryChargeFromGrid": 0, "consumption": 7.8, "selfConsumptionRate": 66.9, "autarky": 84.6
158
+ },
159
+ "yesterday": { "...": "gleicher Aufbau" },
160
+ "alarms": [],
161
+ "warnings": ["Battery: Temperature imbalance"],
162
+ "blocks": { "grid": { "age": 0, "stale": false }, "inverter": { "age": 0, "stale": false } },
163
+ "details": { "grid": {}, "battery": {}, "inverter": {}, "systemRun": {}, "systemConfig": {}, "timePeriod": {}, "dispatch": {} },
164
+ "info": {
165
+ "inverterInfo": { "serialNumber": "...", "armSoftwareVersion": "..." },
166
+ "systemInfo": { "emsSerialNumber": "...", "emsVersion": {}, "wifiSerialNumber": "..." },
167
+ "batteryInfo": { "serialNumbers": ["..."] }
168
+ }
169
+ }
170
+ ```
171
+
172
+ Vorzeichen:
173
+
174
+ - `grid`: + = Bezug, − = Einspeisung
175
+ - `battery`: + = Entladen, − = Laden
176
+ - `consumption = modules + grid + battery`
177
+
178
+ Leistungen in W, Energien in kWh.
179
+
180
+ Kann ein Block nicht gelesen werden, wird sein letzter Wert weiterverwendet, bis er veraltet. Danach wird er aus `details` entfernt,
181
+ und die davon abhängigen Werte fehlen. Die Lesefehler des aktuellen Zyklus stehen in `msg.errors`.
182
+
183
+ Werte, die nur neuere Firmware liefert, fehlen bei älteren Anlagen:
184
+
185
+ | Feld | Beschreibung |
186
+ |---|---|
187
+ | `details.grid.energyConsumeFromGridPhase`, `energyFeedToGridPhase` | Gesamtenergie je Phase L1–L3 in kWh |
188
+ | `details.inverter.pvPowerTotalRegister` | PV-Gesamtleistung laut Wechselrichter; `pvPowerTotal` (Summe PV1–PV6) ist immer vorhanden und wird für `modules` verwendet |
189
+ | `details.dispatch.pvSwitch` | PV-Schalter des Dispatch (Note 29) |
190
+ | `info.inverterInfo.armSoftwareVersion`, `info.systemInfo.wifiSerialNumber`, `info.batteryInfo.serialNumbers` | Geräteinformationen |
191
+
192
+ ### Ausgang 2 – Alarm
193
+
194
+ Wird nur gesendet, wenn sich Alarme oder Warnungen ändern, und einmal nach dem Start:
195
+
196
+ ```json
197
+ { "active": true, "alarms": ["System: Grid_Meter_Lost"], "warnings": [], "timestamp": "..." }
198
+ ```
199
+
200
+ Alarme sind: Systemfehler, Batteriefehler, Wechselrichterfehler (fault1/2 und fault extend), Wechselrichter im Modus *Fault*
201
+ und veraltete Blöcke. Warnungen sind Batterie- und Wechselrichterwarnungen (als Text) sowie ein fehlender Kontextspeicher für die Tageswerte.
202
+ Die Texte sind immer Englisch.
203
+
204
+ ### Ausgang 3 – Befehlsantwort
205
+
206
+ Antwort auf jeden Befehl außer `read`: `readInfo`, `readRaw` und alle Schreibbefehle.
207
+ Datennachrichten auf Ausgang 1 enthalten deshalb nie Befehlsantworten.
208
+
209
+ | Eigenschaft | Beschreibung |
210
+ |---|---|
211
+ | `payload` | der betroffene Block, nach dem Befehl zurückgelesen (Rohwerte bei `readRaw`) |
212
+ | `written` | geschriebene Register `[{ "address": "0x0850", "values": [200] }]`, leer, wenn nichts geschrieben wurde |
213
+ | `unchanged` | `true`, wenn `feedIn` / `timePeriod` die gewünschten Werte schon hatten |
214
+
215
+ Alle anderen Eigenschaften der Eingangsnachricht bleiben erhalten. Fehler (ungültige Werte, Schreibschutz, Mindestabstand)
216
+ sind Node-Fehler und lassen sich mit einer *catch*-Node abfangen.
217
+
218
+ ## Tageswerte
219
+
220
+ Das System liefert per Modbus nur Gesamtzähler. Die Tageswerte sind deshalb die Differenz zu den Zählerständen um Mitternacht
221
+ (Zeitzone des Servers). Diese Mitternachtswerte liegen im Kontext der Node.
222
+
223
+ - `daily.since` zeigt den Beginn der Zählung.
224
+ - `daily.complete` ist `true`, wenn die Zählung höchstens 15 Minuten nach Mitternacht begonnen hat, also der ganze Tag erfasst ist.
225
+ `false` bedeutet: Node-RED lief um Mitternacht nicht, oder es gab tagsüber einen Neustart ohne dauerhaften Speicher.
226
+ - Wird ein Zähler tagsüber zurückgesetzt, bleibt der bis dahin erreichte Wert erhalten.
227
+
228
+ Damit die Tageswerte einen Neustart überstehen, in `settings.js` einen dauerhaften Kontextspeicher einrichten und ihn (hier `file`)
229
+ in der Node als *Tageswerte-Speicher* auswählen. Nach der Änderung Node-RED neu starten und den Editor neu laden, dann erscheint der Speicher in der Auswahl:
230
+
231
+ ```js
232
+ contextStorage: {
233
+ default: { module: "memory" },
234
+ file: { module: "localfilesystem" }
235
+ },
236
+ ```
237
+
238
+ Existiert der gewählte Speicher nicht, nutzt Node-RED stillschweigend den Standardspeicher. Die Node erkennt das,
239
+ schreibt beim Start eine Warnung ins Log, zeigt einen gelben Status und ergänzt eine Warnung in `payload.warnings`.
240
+
241
+ ## MQTT
242
+
243
+ In der Node eine vorhandene MQTT-Broker-Konfiguration auswählen. Die Node sendet dann direkt, ohne MQTT-out-Node:
244
+
245
+ | Topic | Inhalt |
246
+ |---|---|
247
+ | `<Präfix>/consumption`, `/grid`, `/modules`, `/battery`, `/soc` | Zahl |
248
+ | `<Präfix>/gridImport`, `/gridExport`, `/batteryCharge`, `/batteryDischarge`, `/autarky`, `/selfConsumptionRate` | Zahl |
249
+ | `<Präfix>/daily/<Feld>` | Zahl (kWh oder %) |
250
+ | `<Präfix>/daily/complete` | `true` oder `false` |
251
+ | `<Präfix>/status` | `ok` oder `alarm` |
252
+ | `<Präfix>/alarm` | JSON, nur bei Änderung |
253
+ | `<Präfix>/info` | JSON, immer mit Retain |
254
+ | `<Präfix>/details/<Block>` | JSON, optional |
255
+
256
+ Standard-Präfix ist `alphaess`. QoS und Retain sind einstellbar.
257
+ Die Node nutzt die Verbindung der Kern-Konfiguration `mqtt-broker`, die deshalb in `settings.js` nicht deaktiviert sein darf.
258
+
259
+ ## Eingangsbefehle (`msg.topic`)
260
+
261
+ | Topic | Payload | Beschreibung |
262
+ |---|---|---|
263
+ | `read` oder leer | – | alle aktivierten Blöcke sofort lesen, auch die langsamen |
264
+ | `readInfo` | – | Geräteinfos erneut lesen |
265
+ | `readRaw` | `{"address":1024,"count":10}` | Rohwerte von Holding-Registern, nur lesend |
266
+ | `dispatch` | `{"power":-3000,"soc":90,"duration":900,"mode":2}` | Dispatch starten |
267
+ | `dispatchStop` | – | Dispatch beenden |
268
+ | `feedIn` | `70` | maximale Einspeisung in % |
269
+ | `timePeriod` | `{"flag":1,"chargeCutSoc":90,"upsReserveSoc":10,"charge1":{"start":"01:00","stop":"05:00"}}` | Lade-/Entladezeitfenster |
270
+
271
+ Die Schreibbefehle benötigen **Schreibzugriff erlauben** – vorher den [Haftungsausschluss](#haftungsausschluss) lesen.
272
+ Die Antwort kommt auf Ausgang 3.
273
+
274
+ `dispatch`:
275
+
276
+ - `power` in W: negativ = laden, positiv = entladen.
277
+ - `soc`: Ziel in %. Standard 100 beim Laden, 10 beim Entladen.
278
+ - `duration` in Sekunden, Standard 300. Danach kehrt das System in den Normalbetrieb zurück.
279
+ - `mode`: Standard 2 = *State of Charge control*. Erlaubt: 1–10 und 19 (*No Battery Charge*).
280
+ Test- und Inselmodi der Registerliste (BurnIn, OSW-Modi) werden abgelehnt.
281
+
282
+ `feedIn`: Eine Einspeisebegrenzung kann von deinem Netzbetreiber vorgeschrieben sein – nur ändern, wenn das zulässig ist.
283
+
284
+ `timePeriod` ändert nur die angegebenen Felder. `flag`: 0 = aus, 1 = laden, 2 = entladen, 3 = beides.
285
+ Vor dem ersten Schreiben die zurückgelesenen SOC-Werte mit der App vergleichen und bei Abweichung die *SOC-Skalierung* anpassen.
286
+
287
+ Ein niedrigerer `upsReserveSoc` wirkt sofort: Der Akku kann direkt bis zum neuen Wert entladen.
288
+ Soll das erst zu einer bestimmten Zeit passieren (z. B. in der Nacht vor einem sonnigen Tag), den Befehl erst dann senden.
289
+
290
+ ### SOC-Skalierung prüfen
291
+
292
+ Laut Dokumentation haben `upsReserveSoc` und `chargeCutSoc` eine Auflösung von 0,1 %/bit, es gibt aber Anlagen mit 1 %/bit.
293
+ Vor dem ersten `timePeriod`-Schreibbefehl prüfen:
294
+
295
+ 1. `{"topic": "readRaw", "payload": {"address": 2128, "count": 1}}` senden (Register 0x0850, UPS-Reserve-SOC).
296
+ 2. Den Rohwert auf Ausgang 3 mit der Reserve in der App vergleichen. Reserve 10 % und Rohwert `10` → *SOC-Skalierung* auf `1`.
297
+ Rohwert `100` → bei `0.1` bleiben.
298
+ 3. Danach muss `details.timePeriod.upsReserveSoc` denselben Wert zeigen wie die App.
299
+
300
+ ### So wird geschrieben
301
+
302
+ - **Unveränderte Werte werden nicht geschrieben.** `feedIn` und `timePeriod` lesen zuerst die aktuellen Register.
303
+ Sind die Werte schon gesetzt, wird nichts geschrieben und `msg.unchanged` ist `true`.
304
+ Ein Flow kann denselben Befehl also beliebig oft senden, ohne den Speicher des EMS abzunutzen.
305
+ - **Nur geänderte Register werden geschrieben.** Jeder zusammenhängende Bereich geänderter Register ist eine eigene Anfrage.
306
+ Einstellungen, die zwischen Lesen und Schreiben in der App geändert wurden, werden nicht überschrieben.
307
+ Stunde und Minute eines Zeitfensters sind getrennte Register, eine Zeitänderung kann also zwei Anfragen brauchen.
308
+ - **Mindestabstand.** Ein zweiter tatsächlicher Schreibvorgang desselben Befehls innerhalb des *Mindestabstands* schlägt mit einem Fehler fehl.
309
+ Das schützt vor Schleifen im Flow. Anfragen ohne Änderung zählen nicht, und `dispatchStop` wird nie blockiert.
310
+ - `dispatch` und `dispatchStop` sind Befehle, keine Einstellungen, und werden immer geschrieben.
311
+
312
+ Bewusst **nicht** beschreibbar: Sicherheitstest, Reset/ATE-Modus, CT-Kalibrierung, Netzwerk- und Modbus-Einstellungen,
313
+ MOS-Steuerung der Batterie, SOC-Kalibrierung und die Dispatch-Testparameter (0x0889/0x088A).
314
+
315
+ ## Beispiele
316
+
317
+ ### Befehle aus anderen Tabs mit Rückmeldung
318
+
319
+ Im sendenden Flow eine *link call*-Node verwenden, vor der AlphaESS-Node eine *link in*-Node.
320
+ Für die Antwort **Ausgang 3** mit einer *switch*-Node auf `msg._linkSource` mit der Regel *ist nicht leer* verbinden,
321
+ danach eine *link out*-Node im Modus *Return to calling link node*:
322
+
323
+ ```
324
+ [link in] → [AlphaESS Modbus] ─ Ausgang 3 → [switch: msg._linkSource ist nicht leer] → [link out: zurück]
325
+ └ sonst → [debug]
326
+ ```
327
+
328
+ Antworten auf Befehle, die nicht per link call kamen (z. B. von einer Inject-Node), haben keine Rücksprungadresse und gehen auf den Ausgang *sonst*.
329
+ In der Node *Schreibzugriff erlauben* aktivieren.
330
+
331
+ ### Reserve-SOC abhängig von der Wetterprognose
332
+
333
+ ```js
334
+ // Function-Node vor dem link call
335
+ const sun = Number(global.get("sunhourstomorrow"));
336
+ if (!Number.isFinite(sun) || sun < 0) return null;
337
+ return { topic: "timePeriod", payload: { upsReserveSoc: sun >= 3 ? 10 : 20 } };
338
+ ```
339
+
340
+ Der Befehl darf beliebig oft gesendet werden: Ist der Wert schon gesetzt, wird nichts geschrieben und die Antwort enthält `unchanged: true`.
341
+
342
+ ### Werte in anderen Flows nutzen
343
+
344
+ ```js
345
+ // Function-Node hinter Ausgang 1
346
+ global.set("alphaess", msg.payload);
347
+ return { payload: msg.payload.consumption };
348
+ ```
349
+
350
+ Andere Tabs lesen dann z. B. `global.get("alphaess").details.timePeriod.upsReserveSoc`.
351
+
352
+ ### Unvollständige Tage in Statistiken ignorieren
353
+
354
+ ```js
355
+ // Function-Node hinter Ausgang 1, einmal täglich
356
+ const y = msg.payload.yesterday;
357
+ if (!y || !y.complete) return null;
358
+ return { payload: y };
359
+ ```
360
+
361
+ ### Nachts 15 Minuten aus dem Netz laden
362
+
363
+ ```json
364
+ { "topic": "dispatch", "payload": { "power": -3000, "soc": 90, "duration": 900 } }
365
+ ```
366
+
367
+ `duration` kurz halten und bei Bedarf wiederholen. Bleibt der Flow stehen, kehrt das System von selbst in den Normalbetrieb zurück.
368
+
369
+ ## Fehlersuche
370
+
371
+ | Symptom | Ursache und Lösung |
372
+ |---|---|
373
+ | `ECONNREFUSED` für alle Blöcke | Ein anderer Modbus-Client ist verbunden; das EMS nimmt nur eine Verbindung an. Nach alten `modbus-getter`/`modbus-write`-Nodes suchen, eine **ungenutzte `modbus-client`-Konfiguration** löschen (unter *Konfigurationsnodes*, dann *Vollständig* deployen) und andere Programme prüfen (Home Assistant, ioBroker, evcc). Sonst prüfen, ob Modbus TCP aktiv ist, oder das EMS neu starten. |
374
+ | `Timeout` / `Connect timeout` | Falsche IP, Netzwerk- oder Docker-Netzwerkproblem, langsame Verbindung. Test mit `nc -zv <ip> 502`; *Timeout* und *Pause* erhöhen. |
375
+ | `Modbus exception 2` für einen Block | Das System unterstützt diesen Block nicht (meist *PV-Zähler*). Block abschalten. Erweiterte Blöcke der neueren Registerliste fallen automatisch auf die ältere Länge zurück; das Log zeigt `extended registers not supported`. |
376
+ | Dauerhafter Alarm `Stale data: <Block>` | Der Block lässt sich nicht lesen; siehe `payload.blocks.<Block>.error`. |
377
+ | Tageswerte beginnen nach jedem Neustart neu, `daily.complete` ist `false` | Kein dauerhafter Kontextspeicher. In `settings.js` einrichten und als *Tageswerte-Speicher* auswählen (gelber Status `Speicher "…" fehlt`, wenn der gewählte Speicher nicht existiert). |
378
+ | SOC der Zeitfenster zeigt 1 statt 10 | Falsche *SOC-Skalierung*, siehe [SOC-Skalierung prüfen](#soc-skalierung-prüfen). |
379
+ | `Schreiben ist deaktiviert` | *Schreibzugriff erlauben* aktivieren. |
380
+ | `Schreiben blockiert, nächster Schreibvorgang in … s möglich` | Schutz durch den *Mindestabstand*. Warten oder den Flow auf Schleifen prüfen. |
381
+ | Neue Ausgänge/Einstellungen nach einem Update nicht sichtbar | Node-RED neu starten und den Editor neu laden (F5). |
382
+ | Node-Typen doppelt / Installationskonflikt nach Update auf 0.4.0 | Das alte Paket ohne Scope ist noch installiert, siehe [Umstieg](#umstieg-vom-paket-ohne-scope-versionen-vor-040). |
383
+ | Editor auf Englisch, obwohl Deutsch erwartet | Im Editor *Benutzereinstellungen → Sprache* auf *Deutsch* stellen oder die Browsersprache auf Deutsch setzen. |
384
+ | Link call läuft in einen Timeout | Ausgang 3 ist nicht mit der *link out*-Node im Rückgabemodus verbunden, siehe [Beispiele](#befehle-aus-anderen-tabs-mit-rückmeldung). |
385
+
386
+ ## Registerblöcke
387
+
388
+ | Block | Start | Anzahl (ältere Firmware) | Gelesen |
389
+ |---|---|---|---|
390
+ | grid | 0x0010 | 51 (39) | jedes Intervall |
391
+ | pvMeter | 0x0090 | 39 | jedes Intervall |
392
+ | battery | 0x0100 | 73 | jedes Intervall |
393
+ | inverter | 0x0400 | 85 (83) | jedes Intervall |
394
+ | systemRun | 0x08D0 | 6 | jedes Intervall |
395
+ | systemConfig | 0x0800 | 18 | langsames Intervall |
396
+ | timePeriod | 0x084F | 19 | langsames Intervall |
397
+ | dispatch | 0x0880 | 11 (9) | langsames Intervall |
398
+ | inverterInfo | 0x0640 | 25 (20) | beim Start, dann täglich |
399
+ | systemInfo | 0x0740 | 25 (15) | beim Start, dann täglich |
400
+ | batteryInfo | 0x0150 | 30 (–) | beim Start, dann täglich |
401
+
402
+ Blöcke mit zwei Angaben werden zuerst mit der größeren Länge gelesen. Lehnt das System ab (Modbus-Exception 2 oder 3),
403
+ liest die Node ab dann die ältere Länge. `payload.blocks.<Block>.registers` zeigt die reduzierte Länge.
404
+ Die Seriennummern der Batteriemodule werden übersprungen, wenn das System sie nicht liefert.
405
+
406
+ Nicht unterstützt: der Wechselrichterblock *Byte Watt* (0x0500), der HHE-MEC-Systemblock (0x06FA–0x072B), Echonet (Japan),
407
+ Frequenz-Dispatch, AUX-, Generator- und PV-Changer-Blöcke.
408
+
409
+ ## Entwicklung
410
+
411
+ ```
412
+ npm test # Unit- und Integrationstests gegen den eingebauten Simulator
413
+ npm run simulator # Modbus-TCP-Simulator auf Port 5020
414
+ node test/mock-server.js 5020 --legacy # Simulator einer älteren Firmware (Registerliste V1.1)
415
+ ```
416
+
417
+ Für einen Test ohne echte Anlage den Simulator starten und die Verbindung auf `127.0.0.1:5020` stellen.
418
+ Beiträge und Übersetzungen: siehe [CONTRIBUTING.md](CONTRIBUTING.md). Fehlermeldungen: https://github.com/impact0815/node-red-contrib-alphaess-modbus/issues
419
+
420
+ ## Lizenz
421
+
422
+ MIT – siehe [LICENSE](LICENSE). Ohne Gewährleistung, siehe [Haftungsausschluss](#haftungsausschluss).