This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Goodwe
#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
Reply
#2
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.
Reply
#3
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?
Reply
#4
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)
Reply
#5
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"}
Reply
#6
Can you send your credentials via PM?
Reply
#7
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
Reply
#8
Admin this works perfectly thanks
I will continue with this.
Reply
#9
(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!
Reply
#10
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
Reply
#11
Use this:
Code:
grp.checkupdate('1/1/1', invert_full.pac)
Reply
#12
(05.06.2023, 06:13)admin Wrote: Use this:
Code:
grp.checkupdate('1/1/1', invert_full.pac)

Many thanks admin! Works perfectly! :-)
Reply
#13
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?
Reply
#14
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)
Reply
#15
(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?
Reply
#16
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)
Reply
#17
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
Reply
#18
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
Reply
#19
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.
Reply
#20
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.
Reply


Forum Jump: