const.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. """Constants for the Solar of Things integration."""
  2. DOMAIN = "solar_of_things"
  3. # ─── Configuration keys ────────────────────────────────────────────────────────
  4. CONF_IOT_TOKEN = "iot_token" # legacy / advanced manual entry
  5. CONF_STATION_ID = "station_id"
  6. CONF_DEVICE_ID = "device_id"
  7. CONF_TIME_ZONE = "time_zone"
  8. # Credential-based auth (preferred)
  9. CONF_USER_ID = "user_id" # Siseli account / user-ID login (not email)
  10. CONF_PASSWORD = "password"
  11. # Fields that are typed or copy-pasted by hand and routinely arrive with stray
  12. # leading/trailing whitespace. The upstream API treats " 4235…" as a different
  13. # (invalid) value, which surfaces to the user as an unhelpful "cannot connect".
  14. # Consumed by normalise_config_fields() in util.py, which is applied both when
  15. # the config flow accepts input and when an entry is read back at setup, so a
  16. # single list keeps the two paths from drifting.
  17. # CONF_PASSWORD is deliberately absent: whitespace in a password may be
  18. # significant, so it is never trimmed.
  19. WHITESPACE_SENSITIVE_FIELDS = (
  20. CONF_USER_ID,
  21. CONF_STATION_ID,
  22. CONF_DEVICE_ID,
  23. CONF_IOT_TOKEN,
  24. CONF_TIME_ZONE,
  25. )
  26. # Runtime-stored token state (written back to config entry)
  27. CONF_REFRESH_TOKEN = "refresh_token"
  28. CONF_ACCESS_TOKEN_EXPIRES = "access_token_expires" # ISO-8601 string
  29. CONF_REFRESH_TOKEN_EXPIRES = "refresh_token_expires" # ISO-8601 string
  30. # ─── API bases ─────────────────────────────────────────────────────────────────
  31. # Both auth and data endpoints live on the production server solar.siseli.com.
  32. # The portal JS bundle embeds both test/prod AppIDs; AppID rBrTRfAPXz is the
  33. # one accepted by solar.siseli.com (confirmed by live API testing 2026-03-07).
  34. API_BASE_URL = "https://solar.siseli.com" # data endpoints
  35. API_AUTH_BASE_URL = "https://solar.siseli.com" # auth / login endpoints
  36. # ─── Auth endpoints (discovered from portal JS bundle) ─────────────────────────
  37. # The login endpoint requires IOT-Open-AppID signing (see api.py _sign_request).
  38. API_LOGIN = "/apis/login/account" # POST + signed headers
  39. API_REFRESH_TOKEN = "/apis/login/refresh/access/token" # POST, no token needed
  40. # ─── IOT Open Platform app credentials (embedded in portal umi.js) ────────────
  41. # rBrTRfAPXz is the production AppID accepted by solar.siseli.com.
  42. # JO4DAiNeys is the test AppID (accepted only by test.solar.siseli.com).
  43. IOT_APP_ID = "rBrTRfAPXz"
  44. IOT_APP_SECRET_ENC = "I4D0KRr2339z3pQ/at91V9BpFAOe54DaTafwSm6suIQ="
  45. # ─── Data endpoints ────────────────────────────────────────────────────────────
  46. API_TIME_SERIES = "/apis/deviceState/simple/attribute/keys/history/v1"
  47. API_MONTHLY_SUMMARY = "/apis/stationOverView/stateAttributeSummary/category/yearly"
  48. # Remote device config endpoints (discovered 2026-03-07 from live API testing).
  49. # These accept a plain IOT-Token header (no IOT-Open-Sign) and use the device ID
  50. # as a query parameter. Write sends one setting key+value per call.
  51. API_SETTINGS_GET = "/apis/remote/device/configs/cache/get" # ?deviceId=<id>
  52. API_SETTINGS_SET = "/apis/remote/device/config/write" # ?deviceId=<id>
  53. API_DEVICE_LIST = "/apis/device/list"
  54. # ─── Token refresh window ──────────────────────────────────────────────────────
  55. # Refresh the access token this many seconds *before* its stated expiry.
  56. # Mirrors the portal JS which refreshes when ≤300 s remain.
  57. TOKEN_REFRESH_LEAD_SECONDS = 300 # 5 minutes
  58. # ─── Sensor keys ───────────────────────────────────────────────────────────────
  59. SENSOR_KEYS = [
  60. "pvInputPower",
  61. "acOutputActivePower",
  62. "batteryDischargeCurrent",
  63. "batteryChargingCurrent",
  64. "batteryVoltage",
  65. "feedInPower",
  66. "batteryPower",
  67. "batterySOC",
  68. "gridPower",
  69. "loadPower",
  70. ]
  71. SENSOR_DEFINITIONS = {
  72. "pvInputPower": {
  73. "name": "PV Input Power",
  74. "unit": "W",
  75. "device_class": "power",
  76. "icon": "mdi:solar-power",
  77. },
  78. "acOutputActivePower": {
  79. "name": "AC Output Power",
  80. "unit": "W",
  81. "device_class": "power",
  82. "icon": "mdi:power-plug",
  83. },
  84. "batteryDischargeCurrent": {
  85. "name": "Battery Discharge Current",
  86. "unit": "A",
  87. "device_class": "current",
  88. "icon": "mdi:battery-arrow-down",
  89. },
  90. "batteryChargingCurrent": {
  91. "name": "Battery Charging Current",
  92. "unit": "A",
  93. "device_class": "current",
  94. "icon": "mdi:battery-arrow-up",
  95. },
  96. "batteryVoltage": {
  97. "name": "Battery Voltage",
  98. "unit": "V",
  99. "device_class": "voltage",
  100. "icon": "mdi:battery",
  101. },
  102. "batteryPower": {
  103. "name": "Battery Power",
  104. "unit": "W",
  105. "device_class": "power",
  106. "icon": "mdi:battery-charging",
  107. },
  108. "batterySOC": {
  109. "name": "Battery State of Charge",
  110. "unit": "%",
  111. "device_class": "battery",
  112. "icon": "mdi:battery",
  113. },
  114. "feedInPower": {
  115. "name": "Grid Feed-in Power",
  116. "unit": "W",
  117. "device_class": "power",
  118. "icon": "mdi:transmission-tower-export",
  119. },
  120. "gridPower": {
  121. "name": "Grid Import Power",
  122. "unit": "W",
  123. "device_class": "power",
  124. "icon": "mdi:transmission-tower-import",
  125. },
  126. "loadPower": {
  127. "name": "Load Power",
  128. "unit": "W",
  129. "device_class": "power",
  130. "icon": "mdi:home-lightning-bolt",
  131. },
  132. # Monthly summary sensors
  133. "monthly_pv_generated": {
  134. "name": "Monthly PV Generated",
  135. "unit": "kWh",
  136. "device_class": "energy",
  137. "icon": "mdi:solar-power",
  138. },
  139. "monthly_grid_import": {
  140. "name": "Monthly Grid Import",
  141. "unit": "kWh",
  142. "device_class": "energy",
  143. "icon": "mdi:transmission-tower-import",
  144. },
  145. "monthly_total_consumption": {
  146. "name": "Monthly Total Consumption",
  147. "unit": "kWh",
  148. "device_class": "energy",
  149. "icon": "mdi:home-lightning-bolt",
  150. },
  151. "monthly_solar_percentage": {
  152. "name": "Monthly Solar Coverage",
  153. "unit": "%",
  154. "icon": "mdi:percent",
  155. },
  156. }