- USB to serial converter module
- I tested this with a cheap & generic CH340G adapter
- 4 male-to-female jumper wires
- 4 male pcb-mount headers
- Soldering iron & accessories
- esptool.py (windows instructions, generic instructions)
- ESPHome image
You'll want to install the clamps & wiring harness into your panel following the instructions at https://www.emporiaenergy.com/installation-guides. At this time, place a label on each wire using masking tape & a pen rather than connecting them to the energy monitor.
Next, we need to figure out which circuits are on which phases, and in the case of multi-pole breakers, the multiplier. There should be a label like the following on your panel:

- clamp number
- circuit number
- phase
- multiplier, if it is a multi-pole breaker
For the wiring harness, you'll want to make a note of which color cable matches which service main clamp (A, B, C).
Here's a starting point for a configuration:
esphome:
name: emporiavue2
external_components:
- source: github://flaviut/esphome@emporia-vue
components: [ emporia_vue ]
esp32:
board: esp32dev
framework:
type: esp-idf
version: recommended
# Enable Home Assistant API
api: {"password": "<ota password>"}
ota: {"password": "<ota password>"}
# Enable logging
logger:
logs:
# Don't log on each sensor reading, that's way too loud. We can see them in
# Home Assistant anyway, or re-enable them per-device if needed.
sensor: INFO
wifi:
ssid: "<wifi ssid>"
password: "<wifi password>"
i2c:
sda: 21
scl: 22
scan: false
frequency: 200kHz # recommended range is 50-200kHz
id: i2c_a
time:
- platform: sntp
id: my_time
# these are called references in YAML. They allow you to reuse
# this configuration in each sensor, while only defining it once
.defaultfilters:
- &df1
# adjust these to fit your needs
# we capture a new sample every 0.24 seconds, so the time can
# be calculated from the number of samples as n * 0.24.
sliding_window_moving_average:
# we average over the past 2.88 seconds
window_size: 12
# we push a new value every 1.44 seconds
send_every: 6
- &df2
# filter out any values below 0. You may want to disable
# this filter while configuring your system to make sure you
# don't get any consistently negative values.
# lambda: 'return x;' // no-op
lambda: 'return max(x, 0.0f);'
sensor:
- platform: emporia_vue
i2c_id: i2c_a
phases:
- id: phase_a
input: BLACK
voltage:
name: "Phase A Voltage"
filters: [*df1, *df2]
- id: phase_b
input: RED
voltage:
name: "Phase B Voltage"
filters: [*df1, *df2]
ct_clamps:
- phase_id: phase_a
input: "A"
power:
name: "Phase A Power"
id: phase_a_power
device_class: power
filters: [*df1, *df2]
- phase_id: phase_b
input: "B"
power:
name: "Phase B Power"
id: phase_b_power
device_class: power
filters: [*df1, *df2]
- { phase_id: phase_a, input: "1", power: { name: "Circuit 1 Power", id: cir1, filters: [ *df1, *df2 ] } }
- { phase_id: phase_b, input: "2", power: { name: "Circuit 2 Power", id: cir2, filters: [ *df1, *df2 ] } }
- { phase_id: phase_a, input: "3", power: { name: "Circuit 3 Power", id: cir3, filters: [ *df1, *df2 ] } }
- { phase_id: phase_a, input: "4", power: { name: "Circuit 4 Power", id: cir4, filters: [ *df1, *df2 ] } }
- { phase_id: phase_a, input: "5", power: { name: "Circuit 5 Power", id: cir5, filters: [ *df1, *df2, multiply: 2 ] } }
- { phase_id: phase_a, input: "6", power: { name: "Circuit 6 Power", id: cir6, filters: [ *df1, *df2, multiply: 2 ] } }
- { phase_id: phase_a, input: "7", power: { name: "Circuit 7 Power", id: cir7, filters: [ *df1, *df2, multiply: 2 ] } }
- { phase_id: phase_b, input: "8", power: { name: "Circuit 8 Power", id: cir8, filters: [ *df1, *df2 ] } }
- { phase_id: phase_b, input: "9", power: { name: "Circuit 9 Power", id: cir9, filters: [ *df1, *df2 ] } }
- { phase_id: phase_b, input: "10", power: { name: "Circuit 10 Power", id: cir10, filters: [ *df1, *df2 ] } }
- { phase_id: phase_a, input: "11", power: { name: "Circuit 11 Power", id: cir11, filters: [ *df1, *df2, multiply: 2 ] } }
- { phase_id: phase_a, input: "12", power: { name: "Circuit 12 Power", id: cir12, filters: [ *df1, *df2, multiply: 2 ] } }
- { phase_id: phase_a, input: "13", power: { name: "Circuit 13 Power", id: cir13, filters: [ *df1, *df2 ] } }
- { phase_id: phase_a, input: "14", power: { name: "Circuit 14 Power", id: cir14, filters: [ *df1, *df2 ] } }
- { phase_id: phase_b, input: "15", power: { name: "Circuit 15 Power", id: cir15, filters: [ *df1, *df2 ] } }
- { phase_id: phase_a, input: "16", power: { name: "Circuit 16 Power", id: cir16, filters: [ *df1, *df2 ] } }
- platform: template
name: "Total Power"
lambda: return id(phase_a_power).state + id(phase_b_power).state;
update_interval: 1s
id: total_power
unit_of_measurement: "W"
- platform: total_daily_energy
name: "Total Daily Energy"
power_id: total_power
accuracy_decimals: 0
- { power_id: cir1, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 1 Daily Energy" }
- { power_id: cir2, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 2 Daily Energy" }
- { power_id: cir3, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 3 Daily Energy" }
- { power_id: cir4, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 4 Daily Energy" }
- { power_id: cir5, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 5 Daily Energy" }
- { power_id: cir6, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 6 Daily Energy" }
- { power_id: cir7, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 7 Daily Energy" }
- { power_id: cir8, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 8 Daily Energy" }
- { power_id: cir9, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 9 Daily Energy" }
- { power_id: cir10, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 10 Daily Energy" }
- { power_id: cir11, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 11 Daily Energy" }
- { power_id: cir12, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 12 Daily Energy" }
- { power_id: cir13, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 13 Daily Energy" }
- { power_id: cir14, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 14 Daily Energy" }
- { power_id: cir15, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 15 Daily Energy" }
- { power_id: cir16, platform: total_daily_energy, accuracy_decimals: 0, name: "Circuit 16 Daily Energy" }
You'll want to replace <ota password>, <wifi ssid>, and <wifi password> with a unique password, and your wifi credentials, respectively.
You'll also want to update the sensor section of the configuration using the information you've collected in Panel installation, part 1.
Note the sliding_window_moving_average. 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.
Note the "Total Power", "Total Daily Energy", and "Circuit x Daily Energy". This is needed for the Home Assistant energy system, which requires daily kWh numbers.
Do not use the web_server since it is not compatible with the esp-idf framework, and you will get odd error messages.
It's not too critical to get this right on the first try, because you can update the board over WiFi using the ESPHome Dashboard.
Pry the lever on one of the jumper cables up using a pencil or a needle or some other sharp thing. If your cables don't have a lever, cut one end of the cable & strip it using scissors or a knife.


Plug in the unmodified end of the cable we modified above into the IO0 pin of the Emporia Vue 2.
Open a console window and test that esptool.py version works.

VCC_5V0 pin on the board.
With your other hand, run the following in the console: esptool.py -b 921600 read_flash 0 0x800000 flash_contents.bin. Successful completion of this step is critical in case something goes wrong later. This file is necessary to restore the device to factory function.
If the above command fails, try again using esptool.py -b 115200 read_flash 0 0x800000 flash_contents.bin.
With your other hand, run the following in the console: esphome run vue2.yaml. This will take a few minutes, and install the new software on the Vue 2!
You'll see a bunch of errors like Failed to read from sensor due to I2C error 3, but that's fine, since they'll go away when it is installed into into the wall.
Reassemble to Vue 2, and follow the instructions to plug everything in & started up!
At this time, it's not possible to use the Vue with MQTT.
Technical background: ESPHome doesn't support MQTT when using the ESP-IDF framework, at least not at the time of writing. However, we can't use the Arduino framework, since we need to this bugfix commit. The bugfix is in the latest version of arduino-esp32, but we access it through platformio/platform-espressif32, which has not yet been updated (please be patent and don't bother the maintainers in the issue!)
- You may have put that clamp on the wire backwards
- You may have selected the wrong phase in the configuration
Sometimes the CTs aren't fully plugged into the 3.5mm jacks on the Vue. It's often not an issue with the initial install, but with stuff getting jostled around as you put things back together.
This issue will often manifest as jumps between 0W and some other wattage for no reason.
Open up the panel, and make sure every connector is fully inserted into the Vue. Check if the problem is solved before putting the panel cover back on.
If your readings are within ±1W, then they're within the expected margin of error. The filters are designed to smooth out noise like this, and it's expected as no physical system can be perfect.
If the readings are significant outside of that, there may be a problem.
(c) 2021 Flaviu Tamas
Licensed under CC-BY-4.0

