Tedee Lock and bridge - get status via local API

I made two javascript scripts for bridge and lock from several comments out of the community. Thanks for great Help

first you have to install crypto-js as described here

(https://community.openhab.org/t/sh256-hash/156948)

if there is a better method, please place a comment.

This is how to get the json object (status of bridge) from the bridge

var CryptoJS = require('crypto-js');

var bridgeApiToken = 'xxxxxxxxxx';   //api token from Tedee app.
var lock_ID = 'xxxxxx';

var timestamp = new Date().getTime();
var hash = CryptoJS.SHA256(bridgeApiToken+timestamp).toString();
var api_key = hash+timestamp;
var api_token= "api_token="+api_key;
var url_auth = [];
    url_auth["WWW-Authenticate"] =  "Token";   
    bridge_url = "http://192.168.1.72/v1.0/bridge?"+ api_token;
    console.log("token = "+ api_key);

var bridgeStatus = actions.HTTP.sendHttpGetRequest(bridge_url, url_auth , 1000);

    console.log("Bridge Status = " + bridgeStatus);  // just to monitor the request

// extract the SSID of the bridge
var ssidValue = actions.Transformation.transform("JSONPATH","$.ssid",bridgeStatus);    //extract SSID
    items.TedeeBridgeSsid.postUpdate(ssidValue);  // I use string items
    console.log("Bridge Ssid Status = " + ssidValue);

// extract the isconnected Status
var connectValue = actions.Transformation.transform("JSONPATH","$.isConnected" ,bridgeStatus);  //extract bridge connected
    items.TedeeBridgeVerbunden.postUpdate(connectValue);  // I use string items
    console.log("Bridge Connect Status = " + connectValue);

This is how to get the json object (status of lock, with lock ID) from the lock

var CryptoJS = require('crypto-js');

var bridgeApiToken = 'xxxxxxxxxx';
var lock_ID = 'xxxxxxxx';

var timestamp = new Date().getTime();
var hash  = CryptoJS.SHA256(bridgeApiToken+timestamp).toString();
var api_key = hash+timestamp;
var api_token= "api_token="+api_key;
var url_auth = [];
    url_auth["WWW-Authenticate"] =  "Token";   

    lock_url   = "http://192.168.1.72/v1.0/lock/"+lock_ID+"?"+ api_token;
    console.log("token = "+ api_key);

var lockStatus   = actions.HTTP.sendHttpGetRequest(lock_url, url_auth , 1000); //
    console.log("lockStatus = " + lockStatus);

var batterieValue = actions.Transformation.transform("JSONPATH","$.batteryLevel" ,lockStatus);
    console.log("Batterie  = " + batterieValue);
    items.TedeeLockBatterie.postUpdate(batterieValue);

var connectValue = actions.Transformation.transform("JSONPATH","$.isConnected" ,lockStatus); 
    console.log("Lock Connect Status = " + connectValue);
    items.TedeeLockVerbunden.postUpdate(connectValue);
1 Like

That’s the way to install a third party library for use in a JS rule.

If you are willing, this is a great candidate for a rule template. Then users can simply install and configure it instead of copy and paste.

If you go down that path I can help. It probably makes sense to put it all into one rule in that case. But you can use whether or not the user defined an Item name for each piece of data to update it.

I am open for all kinds of games. Actually I don‘t even know how to create a rule template. I just test the code for opening and closing the lock.

You can start here and see examples here. One of my simpler ones is the Waze traffic rule template: Driving Time and Distance using Waze [4.0.0.0;4.9.9.9].

At a high level you create the rule as a managed rule in the UI, which it appears you’ve already done.

Then you copy the contents of the code tab and manually add the properties same as you would for a custom widget. See [Do not install] Rule Template Parameters Experiments and Examples for a bunch of examples of different types of properties you can define and some of the options on them. You can get even more control through regular expressions and such if desired but a lot of common formats are already handled (e.g. URL, IP address, location, etc.).

Properties are applied to the rule created from the template using a find and replace. For example, if you have a property called “apitoken”, where you would put that that String in your code would become:

var bridgeApiToken = "{{apitoken}}";

Create a new posting in the marketplace category and follow the template. Test that it can be installed and configured and let a moderator know (I can do it) when it’s ready to publish as we’ll add the published tag. Then it will show up for all users in the add-on store.

1 Like

First time that I see this lock and it seems to be very promising as an alternative to the much more expensive NUKI smart lock.

I actually wonder if it would not even make sense to write a real binding for the Tedee Bridge API ?

You can also use this to lock and unlock the door?
Is there a reason why you are actually using http instead of https?

Anything that would speak against it? What is your experience with this lock?

I use this lock since two-three years. I had no trouble, except with my wife, if it’s not working due to Daddies playground. This lock is really small and quit. A rechargeable battery load lasts for about half a year.
My software background concerning javascript and OH is very basic, so it would be super, if someone could write the binding for that. I just tried http for first trials with that lock.

https://docs.tedee.com/bridge-api#tag/Getting-started/REST-API-request
1 Like

Btw, I just researched a bit about tedee and it seems that the “GO” even supports matter and as the new Matter binding is “coming soon” (there is a already a first version), this look very promising as it wouldn’t even require the bridge.

Update: Matter seems to be more like a promise but unfortunately not reality yet at the time of writing.

That‘s locking or unlocking or pull the lock
Every request (get or post ) needs a new generated token


var CryptoJS = require('crypto-js');

var bridgeApiToken = 'xxxxxxxxxx‘;
var lock_ID = 'xxxxxx';

var timestamp = new Date().getTime();
var hash = CryptoJS.SHA256(bridgeApiToken+timestamp).toString();
var api_key = hash+timestamp;

var content_type = "application/json";
var content= "Content-Length: 0 ";

var header = new Map();
    header.set( "accept","*/*");
    header.set( "api_token",api_key);

    lock_url   = "http://192.168.1.72/v1.0/lock/"+lock_ID+"/lock"; //lock or unlock


var lockStatus   = actions.HTTP.sendHttpPostRequest(lock_url,content_type,content,header,  1000); 
    console.log("lockStatus = " + lockStatus);

If I put all scripts in one rule, I like to trigger the state of lock and bridge with cron job (every hour), and locking or unlocking with two items. All three triggers are in the condition field. How can I separate them in the program, because event.itemName from cron job is ‘undefined’.

If you only have the one thing you want to do with the cron job (get the state of the lock and bridge) then you have your answer.

// Do common stuff

if(event.itemName === undefined) {
  // cron or manually triggered, get the status
}
else if(event.itemName == "MyItem1") {
  // do stuff for MyItem1
}
else if(event.itemName == "MyItem2") {
  // do stuff for MyItem2
}

// Do closing common stuff if necessary

That’s the simple and most straightforward approach.

But you also have access to event.type which you can test as well. The events from Items will be one of “ItemStateEvent”, “ItemStateUpdatedEvent”, “ItemStateChangedEvent” or “ItemCommandEvent”. Manually run, it will be “ExecutionEvent”. Cron triggered it will be “TimerEvent”.

Each trigger type will have its own Event type.

You can further narrow the trigger down using event.getPayload(). For many events there is additional information there. For example, a cron triggered rule will have the cron expression in event.getPayload().

Note that I only really recommend combining rules like this and using this information when one is working on a rule template. The little bit of extra complexity of the rule is to make it so your end users can have just one thing to install and configure from the marketpalce instead of needing to install multiple separate rule templates.

A relatively simple example of this is my MQTT EventBus where I combine the subscription for incoming events and the publication of outgoing events into one rule. Before I figured this out I had to have two separate entries on the marketplace.

        if(this.event !== undefined) {
          console.debug('Processing an MQTT Event Bus event: ' + event.type);
          switch(event.type) {
            // Process an incoming message on the MQTT Channel Trigger, updating/commanding the corresponding Item
            case 'ChannelTriggeredEvent':
              procEvent();
              break;
            // Publish state updates and commands on local Items
            case 'ItemStateEvent':
            case 'ItemStateUpdatedEvent':
            case 'ItemCommandEvent':
              pubEvent();
              break;
            // Warn if we received an unknown event.
            default:
              console.warn('MQTT Event Bus rule triggered without an event we can process: ' + event.type + ', ignoring');
          }
        }

        else {
          console.warn('MQTT Event Bus rule triggered without event, ignoring');
        }

In my rule templates I like to use “ExecutionEvent” to run a configuration check of the rule and report any known problems in the logs so users can know if it’s going to work or not. But in this case I’d expect it to be treated the same as the cron trigger.

1 Like

this project is very inspiring for me, but it also generates a lot of questions.

  1. is there a chance to catch the error message “timeout” within the rule, if the bridge is off (unplugged) or no http answer excists?
  2. is it possible to get information from the router ( Fritzbox 7590 ), whether wifi is switch on (active) or not?

Should be able to with a try/catc. try...catch - JavaScript | MDN

I’m pretty sure that even if the exception is coming from the Java HTTP library you can still catch it in the JS code.

I can’t help there. Fritzbox is not available in the US, I’ve never worked with one. There’s a binding though. AVM FRITZ! - Bindings | openHAB and tons of tutorials and other posts about it.

Thanks, try 
 catch works perfectly. I know, Fritzbox question is more for german guys

From what I see in the binding docs, no.

Using the TR064 Binding gives you that Information!

1 Like

Thank you. I tried that yesterday, but didn’t wait (setting 60s) until the lan switches changed due to unswitched wlan at the router. Works well.

That is the final story for the moment. I will get the Status of lock and bridge via webhooks, which will shorten the rule. also a lot of error messages are needed.


var CryptoJS = require('crypto-js');
var {OH_function} = require ('./kroenert/index.js');

//api-key generieren
function getApiKey(){
  var timestamp = new Date().getTime();
  var hash = CryptoJS.SHA256(bridgeApiToken+timestamp).toString();
  var ret_api_key = hash+timestamp; 
  return ret_api_key;
}

if ((items.Fritzbox7590WlanWiFi2.state !='OFF') && (items.Fritzbox7590WlanWiFi5.state !='OFF') ) {

  var bridge_IP = '192.168.1.72';
  var bridgeApiToken = 'xxxxxxxxx';
  var lock_ID = 'xxxxxx';
  var open_mode = "3"; // 3 = just unlock without pull , 4= unlock with auto pull (open the completely)
  
  // Start durch cron job
  if (event.type == "TimerEvent"){
    console.log("kontrolliere Status von Tedee");
  var url_auth = [];
    url_auth["WWW-Authenticate"] =  "Token"; 
    
  //bridge Status abholen
  var api_key = getApiKey();
  var api_token= "api_token="+api_key;
  var bridge_url = "http://"+bridge_IP+"/v1.0/bridge?"+ api_token;
  try {
    var bridgeRequest = actions.HTTP.sendHttpGetRequest(bridge_url, url_auth , 5000);
    var bridgeConnect = actions.Transformation.transform("JSONPATH","$.isConnected" ,bridgeRequest);  
      items.TedeeBridgeVerbunden.postUpdate(bridgeConnect);      
      if (bridgeConnect != '1'){

        var volume   = 20;
        var sound    = 4; 
        var message  = ['1Tedee Bridge nicht verbunden',volume,sound];
          OH_function.sendMessage(message); 
      }
    }catch (error) {
      console.log("Router oder wlan Fehler :"+error);
    }
    
  //Lock Status abholen
  var api_key = getApiKey();
  var api_token= "api_token="+api_key;
  var lock_url   = "http://"+bridge_IP+"/v1.0/lock/"+lock_ID+"?"+ api_token;

  try { 
    var lockRequest   = actions.HTTP.sendHttpGetRequest(lock_url, url_auth , 5000); 
  
    var lockBatterie = actions.Transformation.transform("JSONPATH","$.batteryLevel" ,lockRequest);
    items.TedeeLockBatterie.postUpdate(lockBatterie);
    var lockZustand = actions.Transformation.transform("JSONPATH","$.state" ,lockRequest);
      items.TedeeLockZustand.postUpdate(lockZustand);
    var lockConnect = actions.Transformation.transform("JSONPATH","$.isConnected" ,lockRequest); 
      items.TedeeLockVerbunden.postUpdate(lockConnect);
      if (lockConnect != '1'){
        var volume   = 20;
        var sound    = 4;
        var message  = ['1Tedee Lock disconnected',volume,sound];
          OH_function.sendMessage(message); 
      }
    }catch (error) {
      console.log("Router oder wlan Fehler : "+error);
    }  
  }

  // Tedee Schloss schliessen oder öffnen
  else if ((event.itemName == "TedeeSchlossSchliessen")|| (event.itemName == "TedeeSchlossOeffnen")) {
    console.log("Schloss wird geschlossen");
  
    var api_key = getApiKey();
    var content_type = "application/json";
    var content= "Content-Length: 0 ";

  try {
    if (event.itemName == "TedeeSchlossSchliessen") {
      var header = new Map();
        header.set( "accept","*/*");
        header.set( "api_token",api_key);
      var lock_url   = "http://"+bridge_IP+"/v1.0/lock/"+lock_ID+"/lock"; 
    }
    if (event.itemName == "TedeeSchlossOeffnen"){
      var header = new Map();
        header.set( "accept","*/*");
        header.set( "mode",open_mode);
        header.set( "api_token",api_key);
      var lock_url   = "http://"+bridge_IP+"/v1.0/lock/"+lock_ID+"/unlock";
    }
  var lockRequest   = actions.HTTP.sendHttpPostRequest(lock_url,content_type,content,header,  5000); 
    console.log("lockRequest schliessen = " + lockRequest);
  }catch (error) {
    console.log("Router oder wlan Fehler : "+error);
    }   
  }
  else{
    console.log("Anfragen nicht durchgefĂŒhrt = "+ event.itemName);
  }
}
else{
  console.log("WLAN nicht verfĂŒgbar ");
}