Background
I have a power meter measuring current (A), voltage (V), and frequency (Hz) for each of my 2 phases 10 times per second. I already had smart plugs for many loads to monitor energy and get notifications (e.g. washer finished) but I didn’t have any for the larger 240V loads such as dryer/oven/stove.
There is a lot of academic research on Energy Disaggregation or Non-Intrusive Load Monitoring (NILM). This picture from the original paper demonstrates the problem
The large loads of interest have a pretty distinctive signature, a sudden increase followed by an almost equal sudden decrease some time later, often many times until the appliance is off. Given that, I thought I should be able to achieve energy disaggregation only for resistive or similar loads using derivative sensors and just yaml code in Home Assistant.
Following image shows the current for each phase, their derivatives, and the estimated power of my dryer using the below setup:
Prerequisites
1. Create derivative sensors for each phase current.
Go to Settings > Devices & Services > Helpers. Click on Create helper. Select Derivative sensor. Give a Name, select the Input sensor with the phase current, set the Time window to e.g. 1 minute or 10 seconds and Time unit to minutes or seconds. You will have to experiment what value works for you. For me, 1 minute works well except for the rice cooker and microwave especially if the duration on the microwave is less than 1 minute. I’m currently using 1 min for all and 10 seconds for the rice cooker and microwave.
Appliances
For each appliance of interest:
1. Create a number helper to hold approximate power in W
Go to Settings > Devices & Services > Helpers. Click on Create helper. Select Number. Give a Name e.g. “Dryer power”, change Maximum value to something larger than the expected power of the appliance e.g. 6000 for dryer, and set Unit of measurement to W.
2. Create a timer helper to avoid thinking the appliance is running forever
Go to Settings > Devices & Services > Helpers. Click on Create helper. Select Timer. Give a Name e.g. “Dryer timer”, select Restore, and set a Duration to e.g. 5 minutes. This needs to be larger than the time the draw stays elevated. e.g. my dryer seems to draw maximum current for ~2 minutes, followed by very low current for ~1 minute and the cycle repeats until done. For EVSE you would need to set this to several hours depending on what’s the maximum time of you EV charging. This timer helper is needed if the automation misses the sudden decrease for whatever reason. At the end of the timer the automation sets the power of the appliance to 0.
3. Create an energy sensor
This is optional. Only if you want to track the energy in the energy dashboard under the individual devices.
After 2023.11.0, go to Settings > Devices & Services > Helpers. Click on Create helper. Select Integration - Riemann sum integral sensor. Give a Name e.g. “Dryer energy”, select Input sensor the number helper created at step 1, select Left Riemann sum. Finally add the created energy sensor in the individual devices of your energy configuration.
Before 2023.11.0, in /config/configuration.yaml
add:
sensor:
- platform: integration
unique_id: dryer_energy
name: "Dryer energy"
source: input_number.dryer_power
unit_prefix: k
unit_time: h
method: left
In /config/configuration.yaml
add:
homeassistant:
customize:
sensor.dryer_energy:
device_class: energy
4. Create a template binary sensor whether the appliance is running.
This is optional. Only if you want to have automations when the appliance finishes, e.g. notify when the dryer is done.
In /config/configuration.yaml
add something like the following. The delay_off
needs to be larger than the duration the appliance stays at low draw, e.g. for my dryer it needs to be at least 1 minute. Having this too short might trigger premature notifications that the appliance finished. Having it too large will delay the notification.
template:
- binary_sensor:
- name: Dryer running
unique_id: dryer_running
device_class: "running"
icon: mdi:tumble-dryer
delay_off:
minutes: 5
state: "{{ states('input_number.dryer_power') | float > 0 }}"
5. Create an automation using this blueprint for 2 phases or this blueprint for 3 phases or this blueprint for 1 phase.
e.g. for my dryer I have:
alias: "Energy Disaggregation: Dryer"
description: ""
use_blueprint:
path: energy_disaggregation.yaml
input:
power_helper: input_number.dryer_power
timer_helper: timer.dryer_timer
l1_current_derivative: sensor.micro2wallbox_current_amp_l1_min_derivative
l2_current_derivative: sensor.micro2wallbox_current_amp_l2_min_derivative
current_derivative_factor: 1
l1_voltage: sensor.micro2wallbox_voltage_l1
l2_voltage: sensor.micro2wallbox_voltage_l2
l1_lower_bound: 19
l1_upper_bound: 24
l2_lower_bound: 17
l2_upper_bound: 24
delay:
hours: 0
minutes: 0
seconds: 5
for my microwave I have:
alias: "Energy Disaggregation: Microwave"
description: ""
use_blueprint:
path: energy_disaggregation.yaml
input:
power_helper: input_number.microwave_power
timer_helper: timer.microwave_timer
l1_current_derivative: sensor.micro2wallbox_current_amp_l1_10s_derivative
l2_current_derivative: sensor.micro2wallbox_current_amp_l2_10s_derivative
current_derivative_factor: 10
l1_voltage: sensor.micro2wallbox_voltage_l1
l2_voltage: sensor.micro2wallbox_voltage_l2
l1_lower_bound: 14
l1_upper_bound: 18
l2_lower_bound: -2
l2_upper_bound: 2
delay:
hours: 0
minutes: 0
seconds: 3
You will have to pick the lower and upper bounds of increases/decreases by inspecting your history.
For getting notified the dryer finished I use the following automation:
alias: "Notify: Dryer finished"
description: ""
trigger:
- platform: state
entity_id:
- binary_sensor.dryer_running
from: "on"
to: "off"
condition: []
action:
- service: notify.nikos
data:
title: Dryer finished
message: "{{ now().strftime('at %-I:%M %p on %m/%d/%Y') }}"
mode: single