I have five Minka Aire fans with LED lights and AireControl remotes.
i was inspired by annoyance and Controlling a Minka Aire fan using rfcat, MQTT, and HA and GitHub - idatum/labrfcat: Using rfcat for home automation to control a MInka Aire fan. · GitHub (thank you @joelp !)
Except, $140 for just the transceiver was a bit steep for me.
I used some of the above information and
- ESP32-S3 WROOM-1-N16R8 ($16 for two pack), B0FPLSQZWF
1.1. i bought two because I am also messing with a lot of blueteeth devices
- CC1101 Wireless Module with SMA Antenna Wireless Transceiver Module ($9), B0D2TMTV5Z
- a pack of multicolored Dupont Wire M-M (only need 8 but could not get less than 120 for $7). B01EV70C78
- and a bit of AI help be decode when i got stuck on the radio signals.
The sequence to get it going:
- If you do not have it yet, install ESPHome Device Builder as an App in Home Assistant Settings
1.1. This is the tool that will allow you to flash the images to the ESP32-S3. Must use Chrome or Edge to do flashing, Firefox will not work. - If you do not have it yet, install ESPHome as an Integration in Home Assistant Settings
2.1. This is the tool that you will use to manage the devices in general. Firefox is fine here. - flash the Empty ESPHome device from Ready-Made Projects - ESPHome - Smart Home Made Simple using the ESPHome Device Builder app in HA through Chrome or Edge.
3.1 Seetings → Apps → ESPHome Device Builder → Open Web UI → Create Device → Create new project
3.2 under select your board choose your appropriate board. the safest is “Generic ESP32-S3 Board”. I chose “Espressif ESP32-S3-DevKitC-1-N16R8V (16 MB Flash Quad, 8 MB PSRAM Octal)” because the ones I got (see above)
3.3 do not get fancy with the name. Do not use anything else besides letters, not even space. Trust me. finish setup.
3.4 leave the YAML config for now alone.
3.5 click Install. There is a three option decision how to install the device. plug it into the computer’s USB you are on using, through the ESP32 COM port (regular USB cable), or plug it into the HomeAssistant server, basically the same, or over the network, but that is not going to work because your board is blank so no WiFi yet. I just used the local computer i was working on. Also pretty colorful blinky lights - blank ESP32-S3 cycles through RGB
3.6 firmware gets compiled. This takes some time. be patient. Click Show details if you need constant stimulation.
3.7 Compiling and flashing too about 5 minutes on my machine
3.8. click Open USB flasher which will take you to https://web.esphome.io/
3.9 click Connect & install. it will open a new window showing a COM port. DO NOT CLICK AWAY
3.10 click Connect on the pop up window. DO NOT CLICK AWAY
3.11 It will first erare then install. you will see the progress in the terminal window. DO NOT CLICK AWAY
3.12 As a feature the browser tab/window that opened for flashing will just disappear, and will be presented with the logs window. you can safely click Stop and close it.
3.13 Close all the various windows/tabs until you get to see in ESPHome Device Builder your device. It will be almost certainly marked Offline. - We need to make sure you have the Wifi details that this device will connect and communicate through.
4.1. In ESPHome Device Builder, on the sishkabob (three vertical dots menu), make sure you have added secrets specifically wifi_password and wifi_ssid these will be used in the script and for the ESP32 to connect to your LAN. - We now need to wire the CC1101 transceiver.
5.1. unplug your ESP32-S3
5.2. The wiring for the two make and models I provided above this is the table. Do not mess this up. Make sure you are double checking your wiring diagram. The wrong wiring can fry your CC1101, the ESP32-S3 and push grandma off the ledge, not thinking of the children, and failing as the tip of the sear.
| CC1101 Pin | Signal | ESP32-S3 Right Header Position | Label on S3 |
|---|---|---|---|
| 1 | GND | 1 | GND |
| 2 | VCC | 21 | 3V3 |
| 3 | GDO0 | 19 | 4 |
| 4 | CSN | 7 | 10 |
| 5 | SCK | 5 | 12 |
| 6 | MOSI | 6 | 11 |
| 7 | MISO | 4 | 13 |
| 8 | GDO2 | 18 | 5 |
- We need to figure out the pin setting for the remote control
7.1. My version of controlers are marked DL4111G-01 (silver tag on the back of the remote). The dip switches which set the “ID” is under the battery cover and are two 4-dip switches. up is 1, down is 0. On my dip switches in the upper left corner is marked as “ON” so i know up is 1. Yours might be slightly different.
7.2. identify the ID by reading the dip switches from left to right. For example: down, down, down, up and down, down, down, down is 00010000.
7.3. Put the remote back together and put it where you usually keep it. - next we need to edit the YAML script. I suggest you do this in a text editor or directly in the ESPHome Device Builder.
8.1. Copy/paste the below YAML to your editor.
8.2. sectionsubstitutions:editing, specifically:name: <the name you gave to your ESP32-S3 when flashed it first, but all lowercase and spaces replaced with _>friendly_name: <Human version of the same as *name*>- come up with a name for the fan location, and append
_addrto the end. do not get fancy. Then in double quotes follow with the ID. See the examples in the YAML code.
8.3. for each remote you will have a separateplatform: templatesection underfan:. The only two thing you need to change is the line markedaddr:and replace the example name with your naming for exampleaddr: "${office_addr}"
8.4. click Save if you are in the ESPHome Device Builder. If you messed up, it will let you know until corrected.
8.5. Plug the ESP32-S3 back into the same computer and same port as you did before for the initial flash. You are going to flash it again.
- Click Install in the ESPHome Device Builder editor.
- In HA, after a few minutes the ESP32-S3 should show up, under the ESPHome Integration. If you click the device, for each
platform: templatethere will be a section under Controls. - you can now install the kit wherever you want. power it through the COM port from a standard USB charger, no need to connect it to a computer.
- go over to your neighbor’s house. figure out the fan remote id, drive them nuts.
Some special important notes:
The fan state in Home Assistant is optimistic. It shows what you last asked for, and it cannot see if someone uses the physical remote or wall switch. Basically, it yells at the fan but the fan will not respond and tell back what its current state is.
The light buttons are toggles with no state. My remotes can dim the LED lights, this solutions does not.
On a fan turn-on, ESPHome may send two commands (turn-on plus speed). The result is the same, so this is harmless.
you have to set your own API key.
Finally, I have negative time to deal with fixing things. It is as is, good luck; no, I cannot debug it for you.
here are some pics:
Dip switch showing 00010000 ID.
the raw setup
the remote model that i got this working with.
how it looks in HA under ESPHome:
the YAML code:
substitutions:
name: mhztransceiver
friendly_name: MHzTransceiver
office_addr: "00010000"
great_room_addr: "11100000"
master_bedroom_addr: "01000000"
mid_guest_bedroom_addr: "00000000"
corner_guest_bedroom_addr: "10000000"
esphome:
name: ${name}
friendly_name: ${friendly_name}
esp32:
board: esp32-s3-devkitc-1
flash_size: 16MB
framework:
type: esp-idf
logger:
level: INFO
api:
encryption:
key: "LongEncryptionKeyGoesHereYouNeedToChange!!!="
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "${name} Fallback"
captive_portal:
spi:
clk_pin: GPIO12
mosi_pin: GPIO11
miso_pin: GPIO13
cc1101:
id: radio
cs_pin: GPIO10
frequency: 303.87MHz
modulation_type: ASK/OOK
output_power: 10
remote_transmitter:
id: rf_tx
pin: GPIO4
carrier_duty_percent: 100%
non_blocking: true
on_transmit:
then:
- cc1101.begin_tx
on_complete:
then:
- cc1101.begin_rx
script:
# Sends one 12-bit frame (address + command), repeated 8 times.
- id: send_fan
mode: queued
parameters:
bits: string
then:
- remote_transmitter.transmit_raw:
code: !lambda |-
std::vector<int32_t> c;
c.push_back(350);
for (char b : bits) {
if (b == '1') { c.push_back(-350); c.push_back(700); }
else { c.push_back(-700); c.push_back(350); }
}
return c;
repeat:
times: 8
wait_time: 12300us
# Speed 1 = Low, 2 = Med, 3 = High.
- id: send_speed
mode: queued
parameters:
addr: string
level: int
then:
- script.execute:
id: send_fan
bits: !lambda |-
std::string cmd = (level <= 1) ? "0010" : (level == 2) ? "0100" : "1000";
return addr + cmd;
fan:
- platform: template
name: "Office Fan"
id: fan_office
speed_count: 3
on_turn_on:
- script.execute:
id: send_speed
addr: "${office_addr}"
level: !lambda |-
int s = id(fan_office).speed;
return s < 1 ? 1 : s;
on_speed_set:
- script.execute:
id: send_speed
addr: "${office_addr}"
level: !lambda 'return x;'
on_turn_off:
- script.execute:
id: send_fan
bits: "${office_addr}1010"
- platform: template
name: "Great Room Fan"
id: fan_great_room
speed_count: 3
on_turn_on:
- script.execute:
id: send_speed
addr: "${great_room_addr}"
level: !lambda |-
int s = id(fan_great_room).speed;
return s < 1 ? 1 : s;
on_speed_set:
- script.execute:
id: send_speed
addr: "${great_room_addr}"
level: !lambda 'return x;'
on_turn_off:
- script.execute:
id: send_fan
bits: "${great_room_addr}1010"
- platform: template
name: "Master Bedroom Fan"
id: fan_master_bedroom
speed_count: 3
on_turn_on:
- script.execute:
id: send_speed
addr: "${master_bedroom_addr}"
level: !lambda |-
int s = id(fan_master_bedroom).speed;
return s < 1 ? 1 : s;
on_speed_set:
- script.execute:
id: send_speed
addr: "${master_bedroom_addr}"
level: !lambda 'return x;'
on_turn_off:
- script.execute:
id: send_fan
bits: "${master_bedroom_addr}1010"
- platform: template
name: "Mid Guest Bedroom Fan"
id: fan_mid_guest_bedroom
speed_count: 3
on_turn_on:
- script.execute:
id: send_speed
addr: "${mid_guest_bedroom_addr}"
level: !lambda |-
int s = id(fan_mid_guest_bedroom).speed;
return s < 1 ? 1 : s;
on_speed_set:
- script.execute:
id: send_speed
addr: "${mid_guest_bedroom_addr}"
level: !lambda 'return x;'
on_turn_off:
- script.execute:
id: send_fan
bits: "${mid_guest_bedroom_addr}1010"
- platform: template
name: "Corner Guest Bedroom Fan"
id: fan_corner_guest_bedroom
speed_count: 3
on_turn_on:
- script.execute:
id: send_speed
addr: "${corner_guest_bedroom_addr}"
level: !lambda |-
int s = id(fan_corner_guest_bedroom).speed;
return s < 1 ? 1 : s;
on_speed_set:
- script.execute:
id: send_speed
addr: "${corner_guest_bedroom_addr}"
level: !lambda 'return x;'
on_turn_off:
- script.execute:
id: send_fan
bits: "${corner_guest_bedroom_addr}1010"
button:
- platform: template
name: "Office Light Bottom"
on_press:
- script.execute:
id: send_fan
bits: "${office_addr}1001"
- platform: template
name: "Office Light Top"
on_press:
- script.execute:
id: send_fan
bits: "${office_addr}0101"
- platform: template
name: "Great Room Light Bottom"
on_press:
-



