iobroker.goodwe-sems 0.1.8 → 0.1.9
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.de.md +224 -0
- package/README.md +120 -115
- package/admin/i18n/de/translations.json +2 -1
- package/admin/i18n/en/translations.json +2 -1
- package/admin/i18n/es/translations.json +2 -1
- package/admin/i18n/fr/translations.json +2 -1
- package/admin/i18n/it/translations.json +2 -1
- package/admin/i18n/nl/translations.json +2 -1
- package/admin/i18n/pl/translations.json +2 -1
- package/admin/i18n/pt/translations.json +2 -1
- package/admin/i18n/ru/translations.json +2 -1
- package/admin/i18n/uk/translations.json +2 -1
- package/admin/i18n/zh-cn/translations.json +2 -1
- package/admin/jsonConfig.json +76 -22
- package/io-package.json +24 -11
- package/lib/mapping.js +41 -232
- package/lib/notify.js +10 -17
- package/lib/semsApi.js +69 -94
- package/main.js +32 -98
- package/package.json +5 -6
package/README.de.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
*[Read this in English](README.md)*
|
|
4
|
+
|
|
5
|
+
# ioBroker.goodwe-sems
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/iobroker.goodwe-sems)
|
|
8
|
+
[](https://www.npmjs.com/package/iobroker.goodwe-sems)
|
|
9
|
+

|
|
10
|
+
[](https://www.paypal.com/ncp/payment/TT6MTBLXX9L9U)
|
|
11
|
+
|
|
12
|
+
Liest Wechselrichter-, Batterie- und Energiefluss-Daten aus dem **GoodWe SEMS Portal (Cloud)** – für Anlagen, die (z. B. weil kein LAN-Zugriff auf den Wechselrichter besteht) **nicht** mit dem lokalen [ioBroker.goodwe](https://github.com/FossyTom/ioBroker.goodwe)-Adapter (Modbus/UDP, Port 8899) abgefragt werden können.
|
|
13
|
+
|
|
14
|
+
Login erfolgt mit dem **ganz normalen SEMS-Portal-Konto** (dasselbe wie unter semsportal.com / in der SEMS-App). Ein GoodWe-"Organization"/OpenAPI-Konto wird **nicht** benötigt.
|
|
15
|
+
|
|
16
|
+
## Inhaltsverzeichnis
|
|
17
|
+
|
|
18
|
+
- [Warum dieser Adapter?](#warum-dieser-adapter)
|
|
19
|
+
- [API-Herkunft und Grenzen (bitte lesen)](#api-herkunft-und-grenzen-bitte-lesen)
|
|
20
|
+
- [Installation](#installation)
|
|
21
|
+
- [Konfiguration](#konfiguration)
|
|
22
|
+
- [Objekt-/State-Struktur](#objekt-state-struktur)
|
|
23
|
+
- [Fehlerbehandlung, Backoff und Rate-Limits](#fehlerbehandlung-backoff-und-rate-limits)
|
|
24
|
+
- [Pushover-Benachrichtigungen](#pushover-benachrichtigungen)
|
|
25
|
+
- [Sicherheit & Datenschutz](#sicherheit--datenschutz)
|
|
26
|
+
- [Entwicklung](#entwicklung)
|
|
27
|
+
- [Changelog](#changelog)
|
|
28
|
+
- [Lizenz](#lizenz)
|
|
29
|
+
|
|
30
|
+
## Warum dieser Adapter?
|
|
31
|
+
|
|
32
|
+
GoodWe ET/EH/BH/BT-Wechselrichter lassen sich normalerweise lokal per Modbus/UDP auslesen (siehe [ioBroker.goodwe](https://github.com/FossyTom/ioBroker.goodwe)). Steht kein LAN-Zugriff auf den Wechselrichter zur Verfügung (z. B. weil nur ein WLAN/LTE-Stick mit dem SEMS-Portal verbunden ist und das Zielnetz nicht erreichbar ist), bleibt nur der Umweg über die Cloud: das **SEMS Portal** (semsportal.com), über das die Anlage ohnehin schon überwacht wird.
|
|
33
|
+
|
|
34
|
+
## API-Herkunft und Grenzen (bitte lesen)
|
|
35
|
+
|
|
36
|
+
GoodWe bietet offiziell drei APIs an (siehe [GoodWe API Technical Document](https://community.goodwe.com/solution/API)):
|
|
37
|
+
|
|
38
|
+
- **OpenAPI** – nur für SEMS-*Organization*-Konten, erfordert Freischaltung durch GoodWe.
|
|
39
|
+
- **Real-time Data Monitoring API** – für Drittanbieter, erfordert Lizenzvertrag + Geräte-Whitelist.
|
|
40
|
+
- **Batch Remote Control Interface** – Kafka-basiert, nur Fernsteuerung.
|
|
41
|
+
|
|
42
|
+
Für ein **normales** SEMS-Portal-Konto (wie es die meisten Privatanwender haben) ist keine davon zugänglich. Dieser Adapter spricht stattdessen dieselbe **undokumentierte HTTPS-API**, die auch die offizielle SEMS-App/Webseite verwendet (Login via `CrossLogin`/`SEMS+ cross-login`, Datenabfrage via `GetMonitorDetailByPowerstationId`). Diese Endpunkte wurden nicht von GoodWe für Drittnutzung freigegeben oder dokumentiert; die Implementierung basiert auf eigener Analyse sowie den quelloffenen Referenzprojekten:
|
|
43
|
+
|
|
44
|
+
- [pygoodwe](https://github.com/yaleman/pygoodwe) (MIT)
|
|
45
|
+
- [goodwe-sems-home-assistant](https://github.com/TimSoethout/goodwe-sems-home-assistant)
|
|
46
|
+
- [openHAB SEMSPortal-Binding](https://www.openhab.org/addons/bindings/semsportal/)
|
|
47
|
+
|
|
48
|
+
**Konsequenzen:**
|
|
49
|
+
|
|
50
|
+
- GoodWe kann die API jederzeit ohne Vorankündigung ändern - der Adapter kann dadurch (temporär) ausfallen.
|
|
51
|
+
- Es gibt **kein dokumentiertes Echtzeit-/Push-Verfahren** (Websocket/SignalR) für Drittanbieter. Ein `msgSocketAdr`-Feld taucht in älteren Login-Antworten auf, wird aber von keinem der oben genannten Referenzprojekte tatsächlich genutzt - es wäre reines Reverse-Engineering ohne belastbare Dokumentation und ein deutlich höheres Risiko (Kontosperrung, instabile Verbindung). Dieser Adapter pollt daher bewusst per HTTPS in konfigurierbarem Intervall (Default 5 Minuten) statt eine ungetestete Websocket-Verbindung vorzutäuschen.
|
|
52
|
+
- Es wurde ein **Rate-Limit-Code (`GY0429`)** beobachtet (u. a. in der Home-Assistant-Integration dokumentiert). Der Adapter erkennt diesen Code und pausiert automatisch (Default 5 Minuten Cool-down), statt das Konto durch wiederholte Anfragen zu gefährden.
|
|
53
|
+
- Nutzung erfolgt auf eigenes Risiko, siehe [LICENSE](LICENSE) (MIT, ohne Gewährleistung).
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
Solange der Adapter noch nicht im offiziellen ioBroker-Repository gelistet ist:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
cd /opt/iobroker
|
|
61
|
+
npm install https://github.com/bueste/ioBroker.goodwe-sems/tarball/main
|
|
62
|
+
iobroker add goodwe-sems
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Konfiguration
|
|
66
|
+
|
|
67
|
+
| Feld | Beschreibung |
|
|
68
|
+
|---|---|
|
|
69
|
+
| SEMS-Konto / Passwort | Dieselben Zugangsdaten wie auf semsportal.com. Passwort wird von ioBroker verschlüsselt gespeichert. |
|
|
70
|
+
| Anlagen-ID (optional) | Leer lassen für automatische Erkennung (`GetPowerStationIdByOwner`). Bei mehreren Anlagen pro Konto: ID manuell aus der Portal-URL übernehmen (`.../powerstation/powerstatussnmin/<ID>`). |
|
|
71
|
+
| Poll-Intervall | Default 300 s. Der Adapter erzwingt ein Minimum von 60 s, unabhängig von der Konfiguration. |
|
|
72
|
+
| Pushover | Siehe [Pushover-Benachrichtigungen](#pushover-benachrichtigungen). |
|
|
73
|
+
|
|
74
|
+
## Objekt-/State-Struktur
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
goodwe-sems.0.info.connection SEMS Portal erreichbar (bool)
|
|
78
|
+
goodwe-sems.0.info.lastSuccess Zeitstempel letzter erfolgreicher Poll
|
|
79
|
+
goodwe-sems.0.info.lastError Letzte Fehlermeldung
|
|
80
|
+
goodwe-sems.0.info.consecutiveErrors Anzahl aufeinanderfolgender Fehlversuche
|
|
81
|
+
goodwe-sems.0.info.rateLimited SEMS Portal limitiert aktuell (bool)
|
|
82
|
+
goodwe-sems.0.info.activePollInterval Aktuell wirksames Intervall inkl. Backoff (s)
|
|
83
|
+
goodwe-sems.0.info.rawResponse Rohe JSON-Antwort (nur wenn Debug-Option aktiv)
|
|
84
|
+
|
|
85
|
+
goodwe-sems.0.Station.Name / .Capacity / .Address / .Latitude / .Longitude / .PortalTimestamp / .Status / .StationId
|
|
86
|
+
goodwe-sems.0.KPI.CurrentPower / .TodayGeneration / .MonthGeneration / .TotalGeneration / .TodayIncome / .TotalIncome / .Currency
|
|
87
|
+
goodwe-sems.0.PowerFlow.PV / .Load / .Grid / .Battery / .LoadStatus / .GridStatus / .PvStatus / .BatteryStatus
|
|
88
|
+
goodwe-sems.0.Battery.SOC / .Status
|
|
89
|
+
goodwe-sems.0.EVCharger.* (nur wenn vom Portal gemeldet)
|
|
90
|
+
|
|
91
|
+
goodwe-sems.0.Inverters.<Seriennummer>.Name / .Model / .Status / .WarningCode
|
|
92
|
+
goodwe-sems.0.Inverters.<Seriennummer>.CurrentPower / .TodayGeneration / .TotalGeneration / .Temperature
|
|
93
|
+
goodwe-sems.0.Inverters.<Seriennummer>.PV1..4.Voltage / .Current
|
|
94
|
+
goodwe-sems.0.Inverters.<Seriennummer>.AC_L1..3.Voltage / .Current / .Frequency
|
|
95
|
+
goodwe-sems.0.Inverters.<Seriennummer>.Battery.SOC / .Voltage / .Current
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Bei zwei Wechselrichtern (wie in der ursprünglichen Anforderung) entstehen automatisch zwei `Inverters.<SN>.*`-Zweige - die Anzahl ist nicht fest codiert, sondern richtet sich nach dem, was das Portal für das jeweilige Konto zurückliefert.
|
|
99
|
+
|
|
100
|
+
Felder, die das Portal liefert, aber dieser Adapter (noch) nicht kennt, gehen nicht verloren: Mit aktivierter Debug-Option landet die komplette Rohantwort in `info.rawResponse` (JSON), sodass sie inspiziert und bei Bedarf per PR ergänzt werden können.
|
|
101
|
+
|
|
102
|
+
## Fehlerbehandlung, Backoff und Rate-Limits
|
|
103
|
+
|
|
104
|
+
- Jeder Poll-Zyklus ist vollständig try/catch-abgesichert; ein einzelner Fehler kann die Polling-Schleife nicht dauerhaft stoppen.
|
|
105
|
+
- Fehlerklassen (`SemsAuthError`, `SemsRateLimitError`, `SemsNetworkError`, `SemsProtocolError`) steuern das Verhalten gezielt:
|
|
106
|
+
- **Rate-Limit (`GY0429`)** → sofortige Pause (Default 300 s), `info.rateLimited = true`.
|
|
107
|
+
- **Login-Fehler** → exponentielles Backoff (bis 1 h Deckel), damit falsche Zugangsdaten das Konto nicht zusätzlich belasten.
|
|
108
|
+
- **Netzwerk-/Protokollfehler** → moderates Backoff.
|
|
109
|
+
- Nach konfigurierbar vielen aufeinanderfolgenden Fehlversuchen (Default 3) gilt die Anlage als "offline" und es wird - falls aktiviert - eine Pushover-Meldung ausgelöst.
|
|
110
|
+
- Alles wird zusätzlich strukturiert ins ioBroker-Log geschrieben (`error`/`warn`/`debug` je nach Schweregrad).
|
|
111
|
+
|
|
112
|
+
## Pushover-Benachrichtigungen
|
|
113
|
+
|
|
114
|
+
Konfigurierbar in drei Modi:
|
|
115
|
+
|
|
116
|
+
1. **Über eine bestehende `ioBroker.pushover`-Instanz** (`sendTo`) - empfohlen, keine doppelte Zugangsdatenverwaltung.
|
|
117
|
+
2. **Direkt über die Pushover-API** (eigener User-Key + API-/App-Token, verschlüsselt gespeichert) - funktioniert auch ohne separate Pushover-Instanz.
|
|
118
|
+
3. **Beides gleichzeitig.**
|
|
119
|
+
|
|
120
|
+
Ausgelöst wird bei: SEMS-Login-Fehler, SEMS-Rate-Limit, länger andauerndem Ausfall, unerwartetem Adapterfehler - jeweils einzeln aktivierbar. Eine interne Sperrfrist (Default 1 h pro Kategorie) verhindert Spam bei andauernden Störungen.
|
|
121
|
+
|
|
122
|
+
## Sicherheit & Datenschutz
|
|
123
|
+
|
|
124
|
+
- SEMS-Passwort und Pushover-API-Token sind in `io-package.json` als `encryptedNative`/`protectedNative` markiert und werden von ioBroker verschlüsselt abgelegt, nicht im Klartext geloggt (Kontoname wird in Log-Meldungen maskiert, z. B. `st***@gmail.com`).
|
|
125
|
+
- Der Adapter führt **ausschließlich lesende** Zugriffe aus (`GetMonitorDetailByPowerstationId`, `GetPowerStationIdByOwner`). Es gibt bewusst **keine** Fernsteuerungs-/Schreibfunktion (`SaveRemoteControlInverter`) - das wäre ein deutlich größeres Sicherheits- und Haftungsrisiko und war nicht Teil der Anforderung.
|
|
126
|
+
- Keine Drittanbieter-Abhängigkeiten für den HTTP-Zugriff: Es wird das in Node.js ≥18 eingebaute `fetch` verwendet statt einer zusätzlichen HTTP-Bibliothek - kleinere Angriffsfläche, weniger Supply-Chain-Risiko.
|
|
127
|
+
- Alle Netzwerkfehler werden typisiert abgefangen; es werden keine ungeprüften Daten aus der API-Antwort ausgeführt (`eval`, `Function`, o. ä. werden nirgends verwendet).
|
|
128
|
+
|
|
129
|
+
## Entwicklung
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
npm install
|
|
133
|
+
npm run lint
|
|
134
|
+
npm test # Unit-Tests (lib/mapping.js, lib/semsApi.js, lib/notify.js) + Package-Konsistenz-Check
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Empfehlung vor jedem Release zusätzlich lokal:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
npx @iobroker/adapter-checker@latest .
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Pull Requests willkommen, insbesondere um zusätzliche, vom Portal gelieferte Felder zu ergänzen (siehe `info.rawResponse` mit aktivierter Debug-Option) oder Übersetzungen zu verbessern.
|
|
144
|
+
|
|
145
|
+
## Changelog
|
|
146
|
+
|
|
147
|
+
### **WORK IN PROGRESS**
|
|
148
|
+
|
|
149
|
+
### 0.1.8 (2026-07-19)
|
|
150
|
+
|
|
151
|
+
ioBroker-Adapter-Check-Befunde behoben:
|
|
152
|
+
|
|
153
|
+
- (Stefan Bühler) **[E254]** News-Einträge für 0.1.1/0.1.2 entfernt - diese Tags wurden zwar gepusht, aber der zugehörige npm-Publish-Job schlug damals fehl (fehlendes NPM_TOKEN bzw. zu alte npm-CLI für OIDC), die Versionen existieren nie auf npm
|
|
154
|
+
- (Stefan Bühler) **[W132]** dadurch automatisch unter dem 7-Einträge-Limit des Repository-Builders für `common.news`
|
|
155
|
+
- (Stefan Bühler) **[W184]** veraltetes `common.title` entfernt (durch `common.titleLang` ersetzt) und veraltetes/ignoriertes `common.main` entfernt (Entry-Point kommt aus `package.json`)
|
|
156
|
+
- (Stefan Bühler) **[W034]** `@iobroker/adapter-core` von ^3.1.6 auf ^3.2.2 angehoben (installiert 3.4.3)
|
|
157
|
+
- (Stefan Bühler) **[W173]/[W174]/[E999]/[W401]**: `password` ist bereits korrekt in `encryptedNative`/`protectedNative` gelistet (per Tarball-Inspektion verifiziert) - diese Meldungen sowie der globale Axios-404-Fehler beim Abruf von `sources-dist-latest.json` sind Nebenwirkungen davon, dass der Adapter noch nicht im offiziellen ioBroker-Repository gelistet ist; sollten nach der Aufnahme verschwinden
|
|
158
|
+
|
|
159
|
+
### 0.1.7 (2026-07-19)
|
|
160
|
+
|
|
161
|
+
- (Stefan Bühler) Branding: Platzhalter-Icon durch das offizielle GoodWe-Logo ersetzt (mit Genehmigung von GoodWe verwendet)
|
|
162
|
+
|
|
163
|
+
### 0.1.6 (2026-07-18)
|
|
164
|
+
|
|
165
|
+
- (Stefan Bühler) Dev-Toolchain aktualisiert: mocha 11, sinon 22, @alcalzone/release-script 5, @iobroker/eslint-config 2; verbleibende transitive CVEs (adm-zip, diff, esbuild, serialize-javascript) per npm-`overrides` erzwungen behoben - `npm audit`: 0 Schwachstellen (auch inkl. Dev-Dependencies)
|
|
166
|
+
|
|
167
|
+
Sicherheits-/Qualitätsaudit (Security-Tester, Maintainer-Review, Fuzzing der Mapping-Schicht):
|
|
168
|
+
|
|
169
|
+
- (Stefan Bühler) **Security:** Wechselrichter-Seriennummern aus der (nicht vertrauenswürdigen) Portal-Antwort werden bereinigt, bevor sie Teil von ioBroker-Objekt-IDs werden (verhindert kaputte/unerwartet verschachtelte Objektbäume durch Sonderzeichen wie `.` `*` `]`)
|
|
170
|
+
- (Stefan Bühler) **Security:** die vom Login-Server gelieferte API-Basis-URL wird validiert - nur HTTPS auf GoodWe-eigenen Domains (`*.semsportal.com`, `*.goodwe.com`), sonst Fallback auf die bekannte Regional-URL. Eine manipulierte Login-Antwort kann das Session-Token damit nicht mehr an fremde Hosts umleiten
|
|
171
|
+
- (Stefan Bühler) **Fix:** `null`/defekte Einträge im `inverter[]`-Array des Portals ließen den kompletten Poll-Zyklus abstürzen - werden jetzt übersprungen, gesunde Wechselrichter derselben Antwort werden weiter verarbeitet
|
|
172
|
+
- (Stefan Bühler) **Fix:** Zahlen in Exponentialschreibweise (`"1e5"`) wurden falsch geparst (ergab 15 statt 100000)
|
|
173
|
+
- (Stefan Bühler) **Fix:** offensichtlich ungültige Portal-Zeitstempel (`99/99/9999 …`) erzeugten durch JS-Date-Rollover absurde Epochen-Werte - werden jetzt verworfen
|
|
174
|
+
- (Stefan Bühler) **Fix:** automatische Anlagen-Erkennung filtert Einträge ohne verwertbare ID (verhinderte sonst dauerhafte Fehlzyklen)
|
|
175
|
+
- (Stefan Bühler) **Robustheit:** keine State-Writes mehr nach Adapter-Unload; `adapterError`-Dedupe wird nach Erholung ebenfalls zurückgesetzt
|
|
176
|
+
- (Stefan Bühler) 14 neue Regressionstests (42 Unit-Tests gesamt); `npm audit`: 0 Schwachstellen in Produktions-Dependencies (verbleibende betreffen ausschließlich Dev-Toolchain)
|
|
177
|
+
|
|
178
|
+
### 0.1.5 (2026-07-18)
|
|
179
|
+
|
|
180
|
+
- (Stefan Bühler) fix: PayPal-Spendenlink im README korrigiert (Button-Link statt Donate-Link)
|
|
181
|
+
|
|
182
|
+
### 0.1.4 (2026-07-18)
|
|
183
|
+
|
|
184
|
+
- (Stefan Bühler) docs: PayPal-Spendenlink im README ergänzt
|
|
185
|
+
|
|
186
|
+
### 0.1.3 (2026-07-18)
|
|
187
|
+
|
|
188
|
+
- (Stefan Bühler) CI: OIDC Trusted Publishing repariert - Node 22 Runner bringt npm 10.9.x mit, was unter der für Trusted Publishing benötigten Version (>=11.5.1) liegt. Release-Job aktualisiert npm jetzt explizit vor `npm publish`.
|
|
189
|
+
|
|
190
|
+
### 0.1.2 (2026-07-18)
|
|
191
|
+
|
|
192
|
+
- (Stefan Bühler) CI: npm-Publish von langlebigem `NPM_TOKEN` auf OIDC Trusted Publishing umgestellt (kein Secret mehr im Repo nötig)
|
|
193
|
+
|
|
194
|
+
### 0.1.1 (2026-07-18)
|
|
195
|
+
|
|
196
|
+
- (Stefan Bühler) fix `repository.url` field format in package.json (removed npm-publish normalization warning)
|
|
197
|
+
|
|
198
|
+
### 0.1.0 (2026-07-18)
|
|
199
|
+
|
|
200
|
+
- (Stefan Bühler) initial release: SEMS-Portal-Login (SEMS+ mit Legacy-Fallback), automatische Anlagen-Erkennung, vollständiges Monitoring (Station/KPI/PowerFlow/Battery/EV-Charger/pro Wechselrichter), Rate-Limit-Handling, Backoff, Pushover-Alarmierung, Admin6-JSON-Config, i18n (11 Sprachen), Unit-Tests.
|
|
201
|
+
|
|
202
|
+
## Lizenz
|
|
203
|
+
|
|
204
|
+
MIT License
|
|
205
|
+
|
|
206
|
+
Copyright (c) 2026 Stefan Bühler
|
|
207
|
+
|
|
208
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
209
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
210
|
+
in the Software without restriction, including without limitation the rights
|
|
211
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
212
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
213
|
+
furnished to do so, subject to the following conditions:
|
|
214
|
+
|
|
215
|
+
The above copyright notice and this permission notice shall be included in all
|
|
216
|
+
copies or substantial portions of the Software.
|
|
217
|
+
|
|
218
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
219
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
220
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
221
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
222
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
223
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
224
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,203 +1,208 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
+
*[Auf Deutsch lesen](README.de.md)*
|
|
4
|
+
|
|
3
5
|
# ioBroker.goodwe-sems
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/iobroker.goodwe-sems)
|
|
6
8
|
[](https://www.npmjs.com/package/iobroker.goodwe-sems)
|
|
7
9
|

|
|
8
|
-
[](https://www.paypal.com/ncp/payment/TT6MTBLXX9L9U)
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
Reads inverter, battery and power-flow data from the **[GoodWe](https://www.goodwe.com) [SEMS Portal](https://www.semsportal.com) (cloud)** - for installations that (e.g. because there is no LAN access to the inverter) **cannot** be polled with the local [ioBroker.goodwe](https://github.com/FossyTom/ioBroker.goodwe) adapter (Modbus/UDP, port 8899).
|
|
11
13
|
|
|
12
|
-
Login
|
|
14
|
+
Login uses your **normal SEMS Portal account** (the same one you use at semsportal.com / in the SEMS app). A GoodWe "organization"/OpenAPI account is **not** required.
|
|
13
15
|
|
|
14
|
-
##
|
|
16
|
+
## Table of contents
|
|
15
17
|
|
|
16
|
-
- [
|
|
17
|
-
- [API
|
|
18
|
+
- [Why this adapter?](#why-this-adapter)
|
|
19
|
+
- [API origin and limitations (please read)](#api-origin-and-limitations-please-read)
|
|
18
20
|
- [Installation](#installation)
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
- [Pushover
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
21
|
+
- [Configuration](#configuration)
|
|
22
|
+
- [Object/state structure](#objectstate-structure)
|
|
23
|
+
- [Error handling, backoff and rate limits](#error-handling-backoff-and-rate-limits)
|
|
24
|
+
- [Pushover notifications](#pushover-notifications)
|
|
25
|
+
- [Security & privacy](#security--privacy)
|
|
26
|
+
- [Development](#development)
|
|
25
27
|
- [Changelog](#changelog)
|
|
26
|
-
- [
|
|
28
|
+
- [License](#license)
|
|
27
29
|
|
|
28
|
-
##
|
|
30
|
+
## Why this adapter?
|
|
29
31
|
|
|
30
|
-
GoodWe ET/EH/BH/BT
|
|
32
|
+
GoodWe ET/EH/BH/BT inverters can normally be read out locally via Modbus/UDP (see [ioBroker.goodwe](https://github.com/FossyTom/ioBroker.goodwe)). If there is no LAN access to the inverter (e.g. because only a WLAN/LTE stick is connected to the SEMS Portal and the target network is otherwise unreachable), the only remaining option is the cloud detour via the **[SEMS Portal](https://www.semsportal.com)** ([GoodWe](https://www.goodwe.com)) that the installation is already being monitored through anyway.
|
|
31
33
|
|
|
32
|
-
## API
|
|
34
|
+
## API origin and limitations (please read)
|
|
33
35
|
|
|
34
|
-
GoodWe
|
|
36
|
+
GoodWe officially offers three APIs (see the [GoodWe API technical document](https://community.goodwe.com/solution/API)):
|
|
35
37
|
|
|
36
|
-
- **OpenAPI**
|
|
37
|
-
- **Real-time Data Monitoring API**
|
|
38
|
-
- **Batch Remote Control Interface**
|
|
38
|
+
- **OpenAPI** - only for SEMS *organization* accounts, requires activation by GoodWe.
|
|
39
|
+
- **Real-time Data Monitoring API** - for third parties, requires a license agreement plus a device whitelist.
|
|
40
|
+
- **Batch Remote Control Interface** - Kafka-based, remote control only.
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
None of these are accessible with a **normal** SEMS Portal account (the kind most private users have). This adapter instead speaks the same **undocumented HTTPS API** that the official SEMS app/website itself uses (login via `CrossLogin`/`SEMS+ cross-login`, data retrieval via `GetMonitorDetailByPowerstationId`). These endpoints have not been released or documented by GoodWe for third-party use; the implementation is based on independent traffic analysis as well as the following open-source reference projects:
|
|
41
43
|
|
|
42
44
|
- [pygoodwe](https://github.com/yaleman/pygoodwe) (MIT)
|
|
43
45
|
- [goodwe-sems-home-assistant](https://github.com/TimSoethout/goodwe-sems-home-assistant)
|
|
44
|
-
- [openHAB SEMSPortal
|
|
46
|
+
- [openHAB SEMSPortal binding](https://www.openhab.org/addons/bindings/semsportal/)
|
|
45
47
|
|
|
46
|
-
**
|
|
48
|
+
**Consequences:**
|
|
47
49
|
|
|
48
|
-
- GoodWe
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
50
|
+
- GoodWe can change the API at any time without notice - the adapter may (temporarily) break as a result.
|
|
51
|
+
- There is **no documented real-time/push mechanism** (websocket/SignalR) for third parties. An `msgSocketAdr` field appears in some older login responses but is not actually used by any of the reference projects above - using it would be pure reverse engineering without reliable documentation and a significantly higher risk (account lockout, unstable connection). This adapter therefore deliberately polls over HTTPS at a configurable interval (default 5 minutes) instead of faking an untested websocket connection.
|
|
52
|
+
- A **rate-limit code (`GY0429`)** has been observed (documented, among others, in the Home Assistant integration). The adapter recognizes this code and automatically pauses (default 5-minute cool-down) instead of endangering the account with repeated requests.
|
|
53
|
+
- Use at your own risk, see [LICENSE](LICENSE) (MIT, no warranty).
|
|
52
54
|
|
|
53
55
|
## Installation
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
Once this adapter is listed in the official ioBroker adapter repository, install it the normal way: **Admin -> Adapters -> search for "goodwe-sems" -> install**.
|
|
58
|
+
|
|
59
|
+
Until then, an ioBroker administrator can add it manually on the ioBroker host:
|
|
56
60
|
|
|
57
61
|
```
|
|
58
|
-
|
|
59
|
-
npm install https://github.com/bueste/ioBroker.goodwe-sems/tarball/main
|
|
60
|
-
iobroker add goodwe-sems
|
|
62
|
+
iobroker url iobroker.goodwe-sems
|
|
61
63
|
```
|
|
62
64
|
|
|
63
|
-
##
|
|
65
|
+
## Configuration
|
|
64
66
|
|
|
65
|
-
|
|
|
67
|
+
| Field | Description |
|
|
66
68
|
|---|---|
|
|
67
|
-
| SEMS
|
|
68
|
-
|
|
|
69
|
-
| Poll
|
|
70
|
-
| Pushover |
|
|
69
|
+
| SEMS account / password | Same credentials as at semsportal.com. The password is stored encrypted by ioBroker. |
|
|
70
|
+
| Plant ID (optional) | Leave empty for automatic detection (`GetPowerStationIdByOwner`). For accounts with several plants: copy the ID manually from the portal URL (`.../powerstation/powerstatussnmin/<ID>`). |
|
|
71
|
+
| Poll interval | Default 300 s. The adapter enforces a minimum of 60 s regardless of configuration. |
|
|
72
|
+
| Pushover | See [Pushover notifications](#pushover-notifications). |
|
|
71
73
|
|
|
72
|
-
##
|
|
74
|
+
## Object/state structure
|
|
73
75
|
|
|
74
76
|
```
|
|
75
|
-
goodwe-sems.0.info.connection SEMS Portal
|
|
76
|
-
goodwe-sems.0.info.lastSuccess
|
|
77
|
-
goodwe-sems.0.info.lastError
|
|
78
|
-
goodwe-sems.0.info.consecutiveErrors
|
|
79
|
-
goodwe-sems.0.info.rateLimited SEMS Portal
|
|
80
|
-
goodwe-sems.0.info.activePollInterval
|
|
81
|
-
goodwe-sems.0.info.rawResponse
|
|
77
|
+
goodwe-sems.0.info.connection SEMS Portal reachable (bool)
|
|
78
|
+
goodwe-sems.0.info.lastSuccess Timestamp of the last successful poll
|
|
79
|
+
goodwe-sems.0.info.lastError Last error message
|
|
80
|
+
goodwe-sems.0.info.consecutiveErrors Number of consecutive failed attempts
|
|
81
|
+
goodwe-sems.0.info.rateLimited SEMS Portal is currently rate-limiting (bool)
|
|
82
|
+
goodwe-sems.0.info.activePollInterval Currently effective interval incl. backoff (s)
|
|
83
|
+
goodwe-sems.0.info.rawResponse Raw JSON response (only when the debug option is enabled)
|
|
82
84
|
|
|
83
85
|
goodwe-sems.0.Station.Name / .Capacity / .Address / .Latitude / .Longitude / .PortalTimestamp / .Status / .StationId
|
|
84
86
|
goodwe-sems.0.KPI.CurrentPower / .TodayGeneration / .MonthGeneration / .TotalGeneration / .TodayIncome / .TotalIncome / .Currency
|
|
85
87
|
goodwe-sems.0.PowerFlow.PV / .Load / .Grid / .Battery / .LoadStatus / .GridStatus / .PvStatus / .BatteryStatus
|
|
86
88
|
goodwe-sems.0.Battery.SOC / .Status
|
|
87
|
-
goodwe-sems.0.EVCharger.* (
|
|
89
|
+
goodwe-sems.0.EVCharger.* (only if reported by the portal)
|
|
88
90
|
|
|
89
|
-
goodwe-sems.0.Inverters.<
|
|
90
|
-
goodwe-sems.0.Inverters.<
|
|
91
|
-
goodwe-sems.0.Inverters.<
|
|
92
|
-
goodwe-sems.0.Inverters.<
|
|
93
|
-
goodwe-sems.0.Inverters.<
|
|
91
|
+
goodwe-sems.0.Inverters.<serial>.Name / .Model / .Status / .WarningCode
|
|
92
|
+
goodwe-sems.0.Inverters.<serial>.CurrentPower / .TodayGeneration / .TotalGeneration / .Temperature
|
|
93
|
+
goodwe-sems.0.Inverters.<serial>.PV1..4.Voltage / .Current
|
|
94
|
+
goodwe-sems.0.Inverters.<serial>.AC_L1..3.Voltage / .Current / .Frequency
|
|
95
|
+
goodwe-sems.0.Inverters.<serial>.Battery.SOC / .Voltage / .Current
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
|
|
98
|
+
With two inverters (as in the original requirement this adapter was built for), two `Inverters.<serial>.*` branches are created automatically - the number is not hardcoded, it is driven entirely by what the portal returns for the configured account.
|
|
97
99
|
|
|
98
|
-
|
|
100
|
+
Fields that the portal delivers but this adapter does not (yet) know about are not lost: with the debug option enabled, the full raw response ends up in `info.rawResponse` (JSON), so it can be inspected and added via PR if needed.
|
|
99
101
|
|
|
100
|
-
##
|
|
102
|
+
## Error handling, backoff and rate limits
|
|
101
103
|
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
- **Rate
|
|
105
|
-
- **Login
|
|
106
|
-
- **
|
|
107
|
-
-
|
|
108
|
-
-
|
|
104
|
+
- Every poll cycle is fully wrapped in try/catch; a single failure can never permanently stop the polling loop.
|
|
105
|
+
- Dedicated error classes (`SemsAuthError`, `SemsRateLimitError`, `SemsNetworkError`, `SemsProtocolError`) drive targeted behaviour:
|
|
106
|
+
- **Rate limit (`GY0429`)** -> immediate pause (default 300 s), `info.rateLimited = true`.
|
|
107
|
+
- **Login failure** -> exponential backoff (capped at 1 h) so that wrong credentials do not put additional strain on the account.
|
|
108
|
+
- **Network/protocol errors** -> moderate backoff.
|
|
109
|
+
- After a configurable number of consecutive failures (default 3), the plant is considered "offline" and, if enabled, a Pushover notification is triggered.
|
|
110
|
+
- Everything is additionally written to the ioBroker log in a structured way (`error`/`warn`/`debug` depending on severity).
|
|
109
111
|
|
|
110
|
-
## Pushover
|
|
112
|
+
## Pushover notifications
|
|
111
113
|
|
|
112
|
-
|
|
114
|
+
Configurable in three modes:
|
|
113
115
|
|
|
114
|
-
1.
|
|
115
|
-
2. **
|
|
116
|
-
3. **
|
|
116
|
+
1. **Via an existing `ioBroker.pushover` instance** (`sendTo`) - recommended, no duplicate credential management.
|
|
117
|
+
2. **Directly via the Pushover API** (your own user key + API/app token, stored encrypted) - also works without a separate Pushover instance.
|
|
118
|
+
3. **Both at the same time.**
|
|
117
119
|
|
|
118
|
-
|
|
120
|
+
Triggered on: SEMS login failure, SEMS rate limit, a prolonged outage, unexpected adapter error - each individually toggleable. An internal cool-down (default 1 h per category) prevents spam during ongoing issues.
|
|
119
121
|
|
|
120
|
-
##
|
|
122
|
+
## Security & privacy
|
|
121
123
|
|
|
122
|
-
- SEMS
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
-
|
|
124
|
+
- The SEMS password and the Pushover API token are marked as `encryptedNative`/`protectedNative` at the root of `io-package.json` and are stored encrypted by ioBroker, never logged in plain text (the account name is masked in log messages, e.g. `st***@gmail.com`).
|
|
125
|
+
- The adapter performs **read-only** access only (`GetMonitorDetailByPowerstationId`, `GetPowerStationIdByOwner`). There is deliberately **no** remote-control/write function (`SaveRemoteControlInverter`) - that would be a considerably larger security and liability risk and was not part of the requirement.
|
|
126
|
+
- No third-party dependency for HTTP access: the built-in `fetch` of Node.js >=20 is used instead of an additional HTTP library - a smaller attack surface, less supply-chain risk.
|
|
127
|
+
- The API base URL returned by the login response is validated (HTTPS on GoodWe-owned domains only) before any further request uses it, so a manipulated login response cannot redirect the session token to a foreign host.
|
|
128
|
+
- All network errors are caught in a typed way; no unchecked data from the API response is ever executed (`eval`, `Function`, and similar are not used anywhere).
|
|
126
129
|
|
|
127
|
-
##
|
|
130
|
+
## Development
|
|
128
131
|
|
|
129
132
|
```
|
|
130
133
|
npm install
|
|
131
134
|
npm run lint
|
|
132
|
-
npm test #
|
|
135
|
+
npm test # unit tests (lib/mapping.js, lib/semsApi.js, lib/notify.js) + package consistency check
|
|
133
136
|
```
|
|
134
137
|
|
|
135
|
-
|
|
138
|
+
Recommended additionally before every release:
|
|
136
139
|
|
|
137
140
|
```
|
|
138
|
-
npx @iobroker/
|
|
141
|
+
npx @iobroker/repochecker@latest .
|
|
139
142
|
```
|
|
140
143
|
|
|
141
|
-
Pull
|
|
144
|
+
Pull requests are welcome, especially to add further fields delivered by the portal (see `info.rawResponse` with the debug option enabled) or to improve translations.
|
|
142
145
|
|
|
143
146
|
## Changelog
|
|
144
147
|
|
|
148
|
+
<!--
|
|
149
|
+
Placeholder for the next version (at the beginning of the line):
|
|
150
|
+
### **WORK IN PROGRESS**
|
|
151
|
+
-->
|
|
145
152
|
### **WORK IN PROGRESS**
|
|
146
153
|
|
|
154
|
+
### 0.1.9 (2026-07-19)
|
|
155
|
+
|
|
156
|
+
Addressed the stricter automated `@iobroker/repochecker` findings surfaced on the `ioBroker.repositories` listing PR:
|
|
157
|
+
|
|
158
|
+
- (Stefan Bühler) **[E1057]** moved `encryptedNative`/`protectedNative` from `common` to the `io-package.json` root, matching the current schema
|
|
159
|
+
- (Stefan Bühler) **[E3009]/[E3010]/[E3011]/[E3012]** raised `engines.node` to `>=20`, `@iobroker/adapter-core` to `^3.4.1`, `js-controller` dependency to `>=6.0.11`, `admin` globalDependency to `>=7.6.20`
|
|
160
|
+
- (Stefan Bühler) **[E3040]** updated devDependencies (`@iobroker/adapter-dev`, `@iobroker/testing`, mocha, esbuild and others) to current major versions
|
|
161
|
+
- (Stefan Bühler) **[E3000-series]** rewrote `.github/workflows/test-and-release.yml` to the current official template: renamed jobs (`check-and-lint`, `adapter-tests`, `adapter-check`, `deploy`), full OS/Node test matrix (ubuntu/windows/macos x 20/22/24), `concurrency` group, deploy job pinned to Node 24
|
|
162
|
+
- (Stefan Bühler) **[E5005]** replaced global `setTimeout`/`clearTimeout` with adapter-managed timers (`adapter.setTimeout`/`adapter.clearTimeout`) in `lib/notify.js` and `lib/semsApi.js`
|
|
163
|
+
- (Stefan Bühler) **[E5043]** switched to `require("node:crypto")`
|
|
164
|
+
- (Stefan Bühler) **[E5507]/[E5510]/[E5512]/[E5612]** fixed `admin/jsonConfig.json`: added missing `lg`/`xl` responsive sizes on every item, replaced a literal label string with a proper i18n key (`loginTab`, added to all 11 translation files)
|
|
165
|
+
- (Stefan Bühler) **[E6004]/[E6015]/[W0037]/[W0076]** translated `README.md` to English (required language), moved the previous German content to `README.de.md`, added `CHANGELOG_OLD.md` for older entries
|
|
166
|
+
- (Stefan Bühler) **[W9501]** removed the redundant `.npmignore` (superseded by package.json `files`)
|
|
167
|
+
- (Stefan Bühler) **[E9006]** added `.commitinfo` to `.gitignore`
|
|
168
|
+
- (Stefan Bühler) **[S4036]/[S5026]** added `prettier.config.mjs`, re-formatted the codebase, disabled `jsdoc/reject-any-type` for the opaque Node timer-handle type with a justifying comment
|
|
169
|
+
|
|
147
170
|
### 0.1.8 (2026-07-19)
|
|
148
171
|
|
|
149
|
-
ioBroker-
|
|
172
|
+
Addressed ioBroker adapter-check findings:
|
|
150
173
|
|
|
151
|
-
- (Stefan Bühler) **[E254]**
|
|
152
|
-
- (Stefan Bühler) **[W132]**
|
|
153
|
-
- (Stefan Bühler) **[W184]**
|
|
154
|
-
- (Stefan Bühler) **[W034]** `@iobroker/adapter-core`
|
|
155
|
-
- (Stefan Bühler) **[W173]/[W174]/[E999]/[W401]**: `password`
|
|
174
|
+
- (Stefan Bühler) **[E254]** removed changelog entries for 0.1.1/0.1.2 - those tags were pushed but their npm-publish CI job failed at the time (missing `NPM_TOKEN` / npm CLI too old for OIDC), so the versions never existed on npm
|
|
175
|
+
- (Stefan Bühler) **[W132]** this automatically brought the entry count under the repository builder's 7-entry truncation limit for `common.news`
|
|
176
|
+
- (Stefan Bühler) **[W184]** removed deprecated `common.title` (superseded by `common.titleLang`) and deprecated/ignored `common.main` (the entry point comes from `package.json`)
|
|
177
|
+
- (Stefan Bühler) **[W034]** raised `@iobroker/adapter-core` from ^3.1.6 to ^3.2.2
|
|
178
|
+
- (Stefan Bühler) **[W173]/[W174]/[E999]/[W401]**: `password` was already correctly listed in `encryptedNative`/`protectedNative` (verified against the published tarball) - these findings, together with the global axios 404 when fetching `sources-dist-latest.json`, are side effects of the adapter not yet being listed in the official ioBroker repository
|
|
156
179
|
|
|
157
180
|
### 0.1.7 (2026-07-19)
|
|
158
181
|
|
|
159
|
-
- (Stefan Bühler)
|
|
182
|
+
- (Stefan Bühler) branding: replaced the placeholder icon with the official GoodWe logo (used with permission from GoodWe)
|
|
160
183
|
|
|
161
184
|
### 0.1.6 (2026-07-18)
|
|
162
185
|
|
|
163
|
-
- (Stefan Bühler)
|
|
186
|
+
- (Stefan Bühler) updated the dev toolchain: mocha 11, sinon 22, @alcalzone/release-script 5, @iobroker/eslint-config 2; remaining transitive CVEs (adm-zip, diff, esbuild, serialize-javascript) resolved via npm `overrides` - `npm audit`: 0 vulnerabilities (including dev dependencies)
|
|
164
187
|
|
|
165
|
-
|
|
188
|
+
Security/quality audit (security tester, maintainer review, fuzzing of the mapping layer):
|
|
166
189
|
|
|
167
|
-
- (Stefan Bühler) **Security:**
|
|
168
|
-
- (Stefan Bühler) **Security:**
|
|
169
|
-
- (Stefan Bühler) **Fix:** `null`/
|
|
170
|
-
- (Stefan Bühler) **Fix:**
|
|
171
|
-
- (Stefan Bühler) **Fix:**
|
|
172
|
-
- (Stefan Bühler) **Fix:**
|
|
173
|
-
- (Stefan Bühler) **
|
|
174
|
-
- (Stefan Bühler) 14
|
|
190
|
+
- (Stefan Bühler) **Security:** inverter serial numbers from the (untrusted) portal response are sanitized before becoming part of ioBroker object IDs (prevents broken/unexpectedly nested object trees caused by special characters such as `.` `*` `]`)
|
|
191
|
+
- (Stefan Bühler) **Security:** the API base URL returned by the login server is validated - HTTPS on GoodWe-owned domains only (`*.semsportal.com`, `*.goodwe.com`), otherwise falls back to the known regional URL. A manipulated login response can no longer redirect the session token to a foreign host
|
|
192
|
+
- (Stefan Bühler) **Fix:** `null`/broken entries in the portal's `inverter[]` array crashed the entire poll cycle - now skipped, healthy inverters from the same response are still processed
|
|
193
|
+
- (Stefan Bühler) **Fix:** numbers in scientific notation (`"1e5"`) were parsed incorrectly (yielded 15 instead of 100000)
|
|
194
|
+
- (Stefan Bühler) **Fix:** obviously invalid portal timestamps (`99/99/9999 ...`) produced absurd epoch values via JavaScript's `Date` rollover behaviour - now rejected
|
|
195
|
+
- (Stefan Bühler) **Fix:** automatic plant discovery now filters out entries without a usable ID (previously caused permanently failing poll cycles)
|
|
196
|
+
- (Stefan Bühler) **Robustness:** no more state writes after adapter unload; the `adapterError` notification dedupe window is also reset after recovery
|
|
197
|
+
- (Stefan Bühler) 14 new regression tests (42 unit tests in total); `npm audit`: 0 vulnerabilities in production dependencies (remaining findings were dev-toolchain only)
|
|
175
198
|
|
|
176
199
|
### 0.1.5 (2026-07-18)
|
|
177
200
|
|
|
178
|
-
- (Stefan Bühler) fix: PayPal
|
|
179
|
-
|
|
180
|
-
### 0.1.4 (2026-07-18)
|
|
181
|
-
|
|
182
|
-
- (Stefan Bühler) docs: PayPal-Spendenlink im README ergänzt
|
|
183
|
-
|
|
184
|
-
### 0.1.3 (2026-07-18)
|
|
185
|
-
|
|
186
|
-
- (Stefan Bühler) CI: OIDC Trusted Publishing repariert - Node 22 Runner bringt npm 10.9.x mit, was unter der für Trusted Publishing benötigten Version (>=11.5.1) liegt. Release-Job aktualisiert npm jetzt explizit vor `npm publish`.
|
|
187
|
-
|
|
188
|
-
### 0.1.2 (2026-07-18)
|
|
189
|
-
|
|
190
|
-
- (Stefan Bühler) CI: npm-Publish von langlebigem `NPM_TOKEN` auf OIDC Trusted Publishing umgestellt (kein Secret mehr im Repo nötig)
|
|
191
|
-
|
|
192
|
-
### 0.1.1 (2026-07-18)
|
|
193
|
-
|
|
194
|
-
- (Stefan Bühler) fix `repository.url` field format in package.json (removed npm-publish normalization warning)
|
|
195
|
-
|
|
196
|
-
### 0.1.0 (2026-07-18)
|
|
201
|
+
- (Stefan Bühler) fix: corrected the PayPal donation link in the README (button link instead of the old donate link)
|
|
197
202
|
|
|
198
|
-
|
|
203
|
+
Older changelog entries can be found in [CHANGELOG_OLD.md](CHANGELOG_OLD.md).
|
|
199
204
|
|
|
200
|
-
##
|
|
205
|
+
## License
|
|
201
206
|
|
|
202
207
|
MIT License
|
|
203
208
|
|