Configuration
Complete Configuration
This firmware uses ESPHome as the base, which means that your system is configured with a text-based configuration format. You'll need your notes from the panel installation to fill out that configuration file with the details for your specific system.
A common feature in configuration files are comments. Everything after the # symbol on a given line will be ignored by the computer.
In the text below, these comments frequently contain explanation or instruction.
Below you'll find a starting point. You'll need to tailor this to your own system using the information you've collected in Panel installation.
We've broken out some substitutions for the repeated circuit names but note that you will still need to go to each circuit within the ct_clamps section and make sure for each:
- the
phase_idis set to the correct leg of your panel for that circuit- see this explanation
- you have the right either
*posor*negfilter depending on which direction the CT reads- note that the suggested filters also truncate out noise that would lead to negative energy readings. for solar or other generation this may not be what you want!
- if you want to adjust the reading, e.g. it is common to
multiply: 2if you are monitoring half of a double-pole breaker- compare this alternative wiring
- if a CT clamp is unused, remove its entry from
ct_clampsand delete any template expressions that reference it (see the FAQ)
- Emporia Vue Gen 2
- Emporia Vue Gen 3
esphome:
name: emporia-vue
friendly_name: Emporia Monitor
esp32:
board: esp32dev
framework:
type: esp-idf
version: recommended
external_components:
- source: github://emporia-vue-local/esphome@dev
components:
- emporia_vue
# Enable Home Assistant API…
api:
encryption:
key: !secret api_key
# (optional) add this to the shared API config above for buzzer features
services:
- service: play_rtttl
variables:
song_str: string
then:
- rtttl.play:
rtttl: !lambda 'return song_str;'
# …and use HA for setting our RTC time locally
time:
- platform: homeassistant
# …and expose a (virtual) "switch" that HA can use to restart us
switch:
- platform: restart
name: Restart
# enable OTA updates after first flash
ota:
platform: esphome
password: !secret ota_key
# enable logging, with some customizations
logger:
logs:
# by default, every reading will be printed to the UART, which is very slow
# This will disable printing the readings but keep other helpful messages
sensor: INFO
preferences:
# avoid wearing out the flash lifespan with rapidly-updating sensor data!
# please also make sure `restore: false` is set on all `platform: total_daily_energy` sensors below.
flash_write_interval: "48h"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
output:
- platform: ledc
pin: GPIO12
id: buzzer
- platform: gpio
pin: GPIO27
id: buzzer_gnd
rtttl:
output: buzzer
on_finished_playback:
- logger.log: 'Song ended!'
button:
- platform: template
name: "Two Beeps"
on_press:
- rtttl.play: "two short:d=4,o=5,b=100:16e6,16e6"
light:
- platform: status_led
name: "D3_LED"
pin: 23
restore_mode: ALWAYS_ON
entity_category: config
i2c:
sda: 21
scl: 22
scan: false
frequency: 400kHz
timeout: 1ms
id: i2c_a
substitutions:
leg_1: "Phase A"
leg_2: "Phase B"
#leg_3 (`input: "C"` available but not used in this example)
cir_1: "Circuit 1"
cir_2: "Circuit 2"
cir_3: "Circuit 3"
cir_4: "Circuit 4"
cir_5: "Circuit 5"
cir_6: "Circuit 6"
cir_7: "Circuit 7"
cir_8: "Circuit 8"
cir_9: "Circuit 9"
cir_10: "Circuit 10"
cir_11: "Circuit 11"
cir_12: "Circuit 12"
cir_13: "Circuit 13"
cir_14: "Circuit 14"
cir_15: "Circuit 15"
cir_16: "Circuit 16"
xtra: "Balance" # displaying leftover/unmonitored energy (total minus circs 1–16)
.filters:
# these are called references in YAML. They allow you to reuse
# this configuration in each sensor, while only defining it once
# you can adjust them here, split them up differently, even copy some inline…
- &throttle_avg
# average all raw readings together over a 5 second span before publishing
throttle_average: 5s
- &throttle_time
# only send the most recent measurement every 60 seconds
throttle: 60s
- &neg
# invert and filter out any values below 0.
lambda: 'return max(-x, 0.0f);'
- &pos
# filter out any values below 0.
lambda: 'return max(x, 0.0f);'
- &abs
# take the absolute value of the value
lambda: 'return abs(x);'
sensor:
- platform: emporia_vue
variant: vue2
i2c_id: i2c_a
phases:
- id: phase_a # Verify that this specific phase/leg is connected to correct input wire color on device listed below
input: BLACK # Vue device wire color
calibration: 0.022 # a starting point, may need adjusted to ensure accuracy!
# To calculate new calibration value use the formula <in-use calibration value> * <accurate voltage> / <reporting voltage>
voltage:
name: "${leg_1} Voltage"
filters: [*throttle_avg, *pos]
frequency:
name: "${leg_1} Frequency"
filters: [*throttle_avg, *pos]
- id: phase_b # see notes above
input: RED
calibration: 0.022
voltage:
name: "${leg_2} Voltage"
filters: [*throttle_avg, *pos]
phase_angle:
name: "${leg_2} Phase Angle"
filters: [*throttle_avg, *pos]
ct_clamps:
# These non-throttled power sensors are used for accurately calculating energy.
# Recommend not to specify a `name` for any of the power sensors here — only the `id`!
# This leaves them internal to ESPHome locally; post-processed data is sent to HA below.
- phase_id: phase_a
input: "A" # Verify the CT going to these devices input also matches the phase/leg
power:
id: phase_a_power
device_class: power
filters: [*pos]
- phase_id: phase_b
input: "B"
power:
id: phase_b_power
device_class: power
filters: [*pos]
# Pay close attention to set the `phase_id` for each breaker by matching it to the phase/leg it connects to in the panel
- { phase_id: phase_a, input: "1", power: { id: cir1, filters: [ *neg, multiply: 2 ] } }
- { phase_id: phase_a, input: "2", power: { id: cir2, filters: [ *neg, multiply: 2 ] } }
- { phase_id: phase_a, input: "3", power: { id: cir3, filters: [ *neg ] } }
- { phase_id: phase_b, input: "4", power: { id: cir4, filters: [ *neg ] } }
- { phase_id: phase_a, input: "5", power: { id: cir5, filters: [ *neg ] } }
- { phase_id: phase_b, input: "6", power: { id: cir6, filters: [ *neg ] } }
- { phase_id: phase_a, input: "7", power: { id: cir7, filters: [ *neg ] } }
- { phase_id: phase_b, input: "8", power: { id: cir8, filters: [ *neg ] } }
- { phase_id: phase_a, input: "9", power: { id: cir9, filters: [ *neg ] } }
- { phase_id: phase_b, input: "10", power: { id: cir10, filters: [ *neg ] } }
- { phase_id: phase_a, input: "11", power: { id: cir11, filters: [ *neg ] } }
- { phase_id: phase_b, input: "12", power: { id: cir12, filters: [ *neg ] } }
- { phase_id: phase_a, input: "13", power: { id: cir13, filters: [ *neg ] } }
- { phase_id: phase_b, input: "14", power: { id: cir14, filters: [ *neg ] } }
- { phase_id: phase_a, input: "15", power: { id: cir15, filters: [ *neg ] } }
- { phase_id: phase_b, input: "16", power: { id: cir16, filters: [ *neg ] } }
on_update:
then:
- component.update: total_power
- component.update: balance_power
# these `copy` sensors filter and send the power state to HA
- { platform: copy, name: "${leg_1} Power", source_id: phase_a_power, filters: *throttle_avg }
- { platform: copy, name: "${leg_2} Power", source_id: phase_b_power, filters: *throttle_avg }
- { platform: copy, name: "Total Power", source_id: total_power, filters: *throttle_avg }
- { platform: copy, name: "${xtra} Power", source_id: balance_power, filters: *throttle_avg }
- { platform: copy, name: "${cir_1} Power", source_id: cir1, filters: *throttle_avg }
- { platform: copy, name: "${cir_2} Power", source_id: cir2, filters: *throttle_avg }
- { platform: copy, name: "${cir_3} Power", source_id: cir3, filters: *throttle_avg }
- { platform: copy, name: "${cir_4} Power", source_id: cir4, filters: *throttle_avg }
- { platform: copy, name: "${cir_5} Power", source_id: cir5, filters: *throttle_avg }
- { platform: copy, name: "${cir_6} Power", source_id: cir6, filters: *throttle_avg }
- { platform: copy, name: "${cir_7} Power", source_id: cir7, filters: *throttle_avg }
- { platform: copy, name: "${cir_8} Power", source_id: cir8, filters: *throttle_avg }
- { platform: copy, name: "${cir_9} Power", source_id: cir9, filters: *throttle_avg }
- { platform: copy, name: "${cir_10} Power", source_id: cir10, filters: *throttle_avg }
- { platform: copy, name: "${cir_11} Power", source_id: cir11, filters: *throttle_avg }
- { platform: copy, name: "${cir_12} Power", source_id: cir12, filters: *throttle_avg }
- { platform: copy, name: "${cir_13} Power", source_id: cir13, filters: *throttle_avg }
- { platform: copy, name: "${cir_14} Power", source_id: cir14, filters: *throttle_avg }
- { platform: copy, name: "${cir_15} Power", source_id: cir15, filters: *throttle_avg }
- { platform: copy, name: "${cir_16} Power", source_id: cir16, filters: *throttle_avg }
- platform: template
lambda: return id(phase_a_power).state + id(phase_b_power).state;
update_interval: never # will be updated after all power sensors update via on_update trigger
id: total_power
device_class: power
state_class: measurement
unit_of_measurement: "W"
- platform: total_daily_energy
name: "Total Daily Energy"
power_id: total_power
accuracy_decimals: 0
restore: false
filters: *throttle_time
- platform: template
lambda: !lambda |-
return max(0.0f, id(total_power).state -
id( cir1).state -
id( cir2).state -
id( cir3).state -
id( cir4).state -
id( cir5).state -
id( cir6).state -
id( cir7).state -
id( cir8).state -
id( cir9).state -
id(cir10).state -
id(cir11).state -
id(cir12).state -
id(cir13).state -
id(cir14).state -
id(cir15).state -
id(cir16).state);
update_interval: never # still happens, but via `on_update` trigger (*after* all power sensors update)
id: balance_power
device_class: power
state_class: measurement
unit_of_measurement: "W"
- platform: total_daily_energy
name: "${xtra} Daily Energy"
power_id: balance_power
accuracy_decimals: 0
restore: false
filters: *throttle_time
- { power_id: cir1, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_1} Daily Energy", filters: *throttle_time }
- { power_id: cir2, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_2} Daily Energy", filters: *throttle_time }
- { power_id: cir3, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_3} Daily Energy", filters: *throttle_time }
- { power_id: cir4, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_4} Daily Energy", filters: *throttle_time }
- { power_id: cir5, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_5} Daily Energy", filters: *throttle_time }
- { power_id: cir6, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_6} Daily Energy", filters: *throttle_time }
- { power_id: cir7, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_7} Daily Energy", filters: *throttle_time }
- { power_id: cir8, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_8} Daily Energy", filters: *throttle_time }
- { power_id: cir9, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_9} Daily Energy", filters: *throttle_time }
- { power_id: cir10, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_10} Daily Energy", filters: *throttle_time }
- { power_id: cir11, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_11} Daily Energy", filters: *throttle_time }
- { power_id: cir12, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_12} Daily Energy", filters: *throttle_time }
- { power_id: cir13, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_13} Daily Energy", filters: *throttle_time }
- { power_id: cir14, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_14} Daily Energy", filters: *throttle_time }
- { power_id: cir15, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_15} Daily Energy", filters: *throttle_time }
- { power_id: cir16, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_16} Daily Energy", filters: *throttle_time }
esphome:
name: emporia-vue
friendly_name: Emporia Monitor
esp32:
board: esp32dev
framework:
type: esp-idf
version: recommended
external_components:
- source: github://emporia-vue-local/esphome@dev
components:
- emporia_vue
# Enable Home Assistant API…
api:
encryption:
key: !secret api_key
# …and use HA for setting our RTC time locally
time:
- platform: homeassistant
# …and expose a (virtual) "switch" that HA can use to restart us
switch:
- platform: restart
name: Restart
# enable OTA updates after first flash
ota:
platform: esphome
password: !secret ota_key
# enable logging, with some customizations
logger:
logs:
# by default, every reading will be printed to the UART, which is very slow
# This will disable printing the readings but keep other helpful messages
sensor: INFO
preferences:
# avoid wearing out the flash lifespan with rapidly-updating sensor data!
# please also make sure `restore: false` is set on all `platform: total_daily_energy` sensors below.
flash_write_interval: "48h"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
on_connect:
- light.turn_on: wifi_led
on_disconnect:
- light.turn_off: wifi_led
light:
- platform: status_led
id: wifi_led
pin:
number: 2
ignore_strapping_warning: true
restore_mode: RESTORE_DEFAULT_ON
- platform: status_led
id: ethernet_led
pin: 4
restore_mode: ALWAYS_OFF
i2c:
sda:
number: 5
ignore_strapping_warning: true
scl: 18
scan: false
frequency: 400kHz
timeout: 1ms
id: i2c_a
# you can also use Ethernet for connectivity by uncommenting the following
#ethernet:
# type: RTL8201
# mdc_pin: GPIO32
# mdio_pin: GPIO33
# clk_mode: GPIO0_IN
# on_connect:
# - light.turn_on: ethernet_led
# on_disconnect:
# - light.turn_off: ethernet_led
substitutions:
leg_1: "Phase A"
leg_2: "Phase B"
#leg_3 (`input: "C"` available but not used in this example)
cir_1: "Circuit 1"
cir_2: "Circuit 2"
cir_3: "Circuit 3"
cir_4: "Circuit 4"
cir_5: "Circuit 5"
cir_6: "Circuit 6"
cir_7: "Circuit 7"
cir_8: "Circuit 8"
cir_9: "Circuit 9"
cir_10: "Circuit 10"
cir_11: "Circuit 11"
cir_12: "Circuit 12"
cir_13: "Circuit 13"
cir_14: "Circuit 14"
cir_15: "Circuit 15"
cir_16: "Circuit 16"
xtra: "Balance" # displaying leftover/unmonitored energy (total minus circs 1–16)
.filters:
# these are called references in YAML. They allow you to reuse
# this configuration in each sensor, while only defining it once
# you can adjust them here, split them up differently, even copy some inline…
- &throttle_avg
# average all raw readings together over a 5 second span before publishing
throttle_average: 5s
- &throttle_time
# only send the most recent measurement every 60 seconds
throttle: 60s
- &neg
# invert and filter out any values below 0.
lambda: 'return max(-x, 0.0f);'
- &pos
# filter out any values below 0.
lambda: 'return max(x, 0.0f);'
- &abs
# take the absolute value of the value
lambda: 'return abs(x);'
sensor:
- platform: emporia_vue
variant: vue3
i2c_id: i2c_a
phases:
- id: phase_a # Verify that this specific phase/leg is connected to correct input wire color on device listed below
input: BLACK # Vue device wire color
calibration: 0.01925 # a starting point, may need adjusted to ensure accuracy!
# To calculate new calibration value use the formula <in-use calibration value> * <accurate voltage> / <reporting voltage>
voltage:
name: "${leg_1} Voltage"
filters: [*throttle_avg, *pos]
frequency:
name: "${leg_1} Frequency"
filters: [*throttle_avg, *pos]
- id: phase_b # see notes above
input: RED
calibration: 0.01925
voltage:
name: "${leg_2} Voltage"
filters: [*throttle_avg, *pos]
phase_angle:
name: "${leg_2} Phase Angle"
filters: [*throttle_avg, *pos]
ct_clamps:
# These non-throttled power sensors are used for accurately calculating energy.
# Recommend not to specify a `name` for any of the power sensors here — only the `id`!
# This leaves them internal to ESPHome locally; post-processed data is sent to HA below.
- phase_id: phase_a
input: "A" # Verify the CT going to these devices input also matches the phase/leg
power:
id: phase_a_power
device_class: power
filters: [*pos]
- phase_id: phase_b
input: "B"
power:
id: phase_b_power
device_class: power
filters: [*pos]
# Pay close attention to set the `phase_id` for each breaker by matching it to the phase/leg it connects to in the panel
- { phase_id: phase_a, input: "1", power: { id: cir1, filters: [ *neg, multiply: 2 ] } }
- { phase_id: phase_a, input: "2", power: { id: cir2, filters: [ *neg, multiply: 2 ] } }
- { phase_id: phase_a, input: "3", power: { id: cir3, filters: [ *neg ] } }
- { phase_id: phase_b, input: "4", power: { id: cir4, filters: [ *neg ] } }
- { phase_id: phase_a, input: "5", power: { id: cir5, filters: [ *neg ] } }
- { phase_id: phase_b, input: "6", power: { id: cir6, filters: [ *neg ] } }
- { phase_id: phase_a, input: "7", power: { id: cir7, filters: [ *neg ] } }
- { phase_id: phase_b, input: "8", power: { id: cir8, filters: [ *neg ] } }
- { phase_id: phase_a, input: "9", power: { id: cir9, filters: [ *neg ] } }
- { phase_id: phase_b, input: "10", power: { id: cir10, filters: [ *neg ] } }
- { phase_id: phase_a, input: "11", power: { id: cir11, filters: [ *neg ] } }
- { phase_id: phase_b, input: "12", power: { id: cir12, filters: [ *neg ] } }
- { phase_id: phase_a, input: "13", power: { id: cir13, filters: [ *neg ] } }
- { phase_id: phase_b, input: "14", power: { id: cir14, filters: [ *neg ] } }
- { phase_id: phase_a, input: "15", power: { id: cir15, filters: [ *neg ] } }
- { phase_id: phase_b, input: "16", power: { id: cir16, filters: [ *neg ] } }
on_update:
then:
- component.update: total_power
- component.update: balance_power
# these `copy` sensors filter and send the power state to HA
- { platform: copy, name: "${leg_1} Power", source_id: phase_a_power, filters: *throttle_avg }
- { platform: copy, name: "${leg_2} Power", source_id: phase_b_power, filters: *throttle_avg }
- { platform: copy, name: "Total Power", source_id: total_power, filters: *throttle_avg }
- { platform: copy, name: "${xtra} Power", source_id: balance_power, filters: *throttle_avg }
- { platform: copy, name: "${cir_1} Power", source_id: cir1, filters: *throttle_avg }
- { platform: copy, name: "${cir_2} Power", source_id: cir2, filters: *throttle_avg }
- { platform: copy, name: "${cir_3} Power", source_id: cir3, filters: *throttle_avg }
- { platform: copy, name: "${cir_4} Power", source_id: cir4, filters: *throttle_avg }
- { platform: copy, name: "${cir_5} Power", source_id: cir5, filters: *throttle_avg }
- { platform: copy, name: "${cir_6} Power", source_id: cir6, filters: *throttle_avg }
- { platform: copy, name: "${cir_7} Power", source_id: cir7, filters: *throttle_avg }
- { platform: copy, name: "${cir_8} Power", source_id: cir8, filters: *throttle_avg }
- { platform: copy, name: "${cir_9} Power", source_id: cir9, filters: *throttle_avg }
- { platform: copy, name: "${cir_10} Power", source_id: cir10, filters: *throttle_avg }
- { platform: copy, name: "${cir_11} Power", source_id: cir11, filters: *throttle_avg }
- { platform: copy, name: "${cir_12} Power", source_id: cir12, filters: *throttle_avg }
- { platform: copy, name: "${cir_13} Power", source_id: cir13, filters: *throttle_avg }
- { platform: copy, name: "${cir_14} Power", source_id: cir14, filters: *throttle_avg }
- { platform: copy, name: "${cir_15} Power", source_id: cir15, filters: *throttle_avg }
- { platform: copy, name: "${cir_16} Power", source_id: cir16, filters: *throttle_avg }
- platform: template
lambda: return id(phase_a_power).state + id(phase_b_power).state;
update_interval: never # will be updated after all power sensors update via on_update trigger
id: total_power
device_class: power
state_class: measurement
unit_of_measurement: "W"
- platform: total_daily_energy
name: "Total Daily Energy"
power_id: total_power
accuracy_decimals: 0
restore: false
filters: *throttle_time
- platform: template
lambda: !lambda |-
return max(0.0f, id(total_power).state -
id( cir1).state -
id( cir2).state -
id( cir3).state -
id( cir4).state -
id( cir5).state -
id( cir6).state -
id( cir7).state -
id( cir8).state -
id( cir9).state -
id(cir10).state -
id(cir11).state -
id(cir12).state -
id(cir13).state -
id(cir14).state -
id(cir15).state -
id(cir16).state);
update_interval: never # still happens, but via `on_update` trigger (*after* all power sensors update)
id: balance_power
device_class: power
state_class: measurement
unit_of_measurement: "W"
- platform: total_daily_energy
name: "${xtra} Daily Energy"
power_id: balance_power
accuracy_decimals: 0
restore: false
filters: *throttle_time
- { power_id: cir1, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_1} Daily Energy", filters: *throttle_time }
- { power_id: cir2, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_2} Daily Energy", filters: *throttle_time }
- { power_id: cir3, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_3} Daily Energy", filters: *throttle_time }
- { power_id: cir4, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_4} Daily Energy", filters: *throttle_time }
- { power_id: cir5, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_5} Daily Energy", filters: *throttle_time }
- { power_id: cir6, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_6} Daily Energy", filters: *throttle_time }
- { power_id: cir7, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_7} Daily Energy", filters: *throttle_time }
- { power_id: cir8, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_8} Daily Energy", filters: *throttle_time }
- { power_id: cir9, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_9} Daily Energy", filters: *throttle_time }
- { power_id: cir10, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_10} Daily Energy", filters: *throttle_time }
- { power_id: cir11, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_11} Daily Energy", filters: *throttle_time }
- { power_id: cir12, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_12} Daily Energy", filters: *throttle_time }
- { power_id: cir13, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_13} Daily Energy", filters: *throttle_time }
- { power_id: cir14, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_14} Daily Energy", filters: *throttle_time }
- { power_id: cir15, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_15} Daily Energy", filters: *throttle_time }
- { power_id: cir16, platform: total_daily_energy, accuracy_decimals: 0, restore: false, name: "${cir_16} Daily Energy", filters: *throttle_time }
How it works
There's a lot here, don't get overwhelmed! A lot of it is simply repeated 16x for each of the circuits. The general flow is:
And the composite fields are:
total = A + B
balance = total - (1 + 2 + 3 + … + 16)
The general outline is:
- per-circuit labels (split out for convenience)
- some shared filters (more on these later)
- the core
emporia_vuesensor configuration:- starting with the per-phase A/B(/C) voltage monitoring sometimes called L1/L2/L3 poles
- followed by the per-phase A/B(/C) current sensing CTs again intended for the mains/poles into the panel
- and then the 16x individual circuit CT sensors (really the same, only indented differently, as the per-phase ones)
- NOTE: all these subsensors update every 240ms!
- templates to prepare the readings for more efficient Home Assistant data collection:
- each of the current sensors live power reading copied into HA via a
throttle_avg(5sec) filter - each of the sensors integrated into a total daily energy reading via a 1m
throttle_time(1min) filter - also a
balance_powertemplate (which you can remove if not wanted) which subtracts the 16x individual from the A+B total for comparison
- each of the current sensors live power reading copied into HA via a
Note especially the throttle_avg we set up. This is optional, but since we get a reading every 240ms, it is helpful to average these readings together so that we don't need to store such dense, noisy, data in Home Assistant. Similarly note the "Total Power", "Total Daily Energy", and "Circuit x Daily Energy". These are needed for the Home Assistant energy system, which requires daily kWh numbers. These are (again optionally) processed through a customizable throttle_time filter so HA gets a reading every minute.
Solar & Net metering
To configure energy returned to the grid for net metering (more info here), you need to add the following configuration:
sensor:
- platform: emporia_vue
ct_clamps:
- phase_id: phase_a
input: "A" # Verify the CT going to this device input also matches the phase/leg
power:
name: "Phase A Power Return"
id: phase_a_power_return
filters: [*throttle_avg, *invert] # This measures energy uploaded to grid on phase A
- phase_id: phase_b
input: "B" # Verify the CT going to this device input also matches the phase/leg
power:
name: "Phase B Power Return"
id: phase_b_power_return
filters: [*throttle_avg, *invert] # This measures energy uploaded to grid on phase B
- platform: template
name: "Total Power Return"
lambda: return id(phase_a_power_return).state + id(phase_b_power_return).state;
update_interval: 1s
id: total_power_return
device_class: power
state_class: measurement
unit_of_measurement: "W"
- platform: total_daily_energy
name: "Total Daily Energy Return"
power_id: total_power_return
accuracy_decimals: 0
Your solar sensors' configuration depends on your setup (single phase, split phase, 3-phase). The following example shows a split-phase installation using ct clamps 15 and 16:
sensor:
- platform: template
name: "Solar Power"
lambda: return id(cir15).state + id(cir16).state;
id: solar_power
device_class: power
state_class: measurement
unit_of_measurement: "W"
- platform: total_daily_energy
name: "Solar Daily Energy"
power_id: solar_power
accuracy_decimals: 0