ESPHome Energy Alert Light for Solar Batteries

June 18th, 2022

We’ve finally had our solar battery installed almost ten months after ordering it, and I’m very excited.

We have an AC-connected GivEnergy 8.2kWh battery with their 3kW inverter and I’m going to do a whole video about this system and how I have it connected to Home Assistant once I’ve had it a little bit longer to play with it. But this is an ESPHome video so let me set out the scenario for you: the inverter can push out up to 3kW of power into your home, but let’s say you’re trying your hardest not to draw any power from the grid and rely just on your solar panels and battery instead. If you’re in the kitchen and you have the kettle on, how do you know whether you’re near the limit of your battery’s power output? Would putting the microwave on at the same time take you over that limit and cause you to draw power from the grid? My solution to this problem is a very simple LED traffic light.

The idea is that if you’re exporting solar, or your battery is ticking over keeping your grid draw to a minimum but isn’t being pushed too hard, then the LED is green. If the load in your home is causing the battery to use most of its output power, then the LED turns amber to warn you that turning something else on could cause you to start drawing from the grid. And obviously if you are already drawing from the grid, the LED turns red.

I’m going to dive straight in here and show you the hardware build because it’s very easy. Most of the hard work here is in the software. Here’s the shopping list:

I’ve used a D1 mini as the brains, and a super-bright 5mm RGB LED. The main decision you need to make is which sort of LED to go for. You must get a ‘common cathode’ LED, but after that you can choose between diffused or clear. With a diffused one, the three different colours (red, green and blue, obviously) are mixed very nicely, and you get a coloured glow from the bulb. But if you go with a clear bulb like this one then you can project a perfect circle onto your ceiling. This looks really cool, but the problem is that unless you just want red, green or blue light, then attempting to use other colours will project multiple overlapping circles.

I found an old project box that I wasn’t using anymore, cut a hole in the lid for the LED to poke through, and stuck a D1 mini inside with just the one side soldered to a right-angled header. I used an LED lens to diffuse the light a bit more and help it give more of a spread-out glow.

substitutions:
  device_name: energy-alert-led
  friendly_name: "Energy Alert LED"
  device_description: "RGB LED for alerting energy usage"
  homeassistant_grid_import_power_entity: sensor.smart_meter_electricity_power # must be positive when importing
  homeassistant_battery_export_power_entity: sensor.givtcp_discharge_power # must be positive when exporting to the home
  threshold_watts_alert_red_grid_import: "100" # if you draw more power than this from the grid, the LED turns red
  battery_export_watts_maximum: "3000" # what is the maximum export power of the battery?
  threshold_percentage_alert_amber_battery_export: "0.7" # at what percentage utilisation of the maximum battery export do you want to turn the LED amber? Scale of 0.0-1.0
  
esphome:
  name: '${device_name}'
  comment: '${device_description}'
  platform: ESP8266
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: '${device_name}'
    password: !secret fallback_password

captive_portal:

ota:
  safe_mode: true
  reboot_timeout: 10min
  num_attempts: 5
    
# Output components for LED pins
output:
  - platform: esp8266_pwm
    id: output_red
    pin: D1
  - platform: esp8266_pwm
    id: output_green
    pin: D2
  - platform: esp8266_pwm
    id: output_blue
    pin: D3
    
sensor:
  # The grid power import entity from Home Assistant
  - platform: homeassistant
    name: "Grid Power Entity from Home Assistant"
    entity_id: '${homeassistant_grid_import_power_entity}'
    id: grid_import_power
    internal: true
    
  # The battery power export entity from Home Assistant
  - platform: homeassistant
    name: "Battery Power Entity from Home Assistant"
    entity_id: '${homeassistant_battery_export_power_entity}'
    id: battery_export_power
    internal: true    

light:
  - platform: rgb
    name: "Energy Alert LED"
    id: alert_rgbled
    red: output_red
    green: output_green
    blue: output_blue
    on_turn_on:
    - script.execute: set_rgb_colour

# Every 15 seconds, if the LED is on, then call a script to check the colour is set correctly  
interval:
  - interval: 15sec
    then:
      - if:
          condition:
            light.is_on: alert_rgbled
          then:
            - script.execute: set_rgb_colour
            
            
globals:
 - id: global_threshold_percentage_alert_amber_battery_export
   type: float
   initial_value: '${threshold_percentage_alert_amber_battery_export}'
   
# Script to set the colour of the RGB LED based on power flows
script:
  - id: set_rgb_colour
    then:
      - lambda: |-
          if (id(grid_import_power).state > id(${threshold_watts_alert_red_grid_import})) {
            // If we are drawing from the grid. Set LED to Red
            ESP_LOGD("Energy LED", "Grid power is %d so setting LED to Red", id(grid_import_power).state);
            auto call = id(alert_rgbled).make_call();
            call.set_rgb(1.0,0.0,0.0);
            call.perform();
          } else if (id(battery_export_power).state > (id(${battery_export_watts_maximum})*id(global_threshold_percentage_alert_amber_battery_export)) ) {
            // We're using more than the specified threshold percentage of the max battery output so an amber warning not to use any more is required...
            ESP_LOGD("Energy LED", "Battery power is %d so setting LED to Amber", id(battery_export_power).state);
            auto call = id(alert_rgbled).make_call();
            call.set_rgb(1.0,0.75,0.0); // amber
            call.perform();
          } else {
            // We are not drawing power from the grid, and battery export is low. Set LED to Green
            ESP_LOGD("Energy LED", "Grid power is %d so setting LED to Green", id(grid_import_power).state);
            auto call = id(alert_rgbled).make_call();
            call.set_rgb(0.0,1.0,0.0);
            call.perform();
          }
    

And on to the tricky part, the configuration. I’ve tried to make this as easy as possible for you to adapt to your situation, but there are some prerequisites that you’ll need to sort first. The sensor will need to know your grid import power and the power being discharged from your battery, and both of those must be positive values – so a grid sensor that’s negative when importing is no good for this unless you make changes to my code. Once you have those entities in Home Assistant, type their entity names in the relevant location (homeassistant_grid_import_power_entity and homeassistant_battery_export_power_entity). Now you need to define three parameters: threshold_watts_alert_red_grid_import defines how many watts you need to be importing from the grid before the LED turns red. I’ve set this to 100 watts because when my battery inverter attempts to keep grid draw to a minimum, it tends to sit between 0 and 99 watts most of the time. Next we have to define the maximum export power of your battery (battery_export_watts_maximum) – mine is 3kW so I’ve set this to 3000. Finally, you need to define a threshold above which the LED turns amber (threshold_percentage_alert_amber_battery_export). I’ve set this to 0.7, so when the battery reaches 70% of its 3000 watts of discharge power, we get an amber alert.

Just to run you quickly through the rest of the code, there’s an output section used to define the pins on the D1 that the LED is connected to. There are a couple of Home Assistant sensors to import the grid and battery power values from Home Assistant – these are marked internal so as they’re not shared back out to Home Assistant. There’s the RGB light component which calls a script to set its colour whenever it’s turned on. The interval component runs every 15 seconds, checks to see if the LED is turned on, and then if it is it’ll call the script to check that it’s set to the correct colour. To explain the logic behind this – you can turn the LED on and off from Home Assistant. If you have turned it off intentionally, you don’t want it turning itself back on automatically 15 seconds later, but when you do turn it back on, then it’ll immediately check that it’s the correct colour.

And then there’s the script component which compares the grid import power to the import threshold we defined at the start of the configuration. If we’re over the threshold then the LED is set to red. Else, if the battery export is greater than the threshold percentage of your battery’s maximum power output, it sets it amber, and finally by default it sets it to green for all other scenarios.

This works great for me, but I wasn’t quite happy with that LED being on all of the time when there’s nobody around to see it. I’d made sure that the sensor exposed the LED back to Home Assistant, so I created a new automation. I grouped together motion sensors from our hall, kitchen and dining room as a template binary_sensor and used one of the existing automation blueprints to turn on the LED when that group detects motion, but turn it off a few minutes after motion has stopped.

binary_sensor:
  - platform: template
    sensors:
      energy_alert_led_motion_sensors:
        friendly_name: "Energy Alert LED Motion Sensors"
        device_class: motion
        value_template: >-
          {{ is_state('binary_sensor.hall_motion_sensor', 'on')
            or is_state('binary_sensor.kitchen_motion_sensor', 'on')
             or is_state('binary_sensor.dining_room_st_motion_occupancy', 'on')
             or is_state('binary_sensor.dining_room_motion_sensor', 'on') }}
alias: 'Motion-activated Light: Energy Alert LED'
description: ''
use_blueprint:
  path: homeassistant/motion_light.yaml
  input:
    motion_entity: binary_sensor.energy_alert_led_motion_sensors
    light_target:
      entity_id: light.energy_alert_led
    no_motion_wait: 300
blueprint:
  name: Motion-activated Light
  description: Turn on a light when motion is detected.
  domain: automation
  source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/motion_light.yaml
  input:
    motion_entity:
      name: Motion Sensor
      selector:
        entity:
          domain: binary_sensor
          device_class: motion
    light_target:
      name: Light
      selector:
        target:
          entity:
            domain: light
    no_motion_wait:
      name: Wait time
      description: Time to leave the light on after last motion is detected.
      default: 120
      selector:
        number:
          min: 0
          max: 3600
          unit_of_measurement: seconds

# If motion is detected within the delay,
# we restart the script.
mode: restart
max_exceeded: silent

trigger:
  platform: state
  entity_id: !input motion_entity
  from: "off"
  to: "on"

action:
  - service: light.turn_on
    target: !input light_target
  - wait_for_trigger:
      platform: state
      entity_id: !input motion_entity
      from: "on"
      to: "off"
  - delay: !input no_motion_wait
  - service: light.turn_off
    target: !input light_target
  • As an Amazon Associate I earn from qualifying purchases.