Integrating Nice Flor-S remotes with HomeAssistant

Yet another post about smart home. This time I’ll be integrating Nice Flor-S gate remote control with HomeAssistant, using EspHome, RTL-SDR, and a relay board from Aliexpress.

An ugly solution, but hey, it works!

Getting stuff apart

So, I had those automatic gates for many years, even from times before HomeAssistant came to be. Once I had it setup, I wondered if how to make these two work along with each other. So I started by taking apart one of the spare remotes to see what’s inside and what could be done.

Inside was a 433 radio circuit along with a Motorola HC08. Well, that was a blast from the past, I used to write assembly for those back in my student days.

Plan A. Let’s fire up RTL-SDR

So I popped in my RTL-SDR dogle, fired up gqrx and saw the transmission happening on the 433Mhz beautifully on the waterflow plot. Now, perhaps I can decode it?

The signal looks like drifting a little bit, but it’s there

The plan was: decode the protocol and then emulate the dongle using a cheap 433Mhz RF-GPIO boards from our well-known electronics junkyard called Aliexress.

433 Mhz USB-GPIO. Well-known in the hobbyist society for their ability to emulate simple rf remotes.

For this purpose I fired up rtl-433 and… Saw nothing. The program managed to detect that there was something happening, but no protocol decoder for nice flor-s existed.

I googled the internet and found a Russian site that documented pretty well the protocol and the possible attack on those gate automation. Yikes!

The protocol was not fully documented, but the article still gave me a quick start. I created a proof-of-concept plugin for rtl-433 that could decode the buttons being pressed. With some luck I could also manage to dump timecodes (e.g. how long the button was pressed at a time). But what next?

RTL433 Trying it’s best to decode Nice Flor-S packets

The problem was that the protocol was only partially documented. With the documentation I found the only thing I could decode – button id and timecode. The rest would be a complete mystery. I could give a try and dump the HC08 MCU firmware? Something told me that they had readback protection in place, no worth trying.

Perhaps, running a small automation rig to dump all possible timecodes/serial number combinations for my remote and later giving a spin to analyze it? I was sorry to abandon a fun reverse engineering quest, but sometimes you just have to. That fun required too much of a critical resource I had so little: time.

Plan B. Let’s just rig a few relays to ‘press’ the buttons

I carefully examined the remote control with a multimeter. It is powered by a small 12V battery. The +12V power rail of the battery is connected directly to four buttons. When no button is pressed the whole circuit is completely unpowered. So, we could use any transistor/mosfet to do the job. But that would mean laying out a board and using even more of the precious resource called ‘time’. So instead I opened up Aliexpress and ordered a few 4-channel relay modules with good old esp8266. Some LC-tech company makes those in 5V and 12V versions.

At this point I had to put aside the project and wait for a month or so for the relays to arrive.

Getting started with 4-channel LC-Tech relay modules

Here’s one of those modules
And here are the linear regulators, normally covered by esp-01s

The engineering solutions here were, well, mixed. On one hand the board had optocouplers isolating the relays and even had the cutouts on the board to improve isolation (Just in case the relays would be used to switch mains voltage). But on the other hand, the power circuit was the dumbest possible. Perhaps you’ve read my recent post about me being furious about linear dropouts being hot as hell at the esp-14 devboard. Well, this board had pretty much the same issue: The input is 12 Volts. Next they make 5V using a huge linear regulator, and next they make 3.3 volts using another one. Energy ‘efficiency’ at its best. The power supply part will get pretty hot if you don’t use power saving mode on esp8266 wireless. Besides, those 5mm screw terminals suck when it comes to mains voltage – the cable is to likely to get loose (unless you tin it).

The weirdest part of the whole thing was the Nuvoton n76e003at20 microcontroller. It’s the first time I came across one of these. The ESP8266 is connected via UART to this weird thingie. 2 buttons, three LEDs and four relays are connected to this micro-controller. Judging by the datasheet it’s a yet another 8051 MCU. Perhaps I’ll do another post sometime later about these MCUs, but for now I used the stock firmware.

As for ESP8266 I never took the time what the heck was that ‘ai cloud inside’, I just quickly flashed a ESPhome-based firmware.

With ESPhome we have two options to hook these relays. We can use a UART switch component. Or we can write our own custom one. Since I’ve noticed a thing about the protocol, I decided to take the custom component way.

According to the docs, the UART protocol looked like this:

 Open relay  1:A0 01 01 A2
 Close relay 1:A0 01 00 A1
 Open relay  2:A0 02 01 A3
 Close relay 2:A0 02 00 A2
 Open relay  3:A0 03 01 A4
 Close relay 3:A0 03 00 A3
 Open relay  4:A0 04 01 A5
 Close relay 4:A0 04 00 A4 
  • Byte 1. A0 – start of the packet
  • Byte 2. Relay ID (1-4)
  • Byte 3. Relay state 0/1
  • Byte 4. Checksum. Looks like the sum of the first three bytes.

So, the code to drive all similar relay modules with esphome would look somewhat like this:

#include "esphome.h"
using namespace esphome;
class LCRelay : public Component, public UARTDevice, public switch_::Switch {
    int relayId;
    LCRelay(UARTComponent *parent, int relay): UARTDevice(parent) {
        relayId = relay;
  void setup() override {
  void write_state(bool state) override {
    uint8_t msg[4];
    msg[0] = 0xA0;
    msg[1] = relayId;
    msg[2] = state;
    msg[3] = msg[0] + msg[1] + msg[2];
    /* There are no ack/nack packets. 
       Let's send the payload twice to be sure */
    this->write_array(msg, sizeof(msg));
    this->write_array(msg, sizeof(msg));

Now, to hook this in our yaml config. The minimal config would look like this.

  devicename: "gatekeeper"

  name: $devicename
  platform: ESP8266
  board: esp01_1m
  build_path: build/gatecontrol
    - lcrelay.h
  id: uart
  baud_rate: 115200
  tx_pin: GPIO1
  rx_pin: GPIO3

- platform: custom
  lambda: |-
    auto r1 = new LCRelay(id(uart), 1);
    auto r2 = new LCRelay(id(uart), 2);
    auto r3 = new LCRelay(id(uart), 3);
    auto r4 = new LCRelay(id(uart), 4);        
    return {r1, r2, r3, r4};
    - name: "${devicename} Relay 1"
      id: relay1
      internal: true
    - name: "${devicename} Relay 2"
      id: relay2
      internal: true
    - name: "${devicename} Relay 3"       
      id: relay3
      internal: true
    - name: "${devicename} IceShard Power"
      id: relay4
      internal: true

As you can see, I’ve made all relay switches internal. The trick here is that to drive the nice flor-s buttons we’ll need to turn relay for ~1 second and than immediately turn them off. We can’t just do delay(1000) in our C++ code, since the watchdog will kick in. The clean way (If I got the esphome idea correctly) would be handling that in yaml config. Just add the following to the switch section for each of the relays.

- platform: template
  name: "${devicename} Main Gates"
  icon: mdi:gate
  lambda: |-
      if (id(relay1).state) {
        return true;
      } else {
        return false;
    - switch.turn_on: relay1
    - delay: 1s
    - switch.turn_off: relay1
    switch.turn_off: relay1

Putting it all together

Once the firmware was up and running I printed a plastic box to house all the goodies and soldered the wires to the remote. I only use three buttons, so I hooked the remaining relay to turn on and off 12 volts on a barrel jack outlet.

The wiring is rather simple.

The good stuff

As usual, the artifacts for this project are up for anyone’s interested to download and play with, namely:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.