Posts: 92
Threads: 16
Joined: Jan 2020
Reputation:
2
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
Posts: 7720
Threads: 42
Joined: Jun 2015
Reputation:
446
Posts: 92
Threads: 16
Joined: Jan 2020
Reputation:
2
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
Posts: 7720
Threads: 42
Joined: Jun 2015
Reputation:
446
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).
Posts: 92
Threads: 16
Joined: Jan 2020
Reputation:
2
(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
Posts: 7720
Threads: 42
Joined: Jun 2015
Reputation:
446
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',
},
Posts: 92
Threads: 16
Joined: Jan 2020
Reputation:
2
(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"
Posts: 7720
Threads: 42
Joined: Jun 2015
Reputation:
446
Have you tried */* instead of application/json?
Posts: 92
Threads: 16
Joined: Jan 2020
Reputation:
2
08.08.2022, 07:29
(This post was last modified: 08.08.2022, 07:31 by victor.back.)
(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
Posts: 7720
Threads: 42
Joined: Jun 2015
Reputation:
446
Can you send credentials to us via e-mail so we can check locally?
Posts: 92
Threads: 16
Joined: Jan 2020
Reputation:
2
(08.08.2022, 08:01)admin Wrote: Can you send credentials to us via e-mail so we can check locally?
sure, wich email?
Posts: 7720
Threads: 42
Joined: Jun 2015
Reputation:
446
Posts: 7720
Threads: 42
Joined: Jun 2015
Reputation:
446
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
Posts: 92
Threads: 16
Joined: Jan 2020
Reputation:
2
(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
Posts: 92
Threads: 16
Joined: Jan 2020
Reputation:
2
(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)
Posts: 7720
Threads: 42
Joined: Jun 2015
Reputation:
446
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
Posts: 92
Threads: 16
Joined: Jan 2020
Reputation:
2
(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.
|