Python Plugin: Growatt Inverter

Python and python framework

Moderator: leecollings

User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Python Plugin: Growatt Inverter

Post by sincze »

Currently working on a plugin for Growatt Web.
Solved, Initial Release: https://github.com/sincze/Domoticz-Grow ... ver-Plugin

Looking at the API calls from the APP they have and it is unfortunately HTTP.

I have to post the password in md5 and the username to a url.

Code: Select all

            sendData = { 'Verb' : 'POST',
                         'URL'  : '/newTwoLoginAPI.do',
                         'Headers' : self.apiRequestHeaders(),
                         'Data': "password="+password.hexdigest()+"&userName="+Parameters["Username"]
                         }
            Domoticz.Debug("SendData: "+str(sendData))
            Connection.Send(sendData)
It all seems to work fine however... the framework automatically creates a Authorization Basic now and the webserver does not like that offering me a 400 (Bad Request).

Code: Select all

2019-08-11 00:36:53.666 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** POST./newTwoLoginAPI
2019-08-11 00:36:53.667 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** .do.HTTP/1.1..Author
2019-08-11 00:36:53.667 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ization:Basic.d2JsZX
2019-08-11 00:36:53.667 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ****************==..
Can I disable the Authorization Basic somehow?
Last edited by sincze on Sunday 25 August 2019 10:42, edited 1 time in total.
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
Dnpwwo
Posts: 820
Joined: Sunday 23 March 2014 9:00
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Melbourne, Australia
Contact:

Re: Python Plugin: Growatt Inverter

Post by Dnpwwo »

@sincze,

It only injects that header if both the actual Username & Password Parameters have values. To prevent that you can use a different Parameter to collect the Username (e.g Mode1 with a label of "Username")
The reasonable man adapts himself to the world; the unreasonable one persists to adapt the world to himself. Therefore all progress depends on the unreasonable man. George Bernard Shaw
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

Dnpwwo wrote: Sunday 11 August 2019 6:07 @sincze,

It only injects that header if both the actual Username & Password Parameters have values. To prevent that you can use a different Parameter to collect the Username (e.g Mode1 with a label of "Username")
Thanks for your always quick replys to keep people motivated and going. :D
I would also think you are right indeed. The test setup: Domoticz was running on: V4.11062 now updated to: Domoticz V4.11079

Plugin Parameters setup like:

Code: Select all

<param field="Address" label="Server Address" width="200px" required="true" default="server-api.growatt.com"/>        
<param field="Mode2" label="Portal Username" width="200px" required="true" default="admin"/>
<param field="Password" label="Portal Password" width="200px" required="true" password="true"/>
<param field="Mode1" label="Protocol" width="75px">
That is as described in your answer, right?

Plugin thing that needs to happen:

Code: Select all

        if (Status == 0):
            Domoticz.Debug("Google connected successfully.")            
            password=Parameters["Password"]
            password=hashlib.md5(str.encode(password))
            Domoticz.Debug("Password_1 is now: "+password.hexdigest())
            sendData = { 'Verb' : 'POST',
                         'URL'  : '/newTwoLoginAPI.do',
                         'Headers' : self.apiRequestHeaders(),
                         'Data': "password="+password.hexdigest()+"&userName="+Parameters["Mode2"]
                         }
            Domoticz.Debug("SendData: "+str(sendData))
            Connection.Send(sendData)
        else:
            Domoticz.Log("Failed to connect ("+str(Status)+") to: "+Parameters["Address"]+":"+Parameters["Mode1"]+" with error: "+Description)
But the logging shows BasicAuth

Code: Select all

[spoiler]2019-08-11 09:42:03.881 (Growatt via Google) Calling message handler 'onConnect'.
2019-08-11 09:42:03.881 (Growatt via Google) Google connected successfully.
2019-08-11 09:42:03.881 (Growatt via Google) Password_1 is now: ********************************************
2019-08-11 09:42:03.881 (Growatt via Google) SendData: {'URL': '/newTwoLoginAPI.do', 'Verb': 'POST', 'Headers': {'User-Agent': 'Domoticz/1.0', 'Connection': 'keep-alive', 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}, 'Data': 'password=********************************************&userName='******}
2019-08-11 09:42:03.881 (Growatt via Google) Pushing 'WriteDirective' on to queue
2019-08-11 09:42:03.882 (Growatt via Google) Processing 'WriteDirective' message
2019-08-11 09:42:03.882 (Growatt via Google) Sending 267 bytes of data
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** POST./newTwoLoginAPI
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** .do.HTTP/1.1..Author
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ization:Basic.******
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ****************==..
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** User-Agent:.Domoticz
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** /1.0..Connection:.ke
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ep-alive..Content-Ty
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** pe:.application/x-ww
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** w-form-urlencoded;.c
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** harset=utf-8..Conten
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** t-Length:.51....pass
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** word=***************
2019-08-11 09:42:03.882 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *****************&us
2019-08-11 09:42:03.883 (Growatt via Google) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** erName=*************
2019-08-11 09:42:03.919 (Growatt via Google) Pushing 'ReadEvent' on to queue
2019-08-11 09:42:03.919 (Growatt via Google) Queued asynchronous read aborted (server-api.growatt.com:80), [2] End of file.
2019-08-11 09:42:03.919 (Growatt via Google) Pushing 'DisconnectedEvent' on to queue
2019-08-11 09:42:03.933 (Growatt via Google) Processing 'ReadEvent' message
2019-08-11 09:42:03.933 (Growatt via Google) Received 394 bytes of data
2019-08-11 09:42:03.933 (Growatt via Google) 48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64 20 52 65 71 HTTP/1.1.400.Bad.Req
2019-08-11 09:42:03.933 (Growatt via Google) 75 65 73 74 0d 0a 44 61 74 65 3a 20 53 75 6e 2c 20 31 31 20 [/spoiler]
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

Changing both username and Password fields to 'ModeX' give me a result with HTTP protocol.
Maybe something not 100% in the code if both username and password fields are filled?

Code: Select all

        <param field="Address" label="Server Address" width="200px" required="true" default="server-api.growatt.com"/>        
        <param field="Mode2" label="Portal Username" width="200px" required="true" default="admin"/>
        <param field="Mode3" label="Portal Password" width="200px" required="true" password="true"/>
        <param field="Mode1" label="Protocol" width="75px">
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
Dnpwwo
Posts: 820
Joined: Sunday 23 March 2014 9:00
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Melbourne, Australia
Contact:

Re: Python Plugin: Growatt Inverter

Post by Dnpwwo »

@sincze,

You only had to change one to get the result. If you didn't delete and create the hardware again the old Parameter may have still been handed to the plugin?

Glad its sorted either way.
The reasonable man adapts himself to the world; the unreasonable one persists to adapt the world to himself. Therefore all progress depends on the unreasonable man. George Bernard Shaw
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

Dnpwwo wrote: Sunday 11 August 2019 10:27 @sincze,

You only had to change one to get the result. If you didn't delete and create the hardware again the old Parameter may have still been handed to the plugin?

Glad its sorted either way.
-> "If you didn't delete and create the hardware again the old Parameter may have still been handed to the plugin?"

That must be it did not DELETE the hardware ;-) I'll revert changes later back to your suggestions.
Currently trying to extract from HEADERS:

Code: Select all

2019-08-11 11:25:53.593 (Growatt Test) --->'Headers (5):
2019-08-11 11:25:53.593 (Growatt Test) ------->'Connection':'keep-alive'
2019-08-11 11:25:53.593 (Growatt Test) ------->'Set-Cookie':'JSESSIONID=********************; Path=/; HttpOnly
2019-08-11 11:25:53.593 (Growatt Test) ------->'Date':'Sun, 11 Aug 2019 09:25:53 GMT'
2019-08-11 11:25:53.593 (Growatt Test) ------->'Content-Type':'application/json;charset=UTF-8'
2019-08-11 11:25:53.593 (Growatt Test) ------->'Transfer-Encoding':'chunked'
The session ID so I can submit that in the following request as a Cookie.
Any functions for that on your shelf ? :D
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

The following is shown in the Domoticz log, for authentication I need:

Set-Cookie: JSESSIONID=*****
Set-Cookie: SERVERID=************

Code: Select all

2019-08-11 15:13:14.611 (Growatt Test) 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d 0a 44 61 74 HTTP/1.1.200.OK..Dat
2019-08-11 15:13:14.611 (Growatt Test) 65 3a 20 53 75 6e 2c 20 31 31 20 41 75 67 20 32 30 31 39 20 e:.Sun,.11.Aug.2019.
2019-08-11 15:13:14.611 (Growatt Test) 31 33 3a 31 33 3a 31 34 20 47 4d 54 0d 0a 43 6f 6e 74 65 6e 13:13:14.GMT..Conten
2019-08-11 15:13:14.611 (Growatt Test) 74 2d 54 79 70 65 3a 20 61 70 70 6c 69 63 61 74 69 6f 6e 2f t-Type:.application/
2019-08-11 15:13:14.611 (Growatt Test) 6a 73 6f 6e 3b 63 68 61 72 73 65 74 3d 55 54 46 2d 38 0d 0a json;charset=UTF-8..
2019-08-11 15:13:14.611 (Growatt Test) 54 72 61 6e 73 66 65 72 2d 45 6e 63 6f 64 69 6e 67 3a 20 63 Transfer-Encoding:.c
2019-08-11 15:13:14.611 (Growatt Test) 68 75 6e 6b 65 64 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 hunked..Connection:.
2019-08-11 15:13:14.611 (Growatt Test) 6b 65 65 70 2d 61 6c 69 76 65 0d 0a 53 65 74 2d 43 6f 6f 6b keep-alive..Set-Cook
2019-08-11 15:13:14.611 (Growatt Test) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ie:.JSESSIONID=*****
2019-08-11 15:13:14.612 (Growatt Test) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** 24F0B5EF897CF3AD720A
2019-08-11 15:13:14.612 (Growatt Test) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *******;.Path=/;.Htt
2019-08-11 15:13:14.612 (Growatt Test) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** pOnly..Set-Cookie:.S
2019-08-11 15:13:14.612 (Growatt Test) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ERVERID=************
2019-08-11 15:13:14.612 (Growatt Test) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ********************
2019-08-11 15:13:14.612 (Growatt Test) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ********************
If I use a function like the following I am only able to see/extract the JSESSIONID, what do I miss.

Code: Select all

def ProcessHTTPHeaders(httpDict):
    if isinstance(httpDict, dict):            
        Domoticz.Debug("ProcessHTTPHeaders ("+str(len(httpDict))+"):")
        for x in httpDict:
            if isinstance(httpDict[x], dict):
                if (x == "Headers"):
                    Domoticz.Debug("---> ProcessHTTPHeaders: Headers found")    
                    for y in httpDict[x]:
                        if (y == "Set-Cookie"):
                            Domoticz.Debug("---> ProcessHTTPHeaders Cookies found "+ str(httpDict[x][y]))
                            self.cookieAvailable = True
                            self.cookie=str(httpDict[x][y])
                        else:
                            Domoticz.Debug("---> ProcessHTTPHeaders No Set-Cookies something else "+ str(httpDict[x][y]))
                    else:
                        Domoticz.Debug("---> ProcessHTTPHeaders Set Cookies 2 something else "+ str(httpDict[x][y]))
                else:
                    Domoticz.Debug("---> ProcessHTTPHeaders No Headers something else "+ str(httpDict[x]))
        else:
            Domoticz.Debug("---> ProcessHTTPHeaders What did I find... "+ str(httpDict[x]))

....

Code: Select all

2019-08-11 15:23:42.400 (Growatt Test) Calling message handler 'onMessage'.
2019-08-11 15:23:42.400 (Growatt Test) HTTP Details (3):
2019-08-11 15:23:42.400 (Growatt Test) --->'Headers (5):
2019-08-11 15:23:42.400 (Growatt Test) ------->'Content-Type':'application/json;charset=UTF-8'
2019-08-11 15:23:42.400 (Growatt Test) ------->'Connection':'keep-alive'
2019-08-11 15:23:42.400 (Growatt Test) ------->'Transfer-Encoding':'chunked'
2019-08-11 15:23:42.400 (Growatt Test) ------->'Set-Cookie':'JSESSIONID=*****************; Path=/; HttpOnly
2019-08-11 15:23:42.400 (Growatt Test) ------->'Date':'Sun, 11 Aug 2019 13:23:42 GMT'
2019-08-11 15:23:42.400 (Growatt Test) --->'Status':'200'
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
Dnpwwo
Posts: 820
Joined: Sunday 23 March 2014 9:00
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Melbourne, Australia
Contact:

Re: Python Plugin: Growatt Inverter

Post by Dnpwwo »

@sincze,

The framework currently treats http headers as a simple dictionary of strings which may need to change. Duplicates should probably be handled via a list.

I'm travelling at the moment but can have a look later in the week.
The reasonable man adapts himself to the world; the unreasonable one persists to adapt the world to himself. Therefore all progress depends on the unreasonable man. George Bernard Shaw
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

Dnpwwo wrote: Monday 12 August 2019 23:28 @sincze,

The framework currently treats http headers as a simple dictionary of strings which may need to change. Duplicates should probably be handled via a list.

I'm travelling at the moment but can have a look later in the week.
That would be appreciated as I can only use existing functionalities. :lol:
I am not a (python) developer at all I study other peoples products, to learn, figure out how their plugin works and create my 'own'.
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
Dnpwwo
Posts: 820
Joined: Sunday 23 March 2014 9:00
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Melbourne, Australia
Contact:

Re: Python Plugin: Growatt Inverter

Post by Dnpwwo »

@sincze,

I pushed an upgrade tonight that allows headers to be strings, bytes, bytearrays or lists. When a list is supplied all the list values are injected as separate headers of the specifed type.

e.g:

Code: Select all

            sendData = { 'Verb' : 'GET',
                         'URL'  : '/',
                         'Headers' : { 'Content-Type': 'text/xml; charset=utf-8', \
                                       'Connection': 'keep-alive', \
                                       'Accept': 'Content-Type: text/html; charset=UTF-8', \
                                       'Host': Parameters["Address"]+":"+Parameters["Mode1"], \
                                       'User-Agent':'Domoticz/1.0',
				       'Cookie': ['CONSENT=WP.278be6',
						  b'ANID=AHWqTUkA63jkRUDy98OeQlgB2tzyyYqKWCHPADiSGOGc5ZVtduyCgaMVgzonI3Rn'] }
                       }
            Connection.Send(sendData)
will create 2 'Cookie' headers in the request.

Response Headers will also have List objects when multiple headers are returned
The reasonable man adapts himself to the world; the unreasonable one persists to adapt the world to himself. Therefore all progress depends on the unreasonable man. George Bernard Shaw
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

@Dnpwwo, Wow nice. Let me upgrade tonight or tomorrow to latest beta version and give it a try.
Just finished a Shelly1 project.
Now on to Sonoff at my parents place. :D
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

Currently on: Domoticz V4.11214

This is the request I need to make :lol:
growat0-request.png
growat0-request.png (9.46 KiB) Viewed 7739 times
I extracted the sessionId and serverId into 2 separate variables. :D

Code: Select all

    def ProcessCookie(self, httpDict):
        if isinstance(httpDict, dict):            
            Domoticz.Debug("Analyzing Data ("+str(len(httpDict))+"):")
            for x in httpDict:
                if isinstance(httpDict[x], dict):
                    if (x == "Headers"):
                        Domoticz.Debug("---> Headers found")    
                        for y in httpDict[x]:
                            if (y == "Set-Cookie"):        
                                Domoticz.Debug("---> Process Cookie Started")
                                self.cookieAvailable = True                                                                
                                self.sessionId = re.search(r"(?<=JSESSIONID=).*?(?=;)", str(httpDict[x][y])).group(0)
                                self.serverId =  re.search(r"(?<=SERVERID=).*?(?=;)", str(httpDict[x][y])).group(0)
                                Domoticz.Debug("---> Process Cookie Cookies found "+ str(httpDict[x][y]) )
Now finding out how to use it in the cookie ;-0

Code: Select all

def stationDataRequest(self):
        return {
            'Verb': 'POST',
            'URL': '/newTwoPlantAPI.do?op=getUserCenterEnertyDataByPlantid',
            'Cookie': "[JSESSIONID="+self.sessionid+" ,SERVERID="+self.serverid+"]",
            'Data': "plantId="+str(self.plantId)+"&language=1",
            'Headers': self.apiRequestHeaders()
        }
        
    def apiRequestHeaders(self):
        return {
            'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',            
            'Connection': 'keep-alive',
            'Host': 'server-api.growatt.com',
            'User-Agent': 'Domoticz/1.0',
            'Accept-Encoding': 'gzip'
        }
             
At the moment still out of luck.

Code: Select all

2019-08-19 21:14:20.488 (Growatt No Basic Auth) Received 305 bytes of data
2019-08-19 21:14:20.488 (Growatt No Basic Auth) 48 54 54 50 2f 31 2e 31 20 33 30 32 20 46 6f 75 6e 64 0d 0a HTTP/1.1.302.Found..
2019-08-19 21:14:20.488 (Growatt No Basic Auth) 44 61 74 65 3a 20 4d 6f 6e 2c 20 31 39 20 41 75 67 20 32 30 Date:.Mon,.19.Aug.20
2019-08-19 21:14:20.489 (Growatt No Basic Auth) 31 39 20 31 39 3a 31 34 3a 32 30 20 47 4d 54 0d 0a 43 6f 6e 19.19:14:20.GMT..Con
2019-08-19 21:14:20.489 (Growatt No Basic Auth) 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 30 0d 0a 43 6f 6e 6e tent-Length:.0..Conn
2019-08-19 21:14:20.489 (Growatt No Basic Auth) 65 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a ection:.keep-alive..
2019-08-19 21:14:20.489 (Growatt No Basic Auth) 53 65 74 2d 43 6f 6f 6b 69 65 3a 20 4a 53 45 53 53 49 4f 4e Set-Cookie:.JSESSION
2019-08-19 21:14:20.489 (Growatt No Basic Auth) 49 44 3d .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ID=AE*************80
2019-08-19 21:14:20.489 (Growatt No Basic Auth) .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 54F40F88DF2A465;.Pat
2019-08-19 21:14:20.489 (Growatt No Basic Auth) 68 3d 2f 3b 20 48 74 74 70 4f 6e 6c 79 0d 0a 4c 6f 63 61 74 h=/;.HttpOnly..Locat
2019-08-19 21:14:20.489 (Growatt No Basic Auth) 69 6f 6e 3a 20 2f 65 72 72 6f 72 2e 64 6f 3f 65 72 72 6f 72 ion:./error.do?error
2019-08-19 21:14:20.489 (Growatt No Basic Auth) 4d 65 73 73 3d 65 72 72 6f 72 4e 6f 4c 6f 67 69 6e 0d 0a 53 Mess=errorNoLogin..S
2019-08-19 21:14:20.489 (Growatt No Basic Auth) 65 74 2d 43 6f 6f 6b 69 65 3a 20 53 45 52 56 45 52 49 44 3d et-Cookie:.SERVERID=
2019-08-19 21:14:20.489 (Growatt No Basic Auth) .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 0e***************300
2019-08-19 21:14:20.489 (Growatt No Basic Auth) .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 77***************242
2019-08-19 21:14:20.489 (Growatt No Basic Auth) .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 060|1566242060;Path=
2019-08-19 21:14:20.489 (Growatt No Basic Auth) .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. /....
2019-08-19 21:14:20.489 (Growatt No Basic Auth) Pushing 'onMessageCallback' on to queue
2019-08-19 21:14:20.489 (Growatt No Basic Auth) Processing 'onMessageCallback' message
2019-08-19 21:14:20.489 (Growatt No Basic Auth) Calling message handler 'onMessage'.
2019-08-19 21:14:20.490 (Growatt No Basic Auth) HTTP Details (2):

Code: Select all

2019-08-19 21:19:58.126 (Growatt No Basic Auth) 50 4f 53 54 20 2f 6e 65 77 54 77 6f 50 6c 61 6e 74 41 50 49 POST./newTwoPlantAPI
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 2e 64 6f 3f 6f 70 3d 67 65 74 55 73 65 72 43 65 6e 74 65 72 .do?op=getUserCenter
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 45 6e 65 72 74 79 44 61 74 61 42 79 50 6c 61 6e 74 69 64 20 EnertyDataByPlantid.
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 48 54 54 50 2f 31 2e 31 0d 0a 55 73 65 72 2d 41 67 65 6e 74 HTTP/1.1..User-Agent
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 3a 20 44 6f 6d 6f 74 69 63 7a 2f 31 2e 30 0d 0a 43 6f 6e 6e :.Domoticz/1.0..Conn
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 65 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a ection:.keep-alive..
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 41 63 63 65 70 74 2d 45 6e 63 6f 64 69 6e 67 3a 20 67 7a 69 Accept-Encoding:.gzi
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 70 0d 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 61 70 70 p..Content-Type:.app
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 6c 69 63 61 74 69 6f 6e 2f 78 2d 77 77 77 2d 66 6f 72 6d 2d lication/x-www-form-
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 75 72 6c 65 6e 63 6f 64 65 64 3b 20 63 68 61 72 73 65 74 3d urlencoded;.charset=
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 75 74 66 2d 38 0d 0a 48 6f 73 74 3a 20 73 65 72 76 65 72 2d utf-8..Host:.server-
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 61 70 69 2e 67 72 6f 77 61 74 74 2e 63 6f 6d 0d 0a 43 6f 6e api.growatt.com..Con
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 32 35 0d 0a 0d 0a 70 tent-Length:.25....p
2019-08-19 21:19:58.126 (Growatt No Basic Auth) 6c 61 6e 74 49 64 3d 31 35 35 .. .. .. .. .. .. .. .. 75 61 lantId=******&langua
2019-08-19 21:19:58.127 (Growatt No Basic Auth) 67 65 3d 31 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ge=1
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

Code: Select all

    def stationDataRequest(self):
        return {
            'Verb': 'POST',
            'URL': '/newTwoPlantAPI.do?op=getUserCenterEnertyDataByPlantid',
            'Cookie': str(self.cookie),
            'plantId': str(self.plantId),
            'language': 1,
            'Headers': self.apiRequestHeaders()
        }
Post via Fiddler:

Code: Select all

POST http://server-api.growatt.com/newTwoPlantAPI.do?op=getUserCenterEnertyDataByPlantid HTTP/1.1
Cookie: JSESSIONID=**********************************; SERVERID=*******************|************|***************
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 25
User-Agent: Domoticz/1.0
Host: server-api.growatt.com
Connection: Keep-Alive
Accept-Encoding: gzip

plantId=UUUUUU&language=1
Post via Python Plugin

Code: Select all

2019-08-19 21:39:35.372 (Growatt No Basic Auth) Cookie Available StationDataRequest: {'Verb': 'POST', 'URL': '/newTwoPlantAPI.do?op=getUserCenterEnertyDataByPlantid', 'language': 1, 'plantId': 'UUUUUU', 'Headers': {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'User-Agent': 'Domoticz/1.0', 'Accept-Encoding': 'gzip', 'Host': 'server-api.growatt.com', 'Connection': 'keep-alive'}, 'Cookie': 'JSESSIONID=**********************************; SERVERID=*******************|************|***************'}

mmmm... what did I miss.
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
Dnpwwo
Posts: 820
Joined: Sunday 23 March 2014 9:00
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Melbourne, Australia
Contact:

Re: Python Plugin: Growatt Inverter

Post by Dnpwwo »

@sincze ,

You aren't creating a proper Python list structure. If you check my sample the [ and ] are not enclosed in quotes.

Code: Select all

'Cookie': "[JSESSIONID="+self.sessionid+" ,SERVERID="+self.serverid+"]",
should be

Code: Select all

'Cookie': ["JSESSIONID="+self.sessionid, "SERVERID="+self.serverid],
The reasonable man adapts himself to the world; the unreasonable one persists to adapt the world to himself. Therefore all progress depends on the unreasonable man. George Bernard Shaw
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

So I created this function:

Code: Select all

    def apiRequestHeaders_withcookie(self):
        return {
            'Verb': 'POST',
            'URL': '/newTwoPlantAPI.do?op=getUserCenterEnertyDataByPlantid',
            'Headers' : { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
                          'Connection': 'keep-alive',
                          'Host': 'server-api.growatt.com',
                          'User-Agent': 'Domoticz/1.0',
                          'Accept-Encoding': 'gzip',
                          'Cookie': ["JSESSIONID="+self.sessionid, "SERVERID="+self.serverid],
                          'Data': "plantId"+str(self.plantId)+"&language=1"
                       }
        }
It should represent all data in correct format (checked with separate python program in cli).

Code: Select all

I received: {'URL': '/newTwoPlantAPI.do?op=getUserCenterEnertyDataByPlantid', 'Headers': {'Accept-Encoding': 'gzip', 'Data': 'plantId=12345&language=1', 'Host': 'server-api.growatt.com', 'User-Agent': 'Domoticz/1.0', 'Connection': 'keep-alive', 'Cookie': ['JSESSIONID=********9********DEA18A1A049C69A', 'SERVERID=0e6c72164********30********1651c|********|********], 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}, 'Verb': 'POST'}
However while executing debugging in the plugin using:

Code: Select all

Domoticz.Debug("Cookie 1 apiURL StationDataRequest: "+str(self.apiRequestHeaders_withcookie() ) )
It stops exactly there.. No entry in the Domoticz log.
If I remove the line the plugin continues... ofcourse the

Code: Select all

Connection.Send(self.apiRequestHeaders_withcookie() )
also does not work.. I missed something in the function.. Don't know yet what...
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
Dnpwwo
Posts: 820
Joined: Sunday 23 March 2014 9:00
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Melbourne, Australia
Contact:

Re: Python Plugin: Growatt Inverter

Post by Dnpwwo »

@sincze,

Not sure why the debug statement doesn't work but your 'Data' line is wrong.
  • it isn't a header so should be outside the header dictionary (i.e after the "}")
  • "plantId"+str should be "plantId="+str
if you want to debug the structure I made a better logging function recently:

Code: Select all

def DumpHTTPResponseToLog(httpResp, level=0):
    if (level==0): Domoticz.Debug("HTTP Details ("+str(len(httpResp))+"):")
    indentStr = ""
    for x in range(level):
        indentStr += "----"
    if isinstance(httpResp, dict):
        for x in httpResp:
            if not isinstance(httpResp[x], dict) and not isinstance(httpResp[x], list):
                Domoticz.Debug(indentStr + ">'" + x + "':'" + str(httpResp[x]) + "'")
            else:
                Domoticz.Debug(indentStr + ">'" + x + "':")
                DumpHTTPResponseToLog(httpResp[x], level+1)
    elif isinstance(httpResp, list):
        for x in httpResp:
            Domoticz.Debug(indentStr + "['" + x + "']")
    else:
        Domoticz.Debug(indentStr + ">'" + x + "':'" + str(httpResp[x]) + "'")
don't be put off by the name, it will handle a Request as well, try that.
The reasonable man adapts himself to the world; the unreasonable one persists to adapt the world to himself. Therefore all progress depends on the unreasonable man. George Bernard Shaw
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

"Not sure why the debug statement doesn't work but your 'Data' line is wrong."

This gives me an output with the dumphttp function, however due to missing cookie a 302 no login.:

Code: Select all

    def apiRequestHeaders_withcookie(self):
        return {
            'Verb': 'POST',
            'URL': '/newTwoPlantAPI.do?op=getUserCenterEnertyDataByPlantid',
            'Headers' : { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
#                          'Cookie': ["JSESSIONID="+self.sessionid, "SERVERID="+self.serverid],
                          'Connection': 'keep-alive',
                          'Host': 'server-api.growatt.com',
                          'User-Agent': 'Domoticz/1.0',
                          'Accept-Encoding': 'gzip',
                        },
            'Data': "plantId="+str(self.plantId)+"&language=1",
        }
This does not (no output just as described earlier).

Code: Select all

    def apiRequestHeaders_withcookie(self):
        return {
            'Verb': 'POST',
            'URL': '/newTwoPlantAPI.do?op=getUserCenterEnertyDataByPlantid',
            'Headers' : { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
                          'Cookie': ["JSESSIONID="+self.sessionid, "SERVERID="+self.serverid],
                          'Connection': 'keep-alive',
                          'Host': 'server-api.growatt.com',
                          'User-Agent': 'Domoticz/1.0',
                          'Accept-Encoding': 'gzip',
                        },
            'Data': "plantId="+str(self.plantId)+"&language=1",
        }
The offending article is the Cookie line.. but why
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
Dnpwwo
Posts: 820
Joined: Sunday 23 March 2014 9:00
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Melbourne, Australia
Contact:

Re: Python Plugin: Growatt Inverter

Post by Dnpwwo »

When troubleshooting I always keep it simple, its always the assumptions that kill you.

Try replacing your 'cookie' line with mine which is hard coded. It will fail the logon but should at least dump the output and do the send.

Also turn debugging up to the max.
The reasonable man adapts himself to the world; the unreasonable one persists to adapt the world to himself. Therefore all progress depends on the unreasonable man. George Bernard Shaw
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

Debug is set to all.

using @Dnpwwo excluding technique :D figured out the trick.

Code: Select all

'Cookie': ['JSESSIONID='+self.sessionid, 'SERVERID='+self.serverid]
Success.
Last edited by sincze on Wednesday 21 August 2019 20:56, edited 1 time in total.
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
User avatar
sincze
Posts: 1302
Joined: Monday 02 June 2014 22:46
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.4
Location: Netherlands / Breda Area
Contact:

Re: Python Plugin: Growatt Inverter

Post by sincze »

So "DumpHTTPResponseToLog(self.apiRequestHeaders_withcookie())"
Gives me the following, however no usefull data JSON from the server. :(
But at least we are moving forward. :D

Code: Select all

2019-08-21 20:40:06.521 (Growatt) HTTP Details (4):
2019-08-21 20:40:06.521 (Growatt) >'Verb':'POST'
2019-08-21 20:40:06.521 (Growatt) >'Headers':
2019-08-21 20:40:06.521 (Growatt) ---->'Host':'server-api.growatt.com'
2019-08-21 20:40:06.521 (Growatt) ---->'Accept-Encoding':'gzip'
2019-08-21 20:40:06.521 (Growatt) ---->'Connection':'keep-alive'
2019-08-21 20:40:06.521 (Growatt) ---->'User-Agent':'Domoticz/1.0'
2019-08-21 20:40:06.521 (Growatt) ---->'Cookie':
2019-08-21 20:40:06.521 (Growatt) --------['JSESSIONID=******']
2019-08-21 20:40:06.521 (Growatt) --------['SERVERID=******']
2019-08-21 20:40:06.521 (Growatt) ---->'Content-Type':'application/x-www-form-urlencoded; charset=utf-8'
2019-08-21 20:40:06.521 (Growatt) >'Data':'plantId=******&language=1'
2019-08-21 20:40:06.521 (Growatt) >'URL':'/newTwoPlantAPI.do?op=getUserCenterEnertyDataByPlantid'
2019-08-21 20:40:06.522 (Growatt) Good Response received from Growatt, Dropping connection.
2019-08-21 20:40:06.839 (Growatt) Connection 'server-api.growatt.com:80' released by Python, reference count is 1.
2019-08-21 20:40:06.839 (Growatt) Pushing 'DisconnectDirective' on to queue
2019-08-21 20:40:06.872 (Growatt) Processing 'DisconnectDirective' message
2019-08-21 20:40:06.872 (Growatt) Disconnect directive received for 'server-api.growatt.com:80'.
2019-08-21 20:40:06.872 (Growatt) Handling TCP disconnect, socket (server-api.growatt.com:80) is connected
2019-08-21 20:40:06.872 (Growatt) Queued asynchronous read aborted (server-api.growatt.com:80), [2] End of file.
2019-08-21 20:40:06.872 (Growatt) Pushing 'DisconnectedEvent' on to queue
2019-08-21 20:40:06.873 (Growatt) Processing 'DisconnectedEvent' message
2019-08-21 20:40:06.873 (Growatt) Disconnect event received for 'server-api.growatt.com:80'.
2019-08-21 20:40:06.873 (Growatt) Pushing 'onDisconnectCallback' on to queue
2019-08-21 20:40:06.873 (Growatt) Processing 'onDisconnectCallback' message
2019-08-21 20:40:06.873 (Growatt) Calling message handler 'onDisconnect'.
2019-08-21 20:40:06.873 (Growatt) onDisconnect called for connection to: server-api.growatt.com:80
2019-08-21 20:40:06.873 (Growatt) Deallocating connection object 'HTTP Test' (server-api.growatt.com:80).
2019-08-21 20:40:15.841 (Growatt) Pushing 'onHeartbeatCallback' on to queue
Pass2php
LAN: RFLink, P1, OTGW, MySensors
USB: RFXCom, ZWave, Sonoff 3
MQTT: ZIgbee2MQTT,
ZWAVE: Zwave-JS-UI
WIFI: Mi-light, Tasmota, Xiaomi Shelly
Solar: Omnik, PVOutput
Video: Kodi, Harmony HUB, Chromecast
Sensors: You name it I got 1.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest