Ditch the Remote: Aesthetically clean control for Ventair SkyFan DC with Clipsal, Sonoff, MQTT, Tasmota Home Assistant & TUYA


Do you like the trend of remotes for appliances? I don’t. They often look our of place, require you to drill tiny holes in walls for mounts and even still, they can go missing. Let alone, they need batteries and you never can find them when you need them.

But modern devices do more than just turn on and off, they contain a plethora of functionality, and hence the need of remotes. Yes applications on phone often exist, but they are cumbersome for other reasons.

So how can you keep all that functionality, but integrate these modern devices into your house so they just blend in?

I recently purchased 4 x Ventair SkyFan’s for my house with TUYA modules. I initially explored using Amazon Alexa (one present in every bedroom) and Home Assistant, both work to varying degrees, but they are clumsy in many ways. The time it take to issue an utterance to Alexa (Alexa, Set Parents Fan to Speed 1), or to touch an Home Assistant Lovelace dashboard is dwarfed by the simplicity and speed of a button press, especially as you enter or exit a room.

So in this post, l am going to walk you can add an additional control plane to your remote controlled devices. This additional control plane will be in the form of Clipsal push button switches that match other switches in my house.

And as usual lets start with an end-2-end demo.


Whilst this post is specific to TUYA, the pattern is applicable to any device with only subtle modifications required.

What I will cover in this post

  • Fan & Light Control Logic
  • Adding Buttons With Clipsal & Sonoff
  • Counting Button Presses with Tasmota & Arduino
  • Converting MQTT Messages To TUYA Commands With Home Assistant


Arduino? You may be asking, why don’t you just use Home Assistant? It can read MQTT messages. That would be correct, however Home Assistant is not a RTOS (Real Time Operating System), and if you read on, I am needing to detect up to 3 state changes in less than 550ms. Home Assistant whilst being amazing, is not the reliability powerhouse and a Arduino is when it comes to timing. Pick the right tool for the job and in this case, the Arduino is the right tool. To date this pattern has been running 2 months without incident.

Fan & Light Control Logic
I am going to be adding an additional two buttons to every wall plate. One button for the light, one button for the fan. Electrically they are not connected to the fan in any way. I also want more than just on / off functionality, I want the ability to increase/decrease the fan speed and change the brightness of the light.

It is worth noting I am forgoing functionality that we will seldom use. For the rare occasions, there is the remote or the TUYA application (Home Assistant Integration is very watered down, come on TUYA, come on TUYA please up your game).

As the Clipsal 30PB buttons are not connected electrically to the SkyFan DC, it graces us options. This is what I settled on

FanLight
Single PressToggle state (either on or off)Toggle state (either on or off)
Double Press Increase fan speed or turn on if offIncrease brightness or turn on if off
Triple PressDecrease fan speed or turn off if onDecrease brightness or turn off if on

Read below for more details, but the short of it is, as soon as you press a button, the number or times you press the button is evaluated after 550ms. This pattern provides a wide range of functionality for every day living.


Adding Buttons With Clipsal & Sonoff
The goal here is to add two more buttons and to be able to count button presses. For that I am using an appliance module that can send MQTT messages. I am using a Sonoff DUAL R3 which is a dual channel appliance module. Out of the box it does not provide MQTT functionality but with its firmware changed to Tasmota we can generate a MQTT message every time a button is pressed. I wont cover the how of flashing Tasmota on a Sonoff Dual R3, for that see my post I authored many years ago on this very process – https://automation.baldacchino.net/sonoff-dual-r3-tasmota-home-assistant









I

I am using Clipsal 30PB’s as my switches, you can use anything. I do like push button mechanical switches as their state looks the same regardless of how they are toggled, again back to aesthetics.

As this does require 120v/240v manipulation ensure you follow your country’s regulations as you may need to use a licenses electrician.

And with that, assemble your wall plate and configure Tasmota and ensure that each switch is broadcasting MQTT messages when a push button is pressed.


Counting Button Presses with Tasmota & Arduino
This section is where we start applying our logic. Our Arduino is there ‘hero’ device in this solution. We need to perform a few functions. Firstly we need to subscribe to the MQTT topics of the Sonoff / Tasmota device. There is a topic for each button. They are as follows

client2.subscribe("stat/SONOFF_Tasmota_Skyfan_Parents/POWER1");
client2.subscribe("stat/SONOFF_Tasmota_Skyfan_Parents/POWER2");
Arduino Serial Console – Messages are arriving via MQTT from the Tasmota flashed Sonoff that we can act on.

I am going to step you through parts of my Arduino sketch. Whilst I installed 4 fans, my examples will be for a sole SkyFan to which I call ‘SkyFan Parents’ in my code. Replace this name accordingly. This is a pattern.

Timers and variables

//SkyFan Timers - Parents
unsigned long lastMessageTime = 0;
int messageCount_Parents_Fan = 0;
int messageCount_Parents_Light = 0;


Your sketch will nee to listen for incoming MQTT messages. You will need to use a call-back pattern. For more information on this please look online

 //MQTT callback
void callback(char* topic, byte* payload, unsigned int length) {
  MQTTTopic = String(topic);
  MQTTPayload = ""; 
  Serial.println("MQTT topic is [" + MQTTTopic +"]");
  for (int i=0;i<length;i++) {
      MQTTPayload = String(MQTTPayload + (char)payload[i]);  
  }
  Serial.println("MQTT payload is [" + MQTTPayload + "]");
}

The Arduino needs to subscribe to MQTT topics, this will ensure when the buttons are pressed and messages are published, that the Arduino is able to detect button presses. This code will connect to our MQTT broker and subscribe to our two MQTT topics. Note, in my code below, if I loose connection to my MQTT broker all other aspects of the Arduino sketch will pause. This is by design as I do not want any synchronisation issues between Home Assistant, TUYA and other systems

//IP Address of MQTT Server
IPAddress MQTTserver(10, 0, 0, 200);
// MQTT
EthernetClient ethClient2;
PubSubClient client2(ethClient2);    

void reconnect() {
  // Loop until we're reconnected
  while (!client2.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client2.connect("arduinoClient")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client2.publish("stat/ARDUINO/IP_Address","10.0.0.50");
      // ... and resubscribe
      client2.subscribe("stat/SONOFF_Tasmota_Skyfan_Parents/POWER1");
      client2.subscribe("stat/SONOFF_Tasmota_Skyfan_Parents/POWER2"); 
    } else {
      Serial.print("failed, rc=");
      Serial.print(client2.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

This block of code is listening for incoming messages and is part of the main loop.

//PARENTS SKYFAN LIGHT
    if (String(MQTTTopic) == "stat/SONOFF_Tasmota_Skyfan_Parents/POWER1") {    
      lastMessageTime = millis();
      messageCount_Parents_Light++;
    MQTTPayload = "";
    MQTTTopic = "";
    } 


 //PARENTS SKYFAN FAN
    if (String(MQTTTopic) == "stat/SONOFF_Tasmota_Skyfan_Parents/POWER2") {    
      lastMessageTime = millis();
      messageCount_Parents_Fan++;
    MQTTPayload = "";
    MQTTTopic = "";
    } 

These two functions are used to evaluate the number of button presses. As you can see we have options for 1, 2 and 3 button presses.

void evaluateParentsSkyFan_Light() {
   if (messageCount_Parents_Light == 1) {
      client2.publish("cmnd/SONOFF_Tasmota_Skyfan_Parents/Light", "Toggle");
   } 
   else if (messageCount_Parents_Light == 2 {
     client2.publish("cmnd/SONOFF_Tasmota_Skyfan_Parents/Light", "Brighten");
   }
   else if (messageCount_Parents_Light == 3) {
     client2.publish("cmnd/SONOFF_Tasmota_Skyfan_Parents/Light", "Dim");
   }
}

void evaluateParentsSkyFan_Fan() {
   if (messageCount_Parents_Fan == 1) {
      client2.publish("cmnd/SONOFF_Tasmota_Skyfan_Parents/Fan", "Toggle");
      } 
   else if (messageCount_Parents_Fan == 2) {
      client2.publish("cmnd/SONOFF_Tasmota_Skyfan_Parents/Fan", "Increase");
   } 
   else if (messageCount_Parents_Fan == 3) {
      client2.publish("cmnd/SONOFF_Tasmota_Skyfan_Parents/Fan", "Decrease");
   }
}


Finally we go back to the main loop to stitch this together. Every 550ms we call our functions above and evaluate the number of times the button was pressed. The function that is being called will publish a MQTT message accordingly. 550ms was selected, it could be less, but this is safe amount of time for triple press for various people in my family.

//Parents Skyfan - Light
    if (millis() - lastMessageTime > 550 && messageCount_Parents_Light > 0) {
      evaluateParentsSkyFan_Light();
      messageCount_Parents_Light = 0; // Reset the message count after evaluation
    }

 //Parents Skyfan - Fan
    if (millis() - lastMessageTime > 550 && messageCount_Parents_Fan > 0) {
      evaluateParentsSkyFan_Fan();
      messageCount_Parents_Fan = 0; // Reset the message count after evaluation
    }

Converting MQTT Messages To TUYA Commands With Home Assistant
Messages are now being sent back to our MQTT broker, these messages state the action Home Assistant needs to take. The first thing one should be do after uploading your sketch is to validate it is working. I like to publish messages in the Arduino Serial Console, but I also look to ensure the desired message ends up in my MQTT broker. This is about validating as we build. I am using MQTT Explorer in the images above to ensure my messages are in-fact being sent correctly. This acts as a proof point for our Arduino code. If you have any issues, stop and debug.


Lastly, it should be obvious but you need to ensure you have installed your TUYA module in the SkyFan DC. This is not standard and can be purchased separately online.



In Home Assistant ensure your integrations between Home Assistant and TUYA is setup correctly. This post wont cover this, but follow the TUYA instructions that come with the module. You can use TUYA or TUYA Local. There are pro’s and cons of each. Thus far I have found TUYA to be reliable. Validate your integration is working and allowing you to alter the fan speed and light brightness.

Now, in Home Assistant we need to define Home Assistant Automations. I am using 6 automations per SkyFan DC. The below table summarises the name, trigger and acton. If you get clever, you could combine these in to one automation with more logic, I decided to break each distinct action in to a seperate automation. You could make one automation by taking my approach further and evaluate the MQTT payload and then branching from there.

HA Automation NameTriggerAction
Skyfan – Parents – Fan – ToggleMQTT Topic
cmnd/SONOFF_Tasmota_Skyfan_Parents/Fan
Payload
Toggle
Action
Toggle state (either on or off)
Skyfan – Parents – Fan – DecreaseMQTT Topic
cmnd/SONOFF_Tasmota_Skyfan_Parents/Fan
Payload
Decrease
Action
Fan: Decrease Speed - Decrement 30%
Skyfan – Parents – Fan – IncreaseMQTT Topic
cmnd/SONOFF_Tasmota_Skyfan_Parents/Fan
Payload
Increase
Action
Fan: Increase Speed - Increment 30%
Skyfan – Parents – Light – ToggleMQTT Topic
cmnd/SONOFF_Tasmota_Skyfan_Parents/Light
Payload
Toggle
Action
Toggle state (either on or off)
Skyfan – Parents – Light – Decrease DimMQTT Topic
cmnd/SONOFF_Tasmota_Skyfan_Parents/Light
Payload
Dim
Action
Light: Brightness Step - Decrease 20%
Skyfan – Parents – Light – Increase BrightnessMQTT Topic
cmnd/SONOFF_Tasmota_Skyfan_Parents/Light
Payload
Brighten
Action
Light: Brightness Step - Increase 20%

Below are screenshots for each of the 6 scenarios in Home Assistant as Automations. You should be able to use these screenshots to craft your own automations.


How ever you can edit your automations.yaml configuration file and inject the following code, obviously replacing entity names to suit your installation.

- id: '1735365809877'
  alias: Skyfan - Parents - Light - Toggle
  description: ''
  triggers:
  - trigger: mqtt
    topic: cmnd/SONOFF_Tasmota_Skyfan_Parents/Light
    payload: Toggle
  conditions: []
  actions:
  - action: light.toggle
    metadata: {}
    data: {}
    target:
      entity_id: light.skyfan_dc
  mode: single
- id: '1735366123723'
  alias: Skyfan - Parents - Light - Increase Brightness
  description: ''
  triggers:
  - topic: cmnd/SONOFF_Tasmota_Skyfan_Parents/Light
    payload: Brighten
    trigger: mqtt
  conditions: []
  actions:
  - target:
      entity_id: light.skyfan_dc
    data:
      brightness_step_pct: 20
    action: light.turn_on
  mode: single
- id: '1735366294023'
  alias: Skyfan - Parents - Light - Decrease Brightness
  description: ''
  triggers:
  - topic: cmnd/SONOFF_Tasmota_Skyfan_Parents/Light
    payload: Dim
    trigger: mqtt
  conditions: []
  actions:
  - target:
      entity_id: light.skyfan_dc
    data:
      brightness_step_pct: -20
    action: light.turn_on
  mode: single
- id: '1735450133869'
  alias: Skyfan - Parents - Fan - Toggle
  description: ''
  triggers:
  - topic: cmnd/SONOFF_Tasmota_Skyfan_Parents/Fan
    payload: Toggle
    trigger: mqtt
  conditions: []
  actions:
  - target:
      entity_id: fan.skyfan_dc
    action: fan.toggle
  mode: single
- id: '1735450401183'
  alias: Skyfan - Parents - Fan - Increase
  description: ''
  triggers:
  - topic: cmnd/SONOFF_Tasmota_Skyfan_Parents/Fan
    payload: Increase
    trigger: mqtt
  conditions: []
  actions:
  - action: fan.increase_speed
    target:
      entity_id: fan.skyfan_dc
    data:
      percentage_step: 30
  mode: single
- id: '1735450791708'
  alias: Skyfan - Parents - Fan - Decrease
  description: ''
  triggers:
  - topic: cmnd/SONOFF_Tasmota_Skyfan_Parents/Fan
    payload: Decrease
    trigger: mqtt
  conditions: []
  actions:
  - action: fan.decrease_speed
    target:
      entity_id:
      - fan.skyfan_dc
    data:
      percentage_step: 30
  mode: single

Summary & Wrap Up
Complicated, kind of, but really this isn’t that complex either. We are going from button press, to Sonoff Dual R3, to Tasmota, to MQTT, to Arduino, to Home Assistant and finally TUYA.

That sounds complex but hopefully after following along with this post it indeed is rather simple. It aesthetically cleans up your rooms, so put that remote in the draw and use it (or the TUYA Smart App) to change functions like fan direction or light colour (kelvin).

Any negatives? Cost, I love this approach, it is family approved but it is a luxury. If you are using Clipsal wall plates, with Clipsal push buttons (30PB) and a Sonoff Dual R3 / Shelly this does add up. There is close to $100 per bedroom in parts, but if you are using non Clipsal products you could reduce the cost by half.

In the end this is just another control plane. I hope you found this helpful, this pattern can be applied from fan’s to roller shutters or any other solution that uses a remote.

Thanks
Shane Baldacchino

Leave a Comment