Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Package to retrieve PV information from the growatt server.
Special thanks to [Sjoerd Langkemper](https://github.com/Sjord) who has provided a strong base to start off from https://github.com/Sjord/growatt_api_client
That project has since ben archived.

This library now supports both the legacy password-based authentication and the V1 API with token-based authentication for MIN systems (TLX are identified as MIN system in the public API). Certain endpoints are not supported anymore by openapi.growatt.com. For example `api.min_write_parameter()` should be used instead of old `api.update_tlx_inverter_setting()`.
This library supports both the classic password-based API and the token-based V1 API, officially supported by Growatt. Currently, the V1 API implementation supports MIN and SPH devices, where MIN broadly corresponds to classic TLX and SPH to classic MIX. If your inverter supports the V1 API, it is encouraged to use this over the classic API, as it offers better security, more features, and more relaxed rate limiting.

## Usage

Expand Down
28 changes: 27 additions & 1 deletion docs/openapiv1.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ print(plants)

### Methods

Any methods that may be useful.
#### Generic Methods

Methods that work across all device types.

| Method | Arguments | Description |
|:---|:---|:---|
Expand All @@ -30,6 +32,13 @@ Any methods that may be useful.
| `api.plant_energy_overview(plant_id)` | plant_id: String | Get energy overview data for a plant. |
| `api.plant_energy_history(plant_id, start_date, end_date, time_unit, page, perpage)` | plant_id: String, start_date: Date, end_date: Date, time_unit: String, page: Int, perpage: Int | Get historical energy data for a plant for multiple days/months/years. |
| `api.device_list(plant_id)` | plant_id: String | Get a list of devices in specified plant. |

#### MIN Methods

Methods for MIN devices (type 7).

| Method | Arguments | Description |
|:---|:---|:---|
| `api.min_energy(device_sn)` | device_sn: String | Get current energy data for a min inverter, including power and energy values. |
| `api.min_detail(device_sn)` | device_sn: String | Get detailed data for a min inverter. |
| `api.min_energy_history(device_sn, start_date=None, end_date=None, timezone=None, page=None, limit=None)` | device_sn: String, start_date: Date, end_date: Date, timezone: String, page: Int, limit: Int | Get energy history data for a min inverter (7-day max range). |
Expand All @@ -39,6 +48,23 @@ Any methods that may be useful.
| `api.min_write_time_segment(device_sn, segment_id, batt_mode, start_time, end_time, enabled=True)` | device_sn: String, segment_id: Int, batt_mode: Int <0=load priority, 1=battery priority, 2=grid priority>, start_time: Time, end_time: Time, enabled: Bool | Update a specific time segment for a min inverter. see: [details](./openapiv1/min_tlx_settings.md) |
| `api.min_read_time_segments(device_sn, settings_data=None)` | device_sn: String, settings_data: Dict | Read all time segments from a MIN inverter. Optionally pass settings_data to avoid redundant API calls. see: [details](./openapiv1/min_tlx_settings.md) |

#### SPH Methods

Methods for SPH devices (type 5).

| Method | Arguments | Description |
|:---|:---|:---|
| `api.sph_detail(device_sn)` | device_sn: String | Get detailed data for an SPH hybrid inverter. |
| `api.sph_energy(device_sn)` | device_sn: String | Get current energy data for an SPH inverter, including power and energy values. |
| `api.sph_energy_history(device_sn, start_date=None, end_date=None, timezone=None, page=None, limit=None)` | device_sn: String, start_date: Date, end_date: Date, timezone: String, page: Int, limit: Int | Get energy history data for an SPH inverter (7-day max range). |
| `api.sph_settings(device_sn)` | device_sn: String | Get all settings for an SPH inverter. |
| `api.sph_read_parameter(device_sn, parameter_id, start_address=None, end_address=None)` | device_sn: String, parameter_id: String, start_address: Int, end_address: Int | Read a specific setting for an SPH inverter. see: [details](./openapiv1/sph_settings.md) |
| `api.sph_write_parameter(device_sn, parameter_id, parameter_values)` | device_sn: String, parameter_id: String, parameter_values: Dict/Array | Set parameters on an SPH inverter. Parameter values can be a single value, a list, or a dictionary. see: [details](./openapiv1/sph_settings.md) |
| `api.sph_write_ac_charge_time(device_sn, period_id, charge_power, charge_stop_soc, start_time, end_time, mains_enabled=True, enabled=True)` | device_sn: String, period_id: Int (1-3), charge_power: Int (0-100), charge_stop_soc: Int (0-100), start_time: Time, end_time: Time, mains_enabled: Bool, enabled: Bool | Configure an AC charge time period for an SPH inverter. see: [details](./openapiv1/sph_settings.md) |
| `api.sph_write_ac_discharge_time(device_sn, period_id, discharge_power, discharge_stop_soc, start_time, end_time, enabled=True)` | device_sn: String, period_id: Int (1-3), discharge_power: Int (0-100), discharge_stop_soc: Int (0-100), start_time: Time, end_time: Time, enabled: Bool | Configure an AC discharge time period for an SPH inverter. see: [details](./openapiv1/sph_settings.md) |
| `api.sph_read_ac_charge_times(device_sn, settings_data=None)` | device_sn: String, settings_data: Dict | Read all AC charge time periods from an SPH inverter. Optionally pass settings_data to avoid redundant API calls. see: [details](./openapiv1/sph_settings.md) |
| `api.sph_read_ac_discharge_times(device_sn, settings_data=None)` | device_sn: String, settings_data: Dict | Read all AC discharge time periods from an SPH inverter. Optionally pass settings_data to avoid redundant API calls. see: [details](./openapiv1/sph_settings.md) |

Methods from [here](./shinephone.md#methods) should be available, but it's safer to rely on the functions described in this file where possible. There is no guarantee those methods will work, or remain stable through updates.

### Variables
Expand Down
54 changes: 54 additions & 0 deletions docs/openapiv1/sph_settings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# SPH Inverter Settings

This is part of the [OpenAPI V1 doc](../openapiv1.md).

For SPH (hybrid inverter) systems, the public V1 API provides methods to read and write inverter settings. SPH inverters have different time period configurations compared to MIN inverters:

* **Read Parameter**
* function: `api.sph_read_parameter`
* parameters:
* `device_sn`: The device serial number
* `parameter_id`: Parameter ID to read (e.g., "discharge_power")
* `start_address`, `end_address`: Optional, for reading registers by address

* **Write Parameter**
* function: `api.sph_write_parameter`
* parameters:
* `device_sn`: The device serial number
* `parameter_id`: Parameter ID to write (e.g., "ac_charge")
* `parameter_values`: Value to set (single value, list, or dictionary)

* **AC Charge Time Periods**
* function: `api.sph_write_ac_charge_time`
* parameters:
* `device_sn`: The device serial number
* `period_id`: Period number (1-3)
* `charge_power`: Charging power percentage (0-100)
* `charge_stop_soc`: Stop charging at this SOC percentage (0-100)
* `start_time`: Datetime.time object for period start
* `end_time`: Datetime.time object for period end
* `mains_enabled`: Boolean to enable/disable grid charging (default: True)
* `enabled`: Boolean to enable/disable period (default: True)

* **AC Discharge Time Periods**
* function: `api.sph_write_ac_discharge_time`
* parameters:
* `device_sn`: The device serial number
* `period_id`: Period number (1-3)
* `discharge_power`: Discharge power percentage (0-100)
* `discharge_stop_soc`: Stop discharging at this SOC percentage (0-100)
* `start_time`: Datetime.time object for period start
* `end_time`: Datetime.time object for period end
* `enabled`: Boolean to enable/disable period (default: True)

* **Read AC Charge Time Periods**
* function: `api.sph_read_ac_charge_times`
* parameters:
* `device_sn`: The device serial number
* `settings_data`: Optional settings data to avoid redundant API calls

* **Read AC Discharge Time Periods**
* function: `api.sph_read_ac_discharge_times`
* parameters:
* `device_sn`: The device serial number
* `settings_data`: Optional settings data to avoid redundant API calls
135 changes: 135 additions & 0 deletions examples/sph_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
"""
Example script for SPH devices using the OpenAPI V1.

This script demonstrates controlling SPH interface devices (device type 5)
such as hybrid inverter systems.
You can obtain an API token from the Growatt API documentation or developer portal.
"""

import datetime
import json
from pathlib import Path

import requests

from . import growattServer

# Get the API token from user input or environment variable
# api_token = os.environ.get("GROWATT_API_TOKEN") or input("Enter your Growatt API token: ")

# test token from official API docs https://www.showdoc.com.cn/262556420217021/1494053950115877
api_token = "6eb6f069523055a339d71e5b1f6c88cc" # noqa: S105

try:
# Initialize the API with token instead of using login
api = growattServer.OpenApiV1(token=api_token)

# Plant info
plants = api.plant_list()
print(f"Plants: Found {plants['count']} plants") # noqa: T201
plant_id = plants["plants"][0]["plant_id"]

# Devices
devices = api.device_list(plant_id)

for device in devices["devices"]:
print(device) # noqa: T201
if device["type"] == growattServer.DeviceType.SPH.value:
inverter_sn = device["device_sn"]
print(f"Processing SPH device: {inverter_sn}") # noqa: T201

# Get device details
inverter_data = api.sph_detail(
device_sn=inverter_sn,
)
print("Saving inverter data to inverter_data.json") # noqa: T201
with Path("inverter_data.json").open("w") as f:
json.dump(inverter_data, f, indent=4, sort_keys=True)

# Get energy data
energy_data = api.sph_energy(
device_sn=inverter_sn,
)
print("Saving energy data to energy_data.json") # noqa: T201
with Path("energy_data.json").open("w") as f:
json.dump(energy_data, f, indent=4, sort_keys=True)

# Get energy history
energy_history_data = api.sph_energy_history(
device_sn=inverter_sn,
)
print("Saving energy history data to energy_history.json") # noqa: T201
with Path("energy_history.json").open("w") as f:
json.dump(
energy_history_data.get("datas", []),
f,
indent=4,
sort_keys=True,
)

# Get settings
settings_data = api.sph_settings(
device_sn=inverter_sn,
)
print("Saving settings data to settings_data.json") # noqa: T201
with Path("settings_data.json").open("w") as f:
json.dump(settings_data, f, indent=4, sort_keys=True)

# Read AC charge time periods
charge_times = api.sph_read_ac_charge_times(
device_sn=inverter_sn,
settings_data=settings_data,
)
print("AC Charge Time Periods:") # noqa: T201
print(json.dumps(charge_times, indent=4)) # noqa: T201

# Read AC discharge time periods
discharge_times = api.sph_read_ac_discharge_times(
device_sn=inverter_sn,
settings_data=settings_data,
)
print("AC Discharge Time Periods:") # noqa: T201
print(json.dumps(discharge_times, indent=4)) # noqa: T201

# Read discharge power
discharge_power = api.sph_read_parameter(
device_sn=inverter_sn,
parameter_id="discharge_power",
)
print(f"Current discharge power: {discharge_power}%") # noqa: T201

# Write examples - Uncomment to test

# Set AC charge time period 1: charge at 50% power to 95% SOC between 00:00-06:00
# api.sph_write_ac_charge_time(
# device_sn=inverter_sn,
# period_id=1,
# charge_power=50, # 50% charging power
# charge_stop_soc=95, # Stop at 95% SOC
# start_time=datetime.time(0, 0),
# end_time=datetime.time(6, 0),
# mains_enabled=True, # Enable grid charging
# enabled=True
# )
# print("AC charge period 1 updated successfully")

# Set AC discharge time period 1: discharge at 100% power to 20% SOC between 17:00-22:00
# api.sph_write_ac_discharge_time(
# device_sn=inverter_sn,
# period_id=1,
# discharge_power=100, # 100% discharge power
# discharge_stop_soc=20, # Stop at 20% SOC
# start_time=datetime.time(17, 0),
# end_time=datetime.time(22, 0),
# enabled=True
# )
# print("AC discharge period 1 updated successfully")

except growattServer.GrowattV1ApiError as e:
print(f"API Error: {e} (Code: {e.error_code}, Message: {e.error_msg})") # noqa: T201
except growattServer.GrowattParameterError as e:
print(f"Parameter Error: {e}") # noqa: T201
except requests.exceptions.RequestException as e:
print(f"Network Error: {e}") # noqa: T201
except Exception as e: # noqa: BLE001
print(f"Unexpected error: {e}") # noqa: T201
4 changes: 2 additions & 2 deletions growattServer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Import everything from base_api to ensure backward compatibility
from .base_api import *
# Import the V1 API class
from .open_api_v1 import OpenApiV1
# Import the V1 API class and DeviceType enum
from .open_api_v1 import OpenApiV1, DeviceType
# Import exceptions
from .exceptions import GrowattError, GrowattParameterError, GrowattV1ApiError

Expand Down
Loading