Skip to content

Commit 10c7ceb

Browse files
committed
Merge remote-tracking branch 'indy/master' into srankine/get_mix_invertor_settings
2 parents f165388 + 45e3fce commit 10c7ceb

21 files changed

+2555
-1457
lines changed

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github: [indykoning]
2+
custom: ["https://www.paypal.me/indykoning"]

.github/ISSUE_TEMPLATE/1-bug.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Bug Report
2+
description: File a bug report.
3+
title: "[🐛]: "
4+
labels: ["bug"]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: |
9+
Thanks for participating in this project!
10+
Please fill out the following sections to help us understand the issue you're experiencing.
11+
12+
In any case,
13+
- make sure you are using the latest version of the package;
14+
- do at least one search in current issues or questions (Including closed), your question might already be answered;
15+
- please include as many details as possible;
16+
- type: textarea
17+
id: current-behavior
18+
attributes:
19+
label: What is the current behavior?
20+
validations:
21+
required: true
22+
- type: textarea
23+
id: expected-behavior
24+
attributes:
25+
label: What is the expected behavior?
26+
validations:
27+
required: true
28+
- type: textarea
29+
id: steps-to-reproduce
30+
attributes:
31+
label: How can we reproduce the issue?
32+
- type: textarea
33+
id: proposed-solution
34+
attributes:
35+
label: What is the proposed solution?
36+
- type: textarea
37+
id: additional-information
38+
attributes:
39+
label: do you have any additional information for us?
40+
description: Please include any additional information that may be helpful in resolving the issue. Like logs, screenshots, or any other relevant details.
41+
- type: checkboxes
42+
attributes:
43+
label: Is there an existing issue for this?
44+
description: Please search to see if an issue already exists for the bug you encountered.
45+
options:
46+
- label: I have searched the existing issues
47+
required: true
48+
- type: checkboxes
49+
attributes:
50+
label: Are you willing to create a pull request for this?
51+
description: Thank you so much for your willingness to help us out! We really appreciate it.
52+
options:
53+
- label: If i have a fix i would be willing to create a pull request

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
blank_issues_enabled: true
2+
3+
contact_links:
4+
- name: Feature Requests and Ideas
5+
url: https://github.com/indykoning/PyPi_GrowattServer/discussions/categories/ideas
6+
about: Unfortunately i've implemented eveything i have access to. But with help of the community we can add more features. Please add your ideas here.
7+
- name: Support, Questions & Other
8+
url: https://github.com/indykoning/PyPi_GrowattServer/discussions/categories/q-a
9+
about: If you have questions or need help, take a look here. You can also ask your question here.

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!--
2+
Thanks for submitting a pull request!
3+
Please provide enough information so that others can review your pull request.
4+
-->
5+
6+
**Summary**
7+
8+
<!--
9+
Explain the **motivation** for making this change.
10+
What existing problem does the pull request solve?
11+
What feature does this Pull request add?
12+
Are there any linked issues or discussions?
13+
-->
14+
15+
**Checklist**
16+
17+
- [ ] I've made sure the PR does small incremental changes. (new code additions are dificult to review when e.g. the entire repository got improved codestyle in the same PR.)
18+
- [ ] I've added/updated the relevant docs for code changes i've made.

README.md

Lines changed: 11 additions & 268 deletions
Large diffs are not rendered by default.

docs/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Growatt Server Docs
2+
3+
Welcome to the docs for the [GrowattServer Python package](https://pypi.org/project/growattServer/)
4+
Package to retrieve PV information from the growatt server.
5+
6+
This package uses the Growatt Cloud in order to retrieve information from your "Power Plant"/home, Inverters, Battery banks and more!
7+
It can also be used to update Settings on your Plant, Inverters and Battery banks made by Growatt.
8+
9+
### Legacy API
10+
11+
This is the original way this package has started, at the time of writing it is still the most used.
12+
Please refer to the docs for [ShinePhone/legacy](./shinephone.md) for it's usage and available methods.
13+
14+
### V1 API
15+
16+
This follows Growatt's OpenAPI V1.
17+
Please refer to the docs for [OpenAPI V1](./openapiv1.md) for it's usage and available methods.
18+
19+
## Note
20+
21+
This is based on the endpoints used on the mobile app and could be changed without notice.
22+
23+
## Examples
24+
25+
The `examples` directory contains example usage for the library. You are required to have the library installed to use them `pip install growattServer`. However, if you are contributing to the library and want to use the latest version from the git repository, simply create a symlink to the growattServer directory inside the `examples` directory.
26+
27+
## Disclaimer
28+
29+
The developers & maintainers of this library accept no responsibility for any damage, problems or issues that arise with your Growatt systems as a result of its use.
30+
31+
The library contains functions that allow you to modify the configuration of your plant & inverter which carries the ability to set values outside of normal operating parameters, therefore, settings should only be modified if you understand the consequences.
32+
33+
To the best of our knowledge only the `settings` functions perform modifications to your system and all other operations are read only. Regardless of the operation:
34+
35+
***The library is used entirely at your own risk.***

docs/openapiv1.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# OpenAPI V1
2+
3+
This version of the API follows the newer [OpenAPI V1 API](https://www.showdoc.com.cn/262556420217021/0) Growatt has made available.
4+
5+
It extends our ["Legacy" ShinePhone](./shinephone.md) so methods from [there](./shinephone.md#methods) should be available, but it's safer to rely on the functions described in this file where possible.
6+
7+
## Usage
8+
9+
The public v1 API requires token-based authentication
10+
11+
```python
12+
import growattServer
13+
14+
api = growattServer.OpenApiV1(token="YOUR_API_TOKEN")
15+
# Get a list of growatt plants.
16+
plants = api.plant_list_v1()
17+
print(plants)
18+
```
19+
20+
## Methods and Variables
21+
22+
### Methods
23+
24+
Any methods that may be useful.
25+
26+
| Method | Arguments | Description |
27+
|:---|:---|:---|
28+
| `api.plant_list()` | None | Get a list of plants registered to your account. |
29+
| `api.plant_details(plant_id)` | plant_id: String | Get detailed information about a power station. |
30+
| `api.plant_energy_overview(plant_id)` | plant_id: String | Get energy overview data for a plant. |
31+
| `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. |
32+
| `api.device_list(plant_id)` | plant_id: String | Get a list of devices in specified plant. |
33+
| `api.min_energy(device_sn)` | device_sn: String | Get current energy data for a min inverter, including power and energy values. |
34+
| `api.min_detail(device_sn)` | device_sn: String | Get detailed data for a min inverter. |
35+
| `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). |
36+
| `api.min_settings(device_sn)` | device_sn: String | Get all settings for a min inverter. |
37+
| `api.min_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 a min inverter. see: [details](./openapiv1/min_tlx_settings.md) |
38+
| `api.min_write_parameter(device_sn, parameter_id, parameter_values)` | device_sn: String, parameter_id: String, parameter_values: Dict/Array | Set parameters on a min inverter. Parameter values can be a single value, a list, or a dictionary. see: [details](./openapiv1/min_tlx_settings.md) |
39+
| `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) |
40+
| `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) |
41+
42+
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.
43+
44+
### Variables
45+
46+
Some variables you may want to set.
47+
48+
`api.server_url` The growatt server URL, default: 'https://openapi.growatt.com/v1/'
49+
50+
You may need a different URL depending on where your account is registered:
51+
52+
'https://openapi-cn.growatt.com/v1/' (Chinese server)
53+
'https://openapi-us.growatt.com/v1/' (North American server)
54+
'https://openapi.growatt.com/v1/' (Other regional server: e.g. Europe)
55+
56+
### Initialisation
57+
58+
```python
59+
api = growattServer.GrowattApiV1(token="YOUR_API_TOKEN") # Initialize with your API token
60+
```

docs/openapiv1/min_tlx_settings.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# MIN/TLX Inverter Settings
2+
3+
This is part of the [OpenAPI V1 doc](../openapiv1.md).
4+
5+
For MIN/TLX systems, the public V1 API provides a more robust way to read and write inverter settings:
6+
7+
* **Read Parameter**
8+
* function: `api.min_read_parameter`
9+
* parameters:
10+
* `device_sn`: The device serial number
11+
* `parameter_id`: Parameter ID to read (e.g., "discharge_power")
12+
* `start_address`, `end_address`: Optional, for reading registers by address
13+
14+
* **Write Parameter**
15+
* function: `api.min_write_parameter`
16+
* parameters:
17+
* `device_sn`: The device serial number
18+
* `parameter_id`: Parameter ID to write (e.g., "ac_charge")
19+
* `parameter_values`: Value to set (single value, list, or dictionary)
20+
21+
* **Time Segments**
22+
* function: `api.min_write_time_segment`
23+
* parameters:
24+
* `device_sn`: The device serial number
25+
* `segment_id`: Segment number (1-9)
26+
* `batt_mode`: Battery mode (0=Load First, 1=Battery First, 2=Grid First)
27+
* `start_time`: Datetime.time object for segment start
28+
* `end_time`: Datetime.time object for segment end
29+
* `enabled`: Boolean to enable/disable segment
30+
31+
* **Read Time Segments**
32+
* function: `api.min_read_time_segments`
33+
* parameters:
34+
* `device_sn`: The device serial number
35+
* `settings_data`: Optional settings data to avoid redundant API calls

docs/shinephone.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# ShinePhone reverse engineered api (legacy)
2+
3+
This is where the project was born, when no consumer facing api was available. We reverse-engineered the ShinePhone app.
4+
5+
Currently Growatt does seem to support consumers using their [OpenApi](./openapiv1.md).
6+
At the time of writing this "Legacy API" is still the most used method.
7+
8+
## Getting started
9+
10+
Using username/password basic authentication
11+
12+
```python
13+
import growattServer
14+
15+
api = growattServer.GrowattApi()
16+
login_response = api.login(<username>, <password>)
17+
# Get a list of growatt plants.
18+
print(api.plant_list(login_response['user']['id']))
19+
```
20+
21+
## Methods and Variables
22+
23+
### Methods
24+
25+
Any methods that may be useful.
26+
27+
|method|arguments|description|
28+
|:---|:---:|:---|
29+
| `api.login(username, password)` | username: String, password: String | Log into the growatt API. This must be done beforemaking any request. After this you will be logged in. You will want to capture the response to get the `userId` variable. Should not be used for public v1 APIs. |
30+
| `api.plant_list(user_id)` | user_id: String | Get a list of plants registered to your account. |
31+
| `api.plant_info(plant_id)` | plant_id: String | Get info for specified plant. |
32+
| `api.plant_settings(plant_id)` | plant_id: String | Get the current settings for the specified plant. see: [details](./shinephone/plant_settings.md) |
33+
| `api.plant_detail(plant_id, timespan, date)` | plant_id: String, timespan: Int (1=day, 2=month), date: String | Get details of a specific plant. |
34+
| `api.plant_energy_data(plant_id)` | plant_id: String | Get energy data for the specified plant. |
35+
| `api.device_list(plant_id)` | plant_id: String | Get a list of devices in specified plant. |
36+
| `api.dashboard_data(plant_id, timespan, date)` | plant_id: String, timespan: Int (0=hour, 1=day, 2=month), date: String | Get dashboard values for a timespan. NOTE: Many values are incorrect for 'Mix' systems but still provide some accurate data unavailable elsewhere. |
37+
| `api.inverter_list(plant_id)` | plant_id: String | Get a list of inverters in specified plant. (May be deprecated in the future, use `device_list` instead). |
38+
| `api.inverter_data(inverter_id, date)` | inverter_id: String, date: String | Get some basic data of a specific date for the inverter. |
39+
| `api.inverter_detail(inverter_id)` | inverter_id: String | Get detailed data on inverter. |
40+
| `api.tlx_system_status(plant_id, tlx_id)` | plant_id: String, tlx_id: String | Get system status. |
41+
| `api.tlx_energy_overview(plant_id, tlx_id)` | plant_id: String, tlx_id: String | Get energy overview of the system. |
42+
| `api.tlx_energy_prod_cons(plant_id, tlx_id)` | plant_id: String, tlx_id: String | Get energy production and consumption for the system. |
43+
| `api.tlx_data(tlx_id, date)` | tlx_id: String, date: String | Get some basic data of a specific date for the tlx type inverter. |
44+
| `api.tlx_detail(tlx_id)` | tlx_id: String | Get detailed data on a tlx type inverter. |
45+
| `api.tlx_params(tlx_id)` | tlx_id: String | Get parameters for the tlx type inverter. |
46+
| `api.tlx_get_all_settings(tlx_id)` | tlx_id: String | Get all possible settings for the tlx type inverter. |
47+
| `api.tlx_get_enabled_settings(tlx_id)` | tlx_id: String | Get all enabled settings for the tlx type inverter. |
48+
| `api.tlx_battery_info(serial_num)` | serial_num: String | Get battery info for tlx systems. |
49+
| `api.tlx_battery_info_detailed(serial_num)` | serial_num: String | Get detailed battery info. |
50+
| `api.mix_info(mix_id, plant_id=None)` | mix_id: String, plant_id: String (optional) | Get high-level information about the Mix system, including daily and overall totals. |
51+
| `api.mix_totals(mix_id, plant_id)` | mix_id: String, plant_id: String | Get daily and overall total information for the Mix system (duplicates some of the information from `mix_info`). |
52+
| `api.mix_system_status(mix_id, plant_id)` | mix_id: String, plant_id: String | Get instantaneous values for Mix system, e.g., current import/export, generation, charging rates, etc. |
53+
| `api.mix_detail(mix_id, plant_id, timespan, date)` | mix_id: String, plant_id: String, timespan: Int <0=hour, 1=day, 2=month>, date: String | Get detailed values for a timespan. The API call also returns totals data for the same values in this time window. |
54+
| `api.storage_detail(storage_id)` | storage_id: String | Get detailed data on storage (battery). |
55+
| `api.storage_params(storage_id)` | storage_id: String | Get extensive information on storage (more info, more convoluted). |
56+
| `api.storage_energy_overview(plant_id, storage_id)` | plant_id: String, storage_id: String | Get the information you see in the "Generation overview". |
57+
| `api.is_plant_noah_system(plant_id)` | plant_id: String | Get information if Noah devices are configured for the specified plant. |
58+
| `api.noah_system_status(serial_number)` | serial_number: String | Get the current status for the specified Noah device, e.g., workMode, soc, chargePower, disChargePower, current import/export, etc. |
59+
| `api.noah_info(serial_number)` | serial_number: String | Get all information for the specified Noah device, e.g., configured operation modes, battery management settings, firmware version, etc. |
60+
| `api.update_plant_settings(plant_id, changed_settings, current_settings)` | plant_id: String, changed_settings: Dict, current_settings: Dict (optional) | Update the settings for a plant to the values specified in the dictionary. If `current_settings` are not provided, it will look them up automatically using the `get_plant_settings` function. |
61+
| `api.update_tlx_inverter_setting(serial_number, setting_type, parameter)` | serial_number: String, setting_type: String, parameter: Any | Apply the provided parameter for the specified setting on the specified tlx inverter. see: [details](./shinephone/inverter_settings.md) |
62+
| `api.update_tlx_inverter_time_segment(serial_number, segment_id, batt_mode, start_time, end_time, enabled)` | serial_number: String, segment_id: Int, batt_mode: String, start_time: String, end_time: String, enabled: Bool | Update one of the 9 time segments with the specified battery mode (load, battery, grid first). see: [details](./shinephone/inverter_settings.md) |
63+
| `api.update_mix_inverter_setting(serial_number, setting_type, parameters)` | serial_number: String, setting_type: String, parameters: Dict/Array | Apply the provided parameters for the specified setting on the specified Mix inverter. see: [details](./shinephone/inverter_settings.md) |
64+
| `api.update_ac_inverter_setting(serial_number, setting_type, parameters)` | serial_number: String, setting_type: String, parameters: Dict/Array | Apply the provided parameters for the specified setting on the specified AC-coupled inverter. see: [details](./shinephone/inverter_settings.md) |
65+
| `api.update_noah_settings(serial_number, setting_type, parameters)` | serial_number: String, setting_type: String, parameters: Dict/Array | Apply the provided parameters for the specified setting on the specified Noah device. see: [details](./shinephone/noah_settings.md) |
66+
67+
### Variables
68+
69+
Some variables you may want to set.
70+
71+
`api.server_url` The growatt server URL, default: 'https://openapi.growatt.com/'
72+
73+
You may need a different URL depending on where your account is registered:
74+
75+
'https://openapi-cn.growatt.com/' (Chinese server)
76+
'https://openapi-us.growatt.com/' (North American server)
77+
'https://openapi.growatt.com/' (Other regional server: e.g. Europe)
78+
79+
## Initialisation
80+
81+
The library can be initialised to introduce randomness into the User Agent field that is used when communicating with the servers.
82+
83+
This has been added since the Growatt servers started checking for the presence of a `User-Agent` field in the headers that are sent.
84+
85+
By default the library will use a pre-set `User-Agent` value which identifies this library while also appearing like an Android device. However, it is also possible to pass in parameters to the intialisation of the library to override this entirely, or just add a random ID to the value. e.g.
86+
87+
```python
88+
api = growattServer.GrowattApi() # The default way to initialise
89+
90+
api = growattServer.GrowattApi(True) # Adds a randomly generated User ID to the default User-Agent
91+
92+
api = growattServer.GrowattApi(False, "my_user_agent_value") # Overrides the default and uses "my_user_agent_value" in the User-Agent header
93+
```
94+
95+
## Note
96+
97+
This is based on the endpoints used on the mobile app and could be changed without notice.
98+
99+
## Settings Discovery
100+
101+
The settings for the Plant and Inverter have been reverse engineered by using the ShinePhone Android App and the NetCapture SSL application together to inspect the API calls that are made by the application and the parameters that are provided with it.
102+
103+
See: [Reverse Engineered](./shinephone/reverse_engineering.md)

0 commit comments

Comments
 (0)