Close

Blink service

A project log for Laundry monitoring

A collection of notes and pointers for the usual laundry monitoring project.

wjcarpenterWJCarpenter 07/02/2023 at 00:060 Comments

I haven't decided yet what kind of notifications I'm going to do when the laundry needs attention, but among the possibilities is blinking the LED on or both of the Sonoff S31 switches.

Doing the classic perpetual LED blink in ESPHome is simplicity itself. You just set up actions on the state changes to delay and toggle back and forth. This example uses explicit turning on and off, but it could also have use toggle actions.

switch:
  - platform: gpio
    id: me
    pin:
      number: 15
    on_turn_on:
    - delay: 100ms
    - switch.turn_off: me
    on_turn_off:
    - delay: 1000ms
    - switch.turn_on: me

 For this project, I want to be able to turn blinking on and off as a service call from Home Assistant. Further, I want Home Assistant to decide what the on and off periods are so it can do slow blinking, fast blinking, or some kind of asymmetric on/off cadence. So, the start_blinking service takes two parameters for those durations. The stop_blinking service has one additional job, besides just stopping the blinking. It has to restore the LED to the state it was in (on or off) before the blinking was started. Of course, that status was recorded when the start_blinking service was called. If start_blinking is called multiple times without an intervening stop_blinking call, the new cadence is set but the pre-blinking status of the LED is unaffected.

Here's the code (in two files) as it stands today. It has several other small changes since the previous project log.

substitutions:
  name: s31yellow
  friendly_name: Unknown Yellow Spirit
  alias_icon: mdi:cloud-question
  power_on_boot: 'true'
  use_address: ${name}.carpenter.org

<<: !include { file: s31.yaml.inc}

and

# This is an adaptation of the ESPHome configuration for Sonoff S31
# smart plug described here: https://www.esphome-devices.com/devices/Sonoff-S31/

# Pin     Function
# GPIO0   Push Button (HIGH = off, LOW = on)
# GPIO12  Relay and its status LED
# GPIO13  Blue LED (HIGH = off, LOW = on)
# GPIO1   RX pin (for external sensors)
# GPIO3   TX pin (for external sensors)

# This file is intended to be included from another file that defines
# some configuration choices via substitution variables:
#   name:          the name of this device, appears various places
#   alias_icon:    front-end icon for the alias sensor
#   power_on_boot: boolean, whether to turn on the relay at boot time

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  platform: ESP8266
  board: esp01_1m
  on_boot:
    then:
      - text_sensor.template.publish:
          id: i_alias
          state: ${name}
      - if:
          condition:
            lambda: "return ${power_on_boot};"
          then:
            - switch.turn_on: relay
          else:
            - switch.turn_off: relay
      - switch.turn_off: i_blue_status_led

globals:
  - id: IS_BLINKING
    type: "bool"
    initial_value: "false"
  - id: BLINK_OFF_PERIOD
    type: int
  - id: BLINK_ON_PERIOD
    type: int
  - id: PREBLINK_STATE
    type: "bool"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  use_address: ${use_address}

logger:
  level: DEBUG
  baud_rate: 0 # (UART logging interferes with cse7766)

ota:
  password: !secret ota_password

api:
  services:

    - service: start_blinking
      variables: {on_period: int, off_period: int}
      then:
        - globals.set:
            id: BLINK_ON_PERIOD
            value: !lambda "return on_period;"
        - globals.set:
            id: BLINK_OFF_PERIOD
            value: !lambda "return off_period;"
        - if:
            condition:
              lambda: "return !id(IS_BLINKING);"
            then:
              - globals.set:
                  id: PREBLINK_STATE
                  value: !lambda "return id(i_blue_status_led).state;"
              - globals.set:
                  id: IS_BLINKING
                  value: !lambda "return true;"
              - switch.toggle: i_blue_status_led

    - service: stop_blinking
      then:
        - if:
            condition:
              lambda: "return id(IS_BLINKING);"
            then:
              - globals.set:
                  id: IS_BLINKING
                  value: !lambda "return false;"
              # The blinking doesn't actually stop until one or the other of these delays expires
              # so wait it out (plus a little slop) before setting the LED on or off.
              - delay: !lambda "return 20 + max(id(BLINK_ON_PERIOD), id(BLINK_OFF_PERIOD));"
              - if:
                  condition:
                    lambda: "return id(PREBLINK_STATE);"
                  then:
                    - switch.turn_on: i_blue_status_led
                  else:
                    - switch.turn_off: i_blue_status_led

# The value never changes except on a firmware update,
# so the update interval is never, and the value is
# published at boot time.
text_sensor:
  - platform: template
    name: "${friendly_name} alias"
    id: i_alias
    icon: ${alias_icon}
    update_interval: never

uart:
  rx_pin: RX
  baud_rate: 4800

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO0
      mode: INPUT_PULLUP
      inverted: True
    name: "Button"
    on_press:
      - switch.toggle: relay
  - platform: status
    name: "Status"

sensor:
  - platform: wifi_signal
    name: "WiFi Signal"
    update_interval: 120s
  - platform: cse7766
    update_interval: 30s
    current:
      name: "Current"
      accuracy_decimals: 1
    voltage:
      name: "Voltage"
      accuracy_decimals: 1
    power:
      name: "Power"
      accuracy_decimals: 1
    energy:
      name: "Energy"
      accuracy_decimals: 1

switch:
  - platform: gpio
    name: "Relay"
    pin: GPIO12
    id: relay

# I changed this from a status_led component to a switch component
# so that I could control it independently of the boring status.
# "inverted" so that it's normally off.

  - platform: gpio
    id: i_blue_status_led
    name: "Blue LED"
    pin:
      number: GPIO13
      inverted: true
    on_turn_on:
      then:
        - if:
            condition:
              lambda: "return id(IS_BLINKING);"
            then:
              - delay: !lambda "return (id(BLINK_ON_PERIOD));"
              - switch.turn_off: i_blue_status_led
    on_turn_off:
      then:
        - if:
            condition:
              lambda: "return id(IS_BLINKING);"
            then:
              - delay: !lambda "return (id(BLINK_OFF_PERIOD));"
              - switch.turn_on: i_blue_status_led

Discussions