Просмотр исходного кода

v2.3.1: fix prod AppID, server URL, and MD5-hash password on login

- const.py: IOT_APP_ID rBrTRfAPXz (prod) + API_AUTH_BASE_URL -> solar.siseli.com
- api.py: send MD5(password) instead of plaintext in login payload
- manifest.json: version bumped to 2.3.1
- CHANGELOG.md: document all three fixes

Root-cause analysis:
  v2.3.0 used JO4DAiNeys (test AppID) against test.solar.siseli.com, causing
  code 20007 'account error' for every real user. Correct combo is
  rBrTRfAPXz + solar.siseli.com. Additionally the portal sends the password
  as MD5(plaintext) hex; sending plaintext yields code 7 'password error'.
conexocasa 7 месяцев назад
Родитель
Сommit
757e24297c
4 измененных файлов с 36 добавлено и 8 удалено
  1. 19 0
      CHANGELOG.md
  2. 8 2
      api.py
  3. 8 5
      const.py
  4. 1 1
      manifest.json

+ 19 - 0
CHANGELOG.md

@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [2.3.1] - 2026-03-07
+
+### Fixed
+- **Correct production AppID and server** -- the IOT Open Platform AppID has been
+  corrected to `rBrTRfAPXz` (production) targeting `https://solar.siseli.com`.
+  The previous release used the test-environment AppID (`JO4DAiNeys`) which was
+  only accepted by `test.solar.siseli.com`, causing "account error" (code 20007)
+  for every real user account.
+- **Password is now MD5-hashed before transmission** -- the Siseli portal sends
+  `MD5(plaintext_password)` (lowercase hex) in the login payload. Sending plain
+  text returned code 7 "password error" even with valid credentials. The login
+  method now applies `hashlib.md5(password).hexdigest()` before building the
+  JSON body, matching the portal behaviour confirmed by live API testing.
+
+### Changed
+- `API_AUTH_BASE_URL` changed from `https://test.solar.siseli.com` to `https://solar.siseli.com`.
+- `IOT_APP_ID` changed from `JO4DAiNeys` to `rBrTRfAPXz`.
+- `IOT_APP_SECRET_ENC` updated to the production-AppID encrypted secret.
+
 ## [2.3.0] - 2026-03-07
 
 ### Changed

+ 8 - 2
api.py

@@ -296,7 +296,10 @@ class SolarOfThingsAPI:
 
         Uses the IOT Open Platform signed request format discovered from the
         portal JS bundle.  The login endpoint is:
-          POST https://test.solar.siseli.com/apis/login/account
+          POST https://solar.siseli.com/apis/login/account
+
+        The password is sent as MD5(plaintext_password) lowercase hex — this
+        is how the portal processes it before transmitting.
 
         Raises AuthenticationError on bad credentials, or requests.RequestException
         on network failure.  Safe to call from a background thread.
@@ -307,9 +310,12 @@ class SolarOfThingsAPI:
         _LOGGER.debug("SolarOfThings: logging in as %s", self._user_id)
 
         import json as _json
+        # The portal sends the password as MD5(plaintext_password) lowercase hex.
+        # Sending plaintext returns code 7 "password error" even with valid creds.
+        password_md5 = hashlib.md5(self._password.encode("utf-8")).hexdigest()
         payload = {
             "account": self._user_id,
-            "password": self._password,
+            "password": password_md5,
         }
         body_bytes = _json.dumps(payload, separators=(",", ":")).encode("utf-8")
 

+ 8 - 5
const.py

@@ -18,10 +18,11 @@ CONF_ACCESS_TOKEN_EXPIRES = "access_token_expires"   # ISO-8601 string
 CONF_REFRESH_TOKEN_EXPIRES = "refresh_token_expires" # ISO-8601 string
 
 # ─── API bases ─────────────────────────────────────────────────────────────────
-# The portal JS bundle uses test.solar.siseli.com for auth/login endpoints
-# and solar.siseli.com for data (device-state, station) endpoints.
+# Both auth and data endpoints live on the production server solar.siseli.com.
+# The portal JS bundle embeds both test/prod AppIDs; AppID rBrTRfAPXz is the
+# one accepted by solar.siseli.com (confirmed by live API testing 2026-03-07).
 API_BASE_URL        = "https://solar.siseli.com"         # data endpoints
-API_AUTH_BASE_URL   = "https://test.solar.siseli.com"    # auth / login endpoints
+API_AUTH_BASE_URL   = "https://solar.siseli.com"         # auth / login endpoints
 
 # ─── Auth endpoints (discovered from portal JS bundle) ─────────────────────────
 # The login endpoint requires IOT-Open-AppID signing (see api.py _sign_request).
@@ -29,8 +30,10 @@ API_LOGIN           = "/apis/login/account"              # POST + signed headers
 API_REFRESH_TOKEN   = "/login/refresh/access/token"      # POST, no token needed
 
 # ─── IOT Open Platform app credentials (embedded in portal umi.js) ────────────
-IOT_APP_ID          = "JO4DAiNeys"
-IOT_APP_SECRET_ENC  = "VK51roUwyT4CJGcDWRp17WMhzX4F702I10fWc0FnC6A="
+# rBrTRfAPXz is the production AppID accepted by solar.siseli.com.
+# JO4DAiNeys is the test AppID (accepted only by test.solar.siseli.com).
+IOT_APP_ID          = "rBrTRfAPXz"
+IOT_APP_SECRET_ENC  = "I4D0KRr2339z3pQ/at91V9BpFAOe54DaTafwSm6suIQ="
 
 # ─── Data endpoints ────────────────────────────────────────────────────────────
 API_TIME_SERIES    = "/apis/deviceState/simple/attribute/keys/history/v1"

+ 1 - 1
manifest.json

@@ -9,5 +9,5 @@
   "requirements": [
     "pycryptodome>=3.9.0"
   ],
-  "version": "2.3.0"
+  "version": "2.3.1"
 }