I only had one light left in my house that was not controlled by Home Assistant (microwave, fridge and oven lights excluded) and that was the one over our range (stove). It also has a built in two speed fan, so I decided that needed to be automated too. Also a thermometer was included to create automations to automatically turn on/off the light and fan.
Here are the old switches and wiring:
I had an SONOFF 4CH PRO that seemed like a good fit for the project left over from a garage door automation on an old house.
One of the automation rules in our house is that everything has to work as you would expect a normal device to work. So I decided to replace the front switched on the range hood with SPDT momentary rockers. I ordered some new switches from DigiKey. This would allow an on and off for the light and a high/low for the fans that would act as both speed changing and on off. For the fan you would push to turn on and push the same side to turn off. This is not perfectly intuitive, but it works pretty well.
These are the new switches that I put in:
There are some custom lambda functions in the code to cover this logic and link it all into the multispeed fan component.
esphome:
name: range-hood
platform: ESP8266
board: esp01_1m
comment: "Sonoff 4CH Pro Range Hood Contoller"
<<: !include templates/common.yaml
binary_sensor:
- platform: gpio
pin:
number: GPIO0
mode:
input: true
pullup: true
inverted: true
name: "Light On Button"
on_press:
then:
- light.turn_on: range_light
- platform: gpio
pin:
number: GPIO9
mode:
input: true
pullup: true
inverted: true
name: "Light Off Button"
on_press:
then:
- light.turn_off: range_light
- platform: gpio
pin:
number: GPIO10
mode:
input: true
pullup: true
inverted: true
name: "Fan Low Button"
on_press:
then:
- lambda: |-
if (id(range_fan).state) {
if (id(range_fan).speed == 1) {
// Fan in on and speed is low, so toggle to off
// Turn the fan off
auto call = id(range_fan).turn_off();
call.perform();
} else {
//Turn on fan and set to speed low
auto call = id(range_fan).turn_on();
call.set_speed(1);
call.perform();
}
} else {
// Fan is OFF, turn it on to low
auto call = id(range_fan).turn_on();
call.set_speed(1);
call.perform();
}
- platform: gpio
pin:
number: GPIO14
mode:
input: true
pullup: true
inverted: true
name: "Fan High Button"
on_press:
then:
- lambda: |-
if (id(range_fan).state) {
if (id(range_fan).speed == 2) {
// Fan in on and speed is 2, so toggle to off
// Turn the fan off
auto call = id(range_fan).turn_off();
call.perform();
} else {
//Turn on fan and set to speed 2
auto call = id(range_fan).turn_on();
call.set_speed(2);
call.perform();
}
} else {
// Fan is OFF, turn it on to high
auto call = id(range_fan).turn_on();
call.set_speed(2);
call.perform();
}
- platform: status
name: "Range Fan and Light"
switch:
# - platform: gpio
# name: "Sonoff 4CH Relay 1" #Light on/off
# pin: GPIO12
# id: relay1
# internal: true
- platform: gpio
name: "Range Hood Relay 2" #On/Off for fan
pin: GPIO5
id: relay2
internal: true
- platform: gpio
name: "Range Hood Relay 3" #High/low for fan
pin: GPIO4
id: relay3
internal: true
- platform: gpio
name: "Range Hood Relay 4" #Not used yet
pin: GPIO15
id: relay4
internal: true
fan:
- platform: speed
id: range_fan
speed_count: 2
output: range_fan_output
name: "Range Fan"
output:
# Register the blue LED as a dimmable output
- platform: esp8266_pwm
id: blue_led
pin: GPIO13
inverted: true
- id: range_light_output
platform: gpio
pin: GPIO12
- platform: template
id: range_fan_output
type: float
write_action:
- if:
condition:
lambda: return ((state == 0));
then:
- switch.turn_off: relay2
- switch.turn_off: relay3
- if:
condition:
lambda: return ((state > 0) && (state < 1));
then:
- switch.turn_on: relay2
- switch.turn_off: relay3
- if:
condition:
lambda: return ((state == 1));
then:
- switch.turn_on: relay2
- switch.turn_on: relay3
light:
- platform: monochromatic
name: "Range Hood Blue LED"
output: blue_led
internal: true
- platform: binary
name: "Range Light"
output: range_light_output
id: range_light
dallas:
- pin: GPIO2
update_interval: 30s
sensor:
- platform: dallas
address: 0xFA000006C56BB528
name: "Range Hood"
The four switches were wired on pigtails and soldered to the back side of the buttons on the SONOFF 4PRO.
I then attached the light to one relay to turn it on and off.
I then took the hot line and attached it to another relay normally open and then attached the output of that to a third relay with the NC attached to the fan low speed wire and NO attached to the high speed wire.
Finally I took and attached a dallas one wire temperature probe in a stainless steel case to GPIO2 along with a pull-up resistor to 3.3V and placed it inside the range hood.
I powered the SONOFF with 120VAC and fired it up.
Everything worked as expected until you (accidentally) double clicked one of the switches. With the SONOFF 4CH PRO this puts that channel into RF pairing mode, and it does not seem to timeout without pairing a switch, meaning you need to power cycle the box! This made that channel unresponsive to future commands, making the user interface non-functional (control through WiFi still worked)
There is apparently no way to disable this functionality with the stock firmware on the STM32. So the only solution was to rewrite all the firmware on the STM32 to make it just a dumb bit flipper that mirrored inputs onto the outputs since I was not taking advantage of any of the other features.
I whipped this up in STM32CubeIDE, forgive the hardcoded everything!
#include "main.h"
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* Infinite loop */
while (1)
{
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3)==GPIO_PIN_SET) //Check if button 1 pressed
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6,GPIO_PIN_SET); //Turn on key out 1
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15,GPIO_PIN_RESET); //Turn on Green LED 1
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6,GPIO_PIN_RESET); //Turn off key out 1
//HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2,GPIO_PIN_RESET); //Turn off Green LED 1
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15,GPIO_PIN_SET); //Turn off Green LED 4
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5)==GPIO_PIN_SET) //Check if button 2 pressed
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_SET); //Turn on key out 2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2,GPIO_PIN_RESET); //Turn on Green LED 2
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_RESET); //Turn off key out 2
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2,GPIO_PIN_SET); //Turn off Green LED 2
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)==GPIO_PIN_SET) //Check if button 3 pressed
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4,GPIO_PIN_SET); //Turn on key out 3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1,GPIO_PIN_RESET); //Turn on Green LED 3
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4,GPIO_PIN_RESET); //Turn off key out 3
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1,GPIO_PIN_SET); //Turn off Green LED 3
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4)==GPIO_PIN_SET) //Check if button 4 pressed
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7,GPIO_PIN_SET); //Turn on key out 4
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0,GPIO_PIN_RESET); //Turn on Green LED 4
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7,GPIO_PIN_RESET); //Turn off key out 4
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0,GPIO_PIN_SET); //Turn off Green LED 4
}
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15)==GPIO_PIN_SET) //Check if ESP relay 1 pressed
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14,GPIO_PIN_SET); //Turn on relay out 1 C14
}
else
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14,GPIO_PIN_RESET); //Turn off relay out 1
}
if(HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_1)==GPIO_PIN_SET) //Check if ESP relay 2 pressed
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8,GPIO_PIN_SET); //Turn on relay out 2 B8
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8,GPIO_PIN_RESET); //Turn off relay out 2
}
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)==GPIO_PIN_SET) //Check if ESP relay 3 pressed
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13,GPIO_PIN_SET); //Turn on relay out 3 B9
}
else
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13,GPIO_PIN_RESET); //Turn off relay out 3
}
if(HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_0)==GPIO_PIN_SET) //Check if ESP relay 4 pressed
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9,GPIO_PIN_SET); //Turn on relay out 4 C13
}
else
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9,GPIO_PIN_RESET); //Turn off relay out 4
}
}
}
/**
* @brief System Clock Configuration
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1|GPIO_PIN_2, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET);
/*Configure GPIO pins : PC13 PC14 PC15 */
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : PF0 PF1 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
/*Configure GPIO pins : PA0 PA1 PA2 */
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PA3 PA5 PA15 */
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PA4 PA6 */
GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : PB3 */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pins : PB4 PB5 PB6 PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pins : PB8 PB9 */
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
I then programmed it to the STM32 and no more issues with the double click! I also get status lights for the button presses now, not just the relays which is nice since they are not one to one mapped.
I think the whole thing came out looking very clean and functional and now all of my lights are controlled!