HomeHome AutomationTurning buttons into dimmers - Xiaomi MiHome Switch in NodeRED

Turning buttons into dimmers – Xiaomi MiHome Switch in NodeRED

Click to A, Double Click to B, and much more

A button can be more than just a click. It could be at least three things, as shown in this Xiaomi MiHome Switch overview. I have even more freedom in interpreting button behaviour thanks to Xiaomi MiHome Switch in NodeRED. After all, if I’m going to spend the kindly donated funds onto something – I will squeeze out every possible functionality out of it.

Smart dimming – Xiaomi MiHome Switch in NodeRED

There are a couple of ways I can approach this problem. The simplest of options would be to cycle through the presets with one of the click functions. I want to preserve the clicks for other options.  Clicks are far too precious for that. In this article, I will show you how to map Xiaomi MiHome Switch in NodeRED and use all available actions. To showcase this I will use my trusty Yeelight Smart bulbs which are just plain awesome.

I will showcase how to add the following options to the Xiaomi Aqara Switch in NodeRED:

  • click – toggle lights (retain previous values)
  • double click – cycle through temperature presets (retaining other values)
  • press & hold – smart dimmer (changing brightness up/down)

bonus:

  • press & hold – select different actions based on the length of a press

Because of the Xiaomi MiHome Switch has to be linked to Xiaomi MiHome Gateway, you will need as well. I will assume you have read that article and you know how the Xiaomi gadgets work with NodeREd already. If not – you may want to brush up on that knowledge.

Buy Aqara Switch

Buy it using these links to support NotEnoughTech.

Click… ON, Click… OFF

The toggle is the most basic behaviour you could imagine. To my surprise, the toggle behaviour is not available to IKEA Trådfri devices connected to MiHome. I’m sure the support will come soon, but for now, you have to map 2 actions to turn the lights on and off. Yeelight posses no such issues, and I had implemented a toggle like that previously in my Yeelight & Amazon Dash write up.

The xiaomi switch node in node red comes with a couple of options. If you want to map just the click and double-click, the template will make it easy, but that option won’t return any meaningful data for long presses. Setting it to just values gives us what we need, without dropping support for the press & hold functionality!

{
	"status": "click",
	"voltage": 3.092,
	"voltage_level": "high",
	"time": 1547997692968,
	"device": "Button"
}

I’m checking msg.payload.status to filter out unwanted behaviour, but before I can apply my actions, I want to see what is the current state of my Yeelight. Bear in mind that if you have more than one lightbulb, you will have to pick one which you are going to monitor. The other bulbs will get their statuses overridden. The best way to pass these values over is to save them as flow variables. I know I will use more than the current status so I’m saving them in bulk with a function node:

FUNCTION NODE: Update variables
flow.set('yeelight1_on',msg.payload.state.on);
flow.set('yeelight1_bri',msg.payload.state.bri);
flow.set('yeelight1_colormode',msg.payload.state.colormode);
flow.set('yeelight1_ct',msg.payload.state.ct);
flow.set('yeelight1_hex',msg.payload.state.hex);
flow.set('yeelight1_hue',msg.payload.state.hue);
flow.set('yeelight1_sat',msg.payload.state.sat);

Once I know the current values, I can perform the opposite action to create the toggle behaviour. Because I’m dealing with more than just one value, I’m going to use another function node for that.

FUNCTION NODE: toggle
var on = flow.get('yeelight1_on');
var bri = flow.get('yeelight1_bri');
var colormode = flow.get('yeelight1_colormode');
var ct = flow.get('yeelight1_ct');
var hex = flow.get('yeelight1_hex');
var hue = flow.get('yeelight1_hue');
var sat = flow.get('yeelight1_sat');

if(on === true){
    var x = false;
}

if(on === false){
    var x = true;
    }
msg.payload = {
        "on": x,
        "bri": bri,
        "colormode": colormode,
        "ct": ct,
        "hex": hex,
        "hue": hue,
        "sat": sat
    };

return msg;

If you monitor more than one lightbulb you have to make sure that you won’t end up in the not toggable state. Each time I create an object with all the variables stored previously. This way, the Yeelight will change just the ON|OFF state, keeping all the previous values intact.

Lastly, all I need is to send this msg.payload to the Yeelight node to send the new command.

Click, click… action, click, click… action

Since the double click is also available, I want to toggle through my preferred colour values. I’m not going to use anything outlandish (reds, blues, greens). I just want to modify the temperature of the lights to suit the mood and situation. I will create 3 presets, but following this example you can map a virtually unlimited number of presets.

I’m going to use the same flow variables as in the click option, so I don’t have to recreate the Yeelight update flow. A simple switch node with “double_click” string will filter out all the messages sent by Xiaomi MiHome Switch in NodeRED.

{
	"status": "double_click",
	"voltage": 3.092,
	"voltage_level": "high",
	"time": 1547999032815,
	"device": "Button"
}

Then I picked three prefered presets, and modified the JSON files accordingly. Each preset has temperature & colour, while other values are pulled once again from the previously saved flow variables:

msg.payload = {
	"on": true,
	"bri": bri,
	"colormode": "ct",
	"ct": 1902,
	"hex": "#FF8400",
	"hue": hue,
	"sat": sat}

To toggle through the presets, I will iterate a number that is stored in a context variable. Each time the payload reaches this function node the x increases by one. Then I simply set hardcoded values as msg.payload and send it to the Yeelight node. The x resets to 1 in the last preset, so it would circle through again.

FUNCTION NODE: Select Preset
var x = flow.get('color_preset');
var bri = flow.get('yeelight1_bri');
var ct;
var hex;
var colormode = "ct";
var hue = flow.get('yeelight1_hue');
var sat = flow.get('yeelight1_sat');

if(x === null || x === undefined|| x == 1 || x > 3){
    ct  = 1902;
	hex = "#FF8400";
	x=1;
	}
if(x == 2){
    ct  = 5347;
	hex = "#FFFCF6";
	}
if(x == 3){
    ct  = 6600;
	hex = "#FFEFE1";
	}	
	

msg.payload = {
	"on": true,
	"bri": bri,
	"colormode": "ct",
	"ct": ct,
	"hex": hex,
	"hue": hue,
	"sat": sat
}; 	
x++;
flow.set('color_preset', x);
msg.xvalue = x;
return msg;

You could use arrays to store the presets as well, but for such a small number of presets, this method is efficient enough.

Smart Dimmer

The most time-consuming task was to figure out how to dim lights using Xiaomi MiHome Switch in NodeRED. I had to overcome some limitations to combat the timeouts of the Yeelight node and create a dimmer that will recognise what I am trying to do. I settled for a dimmer that works like this:

  • increase/decrease brightness in 8 steps over 4 seconds
  • save brightness value when the button is released
  • provide the visual feedback while a button is pressed
  • works with multiple lights
  • retains other values like colour or temperature
  • dims lights if brightness is set above 50%
  • increases lights if brightness is below 50%

I’m really pleased with the result. The number of steps and the timing can be modified a little as long as you don’t spam the Yeelight node too much with constant updates.

Trigger

When the button is pressed down for longer than what would constitute a click it, Xiaomi MiHome Switch in NodeRED issues two payloads. One is associated with the button pressed action, the other one appears when the button is released:

{
	"status": "long_click_press",
	"voltage": 3.082,
	"voltage_level": "high",
	"time": 1548000078945,
	"device": "Button"
}
{
	"status": "long_click_release",
	"voltage": 3.082,
	"voltage_level": "high",
	"time": 1548000078998,
	"device": "Button"
}

I can use this to start iterating through the brightness steps until the button is released. I talked about controlling the flows in my NodeRED for beginners: Tip & Tricks. I can start the flow (“true”) when the button is pressed and stop (“false”) it when the button is released.

Buy Yeelight RGB smart bulb

Buy it using these links to support NotEnoughTech.

Smart dimming

When the button is pressed down, I want to check the initial brightness value. I can do this by looking up the global variable against a simple IF statement:

FUNCTION NODE: Read Bri
var x = flow.get('yeelight1_bri')

if(x <= 125){ 
    msg.payload = x; 
    return[msg,null]; 
} 
if(x > 125){
    msg.payload = x;
    return[null,msg];
}

Note, that I’m only checking the value of a single Yeelight bulb. I will override the other bulbs.

Dim/Bright up loop

The main function iterates the brightness levels – starting from the saved in the variable level, and iterating up or down depending on the initial value. I picked 30 as a reasonable modifier. I wanted to have a reasonable degree of control and not wait forever for the lights to go from 0-254.

The script isn’t complicated, but I had to add the limiting conditions for where the brightness value would fall outside of the 0-254 range.

FUNCTION NODE: brightness up
var colormode = flow.get('yeelight1_colormode');
var ct = flow.get('yeelight1_ct');
var hex = flow.get('yeelight1_hex');
var hue = flow.get('yeelight1_hue');
var sat = flow.get('yeelight1_sat');

var brightness =  msg.payload;

if(brightness > 0){
    brightness = brightness + 30;    
	}
	
if(brightness >= 255){
    brightness = 254;
    flow.set('yeelight1_bri',brightness);
    flow.set('press', false);
    }

msg.payload = {
        "on": true,
        "bri": brightness,
        "colormode": colormode,
        "ct": ct,
        "hex": hex,
        "hue": hue,
        "sat": sat
    };
	msg.feedback = brightness;
	flow.set('yeelight1_bri', brightness);
return msg;
FUNCTION NODE: brightness down
var colormode = flow.get('yeelight1_colormode');
var ct = flow.get('yeelight1_ct');
var hex = flow.get('yeelight1_hex');
var hue = flow.get('yeelight1_hue');
var sat = flow.get('yeelight1_sat');

var brightness =  msg.payload;

if(brightness <= 255){
    brightness = brightness - 30;    
	}
	
if(brightness <= 0){
    brightness = 1;
    flow.set('yeelight1_bri',brightness);
    flow.set('press', false);
    }

msg.payload = {
        "on": true,
        "bri": brightness,
        "colormode": colormode,
        "ct": ct,
        "hex": hex,
        "hue": hue,
        "sat": sat
    };
	msg.feedback = brightness;
	flow.set('yeelight1_bri', brightness);
return msg;

The payload with the new brightness level is sent back to the same function with a 500ms delay, and passed over as msg.payload and submitted to Yeelight node to provide visual feedback. The loop is controlled by the node, so it only passes the payload for iteration while the button is pressed.

Bonus

If you don’t want a dimmer, but you rather pick actions in NodeRED based on how long the button is held for, you can do this too! I can easily measure at what time the button has been pressed. Subtracting that from the timestamp of the button being released will give me the number of milliseconds the button had been pressed for. Just note that the actual timing is shorter as the event doesn’t start until the “long_click_press” is issued (after a timeout responsible for the “click” event).

The long presses of the Xiaomi MiHome Switch in NodeRED provided me with the time in ms. To calculate this I used the function node:

FUNCTION NODE: get press time
if(msg.topic === "press"){
    var d = new Date();
    var x = d.getTime();
    context.set('press', x);
}
if(msg.topic === "letgo"){
    var d = new Date();
    var y = d.getTime();
    context.set('letgo', y);
}


var press = context.get('press');
var letgo = context.get('letgo');

if(press !== undefined && letgo !== undefined){
    var sec = letgo - press;
    msg.payload = sec;
    context.set('letgo', undefined);
    context.set('press', undefined);
    return msg;
}

Because the function node would send the payload instantly after receiving the press down notification, I used the topics and the context variable to store the timing of the press down and release. This way, unless both the press down and release values are noted, the function returns nothing.

Conclusion

It’s simply impressive how much you can achieve with a simple button. Home automation is about ease of use and efficiency. This tutorial shows how you can take advantage of theoretically limiting ON|OFF functionality and use the Xiaomi MiHome Switch in NodeRED in a really creative way.

Project Download

Download project files here. Bear in mind that Patreon supporters have early access to project files and videos.

PayPal

Nothing says "Thank you" better than keeping my coffee jar topped up!

Patreon

Support me on Patreon and get an early access to tutorial files and videos.

image/svg+xml

Bitcoin (BTC)

Use this QR to keep me caffeinated with BTC: 1FwFqqh71mUTENcRe9q4s9AWFgoc8BA9ZU

Smart Ideas with

Automate your space in with these ecosystems and integrate it with other automation services

client-image
client-image
client-image
client-image
client-image
client-image
client-image
client-image
client-image

Learn NodeRED

NodeRED for beginners: 1. Why do you need a NodeRED server?

0
To server or not to server? That's a very silly question!

Best Automation Projects

Tuya SDK for beginners: Intro to Tuya Cloud API

0
Working with Tuya Cloud API. A guide to Cloud automation for beginners, get started with REST!

NEST your old thermostat under $5

0
Nest-ing up your older thermostat under $5

Nora – Google Assistant in NodeRED

0
Integrate Google Assistant with NodeRED thanks to Nora - NodeRED home automation

DIY Smart Washing Machine – for about 15 bucks!

0
Learn how to add washing machine notifications to your Google Home on the cheap

Sonoff Zigbee Bridge – review

0
Sonoff line up will soon include Sonoff Zigbee Bridge and more Zigbee sensors - here is the first look

Smart Home

Remotes aren’t done with us yet: SwitchBot Remote

0
If you hate having 20 remotes hidden in your sofa, or your parents despise talking to Alexa to turn the lights on - SwitchBot Universal Remote is here to deal with all this!

We’ve seen this before: SwitchBot K10+ PRO

0
This is an odd one. Building on the success of SwitchBot K10+ they released SwitchBot K10+ PRO - but is the experience actually better?

Is this the smart panel we have waited for?

0
ITEAD has released a new smart panel: Sonoff NSPanel Pro 120 - have they learned the lesson from the terrible launch of the original Pro? Let's see what's new.

Aqara FP1E detects motionless humans

0
This isn't exactly a new device, it's an interaction of the original Aqara presence sensor. Aqara FP1E brings Matter, ZigBee and new triggers to your smart home

SwitchBot S10: cleaning re-imagined!

0
SwitchBot S10 promises unattended vacuuming and mopping so you can focus on things you love and care for. Does it deliver?