Logic Machine Forum
Ebeco floorheat thermostat - Printable Version

+- Logic Machine 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.