Controlling a Hampton Bay Ceiling Fan with Home Assistant
March 21, 2024
Original Version: 2024-March-21
Background
We live in a split-entry home. Our basement family room has a pellet stove. In the winter, the pellet stove heats the room up nicely. I would like to be able to move some of the warm air into the basement foyer where it would naturally rise to the first floor.
The first part of the solution was to install a fan in the doorway. This fan is controlled by a smart switch and a thermostatic control on the home assistant. Details can be found in the blog post “Door Fan to Move Warm Air From Basement to First Floor.”
The second part of moving the air around is to turn on the ceiling fan in the family room. We have been doing it manually (via the remote on the wall). It really isn’t much trouble, but we turn it off when we go to bed and I’d like it to continue to run until the room is cooled down, without continuing to run all night. So, how to get the home assistant to be able to turn the fan on and off.
A Universal Remote?
I purchased a universal remote (Broadlink RM4 Pro). It was advertised to be able to replace both RF and IR remotes. In trying to clone my fan’s remote control, I found that the Broadlink did not support the frequency that my remote uses (303.85 MHz).
Craft My Own Remote?
An Internet search uncovered a couple of projects that used an ESP8266 to communicate with a CC1101 transmitter. These are the two projects that offered the most help:
My Journey
The biggest problem I had with the circuit was the pinouts for the CC1101. There are many variants of this little board. Some of them have eight pins and others have ten. They all seem to have slightly different pinouts. My board came with no documentation and I tried pinouts from boards that looked similar from the Internet. It took me several tries, but eventually I was able to get the circuit to recognize the signal from my remote control!
This is what my board looks like:
… and the pinout looks like this (note, this is the view from the back … pin #1 is in the lower-left in the photo and the lower-right in this pinout diagram):
This is what the ESP8266 looks like:
… and the pinouts for it are just like those documented in the links above:
CC1101 | ESP8266 D1 Mini |
---|---|
GND | G (GND) |
VCC | 3V3 (3.3V) |
GDO0 | D1 (GPIO5) |
CSN | D8 (GPIO15) |
SCK | D5 (GPIO14) |
MOSI | D7 (GPIO13) |
MISO | D6 (GPIO12) |
GD02 | D2 (GPIO4) |
I found the frequency of my remote by doing an internet search for the FCC page and the FCC id from the back of the remote (CHQ8BT7096T) … 303.85. After setting up WiFi, MQTT settings, and the frequency of my remote, the code from the “Hampton Bay Fan MQTT” project compiled and uploaded to the ESP8266 perfectly.
I used many of the ideas from the “Ceiling Fan Remote Hacking” project to create a control matrix on my home assistant dashboard to control the fan and light. Unfortunately, the fan just wouldn’t respond to any command. I wondered if the board was even transmitting.
Before trying to obtain an SDR to determine if there was actually RF being generated by my circuit, I added some debugging code to display the message being sent by my remote control.
Remote Control Code Details
Ben Owen documented the data his code would send as follows:
- RF Protocol 6
- Message length 21 bits
- Format of the message
- 000xxxx000000yzz00000
- Where:
- xxxx is a value based on the DIP switch settings assigned to the remote/fan combination
(check Mr. Owen’s code for details about the conversion of DIP setting to message bits) - y is light on/off
- zz indicates fan speed
- xxxx is a value based on the DIP switch settings assigned to the remote/fan combination
The messages I was decoding had more information in some of the places that Mr. Owen indicated only 0’s.
- The Format of my message
- 000xxxxaaaaaaazzbbbbb
- Where:
- xxxx was the same as Mr. Owen’s documentation
- aaaaaaa was some indication of light intensity.
I have not, yet, been successful at decoding this value, but 0000001 and 0000000 work for 100% on and off, so I can turn the light on and off, even if I don’t, yet, know how to dim it. - zz was the same as Mr. Owen’s documentation
- bbbbb I determined to be a checksum
It is calculated by summing each 4-bit chunk from the left-most 16 bits and recording the remainder of a division by 16 in the message bits.
Here is the function I added to calculate this checksum:
int calculateChecksum(int number) {
int sum = 0;
while (number > 0) {
sum += number & 0xF;
number >>= 4;
}
return sum % 16;
}
In the transmitState function, I had to change the line of code that constructs rfCode to call and add the checksum, thus:
int rfCode = dipToRfIds[fanId] << 9 | fans[fanId].lightState << 2 | fanRf;
rfCode = rfCode << 5 | calculateChecksum(rfCode);
Connecting to Home Assistant
I created a card on a home assistant dashboard using documentation from the “Ceiling Fan Remote Hacking” link, above. Here is the code for it:
square: true
columns: 5
title: Family Room Fan
type: grid
cards:
- show_name: false
show_icon: true
type: button
tap_action:
action: call-service
service: fan.toggle
data: {}
target:
entity_id: fan.family_room_fan
entity: fan.family_room_fan
icon: mdi:ceiling-fan
show_state: true
hold_action:
action: none
- show_name: false
show_icon: true
type: button
tap_action:
action: call-service
service: light.toggle
data: {}
target:
entity_id: light.family_room_fan_light
entity: light.family_room_fan_light
icon: mdi:ceiling-fan-light
hold_action:
action: none
show_state: true
- show_name: true
show_icon: true
type: button
tap_action:
action: call-service
service: fan.set_preset_mode
data:
preset_mode: low
target:
entity_id: fan.family_room_fan
entity: ''
icon: mdi:fan-speed-1
hold_action:
action: none
- show_name: true
show_icon: true
type: button
tap_action:
action: call-service
service: fan.set_preset_mode
data:
preset_mode: medium
target:
entity_id: fan.family_room_fan
entity: ''
icon: mdi:fan-speed-2
hold_action:
action: none
- show_name: true
show_icon: true
type: button
tap_action:
action: call-service
service: fan.set_preset_mode
data:
preset_mode: high
target:
entity_id: fan.family_room_fan
entity: ''
icon: mdi:fan-speed-3
hold_action:
action: none
Now I could control the fan using my smartphone or web interface. Time to add the automation that prompted this whole thing.
The thermostat card for the door fan worked they way I wanted, so I created two automations to send the proper MQTT messages when the door fan turned on and off.
Turn on the ceiling fan when the door fan turns on:
alias: Family Room Ceiling Fan - on to cool room
description: ""
trigger:
- platform: device
type: turned_on
device_id: 8d672c3ce4df60dbc1a41b4226574bd4
entity_id: 7dc2667ebdf9eea1dacdb57d9eca16d4
domain: switch
condition: []
action:
- service: fan.turn_on
target:
entity_id: fan.family_room_fan
data: {}
mode: single
Turn off the ceiling fan when the door fan turns off:
alias: Family Room Ceiling Fan - off when cooled
description: ""
trigger:
- platform: device
type: turned_off
device_id: 8d672c3ce4df60dbc1a41b4226574bd4
entity_id: 7dc2667ebdf9eea1dacdb57d9eca16d4
domain: switch
condition: []
action:
- service: fan.turn_off
target:
entity_id:
- fan.family_room_fan
data: {}
mode: single
To Do
I will wait until after heating season to clean up the implementation.
- Solder the two boards together
Currently I have a breadboard with the ESP8266 on it and some breadboard jumper wires. - 3D print a box to house the electronics
There are a couple of projects on thingiverse.com that I will either make as-is or modify if necessary.