Skip to content

Instantly share code, notes, and snippets.

@zeroping
Created July 11, 2022 19:22
Show Gist options
  • Save zeroping/0a2543cb33147cf20d376fbb6f682ef9 to your computer and use it in GitHub Desktop.
Save zeroping/0a2543cb33147cf20d376fbb6f682ef9 to your computer and use it in GitHub Desktop.
ESPHome template for the Milfra MFA05 Motion-sensing wall switch.
#---
#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"
@TommySharpNZ
Copy link

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

@TommySharpNZ
Copy link

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.

@zeroping
Copy link
Author

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.

@AJolly
Copy link

AJolly commented Nov 9, 2024

The ones I bought from aliexpress were labeled as wanted MFA05 but I got wanted MFA05F

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment