indykoning / PyPi_GrowattServer

MIT License
70 stars 32 forks source link

Can't get chartdata #33

Open philwareublox opened 1 year ago

philwareublox commented 1 year ago

When performing a simple request like this:

for plant in plantList:
    name = plant['plantName']
    id = plant['plantId']
    print(name, id)
    info = api.plant_info(id)
    for device in info['deviceList']:
        device_sn = device['deviceSn']
        device_type = device['deviceType']
        print(device_sn, device_type)

        mix_info = api.mix_info(device_sn)
        print(device_sn, id, mix_info)

        mix_totals = api.mix_totals(device_sn, id)
        print(device_sn, id, mix_totals)

        mix_detail = api.mix_detail(device_sn)
        print(mix_detail)

I get back None for the mix info and other mix data. What could be going wrong?

muppet3000 commented 1 year ago

At the risk of sounding patronising - do you have a "mix" style of system?

philwareublox commented 1 year ago

;) I have one inverter and all I want is the day timestamped data throughout the day. What API call do I need to do?

grunkyb commented 1 year ago

I have a non-mix converter and it wasn't too hard to reverse-engineer the API by using a web browser on the Shine server site, and watching network calls. Here's a bit for setting AC charging added to the end of __init__.py. I haven't put it into this repo until I give it a few more days on my system.

Addition to __init__.py ``` def update_mix_inverter_setting(self, serial_number, setting_type, parameters): """ Applies settings for specified system based on serial number See README for known working settings Keyword arguments: serial_number -- The serial number (device_sn) of the inverter setting_type -- The setting to be configured (list of known working settings below) parameters -- A python dictionary of the parameters to be sent to the system based on the chosen setting_type, OR a python array which will be converted to a params dictionary Returns: A response from the server stating whether the configuration was successful or not """ setting_parameters = parameters #If we've been passed an array then convert it into a dictionary if isinstance(parameters, list): setting_parameters = {} for index, param in enumerate(parameters, start=1): setting_parameters['param' + str(index)] = param default_params = { 'op': 'mixSetApiNew', 'serialNum': serial_number, 'type': setting_type } settings_params = {**default_params, **setting_parameters} response = self.session.post(self.get_url('newTcpsetAPI.do'), params=settings_params) data = json.loads(response.content.decode('utf-8')) return data def update_ac_inverter_setting(self, serial_number, setting_type, parameters): """ Applies settings for specified system based on serial number See README for known working settings Keyword arguments: serial_number -- The serial number (device_sn) of the inverter setting_type -- The setting to be configured (list of known working settings below) parameters -- A python dictionary of the parameters to be sent to the system based on the chosen setting_type, OR a python array which will be converted to a params dictionary Returns: A response from the server stating whether the configuration was successful or not """ setting_parameters = parameters #If we've been passed an array then convert it into a dictionary if isinstance(parameters, list): setting_parameters = {} for index, param in enumerate(parameters, start=1): setting_parameters['param' + str(index)] = param default_params = { 'action': 'spaSet', 'serialNum': serial_number, 'type': setting_type } settings_params = {**default_params, **setting_parameters} response = self.session.post(self.get_url('tcpSet.do'), params=settings_params) data = json.loads(response.content.decode('utf-8')) return data ```

You need to send a full set of parameters to the API, even if they do nothing.

Sample script to set overnight charging on Growatt AC inverter ``` import growattServer import sys # check for SOC percent and whether to run if len(sys.argv) != 7: SOC = '40' startH = '0' startM = '40' endH = '04' endM = '30' run = '1' else: SOC = str(sys.argv[1]) startH = '{:02.0f}'.format(int(sys.argv[2])) startM = '{:02.0f}'.format(int(sys.argv[3])) endH = '{:02.0f}'.format(int(sys.argv[4])) endM = '{:02.0f}'.format(int(sys.argv[5])) run = str(sys.argv[6]) api = growattServer.GrowattApi() login_response = api.login('christopher.blanford@gmail.com', 'excellent-BEANS-f0rg0tt3n') # print(login_response) # Get a list of growatt plants. plant_list = api.plant_list(login_response['user']['id']) plant = plant_list['data'][0] plant_id = plant['plantId'] plant_info = api.plant_info(plant_id) device = plant_info['deviceList'][0] device_sn = device['deviceSn'] schedule_settings = ['100', # Charging power % SOC, # Stop charging SoC % startH, startM, # Schedule 1 - Start time endH, endM, # Schedule 1 - End time run, # Schedule 1 - Enabled/Disabled (1 = Enabled) '00','00', # Schedule 2 - Start time '00','00', # Schedule 2 - End time '0', # Schedule 2 - Enabled/Disabled (1 = Enabled) '00','00', # Schedule 3 - Start time '00','00', # Schedule 3 - End time '0'] # Schedule 3 - Enabled/Disabled (1 = Enabled) response = api.update_ac_inverter_setting(device_sn, 'spa_ac_charge_time_period', schedule_settings) print(response) ```
philwareublox commented 1 year ago

Many thanks grunkyb. Looks a little more effort than I was hoping, but your extra help with the set overnight charging was very helpful.

philwareublox commented 1 year ago

Hi @grunkyb.

Using the web browser network debug tool I can see the calls to the server.growatt.com server for the getTLXEnergyDayChart calls etc, but I don't understand how I can relate that to the url's I see you use and what this library uses, with the ".do" at the end of the URLs, like 'tcpSet.do'

How do I get to see the correct url for these operations?

Another example: https://server.growatt.com/panel/tlx/getTLXTotalData?plantId=1481705

thanks, Phil.

muppet3000 commented 1 year ago

I'm not sure how @grunkyb is doing his stuff, however this library has been made by looking at the castle made by the Android app (ShinePhone) not the web page. We use the app "NetCapture" running in the background to capture the calls that are made and then get the URLs from there. That's how I recommend testing and making any PRs to this library.

philwareublox commented 1 year ago

@muppet3000 - AH! That's what I'm missing... Thanks for the tip.

grunkyb commented 1 year ago

On my side, here's what I'm doing to make the changes.

  1. Open up the Web Developer settings on your browser of choice. Go to the Network tab.
  2. Go to the settings page on https://server.growatt.com/. Accessing Growatt settings on web
  3. After "logging in" with the growattYYYYMMDD password, select the settings you want to change. Here's I'm setting up my battery for overnight charging. Changing settings web
  4. When you've clicked on the button to send the request, look at the file name of the POST command and the structure of the request. Growatt Web server command change

For @philwareublox's request, this is what I send:

Growatt web server get energy chart request

and what the structure of the response is:

Growatt web server energy chart data response

I can add that in after I've cleaned up my PR with doing the settings on an AC-coupled inverter.

grunkyb commented 1 year ago

@philwareublox I realise I didn’t address the URL question. Will return to that this evening.

grunkyb commented 1 year ago

I withdraw my suggestion that I could get the .do script for the chart data. ☹

muppet3000 commented 1 year ago

The only thing to be aware of is that I don't believe all the URLs are the same between the mobile app and the web page. It would be worth repeating these tests/checks using the method both Indy & I have used to produce the rest of the API.

grunkyb commented 1 year ago

Fair enough about that. I'm on a "if it works"... There seems to be a lot of redundancy/redirects built into what's happening on the server side.

grunkyb commented 1 year ago

I'll see what shows up on a packet inspector on the iOS/MacOS side. I expect it'll be similar to Android, but it's worth checking for subtle differences.

grunkyb commented 1 year ago

@philwareublox Could you try out the get_ac_production_consumption function and SPA_chart_plot.py example I've set up in a branch in my fork? Neither the example nor the function are sufficiently generic yet, but I'm curious how it works on others' systems. Setting discovery was done using the Android packet capture method.

Growatt_prod-cons_2022-10-22