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.

Ebeco floorheat thermostat
#1
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
Reply
#2
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
Reply
#3
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
Reply
#4
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).
Reply
#5
(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
Reply
#6
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',
},
Reply
#7
(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"
Reply
#8
Have you tried */* instead of application/json?
Reply
#9
(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
Reply
#10
Can you send credentials to us via e-mail so we can check locally?
Reply
#11
(08.08.2022, 08:01)admin Wrote: Can you send credentials to us via e-mail so we can check locally?

sure, wich email?
Reply
#12
Send to info@openrb.com
Reply
#13
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
Reply
#14
(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
Reply
#15
(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)
Reply
#16
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
Reply
#17
(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.
Reply


Forum Jump: