indykoning / PyPi_GrowattServer

MIT License
70 stars 32 forks source link

api.mix_detail returns null object, despite successful login #25

Closed ntropy-esa closed 2 years ago

ntropy-esa commented 2 years ago

Hi there,

Thanks for you package. I am trying to use growattServer to download and save the 5-min resolution data from the inverter (pv production, load consumption, export/import, battery production, battery charging).

I manage to login & retrieve some basic data (using functions api.login / api.plant_list / api.plant_info), but as soon as I try to get actual data with api.mix_detail or api.plant_detail, I errors returned by the API.

For instance, when running api.mix_detail with the two print statements below:

def mix_detail(self, mix_id, plant_id, timespan=Timespan.day, date=None):
        date_str = self.__get_date_string(timespan, date)
        print('date', date_str)

        response = self.session.post(self.get_url('newMixApi.do'), params={
            'op': 'getEnergyProdAndCons_KW',
            'plantId': plant_id,
            'mixId': mix_id,
            'type': timespan.value,
            'date': date_str
        })
        print('response', response) ## h
        print('response content', response.content)
        data = json.loads(response.content.decode('utf-8'))

        return data['obj']

I get:

response = <Response [200]>

response.content = b'{"result":0,"obj":null,"msg":"504"}'

I understand that login is successful (Response 200), a response is returned, however, the error message in the response is 504, meaning timeout error, and the obj is null.

I don't have a clue why I am getting this results. If I login on growatt's website, all the data is visible (not issue on that side). Could it be that growatt is shifting to its new web portal (new: https://server.growatt.com/login ; old: https://server.growatt.com/login.do)?

Kindly, Elias.

muppet3000 commented 2 years ago

Hi @ntropy-esa do you have a mix (aka Hybrid) system. On the growatt website it's this bit: image (Where it says "Device Type").

Alternatively if you run something like this:

#!/usr/bin/python3

import growattServer
import datetime
import getpass
import pprint

pp = pprint.PrettyPrinter(indent=4)

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

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

api = growattServer.GrowattApi()
login_response = api.login(username, user_pass)

plant_list = api.plant_list(login_response['user']['id'])
pp.pprint(plant_list)

print("")

for plant in plant_list['data']:
  plant_id = plant['plantId']
  plant_info=api.plant_info(plant_id)

  for device in plant_info['deviceList']:
    device_sn = device['deviceSn']
    device_type = device['deviceType']
    print("Device type: %s" % (device_type))

It'll print out your deviceType.

The functions that start with the mix keyword will only work with Mix aka Hybrid systems.

ntropy-esa commented 2 years ago

You're right. I didn't get these subtleties at first. My device is an SPF3500 (which they sell as a hybrid inverter, but it's more of an offgrid one). The deviceType is 'storage'. In the other python library you reference, I found functions to export data from such devices, on the branch storage (not master) https://github.com/Sjord/growatt_api_client/blob/storage/growatt/__init__.py

Remark: in the ShineApp, I get access to more data (by clicking on the inverter) than is available on the website of Growatt. Working on it to extract the xhr requests from the app (looks like that https://server-api.growatt.com/newStorageAPI.do?op=getDayLineStorage&id=******&date=2022-03-26&type=8) (type corresponding to one time series of one variable)

muppet3000 commented 2 years ago

Glad you sorted it out. If you want to add functionality to get those values to this library you're more than welcome to submit a pull request with a new function using the url you mentioned above, you'd probably need to make the date an argument (see how we've done it for other functions). That way others can hopefully benefit from it too.

ntropy-esa commented 2 years ago

Hi there - check out this here, I made the pull request on Sjord's library since I move to using his storage functions. https://github.com/Sjord/growatt_api_client/pull/29

flopp999 commented 8 months ago

You're right. I didn't get these subtleties at first. My device is an SPF3500 (which they sell as a hybrid inverter, but it's more of an offgrid one). The deviceType is 'storage'. In the other python library you reference, I found functions to export data from such devices, on the branch storage (not master) https://github.com/Sjord/growatt_api_client/blob/storage/growatt/__init__.py

Remark: in the ShineApp, I get access to more data (by clicking on the inverter) than is available on the website of Growatt. Working on it to extract the xhr requests from the app (looks like that https://server-api.growatt.com/newStorageAPI.do?op=getDayLineStorage&id=******&date=2022-03-26&type=8) (type corresponding to one time series of one variable)

Hi, would you please reply how you got it to work? Did you use cURL? I am trying with cURL but it never works

ntropy-esa commented 8 months ago

Hi @flopp999 :-) Not sure I understand what cURL is. In the end, I am no longer working on extracting the data from that SPF3500. But I can paste here the last python script I used to extract the data (for each day of operation) and save it to CSV, for further processsing.

import sys
import datetime
import json
import pandas as pd
import glob 

from growatt import hash_password, GrowattApi, Timespan

username = sys.argv[1]
password = sys.argv[2] 
retrieveAll = sys.argv[3]  # True or False, as string

with GrowattApi() as api:
    api.login(username, password)
    plant_info = api.plant_list()

    plant_id = plant_info["data"][0]["plantId"]

    device_list = api.get_all_device_list(plant_id)
    device_sn = device_list['deviceList'][0]['deviceSn'] 

    storage = api.storage(plant_id, device_sn)
    if retrieveAll == "True":
        date_start = datetime.date.fromisoformat('2022-02-07')
    else:
        # next date since last load
        allfilenames = glob.glob('data/storage-data_*.csv')
        alldates = [datetime.date.fromisoformat(k.lstrip('data\\storage-data_').rstrip('.csv')) for k in allfilenames] # they happen to be sorted from old to new, since alphabetical order
        date_start = alldates[-1]
        print(date_start)

    date_yesterday = datetime.date.today()-datetime.timedelta(days=1)
    daterange = pd.date_range(date_start, date_yesterday)
    L1 = [8,10,3,9,7,11,12,13,14,15,16,17,18]
    L2 = ['PPV1', 'PPV2', 'PV1 Voltage', 'PV2 Voltage', 'Battery SOC', 'VBattery', 'OutPutPower', 'OutputVoltage', 'Grid Voltage', 'EPV Today', 'EAC Today', 'Ebat Today', 'EBatDischarge Today'] 
    Ldict = {k:v for k,v in zip(L1,L2)}

    for day in daterange:
        print('retrieving...', day.strftime("%Y-%m-%d"))
        # 1/ get_energy_prod_and_cons_data
        storage_energy = storage.get_energy_prod_and_cons_data(date=day.strftime("%Y-%m-%d"))
        df = pd.DataFrame.from_dict(storage_energy['chartData']).T
        df.index.names = ['Time']
        df.sort_values(by='Time', axis='index', inplace=True)
        df.to_csv('data/storage-data_'+day.strftime("%Y-%m-%d")+'.csv')
        # 2/ get_storage_energy_data
        SOC_data = storage.get_storage_energy_data(date=day.strftime("%Y-%m-%d"))
        df = pd.DataFrame.from_dict(SOC_data).T
        df.index.names = ['Time']
        df.sort_values(by='Time', axis='index', inplace=True)
        df.to_csv('data/storage-soc_'+day.strftime("%Y-%m-%d")+'.csv')
        # 3/ get_storage_day_line
        daylines = storage.get_storage_day_line(date=day.strftime("%Y-%m-%d"), typ=0)
        for key, val in daylines.items():
              val = { x:[y] for x,y in val.items() }
              print(val)
              df = pd.DataFrame.from_dict(val).T
              df.index.names = ['Time']
              df.sort_values(by='Time', axis='index', inplace=True)
              df.to_csv('data/storage-daylines_'+day.strftime("%Y-%m-%d")+'_'+str(key)+'-'+Ldict[key]+'.csv')

It's based on the API for Storage devices from https://github.com/Sjord/growatt_api_client/tree/storage

See also the pull request I had made there, defining the function .get_storage_day_line https://github.com/Sjord/growatt_api_client/commit/697e7eee7a3855258bd9d3859ac9f57d46d510b7 I am self-surprised that I had written a docstring to the function