Mapping IKEA TRADFRI remote in NodeRED

Buttons, more buttons in NodeRED

I was really disappointed when I couldn’t use the IKEA TRADFRI remote in NodeRED or with Xiaomi MiHome ecosystem despite the promised IKEA integration. The remote has been collecting dust ever since as the Swedish company did not win my heart with their less than ideal smart home solutions. Now, I have the CC2531 USB Zigbee sniffer flashed and ready (no CC debugger flash guide) for use with Zigbee2MQTT (how to add new devices) – so all I have left is to map buttons in NodeRED to recreate the original functionality.

IKEA TRADFRI remote

If you have the IKEA’s dimmer, I already covered the transition to NodeRED here. IKEA TRADFRI remote comes with 3 basic functions:

  • toggle (on/off)
  • brightness (up/down)
  • change device (left/right)

Thanks to NodeRED, there are 2 more actions available:

  • brightness (long press)
  • change device (long press)

I can use all these inputs to create the following actions:

  • toggle light
  • change brightness (step)
  • change brightness (smooth)
  • change device
  • pick 2 favourite devices

IKEA TRADFRI remote in NodeRED

If you connect a debug node to IKEA TRADFRI remote in NodeRED, you will see 13 actions available. I will map almost all of them. The actions are:

  • toggle
  • brightness_up_click|brightness_down_click
  • brightness_up_hold|brightness_up_release
  • brightness_down_hold|brightness_down_release
  • arrow_right_click|arrow_left_click
  • arrow_right_hold|arrow_right_release
  • arrow_left_hold|arrow_left_release

Preparation

Since I know I will control more than one device (there is no limit) I will create an array that stores all names of the devices. These names will correspond with the names given by Zigbee2MQTT (important). I would also encourage you to enable saving contexts in NodeRED otherwise everything written in variables will be lost on reboot.

["spotlight1","ikeabulb1","ikeabulb2"]

This will be stored as a global variable: “ZigbeeLights“. I will be able to cycle through each device using arrow buttons. Since I can toggle and change the brightness of each light, I want to store these values as well in flow contexts. I created a set of flow variables for each light I have:

var state = msg.payload.state;
var brightness = msg.payload.brightness;

flow.set("spotlight1_brightness", brightness);
flow.set("spotlight1_state", state);

Try keeping the naming convention consistent so you could keep track of the variables.

Changing devices

Before I take care of other functions, I need to sort out how NodeRED will know which light to control. There are 2 mechanisms in place. First, I took the advantage of the “long press” option to set the favourite devices:

var x = msg.payload.action;
 if(x === "arrow_right_release"){
    flow.set("activeDevice", "spotlight1"); 
 }
 if(x === "arrow_left_release"){
    flow.set("activeDevice", "ikeabulb2"); 
 }
 return msg;

These will trigger only when the button is released – and action: arrow_left_release|arrow_right_release appears. With that sorted, let’s cycle through the devices.

Cycling through lights

There are 2 different functions triggered, one to count up and one to count down. These are very similar in structure, they just iterate the variable “count” and update previously created flow variable “activeDevice“.

FUNCTION NODE: toggle devices ++
var devices = global.get("ZigbeeLights");

var x = devices.length - 1;
var count = flow.get("count");
var y = isNaN(count);

if(y === true ){
    count = 0;
    flow.set("count", count);
}

var currentDevice = devices[count];
count++;

if(count > x){
    count = 0;
}

flow.set("count", count);
flow.set("activeDevice", currentDevice);

return msg;
FUNCTION NODE: toggle devices --
var devices = global.get("ZigbeeLights");

var x = devices.length - 1;
var count = flow.get("count");
var y = isNaN(count);

if(y === true ){
    count = 0;
    flow.set("count", count);
}

var currentDevice = devices[count];
count--;

if(count < 0){
    count = x;
}

flow.set("count", count);
flow.set("activeDevice", currentDevice);
return msg;

Each time I press the arrow button the variable count will change and pick the next device form the array stored in "ZigbeeDevices".

Toggle

Now that NodeRED knows what device I want to control, time to turn it on or off! The present state is always stored in an appropriate light context, so I just have to look up the current value, change it to the opposite one and send that to the correct light using MQTT OUT.

To pick the correct device, I have to compile the correct MQTT topic. It's easy, as I alredy know the name of the active device, which is required for this.

FUNCTION NODE: toggle
var device = flow.get("activeDevice");
var currentState = device + "_state";
var current = flow.get(currentState);



if(current === "ON"){
    state = "OFF";
    msg.topic = "zigbee2mqtt/" + device + "/set";
    msg.payload = {
        "state": state
    };
    return msg;
}

if(current === "OFF"){
    state = "ON";
    msg.topic = "zigbee2mqtt/" + device + "/set";
    msg.payload = {
        "state": state
    };
    return msg;
}

The message ON|OFF is sent as msg.payload.state to the msg.topic = zigbee2mqtt/devicename/set.

Brightness

There are 2 ways to increase/decrease the brightness. You can click the button to go up a step (up/down by 50 out of 255) or you can press and hold the button to increase it by a smaller increment (30) until the right level.

Brightness steps

I'm using mirrored functions again to increase and decrease the brightness of the light and make sure that msg.payload.brightness where the value is stored doesn't go outside the bounds 0:255.

Each time the button is pressed, the current value is read and adjusted by 50. To update the light source, I compile the topic with the current device again and send the msg.payload.brightness to the MQTT OUT node.

FUNCTION NODE: Brightness UP 50
var device = flow.get("activeDevice");
var currentState = device + "_brightness";
var current = flow.get(currentState);

var brightness = current + 50;


if(brightness > 255){
    brightness = 255;
    msg.topic = "zigbee2mqtt/" + device + "/set";
    msg.payload = {
        "brightness": brightness
    };
    return msg;
}

if(brightness < 0){
    brightness = 0;
    msg.topic = "zigbee2mqtt/" + device + "/set";
    msg.payload = {
        "brightness": brightness
    };
    return msg;
}
else{
    msg.topic = "zigbee2mqtt/" + device + "/set";
    msg.payload = {
        "brightness": brightness
    };
    return msg;
}
FUNCTION NODE: Brightness DOWN 50
var device = flow.get("activeDevice");
var currentState = device + "_brightness";
var current = flow.get(currentState);


var brightness = current - 50;


if(brightness > 255){
    brightness = 255;
    msg.topic = "zigbee2mqtt/" + device + "/set";
    msg.payload = {
        "brightness": brightness
    };
    return msg;
}

if(brightness < 0){
    brightness = 0;
    msg.topic = "zigbee2mqtt/" + device + "/set";
    msg.payload = {
        "brightness": brightness
    };
    return msg;
}
else{
    msg.topic = "zigbee2mqtt/" + device + "/set";
    msg.payload = {
        "brightness": brightness
    };
    return msg;
}

The state updates defined in the preparation section will assure that new values are set in the NodeRED.

Brightness by smaller increment

I actually wrote about this before in my Smart Dimmer article. I used a single button to change the brightness up/down based on the current values. I didn't have a second button so I had to use click and hold to control both changes.

When the button is pressed, there is a loop that keeps changing the value by 30 and sends the update to the light until the loop is stopped by the the brightness_down_release|brightness_up_release.

When I press and hold the button down, the flow.press value turns true and it allows the messages in my loop to pass through the switch node. The same variable is set to false when the button is released and the loop stops.

It's worth noting that each time I update the light with the brightness value, the same is sent as msg.feedback to for iteration.

Zigbee CC2531
Support NotEnoughTech
Buy Zigbee CC2531
Use Zigbee2MQTT to control your Zigbee enabled devices.

Conclusion

This is a mere recreation of the original functionality of the IKEA TRADFRI remote in NodeRED. There are 13 actions available that could be used for lights, garage door controls, blinds and other connected devices. The sky is the limit! I will be posting from time to time about interesting concepts. Let me know what would be a cool use of the IKEA TRADFRI remote in NodeRED in this Reddit thread and I might write about the best concepts in details.

Support NotEnoughTech
A lot of time and effort goes into keeping NotEnoughTech alive! If my work helped you out, consider buying me a coffee or check out exclusive rewards available to Patreon supporters.
SHARE