LogicMachine Forum
Ebeco floorheat thermostat - 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: Ebeco floorheat thermostat (/showthread.php?tid=3756)



Ebeco floorheat thermostat - victor.back - 17.12.2021

Hi.

I have a floorheat brand that has an REST API  that I want to implement into a buildings KNX installation.
is it possible to make this with some script or is it someone that has done this already? I want to control and see status och the floorheating. 
Attach the link to its api information if that’s explains my goal better. 
https://www.ebeco.se/support/ebeco-open-api


RE: Ebeco floorheat thermostat - admin - 17.12.2021

First you need to get the bearer token manually as described in the API link you've provided. Then you can use it to make requests as documented here: https://ebecoconnect.com/swagger/index.html

Topics where you can find example code:
https://forum.logicmachine.net/showthread.php?tid=2185
https://forum.logicmachine.net/showthread.php?tid=3308
https://forum.logicmachine.net/showthread.php?tid=3121
https://forum.logicmachine.net/showthread.php?tid=1786


RE: Ebeco floorheat thermostat - victor.back - 07.08.2022

Hi.

I ran this in Postman where I got the accessToken with success.
Code:
curl --location --request POST 'https://ebecoconnect.com/api/TokenAuth/Authenticate' \ --header 'Content-Type: application/json' \ --header 'Abp.TenantId: 1' \ --data-raw '    {     "userNameOrEmailAddress": "xxxxx@xxxxx.com",     "password": "xxxxx"    }'

But in my script I don´t get the response of the connected devices, in fact I dopnt get much at all in response, what is wrong here? 
Code:
-- Load modules require('json') require('ssl.https') require('ltn12') -- Set credentials for Ebecoconnect API userNameOrEmailAddress = "xxxxx@xxxxx.com" -- your email for Ebecoconnect account password = "xxxxx" -- your password for Ebecoconnect account function GetToken()  request_token_url = 'https://ebecoconnect.com/api/TokenAuth/Authenticate'  local response_body = {}  local request_body = "grant_type=password&username=" .. userNameOrEmailAddress .. "&password=" .. password  local body, code, hdrs, stat = ssl.https.request{    url = request_token_url;    method = "POST";    headers =    {      ["Content-Type"] = "application/x-www-form-urlencoded";       ["Content-Length"] = #request_body;       ["Abp.TenantId"] = 1;    };    source = ltn12.source.string(request_body);    sink = ltn12.sink.table(response_body);  }  if code == 200 then    ret = table.concat(response_body)    ret = json.pdecode(ret)     log(ret)    return ret.access_token -- could be some other field, try to log ret to be sure  end end -- Request token if not API_Token then     API_Token = GetToken() end function RequestFromEbeco(request)  request_url = 'https://api/services/app/Devices/GetUserDevices' .. request  local response_body = {}  local request_body = ""  local body, code, hdrs, stat = ssl.https.request{    url = request_url;    method = "GET";    headers =    {      ["Content-Type"] = "application/json";      ["Authorization"] = "Bearer " .. API_Token;    };    source = ltn12.source.string(request_body);    sink = ltn12.sink.table(response_body);  }  if code == 200 then    ret = table.concat(response_body)    ret = json.pdecode(ret)    return ret  else    API_Token = GetToken() -- request a new token  end end -- Get products products = RequestFromEbeco('products') log(products) -- Checks your Ebeco thermostats

Ebecos API documentation here
https://www.ebeco.se/radgivning/guider/ebeco-oppet-api


RE: Ebeco floorheat thermostat - admin - 08.08.2022

Use this:
Code:
function GetToken()   local response_body = {}   local request_body = json.encode({     userNameOrEmailAddress = userNameOrEmailAddress,     password = password,   })   local body, code, hdrs, stat = ssl.https.request{     url = 'https://ebecoconnect.com/api/TokenAuth/Authenticate',     method = 'POST',     headers = {       ['Content-Type'] = 'application/json',       ['Content-Length'] = #request_body,       ['Abp.TenantId'] = 1,     },     source = ltn12.source.string(request_body),     sink = ltn12.sink.table(response_body),   }   if code == 200 then     ret = table.concat(response_body)     ret = json.pdecode(ret)     log(ret)     return ret.access_token -- could be some other field, try to log ret to be sure   else     log('request failed', body, code, hdrs, stat)   end end

The request_url in RequestFromEbeco is wrong, it should be "https://ebecoconnect.com/api/services/app/Devices/GetUserDevices" (without any additional parameters).


RE: Ebeco floorheat thermostat - victor.back - 08.08.2022

(08.08.2022, 06:04)admin Wrote: Use this:
Code:
function GetToken()   local response_body = {}   local request_body = json.encode({     userNameOrEmailAddress = userNameOrEmailAddress,     password = password,   })   local body, code, hdrs, stat = ssl.https.request{     url = 'https://ebecoconnect.com/api/TokenAuth/Authenticate',     method = 'POST',     headers = {       ['Content-Type'] = 'application/json',       ['Content-Length'] = #request_body,       ['Abp.TenantId'] = 1,     },     source = ltn12.source.string(request_body),     sink = ltn12.sink.table(response_body),   }   if code == 200 then     ret = table.concat(response_body)     ret = json.pdecode(ret)     log(ret)     return ret.access_token -- could be some other field, try to log ret to be sure   else     log('request failed', body, code, hdrs, stat)   end end

The request_url in RequestFromEbeco is wrong, it should be "https://ebecoconnect.com/api/services/app/Devices/GetUserDevices" (without any additional parameters).

Thanks, but did´nt get that to work either. But change my script line from having ". . ." to '. . .'
Then I put in log('request failed') and got this in response:
Ebeco REST API copy 08.08.2022 08:48:58
* arg: 1
  * string: request failed
* arg: 2
  * number: 1
* arg: 3
  * number: 415
* arg: 4
  * table:
  ["server"]
    * string: Microsoft-IIS/10.0
  ["connection"]
    * string: close
  ["content-length"]
    * string: 0
  ["x-rate-limit-limit"]
    * string: 60s
  ["x-rate-limit-remaining"]
    * string: 29
  ["request-context"]
    * string: appId=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  ["x-frame-options"]
    * string: SAMEORIGIN
  ["x-rate-limit-reset"]
    * string: 2022-08-08T06:49:58.5482160Z
  ["date"]
    * string: Mon, 08 Aug 2022 06:48:57 GMT
  ["x-xss-protection"]
    * string: 1; mode=block
  ["set-cookie"]
    * string: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPath=/;HttpOnly;SameSite=None;Secure;Domain=ebecoconnect.com
  ["x-content-type-options"]
    * string: nosniff
* arg: 5
  * string: HTTP/1.1 415 Unsupported Media Type

I added the X:es if it might have been sensitive stuff Smile


RE: Ebeco floorheat thermostat - admin - 08.08.2022

Try adding Accept: application/json or Accept: */* to headers in GetToken function:
Code:
headers = {   ['Content-Type'] = 'application/json',   ['Content-Length'] = #request_body,   ['Abp.TenantId'] = 1,   ['Accept'] = 'application/json', },



RE: Ebeco floorheat thermostat - victor.back - 08.08.2022

(08.08.2022, 07:06)admin Wrote: Try adding Accept: application/json or Accept: */* to headers in GetToken function:
Code:
headers = {   ['Content-Type'] = 'application/json',   ['Content-Length'] = #request_body,   ['Abp.TenantId'] = 1,   ['Accept'] = 'application/json', },

Now intsted of "HTTP/1.1 415 Unsupported Media Type"

I get "HTTP/1.1 400 Bad Request"


RE: Ebeco floorheat thermostat - admin - 08.08.2022

Have you tried */* instead of application/json?


RE: Ebeco floorheat thermostat - victor.back - 08.08.2022

(08.08.2022, 07:24)admin Wrote: Have you tried */* instead of application/json?

Yes it´s the same response as well "HTTP/1.1 400 Bad Request"

The response look like this:
* arg: 1
  * string: request failed
* arg: 2
  * number: 1
* arg: 3
  * number: 400
* arg: 4
  * table:
  ["server"]
    * string: Microsoft-IIS/10.0
  ["content-type"]
    * string: application/json; charset=utf-8
  ["connection"]
    * string: close
  ["content-length"]
    * string: 412
  ["x-rate-limit-limit"]
    * string: 60s
  ["x-rate-limit-remaining"]
    * string: 29
  ["request-context"]
    * string: appId=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  ["x-frame-options"]
    * string: SAMEORIGIN
  ["x-rate-limit-reset"]
    * string: 2022-08-08T07:31:10.2468154Z
  ["date"]
    * string: Mon, 08 Aug 2022 07:30:10 GMT
  ["x-xss-protection"]
    * string: 1; mode=block
  ["set-cookie"]
    * string: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDomain=ebecoconnect.com
  ["x-content-type-options"]
    * string: nosniff
* arg: 5
  * string: HTTP/1.1 400 Bad Request


RE: Ebeco floorheat thermostat - admin - 08.08.2022

Can you send credentials to us via e-mail so we can check locally?


RE: Ebeco floorheat thermostat - victor.back - 08.08.2022

(08.08.2022, 08:01)admin Wrote: Can you send credentials to us via e-mail so we can check locally?

sure, wich email?


RE: Ebeco floorheat thermostat - admin - 08.08.2022

Send to info@openrb.com


RE: Ebeco floorheat thermostat - admin - 08.08.2022

This works for me using your credentials:
Code:
require('json') require('ssl.https') require('ltn12') -- Set credentials for Ebecoconnect API userNameOrEmailAddress = '...' -- your email for Ebecoconnect account password = '...' -- your password for Ebecoconnect account function GetToken()   local response_body = {}   local request_body = json.encode({     userNameOrEmailAddress = userNameOrEmailAddress,     password = password,   })   local body, code, hdrs, stat = ssl.https.request{     url = 'https://ebecoconnect.com/api/TokenAuth/Authenticate',     method = 'POST',     headers = {       ['Content-Type'] = 'application/json',       ['Content-Length'] = #request_body,       ['Abp.TenantId'] = 1,     },     source = ltn12.source.string(request_body),     sink = ltn12.sink.table(response_body),   }   if code == 200 then     local ret = table.concat(response_body)     ret = json.pdecode(ret)     if type(ret) == 'table' and type(ret.result) == 'table' then       return ret.result.accessToken     end   else     log('request failed', body, code, hdrs, stat)   end end API_Token = GetToken() function RequestFromEbeco(request, params)   local url = 'https://ebecoconnect.com/api/services/app/Devices/' .. request   local response_body = {}   local request_body = ''   local body, code, hdrs, stat = ssl.https.request{     url = url,     method = 'GET',     headers = {       ['Content-Type'] = 'application/json',       ['Authorization'] = 'Bearer ' .. API_Token,     },     source = ltn12.source.string(request_body),     sink = ltn12.sink.table(response_body),   }   if code == 200 then     local ret = table.concat(response_body)     ret = json.pdecode(ret)     return ret   else     log('request failed', body, code, hdrs, stat)   end end if API_Token then   devices = RequestFromEbeco('GetUserDevices')   log(devices) end



RE: Ebeco floorheat thermostat - victor.back - 08.08.2022

(08.08.2022, 12:07)admin Wrote: This works for me using your credentials:
Code:
require('json') require('ssl.https') require('ltn12') -- Set credentials for Ebecoconnect API userNameOrEmailAddress = '...' -- your email for Ebecoconnect account password = '...' -- your password for Ebecoconnect account function GetToken()   local response_body = {}   local request_body = json.encode({     userNameOrEmailAddress = userNameOrEmailAddress,     password = password,   })   local body, code, hdrs, stat = ssl.https.request{     url = 'https://ebecoconnect.com/api/TokenAuth/Authenticate',     method = 'POST',     headers = {       ['Content-Type'] = 'application/json',       ['Content-Length'] = #request_body,       ['Abp.TenantId'] = 1,     },     source = ltn12.source.string(request_body),     sink = ltn12.sink.table(response_body),   }   if code == 200 then     local ret = table.concat(response_body)     ret = json.pdecode(ret)     if type(ret) == 'table' and type(ret.result) == 'table' then       return ret.result.accessToken     end   else     log('request failed', body, code, hdrs, stat)   end end API_Token = GetToken() function RequestFromEbeco(request, params)   local url = 'https://ebecoconnect.com/api/services/app/Devices/' .. request   local response_body = {}   local request_body = ''   local body, code, hdrs, stat = ssl.https.request{     url = url,     method = 'GET',     headers = {       ['Content-Type'] = 'application/json',       ['Authorization'] = 'Bearer ' .. API_Token,     },     source = ltn12.source.string(request_body),     sink = ltn12.sink.table(response_body),   }   if code == 200 then     local ret = table.concat(response_body)     ret = json.pdecode(ret)     return ret   else     log('request failed', body, code, hdrs, stat)   end end if API_Token then   devices = RequestFromEbeco('GetUserDevices')   log(devices) end
Now works fine for me too, thanks!

Looks like the problem was that I had used "..." insted of '...'
for some lines, including username


RE: Ebeco floorheat thermostat - victor.back - 31.10.2023

(08.08.2022, 12:07)admin Wrote: This works for me using your credentials:
Code:
require('json') require('ssl.https') require('ltn12') -- Set credentials for Ebecoconnect API userNameOrEmailAddress = '...' -- your email for Ebecoconnect account password = '...' -- your password for Ebecoconnect account function GetToken()   local response_body = {}   local request_body = json.encode({     userNameOrEmailAddress = userNameOrEmailAddress,     password = password,   })   local body, code, hdrs, stat = ssl.https.request{     url = 'https://ebecoconnect.com/api/TokenAuth/Authenticate',     method = 'POST',     headers = {       ['Content-Type'] = 'application/json',       ['Content-Length'] = #request_body,       ['Abp.TenantId'] = 1,     },     source = ltn12.source.string(request_body),     sink = ltn12.sink.table(response_body),   }   if code == 200 then     local ret = table.concat(response_body)     ret = json.pdecode(ret)     if type(ret) == 'table' and type(ret.result) == 'table' then       return ret.result.accessToken     end   else     log('request failed', body, code, hdrs, stat)   end end API_Token = GetToken() function RequestFromEbeco(request, params)   local url = 'https://ebecoconnect.com/api/services/app/Devices/' .. request   local response_body = {}   local request_body = ''   local body, code, hdrs, stat = ssl.https.request{     url = url,     method = 'GET',     headers = {       ['Content-Type'] = 'application/json',       ['Authorization'] = 'Bearer ' .. API_Token,     },     source = ltn12.source.string(request_body),     sink = ltn12.sink.table(response_body),   }   if code == 200 then     local ret = table.concat(response_body)     ret = json.pdecode(ret)     return ret   else     log('request failed', body, code, hdrs, stat)   end end if API_Token then   devices = RequestFromEbeco('GetUserDevices')   log(devices) end

Hi.

I´ve used this script for one device, but how do I get the data from data table 2-5?

Code:
if API_Token then   devices = RequestFromEbeco('GetUserDevices')   --log(devices) end   if devices and devices.result then     for _, device in ipairs(devices.result) do             -- Enhetsdata 1 grp.checkwrite('32/2/1', device["id"]) -- id (Number) grp.checkwrite('32/2/2', device["relayOn"]) -- relayOn (Bool) grp.checkwrite('32/2/3', device["minutesToTarget"]) -- minutesToTarget (Number) grp.checkwrite('32/2/4', device["temperatureRoom"]) -- temperatureRoom (Number) grp.checkwrite('32/2/5', device["temperatureRoomDecimals"]) -- temperatureRoomDecimals (Number) grp.checkwrite('32/2/6', device["temperatureFloorDecimals"]) -- temperatureFloorDecimals (Number) grp.checkwrite('32/2/7', device["remoteInput"]) -- remoteInput (Bool) grp.checkwrite('32/2/8', device["displayName"]) -- displayName (String) grp.checkwrite('32/2/9', device["temperatureSet"]) -- temperatureSet (Number) -- "errorMessage" ignorerad (userdata: NULL) grp.checkwrite('32/2/10', device["building"]["name"]) -- building name (String) grp.checkwrite('32/2/11', device["building"]["id"]) -- building id (Number) grp.checkwrite('32/2/12', device["programState"]) -- programState (String) grp.checkwrite('32/2/13', device["temperatureFloor"]) -- temperatureFloor (Number) grp.checkwrite('32/2/14', device["hasError"]) -- hasError (Bool) grp.checkwrite('32/2/15', device["installedEffect"]) -- installedEffect (Number) grp.checkwrite('32/2/16', device["powerOn"]) -- powerOn (Bool) grp.checkwrite('32/2/17', device["todaysOnMinutes"]) -- todaysOnMinutes (Number) grp.checkwrite('32/2/18', device["selectedProgram"]) -- selectedProgram (String) -- Enhetsdata 2 grp.checkwrite('32/2/19', device2["id"]) -- id (Number) grp.checkwrite('32/2/20', device2["relayOn"] and 1 or 0) -- relayOn (Bool) grp.checkwrite('32/2/21', device2["minutesToTarget"]) -- minutesToTarget (Number) grp.checkwrite('32/2/22', device2["temperatureRoom"]) -- temperatureRoom (Number) grp.checkwrite('32/2/23', device2["temperatureRoomDecimals"]) -- temperatureRoomDecimals (Number) grp.checkwrite('32/2/24', device2["temperatureFloorDecimals"]) -- temperatureFloorDecimals (Number) grp.checkwrite('32/2/25', device2["remoteInput"] and 1 or 0) -- remoteInput (Bool) grp.checkwrite('32/2/26', device2["displayName"]) -- displayName (String) grp.checkwrite('32/2/27', device2["temperatureSet"]) -- temperatureSet (Number) -- "errorMessage" ignorerad (userdata: NULL) grp.checkwrite('32/2/28', device2["building"]["name"]) -- building name (String) grp.checkwrite('32/2/29', device2["building"]["id"]) -- building id (Number) grp.checkwrite('32/2/30', device2["programState"]) -- programState (String) grp.checkwrite('32/2/31', device2["temperatureFloor"]) -- temperatureFloor (Number) grp.checkwrite('32/2/32', device2["hasError"] and 1 or 0) -- hasError (Bool) grp.checkwrite('32/2/33', device2["installedEffect"]) -- installedEffect (Number) grp.checkwrite('32/2/34', device2["powerOn"] and 1 or 0) -- powerOn (Bool) grp.checkwrite('32/2/35', device2["todaysOnMinutes"]) -- todaysOnMinutes (Number) grp.checkwrite('32/2/36', device2["selectedProgram"]) -- selectedProgram (String)



RE: Ebeco floorheat thermostat - admin - 31.10.2023

Remove the for loop and try this:
Code:
if devices and devices.result then   device = devices.result[1]   device2 = devices.result[2]   device3 = devices.result[3]   ... end



RE: Ebeco floorheat thermostat - victor.back - 31.10.2023

(31.10.2023, 07:57)admin Wrote: Remove the for loop and try this:
Code:
if devices and devices.result then   device = devices.result[1]   device2 = devices.result[2]   device3 = devices.result[3]   ... end

Thanks
It seems to work without the loop.