Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't get chartdata #33

Open
philwareublox opened this issue Sep 20, 2022 · 14 comments
Open

Can't get chartdata #33

philwareublox opened this issue Sep 20, 2022 · 14 comments

Comments

@philwareublox
Copy link

philwareublox commented Sep 20, 2022

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
Copy link
Contributor

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

@philwareublox
Copy link
Author

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

@philwareublox philwareublox changed the title Can't get chartdata] Can't get chartdata Sep 21, 2022
@grunkyb
Copy link
Contributor

grunkyb commented Sep 21, 2022

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('[email protected]',
'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)


</details>

@philwareublox
Copy link
Author

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
Copy link
Author

philwareublox commented Sep 30, 2022

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
Copy link
Contributor

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
Copy link
Author

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

@grunkyb
Copy link
Contributor

grunkyb commented Oct 1, 2022

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
Copy link
Contributor

grunkyb commented Oct 1, 2022

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

@grunkyb
Copy link
Contributor

grunkyb commented Oct 2, 2022

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

@muppet3000
Copy link
Contributor

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
Copy link
Contributor

grunkyb commented Oct 3, 2022

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
Copy link
Contributor

grunkyb commented Oct 3, 2022

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
Copy link
Contributor

grunkyb commented Oct 23, 2022

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants