-
-
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?