Tedee Lock Integration with http calls in rules (updated 2023)

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 :wink:

News from Tedee!
The local Bridge API got finally introduced https://docs.tedee.com/bridge-api

I just stumbled over it in the release notes of the latest bridge firmware update 2.2.16221

1 Like