File: tlx_example_dashboard.py

package info (click to toggle)
growattserver 1.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 296 kB
  • sloc: python: 1,344; makefile: 2
file content (127 lines) | stat: -rw-r--r-- 6,754 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

import growattServer
import getpass

# Example script fetching key power and today+total energy metrics from a Growatt MID-30KTL3-XH (TLX) + APX battery hybrid system 
#
# There is a lot of overlap in what the various Growatt APIs returns. 
# tlx_detail() contains the bulk of the needed data, but some info is missing and is fetched from 
# tlx_system_status(), tlx_energy_overview() and tlx_battery_info_detailed() instead


# Prompt user for username
username=input("Enter username:")

# Prompt user to input password
user_pass=getpass.getpass("Enter password:")

# Login, emulating the Growatt app
user_agent = 'ShinePhone/8.1.17 (iPhone; iOS 15.6.1; Scale/2.00)'
api = growattServer.GrowattApi(agent_identifier=user_agent)
login_response = api.login(username, user_pass)
if not login_response['success']:
    print(f"Failed to log in, msg: {login_response['msg']}, error: {login_response['error']}")
    exit()
    
# Get plant(s)
plant_list = api.plant_list_two()
plant_id = plant_list[0]['id']

# Get devices in plant
devices = api.device_list(plant_id)

# Iterate over all devices. Here we are interested in data from 'tlx' inverters and 'bat' devices
batteries_info = []
for device in devices:
    if device['deviceType'] == 'tlx':
        inverter_sn = device['deviceSn']
        
        # Inverter detail, contains the bulk of energy and power values
        inverter_detail = api.tlx_detail(inverter_sn).get('data')

        # Energy overview is used to retrieve "epvToday" which is not present in tlx_detail() for some reason
        energy_overview = api.tlx_energy_overview(plant_id, inverter_sn)

        # System status, contains power values, not available in inverter_detail()
        system_status = api.tlx_system_status(plant_id, inverter_sn)

    if device['deviceType'] == 'bat':
        batt_info = api.tlx_battery_info(device['deviceSn'])
        if batt_info.get('lost'):
            # Disconnected batteries are listed with 'old' power/energy/SOC data
            # Therefore we check it it's 'lost' and skip it in that case. 
            print("'Lost' battery found, skipping")
            continue

        # Battery info
        batt_info = api.tlx_battery_info_detailed(plant_id, device['deviceSn']).get('data')

        if float(batt_info['chargeOrDisPower']) > 0:
            bdcChargePower =  float(batt_info['chargeOrDisPower'])
            bdcDischargePower = 0
        else:
            bdcChargePower = 0
            bdcDischargePower =  float(batt_info['chargeOrDisPower'])    
            bdcDischargePower = -bdcDischargePower       
                 
        battery_data = {      
            'serialNum': device['deviceSn'],
            'bdcChargePower': bdcChargePower,
            'bdcDischargePower': bdcDischargePower,
            'dischargeTotal': batt_info['dischargeTotal'],
            'soc': batt_info['soc']
        }
        batteries_info.append(battery_data)
        

solar_production     = f'{float(energy_overview["epvToday"]):.1f}/{float(energy_overview["epvTotal"]):.1f}'
solar_production_pv1 = f'{float(inverter_detail["epv1Today"]):.1f}/{float(inverter_detail["epv1Total"]):.1f}'
solar_production_pv2 = f'{float(inverter_detail["epv2Today"]):.1f}/{float(inverter_detail["epv2Total"]):.1f}'
energy_output        = f'{float(inverter_detail["eacToday"]):.1f}/{float(inverter_detail["eacTotal"]):.1f}'
system_production    = f'{float(inverter_detail["esystemToday"]):.1f}/{float(inverter_detail["esystemTotal"]):.1f}'
battery_charged      = f'{float(inverter_detail["echargeToday"]):.1f}/{float(inverter_detail["echargeTotal"]):.1f}'
battery_grid_charge  = f'{float(inverter_detail["eacChargeToday"]):.1f}/{float(inverter_detail["eacChargeTotal"]):.1f}'
battery_discharged   = f'{float(inverter_detail["edischargeToday"]):.1f}/{float(inverter_detail["edischargeTotal"]):.1f}'
exported_to_grid     = f'{float(inverter_detail["etoGridToday"]):.1f}/{float(inverter_detail["etoGridTotal"]):.1f}'
imported_from_grid   = f'{float(inverter_detail["etoUserToday"]):.1f}/{float(inverter_detail["etoUserTotal"]):.1f}'
load_consumption     = f'{float(inverter_detail["elocalLoadToday"]):.1f}/{float(inverter_detail["elocalLoadTotal"]):.1f}'
self_consumption     = f'{float(inverter_detail["eselfToday"]):.1f}/{float(inverter_detail["eselfTotal"]):.1f}'
battery_charged      = f'{float(inverter_detail["echargeToday"]):.1f}/{float(inverter_detail["echargeTotal"]):.1f}'

print("\nGeneration overview             Today/Total(kWh)")
print(f'Solar production          {solar_production:>22}')
print(f' Solar production, PV1    {solar_production_pv1:>22}')
print(f' Solar production, PV2    {solar_production_pv2:>22}')
print(f'Energy Output             {energy_output:>22}')
print(f'System production         {system_production:>22}')
print(f'Self consumption          {self_consumption:>22}')
print(f'Load consumption          {load_consumption:>22}')
print(f'Battery Charged           {battery_charged:>22}')
print(f' Charged from grid        {battery_grid_charge:>22}')
print(f'Battery Discharged        {battery_discharged:>22}')
print(f'Import from grid          {imported_from_grid:>22}')
print(f'Export to grid            {exported_to_grid:>22}')

print("\nPower overview                          (Watts)")
print(f'AC Power                 {float(inverter_detail["pac"]):>22.1f}')
print(f'Self power               {float(inverter_detail["pself"]):>22.1f}')
print(f'Export power             {float(inverter_detail["pacToGridTotal"]):>22.1f}')
print(f'Import power             {float(inverter_detail["pacToUserTotal"]):>22.1f}')
print(f'Local load power         {float(inverter_detail["pacToLocalLoad"]):>22.1f}')
print(f'PV power                 {float(inverter_detail["ppv"]):>22.1f}')
print(f'PV #1 power              {float(inverter_detail["ppv1"]):>22.1f}')
print(f'PV #2 power              {float(inverter_detail["ppv2"]):>22.1f}')
print(f'Battery charge power     {float(system_status["chargePower"])*1000:>22.1f}')
if len(batteries_info) > 0:
    print(f'Batt #1 charge power     {float(batteries_info[0]["bdcChargePower"]):>22.1f}')
if len(batteries_info) > 1:
    print(f'Batt #2 charge power     {float(batteries_info[1]["bdcChargePower"]):>22.1f}')
print(f'Battery discharge power      {float(system_status["pdisCharge"])*1000:>18.1f}')
if len(batteries_info) > 0:
    print(f'Batt #1 discharge power  {float(batteries_info[0]["bdcDischargePower"]):>22.1f}')
if len(batteries_info) > 1:
    print(f'Batt #2 discharge power  {float(batteries_info[1]["bdcDischargePower"]):>22.1f}')
if len(batteries_info) > 0:
    print(f'Batt #1 SOC              {int(batteries_info[0]["soc"]):>21}%')
if len(batteries_info) > 1:
    print(f'Batt #2 SOC              {int(batteries_info[1]["soc"]):>21}%')