Posts: 13
Threads: 5
Joined: Jun 2020
Reputation:
1
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
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
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.
Posts: 13
Threads: 5
Joined: Jun 2020
Reputation:
1
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?
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
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)
Posts: 13
Threads: 5
Joined: Jun 2020
Reputation:
1
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"}
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
Can you send your credentials via PM?
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
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
Posts: 13
Threads: 5
Joined: Jun 2020
Reputation:
1
Admin this works perfectly thanks
I will continue with this.
Posts: 115
Threads: 23
Joined: Sep 2021
Reputation:
2
(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!
Posts: 115
Threads: 23
Joined: Sep 2021
Reputation:
2
04.06.2023, 11:03
(This post was last modified: 04.06.2023, 11:05 by pioneersteffen.)
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
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
Use this:
Code: grp.checkupdate('1/1/1', invert_full.pac)
Posts: 115
Threads: 23
Joined: Sep 2021
Reputation:
2
(05.06.2023, 06:13)admin Wrote: Use this:
Code: grp.checkupdate('1/1/1', invert_full.pac)
Many thanks admin! Works perfectly! :-)
Posts: 34
Threads: 10
Joined: Feb 2020
Reputation:
0
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?
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
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)
Posts: 34
Threads: 10
Joined: Feb 2020
Reputation:
0
(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?
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
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)
Posts: 34
Threads: 10
Joined: Feb 2020
Reputation:
0
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
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
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
Posts: 34
Threads: 10
Joined: Feb 2020
Reputation:
0
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.
Posts: 7773
Threads: 42
Joined: Jun 2015
Reputation:
447
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.
|