iobroker.goodwe-sems 0.1.16 → 0.1.17

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 CHANGED
@@ -151,6 +151,12 @@ Pull Requests willkommen, insbesondere um zusätzliche, vom Portal gelieferte Fe
151
151
  -->
152
152
  ### **WORK IN PROGRESS**
153
153
 
154
+ ### 0.1.17 (2026-07-19)
155
+
156
+ - (Stefan Bühler) Fix: Der SEMS+-Login schlug für manche Konten fehl (`code=C0602 "account_login_abnormal"`), weil der Adapter den globalen Endpunkt (`semsplus.goodwe.com`) statt des EU-regionalen (`eu-semsplus.goodwe.com`) aufrief. Bestätigt durch einen echten Browser-HAR-Mitschnitt: derselbe Request-Body und Passwort-Hash war gegen den regionalen Host erfolgreich. Bewusst **ohne** Host-Fallback-Schleife umgesetzt - mehrere Login-Versuche mit denselben Zugangsdaten gegen verschiedene Hosts sehen für das Backend wie Credential-Stuffing aus und riskieren eine echte Kontosperre
157
+ - (Stefan Bühler) Der Login-Request sendet jetzt zusätzlich den `x-signature`-Header (exakt wie im echten Browser-Traffic), und ein echtes SEMS+-Session-Token wird von der in 0.1.16 eingeführten Gateway-API jetzt korrekt akzeptiert - zuvor bekam der Gateway-Fallback nur ein aus dem Legacy-CrossLogin abgeleitetes Token, das vom Gateway mit demselben C0602-Fehler abgelehnt wurde, da es keine echte SEMS+-Session ist
158
+ - (Stefan Bühler) 1 aktualisierter Regressionstest, der die exakte Login-URL und das Vorhandensein des Signatur-Headers beim Login prüft
159
+
154
160
  ### 0.1.16 (2026-07-19)
155
161
 
156
162
  - (Stefan Bühler) Großer Fund: Manche Konten, deren SEMS+-Login abgelehnt wird und die auf die Legacy-CrossLogin-API zurückfallen, landen gar nicht auf dem klassischen `semsportal.com`-artigen Backend - sie bekommen eine Session für eine komplett andere, moderne Microservice-API ("SEMS+-Gateway", `eu-gateway.semsportal.com`). Das erklärt, warum `GetMonitorDetailByPowerstationId` unter keinem der in 0.1.14/0.1.15 versuchten Pfade (`v1`/`v2`/`v3`) je funktionieren konnte. Bestätigt durch einen echten Browser-HAR-Mitschnitt (`eu-semsplus.goodwe.com`), der die tatsächlich genutzten Endpunkte zeigt (`sems-plant/api/stations/...`, `sems-plant/api/equipments/<sn>/telemetry` usw.)
package/README.md CHANGED
@@ -151,6 +151,12 @@ Pull requests are welcome, especially to add further fields delivered by the por
151
151
  -->
152
152
  ### **WORK IN PROGRESS**
153
153
 
154
+ ### 0.1.17 (2026-07-19)
155
+
156
+ - (Stefan Bühler) fix: SEMS+ login failed for some accounts (`code=C0602 "account_login_abnormal"`) because the adapter called the global endpoint (`semsplus.goodwe.com`) instead of the EU-regional one (`eu-semsplus.goodwe.com`). Confirmed via a real browser HAR capture: the identical request body and password hash succeeded against the regional host. Deliberately implemented **without** a host-fallback loop - repeatedly retrying the same credentials against multiple hosts looks like credential stuffing to the backend and risks a real account lockout
157
+ - (Stefan Bühler) the login request now also sends the `x-signature` header (matching real browser traffic exactly), and a genuine SEMS+ session token is now correctly accepted by the gateway API introduced in 0.1.16 - previously, the gateway fallback only ever received a Legacy-CrossLogin-derived token, which the gateway rejected with the same C0602 error since it isn't a real SEMS+ session
158
+ - (Stefan Bühler) 1 updated regression test verifying the exact login URL and the presence of the login-time signature header
159
+
154
160
  ### 0.1.16 (2026-07-19)
155
161
 
156
162
  - (Stefan Bühler) major finding: some accounts whose SEMS+ login is rejected and fall back to the legacy CrossLogin API do not end up on the classic `semsportal.com`-style backend at all - they get a session for a completely different, modern microservice API ("SEMS+ gateway", `eu-gateway.semsportal.com`), which explains why `GetMonitorDetailByPowerstationId` could never succeed under any of the `v1`/`v2`/`v3` paths tried in 0.1.14/0.1.15. Confirmed via a real account's browser HAR capture (`eu-semsplus.goodwe.com`) showing the actual endpoints in use (`sems-plant/api/stations/...`, `sems-plant/api/equipments/<sn>/telemetry`, etc.)
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "goodwe-sems",
4
- "version": "0.1.16",
4
+ "version": "0.1.17",
5
5
  "news": {
6
+ "0.1.17": {
7
+ "de": "Fix: Der SEMS+-Login schlug für manche Konten fehl (code=C0602 'account_login_abnormal'), weil der Adapter den globalen Endpunkt (semsplus.goodwe.com) statt des EU-regionalen (eu-semsplus.goodwe.com) nutzte - bestätigt durch einen echten Browser-Mitschnitt mit identischem Request/Passwort-Hash, der gegen den regionalen Host erfolgreich war. Bewusst OHNE Host-Fallback-Schleife umgesetzt: mehrere Login-Versuche mit denselben Zugangsdaten gegen verschiedene Hosts sehen wie Credential-Stuffing aus und könnten zu einer Kontosperre führen.",
8
+ "en": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout.",
9
+ "ru": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout.",
10
+ "pt": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout.",
11
+ "nl": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout.",
12
+ "fr": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout.",
13
+ "it": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout.",
14
+ "es": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout.",
15
+ "pl": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout.",
16
+ "zh-cn": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout.",
17
+ "uk": "Fix: SEMS+ login failed for some accounts (code=C0602 'account_login_abnormal') because the adapter used the global endpoint (semsplus.goodwe.com) instead of the EU-regional one (eu-semsplus.goodwe.com) - confirmed via a real browser capture with the identical request/password hash succeeding against the regional host. Deliberately implemented WITHOUT a host-fallback loop: retrying the same credentials against multiple hosts looks like credential stuffing and risks a real account lockout."
18
+ },
6
19
  "0.1.16": {
7
20
  "de": "Großer Fund: Konten, deren SEMS+-Login abgelehnt wird und die auf die Legacy-CrossLogin-API zurückfallen, landen teils auf einem komplett anderen, modernen SEMS+-Gateway-Backend (eu-gateway.semsportal.com) statt dem klassischen semsportal.com - das erklärt, warum v1/v2/v3 GetMonitorDetailByPowerstationId dort NIE funktionieren konnten. Der Adapter spricht jetzt zusätzlich dieses Gateway-Backend (eigene Endpunkte, eigenes Antwortformat, inkl. der zur Authentifizierung nötigen Request-Signatur) und nutzt es automatisch als letzten Fallback, wenn alle klassischen Pfade fehlschlagen.",
8
21
  "en": "Major finding: accounts whose SEMS+ login is rejected and fall back to the legacy CrossLogin API sometimes end up on a completely different, modern SEMS+ gateway backend (eu-gateway.semsportal.com) instead of the classic semsportal.com one - explaining why v1/v2/v3 GetMonitorDetailByPowerstationId could never work there. The adapter now also speaks this gateway backend (its own endpoints, response format, and required request signature) and automatically uses it as a last-resort fallback when all classic paths fail.",
@@ -80,19 +93,6 @@
80
93
  "pl": "naprawiono uszkodzone CI: usunięto Node.js 20.x z macierzy adapter-tests - niekompatybilne z engines.node >=22 (wprowadzone w 0.1.10)",
81
94
  "uk": "виправлено зламаний CI: видалено Node.js 20.x з матриці adapter-tests - несумісний з engines.node >=22 (введено в 0.1.10)",
82
95
  "zh-cn": "修复损坏的 CI:从 adapter-tests 矩阵中移除 Node.js 20.x - 与 engines.node >=22(0.1.10 中引入)不兼容"
83
- },
84
- "0.1.9": {
85
- "en": "address stricter ioBroker.repositories PR check: move encryptedNative/protectedNative to io-package.json root, update deps (adapter-core, testing, adapter-dev, admin, js-controller), rewrite CI workflow (check-and-lint/adapter-tests/deploy jobs, concurrency, full OS+Node matrix), use adapter-managed timers, node:crypto, fix jsonConfig responsive sizes + i18n key, translate README to English (required), add CHANGELOG_OLD.md",
86
- "de": "strengere ioBroker.repositories-PR-Prüfung behoben: encryptedNative/protectedNative auf io-package.json-Root verschoben, Abhängigkeiten aktualisiert (adapter-core, testing, adapter-dev, admin, js-controller), CI-Workflow neu geschrieben (check-and-lint/adapter-tests/deploy Jobs, Concurrency, volle OS+Node-Matrix), adapter-verwaltete Timer, node:crypto, jsonConfig Responsive-Größen + i18n-Key behoben, README auf Englisch übersetzt (Pflicht), CHANGELOG_OLD.md ergänzt",
87
- "ru": "устранены более строгие проверки PR ioBroker.repositories: encryptedNative/protectedNative перемещены в корень io-package.json, обновлены зависимости, переписан CI, README переведён на английский (обязательно)",
88
- "pt": "corrigidas verificações mais rigorosas do PR do ioBroker.repositories: encryptedNative/protectedNative movidos para a raiz do io-package.json, dependências atualizadas, CI reescrito, README traduzido para inglês (obrigatório)",
89
- "nl": "strengere ioBroker.repositories-PR-controles opgelost: encryptedNative/protectedNative naar de root van io-package.json verplaatst, dependencies bijgewerkt, CI herschreven, README naar het Engels vertaald (verplicht)",
90
- "fr": "correction des vérifications plus strictes du PR ioBroker.repositories : encryptedNative/protectedNative déplacés à la racine de io-package.json, dépendances mises à jour, CI réécrit, README traduit en anglais (obligatoire)",
91
- "it": "risolti controlli più severi del PR ioBroker.repositories: encryptedNative/protectedNative spostati alla radice di io-package.json, dipendenze aggiornate, CI riscritta, README tradotto in inglese (obbligatorio)",
92
- "es": "se resolvieron comprobaciones más estrictas del PR de ioBroker.repositories: encryptedNative/protectedNative movidos a la raíz de io-package.json, dependencias actualizadas, CI reescrita, README traducido al inglés (obligatorio)",
93
- "pl": "rozwiązano bardziej rygorystyczne kontrole PR ioBroker.repositories: encryptedNative/protectedNative przeniesiono do katalogu głównego io-package.json, zaktualizowano zależności, przepisano CI, README przetłumaczono na angielski (wymagane)",
94
- "uk": "усунено суворіші перевірки PR ioBroker.repositories: encryptedNative/protectedNative перенесено в корінь io-package.json, оновлено залежності, переписано CI, README перекладено англійською (обов'язково)",
95
- "zh-cn": "解决更严格的 ioBroker.repositories PR 检查:将 encryptedNative/protectedNative 移至 io-package.json 根目录,更新依赖项,重写 CI,将 README 翻译为英文(必需)"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/lib/semsApi.js CHANGED
@@ -29,8 +29,15 @@ const { SemsAuthError, SemsRateLimitError, SemsNetworkError, SemsProtocolError }
29
29
  */
30
30
 
31
31
  // "New" SEMS+ login, in use since ~2024. Needs an MD5(password) hash,
32
- // base64-encoded, plus a few boilerplate flags.
33
- const NEW_LOGIN_URL = "https://semsplus.goodwe.com/web/sems/sems-user/api/v1/auth/cross-login";
32
+ // base64-encoded, plus a few boilerplate flags. Uses the EU-regional host
33
+ // (not the global "semsplus.goodwe.com" one) - confirmed via a real
34
+ // account's browser HAR capture that the global host rejects valid
35
+ // credentials with "C0602 account_login_abnormal" while the identical
36
+ // request against the regional host succeeds (code 00000). Deliberately
37
+ // NOT tried against multiple hosts/variants: repeatedly hitting a login
38
+ // endpoint with the same credentials from different hosts looks like
39
+ // credential-stuffing and risks a real account lockout.
40
+ const NEW_LOGIN_URL = "https://eu-semsplus.goodwe.com/web/sems/sems-user/api/v1/auth/cross-login";
34
41
  // Legacy login, kept as a fallback for accounts / regions where the new
35
42
  // endpoint is unavailable or rejects the credentials.
36
43
  const LEGACY_LOGIN_URL = "https://www.semsportal.com/api/v3/Common/CrossLogin";
@@ -263,12 +270,19 @@ class SemsApi {
263
270
  isChinese: false,
264
271
  isLocal: false,
265
272
  });
273
+ // The real web app sends an x-signature header even on the login
274
+ // call itself (with empty uid/token, since no session exists yet).
275
+ // Harmless to include and matches observed real traffic exactly.
276
+ const headers = {
277
+ ...this._defaultHeaders(),
278
+ "x-signature": this._gatewaySignature("", ""),
279
+ };
266
280
 
267
281
  const { json } = await httpPostJson(
268
282
  NEW_LOGIN_URL,
269
283
  {
270
284
  method: "POST",
271
- headers: this._defaultHeaders(),
285
+ headers,
272
286
  body,
273
287
  timeoutMs: this.requestTimeoutMs,
274
288
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.goodwe-sems",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "ioBroker adapter to read GoodWe inverter data from the SEMS Portal cloud API (for installations without local/LAN access to the inverter).",
5
5
  "author": {
6
6
  "name": "bueste",