Automating Dumb Devices with Node-Red and Home Assistant

HardwareHome AssistantNode-RedSmart Home

Written by:

Here in coastal California, air conditioning is extremely uncommon. So during a recent heat wave, I got desperate and went down to the big box store in search of some relief and came home with a regular old dumb fan. I’m sure this is the kind of gadget you could probably spend hundreds of dollars on for a ‘smart’ version, but I’m more in favor of adapting existing, cheap technology to do my bidding instead. And since my dumb fan came with an infrared remote control, it’s quite simple to integrate it into the smart home using Node-Red and Home Assistant.


What does it mean to say a gadget is ‘smart’? It needs to be able to report its state somehow, and accept commands to change that state. Any old device with a remote can be controlled via an IR transmitter, but we need a way to observe what it’s doing as well. For my dumb fan, we can observe the amount of power used to determine it’s state (off, low, medium, or high).


To accomplish this I’m using:

For a DIY alternative to the Broadlink, try OpenMQTTGateway, and for a commercial alternative to the Sonoff POW try the Wemo Insight Switch.


With the Sonoff POW flashed with the Tasmota firmware, I need to make some observations. What range of power is used with the fan set at each speed setting?

Sonoff Pow with Tasmota firmware

Sonoff POW with Tasmota firmware

On my fan it came out to:

  • Low <= 29
  • Medium <= 34
  • High > 34

Next I captured the IR codes with the Broadlink RM. My fan just has two commands: toggle power, and toggle speed.


The first step to getting this thing smart-ified is quantifying it’s state in Home Assistant. So to get the power reading for the Sonoff Pow with the Tasmota firmware, my sensors.yaml configuration is:

- platform: mqtt
  name: "Fan Power"
  state_topic: "tele/fan/SENSOR"
  value_template: '{{ value_json["ENERGY"]["Power"] }}'
  unit_of_measurement: "W"

I then make a template sensor that converts that power reading into the actual fan speed:

- platform: template
      friendly_name: Actual Fan Speed
      value_template: >-
        {% if states.sensor.fan_power.state | float <= 5 %}
        {% elif states.sensor.fan_power.state | float <= 29 %}
        {% elif states.sensor.fan_power.state | float <= 34 %}
        {% elif states.sensor.fan_power.state | float > 34 %}
        {% else %}
        {% endif %}

For my IR commands I make a script so I can just have the IR packet in one place. This is in my scripts.yaml file:

  alias: Fan Power Toggle
    - service: switch.broadlink_send_packet_192_168_0_xx
  alias: Fan Speed Toggle
    - service: switch.broadlink_send_packet_192_168_0_xx


I now have all the commands and info needed to automate the fan, we just need a little Node-Red logic to put it together.


Ok now for the fun part, we have all the information we need – how do we make this into a smart fan?

Home Assistant provides us with an MQTT fan template. The MQTT entity kind of acts like a shell that our Node-Red logic can inject it’s smarts into. Node-Red will read the sensors we created above, and keep the state of the MQTT fan entity in sync with the observed state. When commands are issued, Node-Red will figure out what to do and issue the appropriate IR signal to the actual fan.

RELATED >>  Pi to NUC Part 1: Migrating Hass.IO to a Virtual Machine with Proxmox & Docker


  - platform: mqtt
    name: "Floor Fan"
    state_topic: "floor_fan/on/state"
    command_topic: "floor_fan/on/set"
    speed_state_topic: "floor_fan/speed/state"
    speed_command_topic: "floor_fan/speed/set"
    availability_topic: "tele/fan/LWT"
    payload_available: "Online"
    payload_not_available: "Offline"
    qos: 0
    payload_on: "true"
    payload_off: "false"
    payload_low_speed: "low"
    payload_medium_speed: "medium"
    payload_high_speed: "high"
      - low
      - medium
      - high

The availability_topic is the last-will-and-testament message from the Sonoff POW – this tells Home Assistant if the device is online or not. The rest of the fan topics and payloads I made up and you can make them whatever makes sense to you.

Home Assistant and Node-Red will use these topics to send the defined messages back and forth.


Click here to import this whole flow into Node-Red.


First thing we need to do is make sure the fan.floor_fan entity I created above is reflecting reality accurately. If the power is at 0 it should be set to ‘Off’, if it’s not ‘Off’ then I need to know what speed it’s set at.

Fan Entity State

Fan Entity State

As the sensor.fan_speed_actual changes based on the power reading, Node-Red will send an MQTT message to Home Assistant so that it can update the fan.floor_fan entity.

This means it doesn’t matter how the state of the fan is changed (via Home Assistant command, it’s own remote, MQTT message, or the button on the actual device) the state will be accurately tracked.


If the fan entity is changed to on or off in Home Assistant, we need to send that command to the fan via IR.

Send On & Off Commands via Infrared

Send On & Off Commands via Infrared


This was the only sort of tricky part. My fan has 3 speeds – low, medium, high – and only a toggle to control them. This means to switch between speeds you either have to press the toggle once or twice to get to the desired speed.

When a command to change the fan speed is given, the actual speed (sensor.fan_speed_actual) is compared to the desired speed (the payload on the floor_fan/speed/set MQTT topic). I then have a function that determines the appropriate number of toggles.

Fan Speed Toggle Logic

Fan Speed Toggle Logic

The function node looks like:

newmsg = {};
currentSpeed = msg.payload["sensor.fan_speed_actual"].toLowerCase();
desiredSpeed = msg.payload["floor_fan/speed/set"].toLowerCase();
if (currentSpeed == desiredSpeed) {
  newmsg.payload = 0;
else if (currentSpeed == "low" && desiredSpeed == "medium") {
  newmsg.payload = 1;
else if (currentSpeed == "high" && desiredSpeed == "low") {
  newmsg.payload = 1;
else if (currentSpeed == "low" && desiredSpeed == "high") {
  newmsg.payload = 2;
else if (currentSpeed == "medium" && desiredSpeed == "high") {
  newmsg.payload = 1;
else if (currentSpeed == "medium" && desiredSpeed == "low") {
  newmsg.payload = 2;
else if (currentSpeed == "high" && desiredSpeed == "medium") {
  newmsg.payload = 2;
else {
  newmsg.payload = "???? Current " + currentSpeed + " Desired " + desiredSpeed;
return newmsg;

Pretty simple!


No longer so dumb

No longer so dumb

So now my dumb fan can be controlled by my automations in Home Assistant, and it’s state will be accurately tracked regardless of how people interact with it (whether through the remote or pushing the fan’s physical buttons). This process can be repeated for pretty much any dumb device that can be controlled through a remote!


5 Replies to “Automating Dumb Devices with Node-Red and Home Assistant”

  1. Kuan Yew says:

    Good write up. What if I don’t have a Sonoff POW and I don’t have a smart switch although I do have the Black Bean Mini. What I’m trying to achieve is that, when we switch on the fan via the dumb switch, based on the current temp of the room/outdoors, the fan will dial to the necessary speed (I have a 5-speed fan). Is that doable with Node-Red?

  2. Edmund says:

    Excellent write up Brad, thank you for building this. Your information fills in a lot of missing areas in the current documentation of Home Assistant / Node-RED interfacing.

  3. Bruce says:

    This is one of my favorites. Unfortunately due to the new Fan MQTT layout in home assistant it’s stopped working. Do you have any plans on an update for this automation. Thanks

  4. hoang says:

    30 Oct 05:51:27 – [error] [function:Here to There] TypeError: Cannot read property ‘toLowerCase’ of undefined

Leave a Reply

Your email address will not be published. Required fields are marked *