latency.png ESPHome Auto-Restart Script

Introduction

Running various services at home can be a great convenience, but it’s incredibly frustrating when the network goes down and you’re not there to restart everything. I’ve considered using my Home Assistant instance to reboot my modem, but I’m uncertain about its effectiveness since it heavily relies on a stable internet connection.

While there are commercial solutions like the WattBox that perform well, they can be quite expensive. As an alternative, ESPHome is an excellent open-source solution that allows you to flash hardware running either the ESP32 or ESP8266 microcontrollers with completely open-source software. I’ve used ESPHome for some personal projects like my Light-Up picture frame and to replace commercial devices like the Sonoff S31.

ESPHome Scripting

Previously, I’ve only used ESPHome as firmware to connect devices to Home Assistant, but it’s capable of so much more. In this guide, I will show you how to create an ESPHome script that automatically restarts your modem when the network signal drops.

What’s really cool about this project is that you can upload it onto a $9 Sonoff S31 Smart Switch and it’ll be able to reboot your modem, router or even Home Assistant instance.

Here’s what this ESPHome script does:

  1. Monitor packet loss: The script continuously monitors packet loss by sending ICMP (ping) requests to a designated target, such as a well-known DNS server like 8.8.8.8.

  2. Determine the need for a restart: If the script detects a certain number of consecutive packet losses (e.g., 3 minutes in a row), it determines that a modem restart is necessary.

  3. Control the Relay inside the Sonoff S31: The script runs on a Sonoff S31 smart plug and it can turn the plug off, and then on again to restart the modem.

  4. Implement a 15 minute pause: At most this script will restart your modem once every 15 minutes. This should give it enough time to reconnect to the internet. It also holds off on restarting within the first 15 minutes of its own boot up process so it gives everything time to stabilize.

Setting Up the ESPHome Script

To set up the ESPHome auto-restart script, follow these steps:

  1. First, install the install ESPHome addon on your Home Assistant instance.

  2. Flash the Sonoff S31 with ESPHome using this guide.

  3. Create a new ESPHome configuration file (e.g., sonoff_s31.yaml) for the Sonoff S31 and customize it with the auto-restart script provided below.

  4. Let Home Assistant do the hard work of compiling your script and uploading it to the device.

  5. Configure the Sonoff S31 in Home Assistant, you’ll be able to see the report of the ping time and packet loss there, but it should handle it’s reboot process without Home Assistant running.

With the ESPHome auto-restart script in place, you can now enjoy a more reliable and stable internet connection without worrying about manual restarts.

 # Basic Config
esphome:
  name: sonoff-cable-modem
  platform: ESP8266
  board: esp01_1m


  libraries:
    - ESP8266WiFi
    - https://github.com/akaJes/AsyncPing#95ac7e4

external_components:
  - source:
      type: git
      url: https://github.com/trombik/esphome-component-ping
      ref: main

wifi:
  ssid: xxxxxxxxxxxxxxxxxxx
  password: xxxxxxxxxxxxxxxxxxx

  ap:
    ssid: "Sonoff5"  # in case it can't connect and you need to reprogram it
    password: "xxxxxxxxxxxxxxxxxxx"

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

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxxxxxxxxxxxxx"

ota:
  password: "xxxxxxxxxxxxxxxxxxx"

# Device Specific Config
uart:
  rx_pin: RX
  baud_rate: 4800

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO0
      mode: INPUT_PULLUP
      inverted: True
    name: "Sonoff Cable Modem Button"
    on_press:
      - script.execute: power_cycle_relay
  - platform: status
    name: "Sonoff Cable Modem Status"

sensor:
  - platform: wifi_signal
    name: "Sonoff Cable Modem WiFi Signal"
    update_interval: 60s
  - platform: cse7766
    current:
      name: "Sonoff Cable Modem Current"
      accuracy_decimals: 1
    voltage:
      name: "Sonoff Cable Modem Voltage"
      accuracy_decimals: 1
    power:
      name: "Sonoff Cable Modem Power"
      accuracy_decimals: 1
  - platform: ping
    ip_address: 8.8.8.8
    id: ping_response
    num_attempts: 1
    timeout: 1sec
    loss:
      name: "Packet Loss to Internet"
      id: loss
    latency:
      name: "Latency to Internet"
      accuracy_decimals: 3
    update_interval: 30s

switch:
  - platform: gpio
    name: "Sonoff Cable Modem Relay"
    pin: GPIO12
    id: relay
    restore_mode: ALWAYS_ON

status_led:
  pin: GPIO13

globals:
  - id: last_power_cycle
    type: time_t
    restore_value: no
    initial_value: '0'
  - id: packet_loss_count
    type: int
    restore_value: no
    initial_value: '0'

# Ping time (in seconds)
interval:
  - interval: 60s
    then:
      - script.execute: ping_check


# Script for checking packet loss
script:
  - id: ping_check
    then:
      - if:
          condition:
            # Condition checks if packet loss has occurred
            lambda: 'return id(loss).state > 0;'
          then:
            # If packet loss has occurred, increment packet_loss_count
            - lambda: |- 
                id(packet_loss_count) += 1;
                // If there are 3 consecutive packet losses, reset the count and execute power_cycle_relay script
                if (id(packet_loss_count) >= 3) {
                  id(packet_loss_count) = 0;
                  id(power_cycle_relay).execute();
                }
          else:
            # If there's no packet loss, reset the packet_loss_count
            - lambda: |- 
                id(packet_loss_count) = 0;

  # Script for power cycling the relay
  - id: power_cycle_relay
    then:
      - lambda: |-
          time_t now = id(esphome_time).now().timestamp;
          // Do not power cycle during the first 15 minutes after boot
          if (now < 900) {
            ESP_LOGD("power_cycle_relay", "Skipping power cycle during the first 15 minutes after boot");
            return;
          }
          // Do not power cycle if the last power cycle was less than 15 minutes ago
          if ((now - id(last_power_cycle)) < 900) {
            ESP_LOGD("power_cycle_relay", "Skipping power cycle since it's been less than 15 minutes since the last one");
            return;
          }
          // Update the last power cycle timestamp and execute the power cycle
          id(last_power_cycle) = now;
          ESP_LOGD("power_cycle_relay", "Power cycling the relay due to button press or packet loss.");
          id(relay).turn_off();          
      - delay: 5s
      - lambda: |- 
          id(relay).turn_on();




time:
  - platform: sntp
    id: esphome_time
    servers:
      - 0.pool.ntp.org