Hi there,
this is an updated version of my 2year old topic (which is no longer editable…)
There is no local API support yet, hence online access is required.
A connected bridge is required to make use of the API
First of all I created a few basic items:
String tedee_token
Number tedee_batteryLevel "Schloss Batteriestand [%.0f %%]"
String tedee_isConnected "Status Schloss [%s]"
String tedee_state "Zustand Schloss [%s]"
Switch tedee_tokenfetcher "Holt Token" {expire="1s,state=OFF"}
String tedee_lockoperation "Schlossbedienung []"
I am accessing the api first to get a token from the API with my tedee username and password.
Get your client ide/scope here: How to authenticate — Tedee API documentation documentation
var String api_version = "1.32"
var String lock_id = "enter your lock id"
rule "Retrieve Token"
when
Item tedee_tokenfetcher changed from OFF to ON
then
var String api = "https://tedee.b2clogin.com/tedee.onmicrosoft.com/B2C_1_SignIn_Ropc/oauth2/v2.0/token"
var String result = ""
var String username = "yr tedee email"
var String password = "yr tedee password"
var String content = "grant_type=password&username=" + username + "&password=" + password + "&client_id=[yr_client_id]&scope=[yr_scope]&response_type=token"
result = sendHttpPostRequest(api, "application/x-www-form-urlencoded", content, 1000)
val evaluation = transform("JSONPATH", "$.error", result)
if (evaluation != "access_denied") {
val token = transform("JSONPATH", "$.access_token", result)
logInfo("tedee", " Updated Token for tedee: " + token)
tedee_token.postUpdate(token)
} else {
logInfo("tedee","API threw error, restarting in 5 minutes")
createTimer(now.plusMinutes(5), [|
tedee_tokenfetcher.sendCommand(ON)
])
}
if (tedee_token.state == null ) {
logInfo("tedee","API threw error, restarting in 5 minutes")
createTimer(now.plusMinutes(5), [|
tedee_tokenfetcher.sendCommand(ON)
])
}
// Request Personal Key PAK
val String year = now.plusYears(1).getYear().toString()
var String api = "https://api.tedee.com/api/v" + api_version + "/my/personalaccesskey"
var String tokenheader = "Bearer " + tedee_token.state.toString()
var String body = '{
"name": "AlexPAK",
"validTo": "'+year+'-12-31T00:00:00.197Z",
"scopes": [
"Device.Read",
"Lock.Operate",
"Organization.ReadWrite"
]
}'
logInfo("tedee","Tokenheader " + tokenheader)
var headers = newHashMap("accept" -> "application/json","Authorization" -> tokenheader)
var String old_id = executeCommandLine(Duration.ofSeconds(1), "head", "/var/lib/openhab/secrets/tedee.id") //check if earlier certificate had been stored
if (old_id.length() == 37) {
old_id = old_id.substring(0,36)
logInfo("tedee", "Alte Key zum Löschen: |" + old_id + "|")
sendHttpDeleteRequest(api + "/" + old_id,headers,1000) // delete old certificate to avoid pileup in tedee portal
}
var String pakResult = ""
var String idResult = ""
result = sendHttpPostRequest(api,"application/json-patch+json",body,headers,1000)
//logInfo("tedee",result)
pakResult = transform("JSONPATH", "$.result.key", result)
idResult = transform("JSONPATH", "$.result.id", result)
//logInfo("tedee",pakResult)
executeCommandLine(Duration.ofSeconds(5), "/etc/openhab/scripts/tedee.sh", pakResult, idResult)
end
Here is the script to write the PAK to your openhab secrets:
#!/bin/bash
touch /var/lib/openhab/secrets/tedee.pak
touch /var/lib/openhab/secrets/tedee.id
echo $1 > /var/lib/openhab/secrets/tedee.pak
echo $2 > /var/lib/openhab/secrets/tedee.id
With the token and PAK, you are able to fetch all connected logs and get the status of respective locks from the API. I update the corresponding items where necessary and run it every 5 minutes. tedee advises not to sync status faster than every 10 seconds.
The lock id changes each time the lock is deleted and re-added to your tedee account. The id can be retrieved by running a full sync against the api (https://api.tedee.com/api/v1.32/my/lock/sync ) without a specific id.
I did not automate this, as I do not expect a lot of changes. I used the uri once during the first call and logged it.
rule "Retrieve Lock status and update items"
when
Time cron "0 0/5 * * * ?"
then
//logInfo("tedee","Updating tedee")
var String pak = executeCommandLine(Duration.ofSeconds(5), "cat", "/var/lib/openhab/secrets/tedee.pak")
if (pak.length() == 52) {
pak = pak.substring(0,51)
//logInfo("tedee", "|" + pak + "|")
}
var String api = "https://api.tedee.com/api/v" + api_version + "/my/lock/" + lock_id + "/sync"
var String tokenheader = "PersonalKey " + pak
var headers = newHashMap("accept" -> "application/json","Authorization" -> tokenheader)
var String result = "{}"
result = sendHttpGetRequest(api,headers,1000)
if (result === null) {
tedee_tokenfetcher.sendCommand(ON)
return
}
if (result.toString() == "{}") {
tedee_tokenfetcher.sendCommand(ON)
}
//in case of error, start new token fetch
val evaluation = transform("JSONPATH", "$.success", result)
if (evaluation == "false" || evaluation == "You are unauthorized." || result == "{}") {
logInfo("tedee","API threw error, fetching token")
tedee_tokenfetcher.sendCommand(ON)
}
val isConnected = transform("JSONPATH", "$.result.isConnected", result)
val state_int = transform("JSONPATH", "$.result.lockProperties.state", result)
val batteryLevel = Integer::parseInt(transform("JSONPATH", "$.result.lockProperties.batteryLevel", result))
logInfo("tedee", "Schloss ID " + lock_id + " ist verbunden (" + isConnected + "), aktueller Status " + state_int + " und Batterie " + batteryLevel + "%")
//update Lockitems only if information has changed
if (isConnected == "true" && tedee_isConnected.state != "Verbunden" ) {
tedee_isConnected.postUpdate("Verbunden")
logInfo("tedee","Tedee verbunden")
//logInfo("tedee","isConnected= " + isConnected + ", tedee_isConnected: " + tedee_isConnected.state.toString())
} else if (isConnected == "false" ) {
tedee_isConnected.postUpdate("Getrennt")
logInfo("tedee","Tedee getrennt")
//logInfo("tedee","isConnected= " + isConnected + ", tedee_isConnected: " + tedee_isConnected.state.toString())
}
if (batteryLevel.toString() != tedee_batteryLevel.state.toString()) {
tedee_batteryLevel.postUpdate(batteryLevel)
logInfo("tedee","Updated tedee Battery to " + batteryLevel)
//logInfo("tedee","batteryLevel= " + batteryLevel + ", tedee_batteryLevel: " + tedee_batteryLevel.state.toString())
}
if (state_int != tedee_state.state.toString()) {
tedee_state.postUpdate(state_int)
tedee_lockoperation.postUpdate(state_int)
logInfo("tedee","Updated tedee State to " + state_int)
}
end
And in order to control the lock, including the option to soft open (without pulling spring) I use:
rule "tedee Bedienung"
when
Item tedee_lockoperation received command
then
var String pak = executeCommandLine(Duration.ofSeconds(5), "cat", "/var/lib/openhab/secrets/tedee.pak")
var String tokenheader = "PersonalKey " + pak
var String api = "https://api.tedee.com/api/v" + api_version + "/my/lock/" + lock_id + "/operation"
var headers = newHashMap("accept" -> "application/json","Authorization" -> tokenheader)
switch(tedee_lockoperation.state) {
case "2": {
api = api + "/unlock"
sendHttpPostRequest(api,"application/json-patch+json","",headers,1000)
tedee_state.postUpdate(2)
}
case "6": {
api = api + "/lock"
sendHttpPostRequest(api,"application/json-patch+json","",headers,1000)
tedee_state.postUpdate(6)
}
case "99": {
api = api + "/unlock?mode=3"
sendHttpPostRequest(api,"application/json-patch+json","",headers,1000)
tedee_state.postUpdate(2)
}
}
end
For a basic sitemap you can use:
Text label="Schloss" {
Text item=tedee_isConnected
Switch item=tedee_lockoperation mappings=[2="Oeffnen",6="Schliessen",99="Soft öffnen"]
Text item=tedee_batteryLevel
Text item=tedee_state label="Status Schloss [MAP(tedee_lockstates.map):%s]"
Switch item=tedee_tokenfetcher
}
Lock is still working great