Skip to main content

sungrow-isolarcloud

PyPI version Python versions License: MIT CI Downloads GitHub Release Buy me a coffee

A maintained fork of the pysolarcloud library for interacting with Sungrow's iSolarCloud API.

Install from PyPI:

pip install sungrow-isolarcloud

This fork adds:

  • Support for requesting additional / custom measure points without modifying the upstream point map (useful for battery charge/discharge power fields that vary by inverter model).
  • A per-device realtime helper for devices such as EV chargers (Plants.async_get_device_realtime), which can optionally fall back to a UserAuth session (pass user_auth=) when the OpenAPI per-device endpoint is unavailable for the account/region — returning the same {uuid: {code: {...}}} shape so callers need no changes. When the endpoint is unavailable and no fallback session is supplied it raises DeviceEndpointUnavailable; an empty dict means "available, but this device type has no points". Plants.async_device_realtime_supported(...) probes the capability once per session.
  • A heartbeat helper for External EMS dispatch mode (Control.async_heartbeat / Control.heartbeat_loop).
  • Convenience constants for dispatch command value sets (Control.CHARGE_DISCHARGE_COMMANDS, Control.FORCED_CHARGING).

The package supports the following functionality:

  • OAuth2 authentication
  • Getting a list plants
  • Getting details of a plant
  • Getting devices of a plant
  • Getting "real-time" data of a plant (Data is updated every 5 minutes according to Sungrow's documentation)
  • Getting historical data
  • Getting and updating grid control settings

Quirks

The iSolarCloud API is quite new and not very mature. Some tips:

  • The authorisation flow is based on OAuth2 but doesn't work exactly as you would expect
  • The state parameter is not passed back after to the authorisation step. This makes it more tricky to resume the flow in a client application.
  • User is asked to approve the authorisation if the flow is invoked again, e.g. in case the tokens have expired - unlike many OAuth2 implementations who will perform a "silent" authorisation if the user has already approved the access.
  • The API documentation lists a lot of data points which do not seem to be returned from my inverter, it probably varies between models.
  • There are different iSolarCloud servers for different regions, see the pysolarcloud.Server enum
  • API endpoints accept a language code but respond with Chinese text when when English is requested

Usage

Register your app

  1. Create an account in the iSolarCloud Developer Portal
  2. Create an app in the developer portal
    • Answer "Yes" to authorize with OAuth2.0
    • Enter a Redirect URL for your app (this can be changed later)
  3. Wait for approval by Sungrow
  4. Find the needed configuration details in the developer portal. You will need:
    • Appkey
    • Secret Key
    • Application Id (This is shown as a query parameter within the Authorize URL in the developer portal)

Example

from pysolarcloud import Auth, Server
from pysolarcloud.plants import Plants

app_key = "your app key"
secret_key = "your secret key"
app_id = "your app id"
redirect_uri = "your redirect uri"

auth = Auth(Server.Europe, app_key, secret_key, app_id)
url = auth.auth_url(redirect_uri)
  1. Redirect user to url
  2. User selects plant(s) and grants authorisation
  3. iSolarCloud will redirect the user to redirect_uri with query parameter code
await auth.async_authorize(code, redirect_uri)
plants_api = Plants(auth)
plant_list = await plants_api.async_get_plants()
if plant_list:
   print(f"{len(plant_list)} plants found:")
   for plant in plant_list:
         print(f"Plant ID: {plant["ps_id"]}, Name: {plant["ps_name"]}")
else:
   print("No plants found.")
   return

print("\nFetching detailed information for each plant...\n")
plant_ids = [str(plant["ps_id"]) for plant in plant_list]
plant_details = await plants_api.async_get_plant_details(plant_ids)
for plant in plant_details:
   print(f"Details for Plant ID {plant["ps_id"]}: {plant}")

print("\nFetching real-time data for each plant...\n")
real_time_data = await plants_api.async_get_realtime_data(plant_ids)
for plant_id, data in real_time_data.items():
   # Print only the data points where value is not None
   data_values = {k: v for k, v in data.items() if v and v.get("value") is not None}
   print(f"Real-time data for Plant ID {plant_id}: {data_values}")

The Auth class keeps the access between calls and refreshes it when needed. If you prefer to manage this state yourself, you can create your own subclass of AbstractAuth.

Measure-point catalog

The documented iSolarCloud measuring points ship with the package as data, in pysolarcloud/data/measure_points.json, and are read through importlib.resources. It is the single source of truth for point IDs, the library's readable codes, and the documented English names, units and value-enum tables; Plants.measure_points (the default request map) is built from it.

from pysolarcloud import load_measure_points

catalog = load_measure_points()          # parsed once, then cached; immutable

point = catalog.get("83124")             # or catalog.get(83124)
point.name, point.unit, point.code       # ("Total Load Consumption", "Wh", "total_load_consumption")
point.catalogs                           # ("common-plant-measuring-points",) — the docs page(s)

catalog.by_code("total_load_consumption") is point   # True
catalog.resolve("total_load_consumption")            # by point ID *or* library code

catalog.decode_enum("33716", "3")        # "Charging" (None for an undocumented code)
catalog.enum_options("29")               # distinct documented labels, in table order

The catalog holds vendor facts only: the point ID, the name and unit as documented, which documentation page lists it, its enum table, plus the library's own code. Presentation — display names, Home Assistant device/state classes, icons — is left to the consumer. The document carries a schema_version; the loader refuses a version it does not understand. The API's per-response point_unit stays authoritative at runtime: the documented unit is reference data. Names and units are the docs' as curated in sungrow-hass (doc typos fixed, superscripts normalised, e.g. W/m^2^ → W/m²); a point seen in API responses but on no docs page has empty catalogs and a note saying where it came from. scripts/gen_measure_points.py is the one-off tool that converted the catalog from the sungrow-hass integration; edit the JSON directly from now on.

Grid Control

The Control class enables retrieving and updating grid control settings. Parameters and value sets are documented in the iSolarCloud Developer portal.

Example

from pysolarcloud.control import Control
from pysolarcloud.plants import DeviceType

devices = await plants_api.async_get_plant_devices(plant_id, device_types=[DeviceType.ENERGY_STORAGE_SYSTEM])
device_uuid = devices[0]["uuid"]
control_api = Control(auth)
# Fetch current config. With no list this requests every known setting; if the model
# rejects the bulk read, settings are read one at a time and the answered ones returned.
# Pass an explicit list (names or codes) to skip the bulk call.
current_settings = await control_api.async_read_parameters(device_uuid)
print(current_settings)
# Make an update using the canonical command values.
# energy_management_mode (10003) must leave Self-consumption for charge/discharge to actuate:
# 0 self-consumption, 2 compulsory/forced, 3 external energy dispatch, 4 VPP.
await control_api.async_update_parameters(
    device_uuid,
    {
        "energy_management_mode": Control.encode_parameter("energy_management_mode", "compulsory"),
        "charge_discharge_command": Control.CHARGE_DISCHARGE_COMMANDS["charge"],
        "charge_discharge_power": "2500",
    },
)

# When using External EMS / forced dispatch, send a heartbeat periodically.
# 10017 = external_ems_heartbeat, value is the heartbeat interval in seconds (1-1000).
# Appendix 10: send the heartbeat when switching EMS modes through the API.
await control_api.async_heartbeat(device_uuid, interval_seconds=60)

User-account login (unofficial, experimental)

In addition to the official OpenAPI OAuth flow (Auth), this fork provides UserAuth, which logs in with a normal iSolarCloud user account (email + password) via the reverse-engineered app/web API — no developer application required.

from pysolarcloud import Server, UserAuth

async with UserAuth(Server.Europe, "you@example.com", "password") as auth:
    plants = await auth.async_get_plants()
    print(plants)

⚠️ Unofficial and experimental. This is not Sungrow's documented OpenAPI. It may change or break without notice and its use may be subject to Sungrow's terms of service. The protocol was reimplemented clean-room from the MIT-licensed homebridge-platform-isolarcloud (see NOTICE). Credentials are only sent to iSolarCloud over TLS and are never logged.

User-account read helpers

UserAuth mirrors the iSolarCloud app's read API. All of the following are read-only; endpoint paths and parameter names were verified against the app, but where the app uses undocumented enum values (e.g. date_type, query_type, fault type/share codes) those are left as caller-supplied optional arguments and documented as unverified against a live device.

Method App endpoint
async_get_plants() getPsList
async_get_plant_detail(ps_id) getPsDetailWithPsType (realtime household view)
async_get_plant_detail_daily(ps_id, date_id) getPsDetail (daily view)
async_get_devices(ps_id) queryDeviceList
async_get_device_realtime(ps_key, *, point_ids=None) queryDeviceRealTimeDataByPsKeys
async_get_historical_data(...) queryMutiPointDataList
async_get_charging_piles(ps_id) / async_get_charge_pile_overview(ps_id) getChargingPileList / getChargePileOverviewInfo
async_get_charging_pile_realtime(uuid) / async_get_charging_pile_last_data(uuid) getChargingPileRealData / getChargingPileLastData
async_get_charging_pile_property(uuid, point_id) getChargingPileProperty
async_get_battery_capacity(ps_id) / async_get_soc_by_ps_id(ps_id) / async_get_soc_by_sn(bt_sn) getBatteryCapacityByPsIdV2 / querySocByPsId / querySocBySn
async_get_battery_info(ps_id, ...) getPsBatteryInfo
async_get_fault_count(ps_id) / async_query_faults(...) / async_get_fault_detail(fault_code) getDevFaultCountByPsId / queryFaultList / getFaultDetail
async_get_open_fault_num() / async_get_unread_fault_count(...) getPsOpenFaultNum / getNotReadFaultCount
async_get_household_storage_report(ps_id, ...) / async_get_energy_summary(ps_id, ...) getHouseholdStoragePsReport / getPsEnergySummaryInfo
async_get_device_day_month_year_history(ps_key, ...) / async_get_device_minute_history(ps_key, ...) queryDevicePointsDayMonthYearDataList / queryDevicePointMinuteDataList
async_get_home_setting(ps_id, home_setting_type) getHomeSettingDetail
async_set_operation_mode(ps_id, uuid=…, device_type=…, energy_management_model=…) paramSetHomeSettingOperationMode
async_get_discharge_template_info(ps_id, fast_discharging_uuid) getDischargeTemplateInfo
async_add_or_update_discharge_plan(ps_id, week_days=…, cycle_type=…, …) addOrUpdateDischargePlan
async_delete_discharge_plan(ps_id) / async_select_discharge_plan(ps_id) deleteDischargePlan / selectDischargePlan
async_set_system_power_backup_param(ps_id, task_name=…, mode=…, status=…, config=…) setSysPowerBackupParam
async_check_can_set_parameters(uuid_list, check_type=…) checkIsCanDoParamSet
async_get_param_set_template_point_info(uuid_list, …) getParamSetTemplatePointInfo
async_issue_parameter_instruction(ps_id, uuid_list, …, energy_management=…, power_control=…) paramSetIssueInstruction
async_get_back_read_value(task_id, uuid, …) getBackReadValue
async_cancel_fast_setting_task(ps_id, task_id=…) cancelFastSettingTask

⚠️ Breaking change in 0.16.0 — async_get_device_realtime now takes a device ps_key (from async_get_devices) as its single positional argument instead of (ps_id, device_sn), and returns data keyed by device uuid ({uuid: {point_id: {"id", "value", "unit", "name"}}}) instead of a flat {point_id: {"value", "unit"}} map. It now posts to queryDeviceRealTimeDataByPsKeys; the previous /v1/devService/queryDevice path did not exist in the app. Migrate by passing the device ps_key and reading points under each uuid.

Contributions

Ideas or contributions are welcome. I am not affiliated with Sungrow, I'm just another user of the API. My main use case will be a HomeAssistant integration based on this package.

See CONTRIBUTING.md for the development setup, the checks CI runs, and the rules that main enforces — notably that commits must be signed and that pull requests need the branch up to date with main.

Enjoy!

Metadata

Release files for sungrow-isolarcloud 0.20.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sungrow-isolarcloud 0.20.0
File Size Uploaded Provenance Actions
sungrow_isolarcloud-0.20.0.tar.gz 72.8 kB With provenance Details for sungrow_isolarcloud-0.20.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for sungrow-isolarcloud 0.20.0
File Interpreter ABI Platform Actions
sungrow_isolarcloud-0.20.0-py3-none-any.whl Python 3 none any Details for sungrow_isolarcloud-0.20.0-py3-none-any.whl

Total release size: 142.7 kB

Release files / sungrow_isolarcloud-0.20.0.tar.gz

Download URL sungrow_isolarcloud-0.20.0.tar.gz
Size 72.8 kB
Tags Source
SHA-256 checksum
How to use checksums
d775dcf7169ee6086756701fa6d7f4bc52888981e074ef38eb5ec3c210a02f6a
BLAKE2b-256 checksum
How to use checksums
dd68bd7a99cdc578ee48cd9286b3b0f2c1a7c83e481a91ac1c44df7bd79163a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Release files / sungrow_isolarcloud-0.20.0-py3-none-any.whl

Download URL sungrow_isolarcloud-0.20.0-py3-none-any.whl
Size 69.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dc4255d1d2a422dcae612ff24ce42a31b99d999aea467ce267e7846488e28f99
BLAKE2b-256 checksum
How to use checksums
337b9dfbaeb72ec66171dffdf0e99e0d9dd5d0f4423e0ecb0f0e5657be02a1db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Security for sungrow-isolarcloud 0.20.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for sungrow_isolarcloud-0.20.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page