- USB to serial converter module
- I tested this with a cheap & generic CH340G adapter
- 4 male-to-female jumper wires
- I have a soldering iron & needed something more secure for development, so I added a header & used that in the pictures, but you can get by without that.
- 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:
For each clamp, you want to make a note of the following information:
- 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://krconv/esphome@remove-freertos-task
components: [ emporia_vue ]
esp32:
board: d-duino-32
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
sensor:
- platform: emporia_vue
i2c_id: i2c_a
phases:
- id: phase_a
input: BLACK
voltage:
name: "Phase A Voltage"
filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }
- id: phase_b
input: RED
voltage:
name: "Phase B Voltage"
filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }
ct_clamps:
- phase_id: phase_a
input: "A"
power:
name: "Phase A Power"
id: phase_a_power
device_class: power
filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }
- phase_id: phase_b
input: "B"
power:
name: "Phase B Power"
id: phase_b_power
device_class: power
filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }
- { phase_id: phase_a, input: "1", power: { name: "Circuit 1 Power", id: cir1, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } } } }
- { phase_id: phase_b, input: "2", power: { name: "Circuit 2 Power", id: cir2, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } } } }
- { phase_id: phase_a, input: "3", power: { name: "Circuit 3 Power", id: cir3, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } } } }
- { phase_id: phase_a, input: "4", power: { name: "Circuit 4 Power", id: cir4, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } } } }
- { phase_id: phase_a, input: "5", power: { name: "Circuit 5 Power", id: cir5, filters: [ multiply: 2, sliding_window_moving_average: { window_size: 6, send_every: 6 } ] } }
- { phase_id: phase_a, input: "6", power: { name: "Circuit 6 Power", id: cir6, filters: [ multiply: 2, sliding_window_moving_average: { window_size: 6, send_every: 6 } ] } }
- { phase_id: phase_a, input: "7", power: { name: "Circuit 7 Power", id: cir7, filters: [ multiply: 2, sliding_window_moving_average: { window_size: 6, send_every: 6 } ] } }
- { phase_id: phase_b, input: "8", power: { name: "Circuit 8 Power", id: cir8, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }} }
- { phase_id: phase_b, input: "9", power: { name: "Circuit 9 Power", id: cir9, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }} }
- { phase_id: phase_b, input: "10", power: { name: "Circuit 10 Power", id: cir10, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }} }
- { phase_id: phase_a, input: "11", power: { name: "Circuit 11 Power", id: cir11, filters: [ multiply: 2, sliding_window_moving_average: { window_size: 6, send_every: 6 } ] } }
- { phase_id: phase_a, input: "12", power: { name: "Circuit 12 Power", id: cir12, filters: [ multiply: 2, sliding_window_moving_average: { window_size: 6, send_every: 6 } ] } }
- { phase_id: phase_a, input: "13", power: { name: "Circuit 13 Power", id: cir13, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }} }
- { phase_id: phase_a, input: "14", power: { name: "Circuit 14 Power", id: cir14, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }} }
- { phase_id: phase_b, input: "15", power: { name: "Circuit 15 Power", id: cir15, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }} }
- { phase_id: phase_a, input: "16", power: { name: "Circuit 16 Power", id: cir16, filters: { sliding_window_moving_average: { window_size: 6, send_every: 6 } }} }
- 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 the USB adapter in, connecting the jumpers for RX, TX, and GND between the two. Do not connect the power at this time.
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.
Remove any jumper from the USB dongle. Connect a jumper to the 5V pin of the dongle. While holding the jumpers in the Vue against the holes, and while holding the modified end of the cable against the shield of the ESP32, insert the other end of the 5V jumper into the Vue in the
VCC_5V0
pin.
Some boards have been reported to have "goop" on these pins. You try cleaning the goop off with isopropyl alcohol & a swab or rag, or you can move the pins up and down against the side of the holes to try and rub off the goop so you get good electrical contact.
If you have a soldering iron and a strip of 2.54mm headers, you can solder those on & save yourself the hassle of holding everything in place.
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!
(c) 2021 Flaviu Tamas Licensed under CC-BY-4.0