Skip to content

Commit f165388

Browse files
committed
Merge remote-tracking branch 'origin/master' into srankine/get_mix_invertor_settings
2 parents de44ba9 + ecb7784 commit f165388

File tree

6 files changed

+983
-72
lines changed

6 files changed

+983
-72
lines changed

README.md

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ Any methods that may be useful.
2828

2929
`api.plant_info(plant_id)` Get info for specified plant.
3030

31+
`api.plant_settings(plant_id)` Get the current settings for the specified plant
32+
3133
`api.plant_detail(plant_id, timespan<1=day, 2=month>, date)` Get details of a specific plant.
3234

35+
`api.plant_energy_data(plant_id)` Get energy data for the specified plant.
36+
3337
`api.inverter_list(plant_id)` Get a list of inverters in specified plant. (May be deprecated in the future, since it gets all devices. Use `device_list` instead).
3438

3539
`api.device_list(plant_id)` Get a list of devices in specified plant.
@@ -38,10 +42,26 @@ Any methods that may be useful.
3842

3943
`api.inverter_detail(inverter_id)` Get detailed data on inverter.
4044

45+
`api.tlx_system_status(plant_id, tlx_id)` Get system status.
46+
47+
`api.tlx_energy_overview(plant_id, tlx_id)` Get energy overview of the system.
48+
49+
`api.tlx_energy_prod_cons(plant_id, tlx_id)` Get energy production and consumption for the system.
50+
4151
`api.tlx_data(tlx_id, date)` Get some basic data of a specific date for the tlx type inverter.
4252

4353
`api.tlx_detail(tlx_id)` Get detailed data on a tlx type inverter.
4454

55+
`api.tlx_params(tlx_id)` Get parameters for the tlx type inverter.
56+
57+
`api.tlx_get_all_settings(tlx_id)` Get all possible settings for the tlx type inverter.
58+
59+
`api.tlx_get_enabled_settings(tlx_id)` Get all enabled settings for the tlx type inverter.
60+
61+
`api.tlx_battery_info(serial_num)` Get battery info for tlx systems.
62+
63+
`api.tlx_battery_info_detailed(serial_num)` Get detailed battery info.
64+
4565
`api.mix_info(mix_id, plant_id=None)` Get high level information about the Mix system including daily and overall totals. NOTE: `plant_id` is an optional parameter, it does not appear to be used by the remote API, but is used by the mobile app these calls were reverse-engineered from.
4666

4767
`api.mix_totals(mix_id, plant_id)` Get daily and overall total information for the Mix system (duplicates some of the information from `mix_info`).
@@ -58,21 +78,37 @@ Any methods that may be useful.
5878

5979
`api.storage_energy_overview(plant_id, storage_id)` Get the information you see in the "Generation overview".
6080

61-
`api.get_plant_settings(plant_id)` Get the current settings for the specified plant
81+
`api.is_plant_noah_system(plant_id)` Get the Information if noah devices are configured for the specified plant
82+
83+
`api.noah_system_status(serial_number)` Get the current status for the specified noah device e.g. workMode, soc, chargePower, disChargePower, current import/export etc.
84+
85+
`api.noah_info(serial_number)` Get all information for the specified noah device e.g. configured Operation Modes, configured Battery Management charging upper & lower limit, configured System Default Output Power, Firmware Version
6286

6387
`api.get_mix_inverter_settings(serial_number)` Get the current inverter settings for the specified serial number including charge/discharge schedule for hybrid systems.
6488

6589
`api.update_plant_settings(plant_id, changed_settings, current_settings)` Update the settings for a plant to the values specified in the dictionary, if the `current_settings` are not provided it will look them up automatically using the `get_plant_settings` function - See 'Plant settings' below for more information
6690

91+
`api.update_tlx_inverter_setting(serial_number, setting_type, parameter)` Applies the provided parameter for the specified setting on the specified tlx inverter; see 'Inverter settings' below for more information.
92+
93+
`api.update_tlx_inverter_time_segment(serial_number, segment_id, batt_mode, start_time, end_time, enabled)` Updates one of the 9 time segments with the specified battery mode (load, battery, grid first); see 'Inverter settings' below for more information.
94+
6795
`api.update_mix_inverter_setting(serial_number, setting_type, parameters)` Applies the provided parameters (dictionary or array) for the specified setting on the specified mix inverter; see 'Inverter settings' below for more information
6896

6997
`api.update_ac_inverter_setting(serial_number, setting_type, parameters)` Applies the provided parameters (dictionary or array) for the specified setting on the specified AC-coupled inverter; see 'Inverter settings' below for more information
7098

99+
`api.update_noah_settings(serial_number, setting_type, parameters)` Applies the provided parameters (dictionary or array) for the specified setting on the specified noah device; see 'Noah settings' below for more information
100+
71101
### Variables
72102

73103
Some variables you may want to set.
74104

75-
`api.server_url` The growatt server URL, default: 'https://server-api.growatt.com/'
105+
`api.server_url` The growatt server URL, default: 'https://openapi.growatt.com/'
106+
107+
You may need a different URL depending on where your account is registered:
108+
109+
'https://openapi-cn.growatt.com/' (Chinese server)
110+
'https://openapi-us.growatt.com/' (North American server)
111+
'https://openapi.growatt.com/' (Other regional server: e.g. Europe)
76112

77113
## Note
78114

@@ -125,7 +161,7 @@ The plant settings function(s) allow you to re-configure the settings for a spec
125161
The function `update_plant_settings` allows you to provide a python dictionary of any/all of the above settings and change their value.
126162

127163
## Inverter Settings
128-
NOTE: The inverter settings function appears to only work with 'mix' systems based on the API call that it makes being specific to 'mix' inverters
164+
NOTE: The inverter settings function appears to only work with 'mix' and 'tlx' systems based on the API call that it makes being specific to those inverter types
129165

130166
The inverter settings function(s) allow you to change individual values on your inverter e.g. time, charging period etc.
131167
From what has been reverse engineered from the api, each setting has a `setting_type` and a set of `parameters` that are relevant to it.
@@ -179,8 +215,67 @@ Known working settings & parameters are as follows (all parameter values are str
179215
* `param15`: Schedule 3 - End time - Hour e.g. "02" (2am)
180216
* `param16`: Schedule 3 - End time - Minute e.g. "00" (0 minutes)
181217
* `param17`: Schedule 3 - Enabled/Disabled (0 = Disabled, 1 = Enabled)
218+
* **TLX inverter settings**
219+
* function: `api.update_tlx_inverter_setting`
220+
* type: `charge_power`
221+
* param1: Charging power % (value between 0 and 100)
222+
* type: `charge_stop_soc`
223+
* param1: Charge Stop SOC
224+
* type: `discharge_power`
225+
* param1: Discharging power % (value between 0 and 100)
226+
* type: `on_grid_discharge_stop_soc`
227+
* param1: On-grid discharge Stop SOC
228+
* type: `discharge_stop_soc`
229+
* param1: Off-grid discharge Stop SOC
230+
* type: `ac_charge`
231+
* param1: Allow AC (grid) charging (0 = Disabled, 1 = Enabled)
232+
* type: `pf_sys_year`
233+
* param1: datetime in format: `YYYY-MM-DD HH:MM:SS`
234+
* function: `api.update_tlx_inverter_time_segment`
235+
* segment_id: The segment to update (1-9)
236+
* batt_mode: Battery Mode for the segment: 0=Load First(Self-Consumption), 1=Battery First, 2=Grid First
237+
* start_time: timedate object with start time of segment with format HH:MM
238+
* end_time: timedate object with end time of segment with format HH:MM
239+
* enabled: time segment enabled, boolean: True (Enabled), False (Disabled)
240+
241+
The four functions `update_tlx_inverter_setting`, `update_mix_inverter_setting`, `update_ac_inverter_setting`, and `update_inverter_setting` take either a dictionary or an array. If an array is passed it will automatically generate the `paramN` key based on array index since all params for settings seem to used the same numbering scheme.
242+
243+
Only the settings described above have been tested with `update_tlx_inverter_setting` and they all take only one single parameter. It is very likely that the function works with all settings returned by `tlx_get_enabled_settings`, but this has not been tested. A helper function `update_tlx_inverter_time_segment` is provided for the settings that require more than one parameter.
244+
245+
## Noah Settings
246+
The noah settings function allow you to change individual values on your noah system e.g. system default output power, battery management, operation mode and currency
247+
From what has been reverse engineered from the api, each setting has a `setting_type` and a set of `parameters` that are relevant to it.
182248

183-
The three functions `update_mix_inverter_setting`, `update_ac_inverter_setting`, and `update_inverter_setting` take either a dictionary or an array. If an array is passed it will automatically generate the `paramN` key based on array index since all params for settings seem to used the same numbering scheme.
249+
Known working settings & parameters are as follows (all parameter values are strings):
250+
* **Change "System Default Output Power"**
251+
* function: `api.update_noah_settings`
252+
* setting type: `default_power`
253+
* params:
254+
* `param1`: System default output power in watt
255+
* **Change "Battery Management"**
256+
* function: `api.update_noah_settings`
257+
* setting type: `charging_soc`
258+
* params:
259+
* `param1`: Charge upper limit in %
260+
* `param2`: Charge lower limit in %
261+
* **Change "Operation Mode" Time Segment**
262+
* function: `api.update_noah_settings`
263+
* setting type: `time_segment` key from `api.noah_info(serial_number)`, for new `time_segment` count the ending number up
264+
* params:
265+
* `param1`: Workingmode (0 = Load First, 1 = Battery First)
266+
* `param2`: Start time - Hour e.g. "01" (1am)
267+
* `param3`: Start time - Minute e.g. "00" (0 minutes)
268+
* `param4`: End time - Hour e.g. "02" (2am)
269+
* `param5`: End time - Minute e.g. "00" (0 minutes)
270+
* `param6`: Output power in watt (For Workingmode "Battery First" always "0")
271+
* `param7`: Enabled/Disabled (0 = Disabled, 1 = Enabled)
272+
* **Change "Currency"**
273+
* function: `api.update_noah_settings`
274+
* setting type: `updatePlantMoney`
275+
* params:
276+
* `param1`: Plant Id
277+
* `param2`: Cost per kWh e.g. "0.22"
278+
* `param3`: Unit value from `api.noah_info(serial_number)` - `unitList`
184279

185280
## Settings Discovery
186281

examples/noah_example.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import growattServer
2+
import datetime
3+
import getpass
4+
import pprint
5+
6+
"""
7+
This is a very trivial script that logs into a user's account and prints out useful data for a "NOAH" system.
8+
This has been tested against my personal system (NOAH2000) which is a 2kW Balcony Storage system.
9+
10+
Throughout the script there are points where 'pp.pprint' has been commented out. If you wish to see all the data that is returned from those
11+
specific library calls, just uncomment them and they will appear as part of the output.
12+
"""
13+
14+
pp = pprint.PrettyPrinter(indent=4)
15+
16+
"""
17+
A really hacky function to allow me to print out things with an indent in-front
18+
"""
19+
def indent_print(to_output, indent):
20+
indent_string = ""
21+
for x in range(indent):
22+
indent_string += " "
23+
print(indent_string + to_output)
24+
25+
#Prompt user for username
26+
username=input("Enter username:")
27+
28+
#Prompt user to input password
29+
user_pass=getpass.getpass("Enter password:")
30+
31+
api = growattServer.GrowattApi()
32+
login_response = api.login(username, user_pass)
33+
34+
plant_list = api.plant_list(login_response['user']['id'])
35+
#pp.pprint(plant_list)
36+
37+
print("***Totals for all plants***")
38+
pp.pprint(plant_list['totalData'])
39+
print("")
40+
41+
print("***List of plants***")
42+
for plant in plant_list['data']:
43+
indent_print("ID: %s, Name: %s"%(plant['plantId'], plant['plantName']), 2)
44+
print("")
45+
46+
for plant in plant_list['data']:
47+
plant_id = plant['plantId']
48+
plant_name = plant['plantName']
49+
plant_info=api.plant_info(plant_id)
50+
#pp.pprint(plant_info)
51+
print("***Info for Plant %s - %s***"%(plant_id, plant_name))
52+
#There are more values in plant_info, but these are some of the useful/interesting ones
53+
indent_print("CO2 Reducion: %s"%(plant_info['Co2Reduction']),2)
54+
indent_print("Nominal Power (w): %s"%(plant_info['nominal_Power']),2)
55+
indent_print("Solar Energy Today (kw): %s"%(plant_info['todayEnergy']),2)
56+
indent_print("Solar Energy Total (kw): %s"%(plant_info['totalEnergy']),2)
57+
print("")
58+
indent_print("Devices in plant:",2)
59+
for device in plant_info['deviceList']:
60+
device_sn = device['deviceSn']
61+
device_type = device['deviceType']
62+
indent_print("- Device - SN: %s, Type: %s"%(device_sn, device_type),4)
63+
64+
is_noah = api.is_plant_noah_system(plant['plantId'])
65+
if is_noah['result'] == 1 and (is_noah['obj']['isPlantNoahSystem'] or is_noah['obj']['isPlantHaveNoah']):
66+
device_sn = is_noah['obj']['deviceSn']
67+
indent_print("**NOAH - SN: %s**"%(device_sn),2)
68+
69+
noah_system = api.noah_system_status(is_noah['obj']['deviceSn'])
70+
pp.pprint(noah_system['obj'])
71+
print("")
72+
73+
noah_infos = api.noah_info(is_noah['obj']['deviceSn'])
74+
pp.pprint(noah_infos['obj']['noah'])
75+
print("")
76+
indent_print("Remaining battery (" + "%" + "): %s"%(noah_system['obj']['soc']),2)
77+
indent_print("Solar Power (w): %s"%(noah_system['obj']['ppv']),2)
78+
indent_print("Charge Power (w): %s"%(noah_system['obj']['chargePower']),2)
79+
indent_print("Discharge Power (w): %s"%(noah_system['obj']['disChargePower']),2)
80+
indent_print("Output Power (w): %s"%(noah_system['obj']['pac']),2)

examples/tlx_example.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import growattServer
2+
import datetime
3+
import getpass
4+
import json
5+
6+
"""
7+
# Example script controlling a Growatt MID-30KTL3-XH + APX battery hybrid system by emulating the ShinePhone iOS app.
8+
# The same API calls are used by the ShinePhone Android app as well. Traffic intercepted using HTTP Toolkit.
9+
#
10+
# The plant / energy / device APIs seem to be generic for all Growatt systems, while the inverter and battery APIs use the TLX APIs.
11+
#
12+
# The available settings under the 'Control' tab in ShinePhone are created by combining the results from two function calls:
13+
# tlx_get_all_settings() seem to returns the sum of all settings for all systems while tlx_get_enabled_settings() tells
14+
# which of these settings are valid for the TLX system.
15+
#
16+
# Settings that takes a single parameter can be set using update_tlx_inverter_setting(). A helper function, update_tlx_inverter_time_segment()
17+
# is provided for updating time segments which take several parameters. The inverter is picky and time intervals can't be overlapping,
18+
# even if they are disabled.
19+
#
20+
# The set functions are commented out in the example, uncomment to test, and use at your own risk. Most likely all settings returned in
21+
# tlx_get_enabled_settings() can be set using update_tlx_inverter_setting(), but has not been tested.
22+
#
23+
"""
24+
25+
# Prompt user for username
26+
username=input("Enter username:")
27+
28+
# Prompt user to input password
29+
user_pass=getpass.getpass("Enter password:")
30+
31+
user_agent = 'ShinePhone/8.1.17 (iPhone; iOS 15.6.1; Scale/2.00)'
32+
api = growattServer.GrowattApi(agent_identifier=user_agent)
33+
34+
login_response = api.login(username, user_pass)
35+
user_id = login_response['user']['id']
36+
print("Login successful, user_id:", user_id)
37+
38+
# Plant info
39+
plant_list = api.plant_list_two()
40+
plant_id = plant_list[0]['id']
41+
plant_info = api.plant_info(plant_id)
42+
print("Plant info:", json.dumps(plant_info, indent=4, sort_keys=True))
43+
44+
# Energy data (used in the 'Plant' Tab)
45+
energy_data = api.plant_energy_data(plant_id)
46+
print("Plant Energy data", json.dumps(energy_data, indent=4, sort_keys=True))
47+
48+
# Devices
49+
devices = api.device_list(plant_id)
50+
print("Devices:", json.dumps(devices, indent=4, sort_keys=True))
51+
52+
for device in devices:
53+
if device['deviceType'] == 'tlx':
54+
# Inverter info (used in inverter view)
55+
inverter_sn = device['deviceSn']
56+
inverter_info = api.tlx_params(inverter_sn)
57+
print("Inverter info:", json.dumps(inverter_info, indent=4, sort_keys=True))
58+
59+
# PV production data
60+
data = api.tlx_data(inverter_sn, datetime.datetime.now())
61+
print("PV production data:", json.dumps(data, indent=4, sort_keys=True))
62+
63+
# System settings
64+
all_settings = api.tlx_all_settings(inverter_sn)
65+
enabled_settings = api.tlx_enabled_settings(inverter_sn)
66+
# 'on_grid_discharge_stop_soc' is present in web UI, but for some reason not
67+
# returned in enabled settings so we enable it manually here instead
68+
enabled_settings['enable']['on_grid_discharge_stop_soc'] = '1'
69+
enabled_keys = enabled_settings['enable'].keys()
70+
available_settings = {k: v for k, v in all_settings.items() if k in enabled_keys}
71+
print("System settings:", json.dumps(available_settings, indent=4, sort_keys=True))
72+
73+
# System status
74+
data = api.tlx_system_status(plant_id, inverter_sn)
75+
print("System status:", json.dumps(data, indent=4, sort_keys=True))
76+
77+
# Energy overview
78+
data = api.tlx_energy_overview(plant_id, inverter_sn)
79+
print("Energy overview:", json.dumps(data, indent=4, sort_keys=True))
80+
81+
# Energy production & consumption
82+
data = api.tlx_energy_prod_cons(plant_id, inverter_sn)
83+
print("Energy production & consumption:", json.dumps(data, indent=4, sort_keys=True))
84+
85+
elif device['deviceType'] == 'bat':
86+
# Battery info
87+
batt_info = api.tlx_battery_info(device['deviceSn'])
88+
print("Battery info:", json.dumps(batt_info, indent=4, sort_keys=True))
89+
batt_info_detailed = api.tlx_battery_info_detailed(plant_id, device['deviceSn'])
90+
print("Battery info: detailed", json.dumps(batt_info_detailed, indent=4, sort_keys=True))
91+
92+
93+
# Examples of updating settings, uncomment to use
94+
95+
# Set charging power to 95%
96+
#res = api.update_tlx_inverter_setting(inverter_sn, 'charge_power', 95)
97+
#print(res)
98+
99+
# Turn on AC charging
100+
#res = api.update_tlx_inverter_setting(inverter_sn, 'ac_charge', 1)
101+
#print(res)
102+
103+
# Enable Load First between 00:01 and 11:59 using time segment 1
104+
#res = api.update_tlx_inverter_time_segment(serial_number = inverter_sn,
105+
# segment_id = 1,
106+
# batt_mode = growattServer.BATT_MODE_LOAD_FIRST,
107+
# start_time = datetime.time(00, 1),
108+
# end_time = datetime.time(11, 59),
109+
# enabled=True)
110+
#print(res)

0 commit comments

Comments
 (0)