LogicMachine Forum
Goodwe - Printable Version

+- LogicMachine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: Goodwe (/showthread.php?tid=4286)



Goodwe - Danny - 06.10.2022

Dear readers,

I am looking for the api of Goodwe inverters (Semsportal)
on Github I found the below but I don't know how to integrate this into the W4K.

Can someone help me with this?

thanks in advance


Code:
#####User details here##### ##Givenergy Portal API Key Goes Below between " " email = "username@email.com" password = "password_here" Powerstation_Id = "Portal_ID_here" ########end user input############# ##Login to SEMS API ## headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" headers.Add("sec-ch-ua", "`" Not A;Brand`";v=`"99`", `"Chromium`";v=`"99`", `"Microsoft Edge`";v=`"99`"") headers.Add("Accept", "application/json, text/javascript, */*; q=0.01") headers.Add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") headers.Add("X-Requested-With", "XMLHttpRequest") headers.Add("sec-ch-ua-mobile", "?0") headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.39") headers.Add("sec-ch-ua-platform", "`"Windows`"") headers.Add("Origin", "https://eu.semsportal.com") headers.Add("Sec-Fetch-Site", "same-origin") headers.Add("Sec-Fetch-Mode", "cors") headers.Add("Sec-Fetch-Dest", "empty") headers.Add("Referer", "https://eu.semsportal.com/home/login") headers.Add("Accept-Language", "en-GB,en;q=0.9,en-US;q=0.8") body = "account=$email&pwd=$password&code=" response = Invoke-WebRequest 'https://eu.semsportal.com/Home/Login' -Method 'POST' -Headers $headers -Body $body -SessionVariable session response.Content | Out-File -FilePath.\Login.txt session | Out-File -FilePath.\Session.txt Write-Output "Login Response Saved to: Login.txt" headers2 = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" headers2.Add("sec-ch-ua", "`" Not A;Brand`";v=`"99`", `"Chromium`";v=`"99`", `"Microsoft Edge`";v=`"99`"") headers2.Add("Accept", "*/*") headers2.Add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") headers2.Add("X-Requested-With", "XMLHttpRequest") headers2.Add("sec-ch-ua-mobile", "?0") headers2.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.39") headers2.Add("sec-ch-ua-platform", "`"Windows`"") headers2.Add("Origin", "https://eu.semsportal.com") headers2.Add("Sec-Fetch-Site", "same-origin") headers2.Add("Sec-Fetch-Mode", "cors") headers2.Add("Sec-Fetch-Dest", "empty") headers2.Add("Referer", "https://eu.semsportal.com/PowerStation/PowerStatusSnMin/$Powerstation_Id") headers2.Add("Accept-Language", "en-GB,en;q=0.9,en-US;q=0.8") body2 = "str=%7B%22api%22%3A%22v1%2FPowerStation%2FGetMonitorDetailByPowerstationId%22%2C%22param%22%3A%7B%22powerStationId%22%3A%22$Powerstation_Id%22%7D%7D" response2 = Invoke-RestMethod 'https://eu.semsportal.com/GopsApi/Post?s=v1/PowerStation/GetMonitorDetailByPowerstationId' -Method 'POST' -Headers $headers2 -Body $body2 -WebSession $session response2.data.inverter[0].invert_full | ConvertTo-Json -Depth 10 | Out-File -FilePath .\Data\GoodweData.txt -Encoding ASCII Write-Output "Data Saved to: GoodweData.txt" Write-Output "All done - Closing Powershell in 5...." start-sleep -s 5 Exit



RE: Goodwe - admin - 07.10.2022

Use this as a starting point:
Code:
require('socket.http') require('ltn12') require('json') email = "username@email.com" password = "password_here" powerstation_id = "Portal_ID_here" function encodepost(t)   local res = {}   local esc = require('socket.url').escape   for k, v in pairs(t) do     res[ #res + 1 ] = esc(k) .. '=' .. esc(v)   end   return table.concat(res, '&') end body = encodepost({   account = email,   pwd = password,   code = "", }) headers = {   ["sec-ch-ua"] = "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"99\", \"Microsoft Edge\";v=\"99\"",   ["Accept"] = "application/json, text/javascript, */*; q=0.01",   ["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8",   ["X-Requested-With"] = "XMLHttpRequest",   ["sec-ch-ua-mobile"] = "?0",   ["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.39",   ["sec-ch-ua-platform"] = "\"Windows\"",   ["Origin"] = "https://eu.semsportal.com",   ["Sec-Fetch-Site"] = "same-origin",   ["Sec-Fetch-Mode"] = "cors",   ["Sec-Fetch-Dest"] = "empty",   ["Referer"] = "https://eu.semsportal.com/home/login",   ["Accept-Language"] = "en-GB,en;q=0.9,en-US;q=0.8",   ["Content-Length"] = #body, } response = {} res, code, hdrs, stat = socket.http.request({   url = "https://eu.semsportal.com/Home/Login",   method = "POST";   headers = headers,   source = ltn12.source.string(body),   sink = ltn12.sink.table(response), }) log(res, code, hdrs, stat, response)
Post what you get in Logs tab.


RE: Goodwe - Danny - 07.10.2022

Code:
* arg: 1   * number: 1 * arg: 2   * number: 200 * arg: 3   * table:    ["server"]     * string: nginx    ["content-type"]     * string: application/json; charset=utf-8    ["connection"]     * string: close    ["content-length"]     * string: 109    ["cache-control"]     * string: private    ["x-aspnetmvc-version"]     * string: 5.2    ["set-cookie"]     * string: ASP.NET_SessionId=pltv2ppqqnlhzlhalnlmh3gu; path=/; HttpOnly; SameSite=Lax, ASP.NET_SessionId=pltv2ppqqnlhzlhalnlmh3gu; path=/; HttpOnly; SameSite=Lax, language=en; path=/    ["x-powered-by"]     * string: ASP.NET    ["x-aspnet-version"]     * string: 4.0.30319    ["date"]     * string: Fri, 07 Oct 2022 06:57:38 GMT * arg: 4   * string: HTTP/1.1 200 OK * arg: 5   * table:    [1]     * string: {"code":0,"msg":"","data":{"redirect":"/PowerStation/PowerStatusSnMin/--Inverter id--"}}
 Here the log tab,
Thanks admin
how do i get the power logs from the inverter?


RE: Goodwe - admin - 07.10.2022

Try this:
Code:
require('socket.http') require('ltn12') require('json') email = "username@email.com" password = "password_here" powerstation_id = "Portal_ID_here" function encodepost(t)   local res = {}   local esc = require('socket.url').escape   for k, v in pairs(t) do     res[ #res + 1 ] = esc(k) .. '=' .. esc(v)   end   return table.concat(res, '&') end body = encodepost({   account = email,   pwd = password,   code = "", }) headers = {   ["sec-ch-ua"] = "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"99\", \"Microsoft Edge\";v=\"99\"",   ["Accept"] = "application/json, text/javascript, */*; q=0.01",   ["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8",   ["X-Requested-With"] = "XMLHttpRequest",   ["sec-ch-ua-mobile"] = "?0",   ["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.39",   ["sec-ch-ua-platform"] = "\"Windows\"",   ["Origin"] = "https://eu.semsportal.com",   ["Sec-Fetch-Site"] = "same-origin",   ["Sec-Fetch-Mode"] = "cors",   ["Sec-Fetch-Dest"] = "empty",   ["Referer"] = "https://eu.semsportal.com/home/login",   ["Accept-Language"] = "en-GB,en;q=0.9,en-US;q=0.8",   ["Content-Length"] = #body, } response = {} res, code, hdrs, stat = socket.http.request({   url = "https://eu.semsportal.com/Home/Login",   method = "POST",   headers = headers,   source = ltn12.source.string(body),   sink = ltn12.sink.table(response), }) cookie = hdrs['set-cookie'] if cookie then   headers["Cookie"] = cookie:split(';')[1] end headers["Referer"] = "https://eu.semsportal.com/PowerStation/PowerStatusSnMin/" .. powerstation_id log(res, code, hdrs, stat, response) body = encodepost({   str = json.encode({     api = "v1/PowerStation/GetMonitorDetailByPowerstationId",     param = {       powerStationId = powerstation_id     }   }) }) response = {} res, code, hdrs, stat = socket.http.request({   url = "https://eu.semsportal.com/GopsApi/Post?s=v1/PowerStation/GetMonitorDetailByPowerstationId",   method = "POST",   headers = headers,   source = ltn12.source.string(body),   sink = ltn12.sink.table(response), }) log(res, code, hdrs, stat, response)



RE: Goodwe - Danny - 07.10.2022

Code:
* arg: 1   * number: 1 * arg: 2   * number: 200 * arg: 3   * table:    ["server"]     * string: nginx    ["content-type"]     * string: application/json; charset=utf-8    ["connection"]     * string: close    ["content-length"]     * string: 55    ["cache-control"]     * string: private    ["x-aspnetmvc-version"]     * string: 5.2    ["date"]     * string: Fri, 07 Oct 2022 10:32:21 GMT    ["x-powered-by"]     * string: ASP.NET    ["x-aspnet-version"]     * string: 4.0.30319 * arg: 4   * string: HTTP/1.1 200 OK * arg: 5   * table:    [1]     * string: {"code":-100,"msg":"Network failure, please try again"}



RE: Goodwe - admin - 07.10.2022

Can you send your credentials via PM?


RE: Goodwe - admin - 10.10.2022

Try this:
Code:
require('socket.http') require('ltn12') require('json') email = "..." password = "..." powerstation_id = "..." function encodepost(t)   local res = {}   local esc = require('socket.url').escape   for k, v in pairs(t) do     res[ #res + 1 ] = esc(k) .. '=' .. esc(v)   end   return table.concat(res, '&') end body = json.encode({   account = email,   pwd = password,   is_local = True,   agreement_agreement = 1 }) token = json.encode({   uid = "",   timestamp = 0,   token = "",   client = "web",   version = "",   language = "en-GB", }) headers = {   ["Content-Type"] = "application/json; charset=utf-8",   ["Accept"] = "Content-Type: application/json; charset=UTF-8",   ["Content-Length"] = #body,   ["Accept"] = "application/json",   ["token"] = token, } response = {} res, code, hdrs, stat = socket.http.request({   url = "https://eu.semsportal.com/api/v2/Common/CrossLogin",   method = "POST",   headers = headers,   source = ltn12.source.string(body),   sink = ltn12.sink.table(response), }) if res and code == 200 then   response = json.decode(table.concat(response))   headers.token = json.encode(response.data)   url = response.api .. "/v2/PowerStation/GetMonitorDetailByPowerstationId"   response = {}   body = json.encode({ powerStationId = powerstation_id })   headers["Content-Length"] = #body   res, code, hdrs, stat = socket.http.request({     url = url,     method = "POST",     headers = headers,     source = ltn12.source.string(body),     sink = ltn12.sink.table(response),   })   response = json.decode(table.concat(response))   invert_full = response.data.inverter[1].invert_full   log(invert_full) end



RE: Goodwe - Danny - 10.10.2022

Admin this works perfectly thanks
I will continue with this.


RE: Goodwe - pioneersteffen - 07.01.2023

(10.10.2022, 12:10)Danny Wrote: Admin this works perfectly thanks
I will continue with this.

@Danny: What is your experience with the script? Is it working? Have you made further modifications?

Many thanks!


RE: Goodwe - pioneersteffen - 04.06.2023

Hi @all,

Many thanks for the script it works perfectly, I getting a response as following:

Code:
SEMS 04.06.2023 12:49:42 * table: ["backUpVload_S"]   * number: 0 ["istr10"]   * number: 670 ["battary_work_mode"]   * number: 0 ["etotal"]   * number: 61.6 ["pbackup"]   * number: 0 ["istr3"]   * number: 0 ["istr15"]   * number: -810.4 ["bms_alarm"]   * number: 0 ["pre_hour_lasttotal"]   * userdata: NULL ["bms_discharge_i_max"]   * userdata: NULL ["sn"]   * string: 56000DTS229R0076 ["isbuettey"]   * bool: false ["backUpPload_T"]   * number: 0 ["istr14"]   * number: 185.2 ["total_buy"]   * number: 0 ["ram"]   * number: 53 ["dataloggersn"]   * string: ["genset_etotal"]   * userdata: NULL ["istr16"]   * number: 560.4 ["genset_mode"]   * userdata: NULL ["iac2"]   * number: 5 ["battery8sn"]   * userdata: NULL ["reactive_power"]   * number: 0 ["name"]   * string: SDT G2 ["ezPro_connect_status"]   * userdata: NULL ["backUpIload_S"]   * number: 0 ["mtActivepowerR"]   * number: 0 ["fac3"]   * number: 50.01 ["meter_type"]   * userdata: NULL ["istr13"]   * number: 220 ["pac"]   * number: 3457 ["vpv2"]   * number: 360.2 ["mtActivepowerT"]   * number: 0 ["cts"]   * userdata: NULL ["tempperature"]   * number: 56.3 ["eDischargeDay"]   * number: 0 ["ibattery1"]   * number: 0 ["istr6"]   * number: 0.1 ["istr8"]   * number: 118 ["istr1"]   * number: 5.1 ["yesterdaysellertotal"]   * number: 0 ["more_batterys"]   * userdata: NULL ["outputpower"]   * number: 3457 ["mtActivepowerS"]   * number: 0 ["eTotalBuy"]   * userdata: NULL ["master"]   * userdata: NULL ["powerstation_id"]   * string: fe648d7b-b84b-4427-bb5f-f4b4a5c4c17b ["istr4"]   * number: 0 ["istr7"]   * number: 64.7 ["ipv2"]   * number: 5 ["eP_connect_status_recover"]   * userdata: NULL ["isbuetteybpu"]   * bool: false ["buy"]   * number: 0 ["last_time"]   * number: 1685875672648 ["ipv4"]   * number: 0 ["genset_yeasterdayTotal"]   * userdata: NULL ["iload"]   * number: 0 ["isESUOREMU"]   * bool: false ["backUpPload_S"]   * number: 0 ["change_type"]   * number: 0 ["backUpVload_T"]   * number: 0 ["meterConnectStatus"]   * userdata: NULL ["current_hour_pv"]   * number: 0 ["battery3sn"]   * userdata: NULL ["pre_hour_time"]   * userdata: NULL ["thismonthetotle"]   * number: 43.1 ["hasmeter"]   * bool: false ["bms_warning"]   * number: 0 ["battery4sn"]   * userdata: NULL ["yesterdayct2sellertotal"]   * userdata: NULL ["eBatteryCharge"]   * userdata: NULL ["pmeter"]   * number: 0 ["hour_total"]   * number: 26 ["istr9"]   * number: 669.3 ["iac3"]   * number: 5 ["battery5sn"]   * userdata: NULL ["soh"]   * number: 0 ["isbuetteybps"]   * bool: false ["all_eday"]   * number: 18.5 ["iac1"]   * number: 5.1 ["turnon_time"]   * number: 1685712233090 ["battery1sn"]   * userdata: NULL ["equipment_name"]   * userdata: NULL ["battery6sn"]   * userdata: NULL ["vac1"]   * number: 226.3 ["vac3"]   * number: 224.2 ["genset_power"]   * userdata: NULL ["status"]   * number: 1 ["itotal"]   * number: 7.392 ["battery_count"]   * userdata: NULL ["total_pbattery"]   * number: 0 ["workmode"]   * number: 1 ["inverter_type"]   * number: 0 ["parallel_code"]   * userdata: NULL ["vpv3"]   * number: 0 ["vpv1"]   * number: 346 ["eBatteryDischarge"]   * userdata: NULL ["deratingMode"]   * userdata: NULL ["safetyConutry"]   * number: 79 ["errors"]   * table: ["extend_properties"]   * userdata: NULL ["istr5"]   * number: 0.1 ["genset_eday"]   * number: 0 ["check_code"]   * string: 045109 ["isoLimit"]   * number: 0 ["eday"]   * number: 18.5 ["eDayBuy"]   * number: 0 ["battery7sn"]   * userdata: NULL ["yesterdaybuytotal"]   * number: 0 ["model_type"]   * string: GW6K-DT ["vac2"]   * number: 225.1 ["fault_messge"]   * number: 0 ["soc"]   * number: 44 ["genset_start_mode"]   * userdata: NULL ["yesterdayetotalload"]   * number: 0 ["yesterdayetotal"]   * userdata: NULL ["yesterdaylastime"]   * number: 0 ["vbattery1"]   * number: 0 ["bms_charge_i_max"]   * number: 3 ["total_sell"]   * number: 0 ["pv_power"]   * number: 0 ["iday"]   * number: 2.22 ["lastmonthetotle"]   * number: 0.4 ["eChargeDay"]   * number: 0 ["backUpIload_T"]   * number: 0 ["vpv4"]   * number: 0 ["battStrings"]   * userdata: NULL ["istr12"]   * number: 220 ["seller"]   * number: 0 ["firmwareversion"]   * number: 1515 ["istr11"]   * number: 0 ["istr2"]   * number: 5 ["change_time"]   * number: 0 ["fac1"]   * number: 50.01 ["capacity"]   * number: 6 ["bmssoftwareversion"]   * userdata: NULL ["ct_solution_type"]   * number: 0 ["leakage_current"]   * userdata: NULL ["pf"]   * number: 0 ["ipv3"]   * number: 0 ["ipv1"]   * number: 5.1 ["fac2"]   * number: 50.01 ["vload"]   * number: 0 ["battery2sn"]   * userdata: NULL ["eP_connect_status_happen"]   * userdata: NULL

How can I pick a variable out of the table? 

For example I want to pick out “pac” and write this value to a group address. How would this work?

Many thanks!

Best Regards 
Steffen


RE: Goodwe - admin - 05.06.2023

Use this:
Code:
grp.checkupdate('1/1/1', invert_full.pac)



RE: Goodwe - pioneersteffen - 10.06.2023

(05.06.2023, 06:13)admin Wrote: Use this:
Code:
grp.checkupdate('1/1/1', invert_full.pac)

Many thanks admin! Works perfectly! :-)


RE: Goodwe - Amelie - 13.06.2023

I have a system with 2 inverters and one batterij, connected to one inverter.

I have copied the code above, but I have the impression that only the data from one inverter is displayed. How can I display the data of the other inverter?


RE: Goodwe - admin - 14.06.2023

Try this for multiple inverters. The order of the returned data might not be constant. So you might have to check the serial number (sn field) invert_full_1 and invert_full_2 tables to distinguish to which inverter the data belongs to.
Code:
invert_full_1 = response.data.inverter[1].invert_full log(invert_full_1) invert_full_2 = response.data.inverter[2].invert_full log(invert_full_2)



RE: Goodwe - Amelie - 17.06.2023

(14.06.2023, 07:02)admin Wrote: Try this for multiple inverters. The order of the returned data might not be constant. So you might have to check the serial number (sn field) invert_full_1 and invert_full_2 tables to distinguish to which inverter the data belongs to.
Code:
invert_full_1 = response.data.inverter[1].invert_full log(invert_full_1) invert_full_2 = response.data.inverter[2].invert_full log(invert_full_2)

I works perfect! Thank you very much.

But, now, I have the error: attempt to concatenate field 'api' (a nil value) stack traceback. The line with causes the error is: 
Code:
url = response.api .. "/v2/PowerStation/GetMonitorDetailByPowerstationId"

A couple of days ago, I didn't have this error. I tried several thing, but I can't find what is causing the error. Can you help?


RE: Goodwe - admin - 19.06.2023

Log the response from the CrossLogin request:
Code:
res, code, hdrs, stat = socket.http.request({   url = "https://eu.semsportal.com/api/v2/Common/CrossLogin",   method = "POST",   headers = headers,   source = ltn12.source.string(body),   sink = ltn12.sink.table(response), }) if res and code == 200 then   response = json.decode(table.concat(response))   log(response)   headers.token = json.encode(response.data)



RE: Goodwe - Amelie - 19.06.2023

I get:

Code:
table: ["msg"]   * string: error ["data"]   * table:    ["timestamp"]     * number: 1687202887500    ["uid"]     * string: 11c54dcf-50cb-48a5-aec2-91b915557029    ["token"]     * string: bcbccfa1fa74b141c8d20cad4e7a1d7d    ["version"]     * string:    ["client"]     * string: web    ["language"]     * string: en-GB ["hasError"]   * bool: false ["api"]   * string: https://eu.semsportal.com/api/ ["components"]   * table:    ["langVer"]     * number: 183    ["api"]     * string: http://eu.semsportal.com:82/api/Auth/GetTokenV2    ["msgSocketAdr"]     * string: https://eu-xxzx.semsportal.com    ["timeSpan"]     * number: 0    ["para"]     * userdata: NULL ["code"]   * number: 0



RE: Goodwe - admin - 20.06.2023

The response looks fine. You can modify this part to log only when api field is missing from the response, then check what the log says:
Code:
if res and code == 200 then   response = json.decode(table.concat(response))   headers.token = json.encode(response.data)   if not response.api then     log('invalid response', response)   else     url = response.api .. "/v2/PowerStation/GetMonitorDetailByPowerstationId"     response = {}     body = json.encode({ powerStationId = powerstation_id })     headers["Content-Length"] = #body     res, code, hdrs, stat = socket.http.request({       url = url,       method = "POST",       headers = headers,       source = ltn12.source.string(body),       sink = ltn12.sink.table(response),     })     response = json.decode(table.concat(response))     -- ...   end end



RE: Goodwe - Amelie - 23.06.2023

Thank you very much for your help. 

I still have one question. In the Goodwe app, I can see how many we inject. I want to load in into the LM, so I can ask to the heat pump to make more hot water so it fulfills also the function of a battery. When I send use the following script:

Code:
url = response.api .. "/v2/PowerStation/GetMonitorDetailByPowerstationId" response = json.decode(table.concat(response)) log (response)  

The log ends with ..., so I have the impression that not everything is saved in the log and that I can't see everything.

Is there a possibility to saved everything, so that I can see alle the information that the LM is getting back.


RE: Goodwe - admin - 26.06.2023

For testing purposes you can write the results into a file before decoding it:
Code:
response = table.concat(response) io.writefile('/www/log.json', response) response = json.decode(response) -- log(response)

Then open the log in your browser: http://LM_IP/log.json
You can use https://codebeautify.org/jsonviewer to view the JSON contents in a more readable form.