-
-
Save zeroping/0a2543cb33147cf20d376fbb6f682ef9 to your computer and use it in GitHub Desktop.
#--- | |
#substitutions: | |
# https://esphome.io/guides/configuration-types.html#substitutions | |
# device_name: garage-overhead # hostname & entity_id | |
# motion_on_time: '15s' # time to leave the relay on after motion is sensed | |
# motion_off_holdoff: '2s' # how long to ignore motion if turned off a short press of the button | |
# button_hold_for_hold_on_time: '2s' # how long to press and hold the button to switch to hold-on mode | |
# This is for the Milfra MFA05 single-button PIR-motion relay switch | |
# This config implements local control of the relay using the button or detected motion | |
# We have two main modes: motion-sensing mode, and hold-on mode | |
# In motion-sensing mode: | |
# If there is motion, the relay is turned on | |
# When the motion stops, the relay waits motion_on_time before turning the lights off | |
# A button press also turns off the relay | |
# Once the relay is off, we ignore motion events for a hold-off period, so we don't re-trigger to quickly | |
# If the button is held down, you switch to hold-on mode | |
# In hold-on mode: | |
# The light stays on indefinately | |
# Pressing the button turns the relay off (and takes us back to motion-sensing mode) | |
# The green LED is used to indicate the relay output state | |
# The blue LED is used to indicate that we're in the most-motion hold-off mode | |
# The blue LED is also used for indicating device status (wifi) | |
## example config (for using this template | |
#substitutions: | |
## https://esphome.io/guides/configuration-types.html#substitutions | |
#device_name: pantry # hostname & entity_id | |
#motion_on_time: '120' # time (in seconds) to leave the relay on after motion is sensed | |
#motion_off_holdoff: '5' # how long (in seconds) to ignore motion if turned off via button or motion ending | |
#button_hold_for_hold_on_time: '1s' # how long to press and hold the button to switch to hold-on mode | |
#<<: !include milfra-mfa05.base.yaml | |
## end example config | |
esphome: | |
name: ${device_name} | |
platform: ESP8266 | |
board: esp8285 | |
wifi: | |
# https://esphome.io/components/wifi | |
ssid: !secret wifi_ssid | |
password: !secret wifi_password | |
logger: | |
# https://esphome.io/components/logger | |
level: INFO | |
#level: VERBOSE | |
ota: | |
web_server: | |
captive_portal: | |
api: | |
#password: !secret esphome_api_password | |
# https://esphome.io/components/api | |
#reboot_timeout: 0s #disable auto-reboot if homeassistant is not connecting | |
light: | |
- platform: status_led | |
id: led2 | |
pin: | |
# Blue LED | |
number: GPIO13 | |
inverted: true | |
# The green LED appears to mask the blue | |
# It also appears to be overridden by the relay | |
output: | |
- platform: gpio | |
# Green LED | |
id: led1 | |
pin: GPIO16 | |
switch: | |
- platform: gpio | |
name: "${device_name}_Relay" | |
id: relay | |
restore_mode: ALWAYS_OFF | |
pin: GPIO12 | |
on_turn_on: | |
- output.turn_off: led1 | |
on_turn_off: | |
- output.turn_off: led1 | |
binary_sensor: | |
- platform: gpio | |
id: button | |
pin: | |
number: GPIO0 | |
mode: INPUT_PULLUP | |
inverted: true | |
on_click: | |
- script.execute: press_action | |
on_multi_click: | |
- timing: | |
- ON for at least ${button_hold_for_hold_on_time} | |
then: | |
- script.execute: hold_on_action | |
- platform: gpio | |
name: ${device_name}_Motion_Sense | |
device_class: "motion" | |
pin: | |
number: GPIO5 | |
inverted: true | |
mode: INPUT_PULLUP | |
on_press: | |
then: | |
- logger.log: | |
format: "state %d, motion high" | |
args: [ 'id(motion_state)' ] | |
- if: | |
condition: | |
lambda: |- | |
return ( id(motion_state) == 0 || id(motion_state) == 1); | |
then: | |
- script.stop: motion_stopped | |
- script.stop: motion_holdoff | |
- light.turn_off: led2 | |
- globals.set: | |
id: motion_state | |
value: "1" | |
- switch.turn_on: relay | |
on_release: | |
#- output.turn_off: led1 | |
then: | |
- logger.log: | |
format: "state %d, motion low" | |
args: [ 'id(motion_state)' ] | |
- if: | |
condition: | |
lambda: |- | |
return ( id(motion_state) == 1 || id(motion_state) == 4 ) ; | |
then: | |
- script.stop: motion_holdoff | |
- script.execute: motion_stopped | |
- light.turn_off: led2 | |
- platform: gpio | |
name: ${device_name}_Light_Sense | |
device_class: "light" | |
pin: | |
number: GPIO14 | |
mode: INPUT | |
globals: | |
- id: motion_state | |
type: int | |
restore_value: no | |
initial_value: '0' | |
# 0 = off | |
# 1 = on by motion | |
# 2 = off after motion, in hold-off | |
# 3 = hold-on state | |
# 4 = quick on via button when no motion | |
- id: motion_keep_on_seconds_remaining | |
type: int | |
restore_value: no | |
initial_value: '0' | |
- id: motion_hold_off_seconds | |
type: int | |
restore_value: no | |
initial_value: '0' | |
script: | |
# immediately stops the timer and turns everything off | |
# only runs if we're turned on by motion or a quick press | |
- id: motion_stopped | |
mode: restart | |
then: | |
- light.turn_off: led2 # the hold-on light | |
- globals.set: | |
id: motion_state | |
value: "1" | |
- globals.set: | |
id: motion_keep_on_seconds_remaining | |
value: ${motion_on_time} | |
- while: | |
condition: | |
lambda: |- | |
return(id(motion_keep_on_seconds_remaining) > 0); | |
then: | |
- logger.log: | |
format: "off after motion, seconds_remaining %d" | |
args: [ 'id(motion_keep_on_seconds_remaining)' ] | |
- lambda: |- | |
id(motion_keep_on_seconds_remaining) -= 1; | |
- delay: 1s | |
- switch.turn_off: relay | |
#now the relay is off, but we need to do a hold-off delay, in case turning off the lights re-triggers us | |
- script.execute: motion_holdoff | |
- id: motion_holdoff | |
mode: restart | |
then: | |
- light.turn_on: led2 # the hold-on light | |
- globals.set: | |
id: motion_hold_off_seconds | |
value: ${motion_off_holdoff} | |
- globals.set: | |
id: motion_state | |
value: "2" | |
- while: | |
condition: | |
lambda: |- | |
return(id(motion_hold_off_seconds) > 0); | |
then: | |
- logger.log: | |
format: "hold off, seconds_remaining %d" | |
args: [ 'id(motion_hold_off_seconds)' ] | |
- lambda: |- | |
id(motion_hold_off_seconds) -= 1; | |
- delay: 1s | |
- light.turn_off: led2 | |
- globals.set: | |
id: motion_state | |
value: "0" | |
- id: press_action | |
then: | |
- logger.log: | |
format: "press action" | |
- if: | |
condition: | |
lambda: |- # on by motion or buttons | |
return ( id(motion_state) == 1 || id(motion_state) == 3 || id(motion_state) == 4); | |
then: | |
- logger.log: | |
format: "state %d, force off" | |
args: [ 'id(motion_state)' ] | |
- script.stop: motion_stopped | |
- light.turn_off: led2 | |
- script.execute: motion_holdoff # we start the holdoff so we ignore motion events for a moment | |
- switch.turn_off: relay | |
else: | |
- logger.log: | |
format: "state %d -> 4, short on" | |
args: [ 'id(motion_state)' ] | |
- script.stop: motion_holdoff | |
- script.stop: motion_stopped | |
- light.turn_off: led2 | |
- switch.turn_on: relay | |
- globals.set: | |
id: motion_state | |
value: "4" | |
- id: hold_on_action | |
then: | |
- logger.log: | |
format: "hold_on_action" | |
- if: | |
condition: | |
lambda: |- # on by motion or buttons | |
return ( id(motion_state) == 1 || id(motion_state) == 3 || id(motion_state) == 4); | |
then: | |
- logger.log: | |
format: "state %d, long force off" | |
args: [ 'id(motion_state)' ] | |
- script.stop: motion_stopped | |
- script.execute: motion_holdoff # we start the holdoff so we ignore motion events for a moment | |
- switch.turn_off: relay | |
else: | |
- script.stop: motion_stopped | |
- script.stop: motion_holdoff | |
- light.turn_off: led2 | |
- switch.turn_on: relay | |
#- output.turn_off: led1 | |
- logger.log: | |
format: "state %d -> 3, hold on" | |
args: [ 'id(motion_state)' ] | |
- globals.set: | |
id: motion_state | |
value: "3" | |
Not quite, but sort of.
The CloudFree shop has a version of these switches, plus some others that look similar-ish, and critically they are intended to always be flashable: https://cloudfree.shop/product/cloudfree-light-switch/
I also have liked the Linkind dimmer and relay switch, but only if you can find the 'V1' versions that were/are ESP32-based.
https://templates.blakadder.com/linkind_WS2400101.html
https://templates.blakadder.com/linkind_WS240010008.html
Finally, I'll say that I have found the discussion over on DigiBlurDIY's discord server to be quite helpful:
https://discord.com/invite/bNtTF2v
I have ordered a couple of these from Hidin Tech on Alibaba.... The claim to be ESP based so will let you know how I get on when they arrive :-)
https://www.amazon.com/Smart-Motion-Sensor-Switch-Occupancy/dp/B0BBVFG7GD
Would you consider creating an alternate version that when we press+hold the button the switch turns to full manual... Simply turning off/on with the button untill someone uses press+hold again to change the mode?
In both cases always reporting the motion sensor to Home Assistant would be good to have regardless of the mode.
I can try, but it may take me a bit to get around to it. I actually only have one of these installed, and it's in a pantry where it's not used for anything sophisticated.
Give it a go yourself if you want. I know the implementation is a mess to read, but the core of it is that we're tracking what state we're in motion_state, and using that to decide what to do when something happens. Right now, there's a state 3, which I called "hold-on state", that basically means "the user held the button to lock the light on". The logic for a single press ("press_action") treats that just like any other on state, so another press turns it back off and into regular mode. I think you just want to add a another state for "manual mode, but off" and handle transitions into and out of it correctly.
The ones I bought from aliexpress were labeled as wanted MFA05 but I got wanted MFA05F
Very cool!!!
Have you found any other light switches in the same "visual style" that are still ESPHome compatible?