Skip to content

Instantly share code, notes, and snippets.

@billchurch
Last active June 2, 2024 12:08
Show Gist options
  • Save billchurch/787fd7b0032889c7bad0871981c5c5bd to your computer and use it in GitHub Desktop.
Save billchurch/787fd7b0032889c7bad0871981c5c5bd to your computer and use it in GitHub Desktop.
An example of an ESPHome based light (martin jerry MJ-S01) with an on-device timer
substitutions:
device_name: garage-light
friendly_name: Garage Light
default_delay: '900' # default delay of 15 minutes (900 seconds)
pre_off_warning: '30' # pre-off warning period in seconds
location: Garage
ha_timer_number: input_number.garage_light_timer
switch_name: Garage Interior Light Switch
motion_sensor_name: binary_sensor.motion_garage_motion_2
esphome:
name: ${device_name}
comment: ${friendly_name}
platform: ESP8266
board: esp01_1m
esp8266_restore_from_flash: true
globals:
- id: delay_length_global
type: int
restore_value: yes
initial_value: ${default_delay}
- id: flash_count
type: int
restore_value: no
initial_value: '0'
wifi:
ssid: !secret wifissid
password: !secret wifipass
fast_connect: off
domain: !secret domain
power_save_mode: none
ap:
ssid: "${device_name} Fallback Hotspot"
password: !secret wifipass
logger:
baud_rate: 0
api:
encryption:
key: !secret esphome_key
reboot_timeout: 0s
services:
- service: start_${device_name}_timer
then:
- script.execute: start_timer
- service: update_delay_length
variables:
delay_length: int
then:
- lambda: |-
id(delay_length_global) = delay_length;
ESP_LOGD("main", "Delay length updated to %d seconds", delay_length);
id(start_timer).execute();
ota:
# Device Specific Config
output:
- platform: gpio
pin: GPIO12
id: relay1
- platform: gpio
pin: GPIO4
id: statuslight
light:
- platform: binary
name: ${friendly_name}
output: relay1
id: lightid
on_turn_on:
then:
- script.execute: start_timer
- script.execute: flash_status_light
on_turn_off:
then:
- output.turn_off: statuslight
binary_sensor:
- platform: gpio
pin:
number: GPIO13
mode: INPUT_PULLUP
inverted: True
name: "${switch_name}"
on_multi_click:
# single click
- timing:
- ON for at most 1s
- OFF for at least 0.5s
then:
- light.toggle: lightid
- script.execute: flash_status_light
- if:
condition:
light.is_on: lightid
then:
- script.execute: start_timer
# double click
- timing:
- ON for at most 1s
- OFF for at most 1s
- ON for at most 1s
- OFF for at least 0.2s
then:
- switch.toggle: timer_disable
# long click
- timing:
- ON for at least 1.5s
then:
- switch.turn_off: timer_disable
internal: true
id: switchid
- platform: status
name: "${friendly_name} Status"
- platform: homeassistant
name: "${motion_sensor_name}"
entity_id: ${motion_sensor_name}
id: motion_sensor
status_led:
pin: GPIO5
text_sensor:
- platform: version
name: ${friendly_name} ESPHome Version
sensor:
- platform: homeassistant
entity_id: ${ha_timer_number}
id: delay_length_sensor
internal: true
on_value:
then:
- lambda: |-
id(delay_length_global) = int(id(delay_length_sensor).state);
ESP_LOGD("main", "Delay length updated to %d seconds from Home Assistant", int(id(delay_length_sensor).state));
interval:
- interval: 2min
then:
- if:
condition:
wifi.connected:
then:
- homeassistant.service:
service: homeassistant.update_entity
data:
entity_id: ${ha_timer_number}
script:
- id: flash_status_light
then:
- output.turn_off: statuslight
- delay: 500ms
- output.turn_on: statuslight
- id: start_timer
mode: restart # Change mode to restart
then:
- if:
condition:
- switch.is_off: timer_disable
then:
- delay: !lambda "return (id(delay_length_global) - ${pre_off_warning}) * 1000;"
- logger.log:
format: "Script has hit pre_off_warning: ${pre_off_warning} seconds"
- repeat:
count: ${pre_off_warning}
then:
- output.turn_off: statuslight
- delay: 1s
- output.turn_on: statuslight
- delay: 1s
- if:
condition:
binary_sensor.is_on: motion_sensor
then:
- script.execute: start_timer
- light.turn_off: lightid
- output.turn_off: statuslight
switch:
- platform: template
name: "Timer Disable"
id: timer_disable
optimistic: true
on_turn_on:
then:
# this is kind of lame but i wanted a different
# flash and this was the easiest way for me
- script.execute: flash_status_light
- delay: 2.1s
- script.execute: flash_status_light
on_turn_off:
then:
- if:
condition:
light.is_on: lightid
then:
- script.execute: start_timer
time:
# when the clock strikes 12, re-enable the timer
- platform: homeassistant
id: homeassistant_time
on_time:
- seconds: 0
minutes: 0
hours: 0
then:
- switch.turn_off: timer_disable
@billchurch
Copy link
Author

billchurch commented May 18, 2024

Garage Light Switch with ESPHome and Home Assistant Integration

This project demonstrates the integration of an ESP8266-based light switch this Martin Jerry MJ-S01 with Home Assistant. The setup includes functionality for handling on/off commands, a timer with a countdown warning, and integration with various sensors and Home Assistant services.

ESPHome Configuration

Substitutions

substitutions:
  device_name: garage-light
  friendly_name: Garage Light
  default_delay: '900' # default delay of 15 minutes (900 seconds)
  pre_off_warning: '30' # pre-off warning period in seconds
  location: Garage
  ha_timer_number: input_number.garage_light_timer
  switch_name: Garage Interior Light Switch

Home Assistant Automation

Define Input Numbers

This allows for control of the timer delay from Home Assistant. The garage_light_timer_previous is meant to hold the value of garage_light_timer temporarily in the event you want to set a different timer via automation, and then revert back. Neither are required if you want to leave it up to the device.

input_number:
  garage_light_timer:
    name: "Garage Light Timer"
    min: 0
    max: 3600
    step: 1
    unit_of_measurement: "seconds"
  garage_light_timer_previous:
    name: "Garage Light Timer Previous"
    min: 0
    max: 3600
    step: 1
    unit_of_measurement: "seconds"

Automation to Update Previous Timer

This stores the value in case you want to change it temporarily, if you want to change it back just do the reverse of this.

automation:
  - alias: "Update Garage Light Timer Previous"
    description: "Sets the previous timer value to the current timer value when it changes."
    trigger:
      - platform: state
        entity_id: input_number.garage_light_timer
    action:
      - service: input_number.set_value
        data_template:
          entity_id: input_number.garage_light_timer_previous
          value: "{{ states('input_number.garage_light_timer') }}"

Automation to Trigger the Switch

You can create an automation that triggers the switch based on various sensors such as door or motion detectors.

automation:
  - alias: "Trigger Garage Light from Sensors"
    description: "Turns on the garage light when door or motion sensors are triggered."
    trigger:
      - platform: state
        entity_id: binary_sensor.garage_door_sensor
        to: 'on'
      - platform: state
        entity_id: binary_sensor.garage_motion_sensor
        to: 'on'
    action:
      - service: light.turn_on
        target:
          entity_id: light.garage_light

Features

  1. Local Timer Reset at Midnight:

    • The timer is reset every night at midnight by the time component in ESPHome.
  2. Disable Timer:

    • The timer can be disabled via a double-press action on the switch or by turning on the "Timer Disable" switch through Home Assistant.
  3. Flashing Status Light:

    • The status light flashes briefly when the light is turned on, signaling that a command was received.
    • The status light flashes twice to indicate the timer function is disabled (double-click or from home assistant)
    • A 30-second countdown warning is indicated by the status light flashing every second before the light turns off.
  4. Persistence Across Reboots:

    • The switch remembers the delay value set by Home Assistant on reboot using the restore_value option in the globals component.

Usage

  • Single Click: Toggles the garage light and starts the timer.
  • Double Click: Toggles the timer disable state.
  • Long Click: Returns timer function to on.

@billchurch
Copy link
Author

Updated to add a motion sensor in Home Assistant referenced by the substitution motion_sensor_name.

This will check to see if a motion sensor is showing occupied or (on) right before timer turns off the switch, if it shows occupied it starts the script again for the defined time.

If you're not using a motion sensor or want this functionality, then you'll need to remove any references to that in the binary_sensor section:

  - platform: homeassistant
    name: "${motion_sensor_name}"
    entity_id: ${motion_sensor_name}
    id: motion_sensor

and the conditional in the script flash_status_light:

            - if:
                condition:
                  binary_sensor.is_on: motion_sensor
                then:
                  - script.execute: start_timer

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