Replies: 2 comments 20 replies
-
It depends on if the load is balanced or unbalanced (does it also have a neutral.) I had a few appliances that were dual breakers (Heat Pump, Dryer, Oven) and I looked through and identified which were unbalanced and which were balanced. If it's unbalanced (i.e. there's a neutral) then the total power may not be equal to one CT * 2. To solve this, you can sum two phases internally like this: sensor:
- platform: emporia_vue
i2c_id: i2c_a
ct_clamps:
- { phase_id: phase_a, input: "2", power: { id: cir2, internal: true, filters: [ *pos ] } }
- { phase_id: phase_b, input: "4", power: { id: cir4, internal: true, filters: [ *pos ] } }
- platform: template
name: "Oven Power"
lambda: return id(cir2).state + id(cir4).state;
update_interval: 3s
id: oven_power_raw
device_class: power
state_class: measurement
unit_of_measurement: "W"
- { power_id: oven_power_raw, platform: total_daily_energy, method: "left", accuracy_decimals: 0, restore: false, unit_of_measurement: "Wh", state_class: "total_increasing", device_class: "energy", filters: [ *throttle_energy ], name: "Oven Energy" } I did notice some incorrect outputs for spiky loads because the update interval caused sampling bias. To counteract this for some appliances, I actually created two internal total_daily_energy sensors to sum up the two phases, then created a sum template over the two integrals. |
Beta Was this translation helpful? Give feedback.
-
ESPHome uses retain for in frequently changing values in case Home Assistant needs to be restarted, but with the Emporia Vue, these values change frequently enough that they shouldn't be retained. This partially why ESPHome introduced the Native API mechanism.
.defaultfilters:
- &moving_avg
# 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
- &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);'
# Reduce noise in the power class
- &power_max
or:
- delta: 5
- throttle: 60s
- &power_min
throttle: 5s
# Energy doesn't need high throughput. every 60 seconds
- &throttle_energy
throttle: 60s
sensor:
- platform: emporia_vue
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
# 0.0226 # 0.022 is used as the default as starting point but may need adjusted to ensure accuracy
# To calculate new calibration value use the formula <in-use calibration value> * <accurate voltage> / <reporting voltage>
calibration: 0.022737
voltage:
name: "Phase A Voltage"
filters: [*moving_avg, *pos]
frequency:
name: "Phase A Frequency"
filters: [*moving_avg, *pos]
- id: phase_b # Verify that this specific phase/leg is connected to correct input wire color on device listed below
input: RED # Vue device wire color
calibration: 0.021295 # 0.022 is used as the default as starting point but may need adjusted to ensure accuracy
# To calculate new calibration value use the formula <in-use calibration value> * <accurate voltage> / <reporting voltage>
voltage:
name: "Phase B Voltage"
filters: [*moving_avg, *pos]
phase_angle:
name: "Phase B Phase Angle"
filters: [*moving_avg, *pos]
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"
id: phase_a_power
device_class: power
internal: true
filters: [*pos]
current:
name: "Phase A Current"
device_class: current
filters: [ *moving_avg, *pos ]
- phase_id: phase_b
input: "B" # Verify the CT going to this device input also matches the phase/leg
power:
name: "Phase B Power"
id: phase_b_power
device_class: power
internal: true
filters: [*pos]
current:
name: "Phase B Current"
device_class: current
filters: [ *moving_avg, *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
# Some circuits are commented out because they don't have a CT connected yet
- { phase_id: phase_a, input: "1", power: { name: "Heat Pump Power #1", internal: true, id: cir1, filters: [ *pos, multiply: 2 ] } }
- { phase_id: phase_a, input: "2", power: { name: "Oven Power #2", id: cir2, internal: true, filters: [ *pos ] } }
# CT 3 is the opposite side of Heat Pump #1. Heat Pump is connected with a NEMA 6 which does not include a neutral, thus only one CT is needed
- { phase_id: phase_b, input: "4", power: { name: "Oven Power #4", id: cir4, internal: true, filters: [ *pos ] } }
- { phase_id: phase_a, input: "5", power: { name: "Dryer Power #5", internal: true, id: cir5, filters: [ *pos ] } }
- { phase_id: phase_a, input: "6", power: { name: "Dishwasher / Disposal Power #6", internal: true, id: cir6, filters: [ *pos ] } }
- { phase_id: phase_b, input: "7", power: { name: "Dryer Power #7", internal: true, id: cir7, filters: [ *pos ] } }
- { phase_id: phase_b, input: "8", power: { name: "Kitchen Power #8", id: cir8, internal: true, filters: [ *pos ] } }
- { phase_id: phase_a, input: "9", power: { name: "Washer Power #9", id: cir9, internal: true, filters: [ *pos ] } }
- { phase_id: phase_a, input: "10", power: { name: "Kitchen Power #10", id: cir10, internal: true, filters: [ *pos ] } }
- { phase_id: phase_a, input: "13", power: { internal: true, id: cir13, filters: [ *pos ] } }
- { phase_id: phase_a, input: "14", power: { name: "Bedroom Power #14", internal: true, id: cir14, filters: [ *pos ] } }
- { phase_id: phase_b, input: "15", power: { name: "General Power #15", internal: true, id: cir15, filters: [ *pos ] } }
- { phase_id: phase_b, input: "16", power: { name: "General Power #16", internal: true, id: cir16, filters: [ *pos ] } }
- { platform: copy, id: phase_a_power_b, device_class: power, state_class: "measurement", source_id: phase_a_power, filters: [ *power_min, *power_max ], name: "Phase A Power" }
- { platform: copy, id: phase_b_power_b, device_class: power, state_class: "measurement", source_id: phase_b_power, filters: [ *power_min, *power_max ], name: "Phase B Power" }
- { platform: copy, id: cir1_b, source_id: cir1, filters: [ *power_min, *power_max ], name: "Heat Pump Power" }
- { platform: copy, id: cir2_b, source_id: oven_power_raw, filters: [ *power_min, *power_max ], name: "Oven Power" }
- { platform: copy, id: cir5_b, source_id: dryer_power, filters: [ *power_min, *power_max ], name: "Dryer Power" }
- { platform: copy, id: cir6_b, source_id: cir6, filters: [ *power_min, *power_max ], name: "Dishwasher / Disposal Power #6" }
- { platform: copy, id: cir8_b, source_id: cir8, filters: [ *power_min, *power_max ], name: "Kitchen Power #8" }
- { platform: copy, id: cir9_b, source_id: cir9, filters: [ *power_min, *power_max ], name: "Washer Power #9" }
- { platform: copy, id: cir10_b, source_id: cir10, filters: [ *power_min, *power_max ], name: "Kitchen Power #10" }
- { platform: copy, id: cir13_b, source_id: cir13, filters: [ *power_min, *power_max ], name: "Microwave Power #13" }
- { platform: copy, id: cir14_b, source_id: cir14, filters: [ *power_min, *power_max ], name: "Bedroom Power #14" }
- { platform: copy, id: cir15_b, source_id: cir15, filters: [ *power_min, *power_max ], name: "General Power #15" }
- { platform: copy, id: cir16_b, source_id: cir16, filters: [ *power_min, *power_max ], name: "General Power #16" }
- platform: template
name: "Dryer Power"
lambda: return id(cir5).state + id(cir7).state;
update_interval: 1s
id: dryer_power
internal: true
device_class: power
state_class: measurement
unit_of_measurement: "W"
#filters: [ *power_min, *power_max ]
- platform: template
name: "Oven Power"
lambda: return id(cir2).state + id(cir4).state;
update_interval: 1s
id: oven_power_raw
internal: true
device_class: power
state_class: measurement
unit_of_measurement: "W"
#filters: [ *power_min, *power_max ]
## Total Power and Energy aggregation
- platform: template
name: "Total Power"
lambda: return id(phase_a_power).state + id(phase_b_power).state;
update_interval: 3s
id: total_power_raw
device_class: power
state_class: measurement
unit_of_measurement: "W"
# Total Energy
# In the default template, we bucketized every 1 second, but this means we compute the integral every 240ms (the fastest we can)
- id: phase_a_energy_raw
power_id: phase_a_power
platform: total_daily_energy
restore: false
internal: true
- id: phase_b_energy_raw
power_id: phase_b_power
platform: total_daily_energy
restore: false
internal: true
- name: "Total Daily Energy"
platform: template
lambda: return id(phase_a_energy_raw).state + id(phase_b_energy_raw).state;
update_interval: 60s
accuracy_decimals: 0
unit_of_measurement: Wh
state_class: total_increasing
device_class: energy
- { power_id: cir1, platform: total_daily_energy, method: "left", accuracy_decimals: 0, restore: false, unit_of_measurement: "Wh", state_class: "total_increasing", device_class: "energy", filters: [ *throttle_energy ], name: "Heat Pump Energy" }
- { power_id: oven_power_raw, platform: total_daily_energy, method: "left", accuracy_decimals: 0, restore: false, unit_of_measurement: "Wh", state_class: "total_increasing", device_class: "energy", filters: [ *throttle_energy ], name: "Oven Energy" } It's kind of complex I realize, but the key parts are for total power I actually have a few different sensors that are calculated. Total Energy is calculated as a sum of two continuous integrals instead of an integral of a 1s recalculated integral to ensure there's no sampling biases. Then, because I have decoupled the energy integral from the 1s-3s interval, I'm free to transmit the power sensor at a slower rate to reduce MQTT traffic and the size of my Home Assistant recorder DB. I remove the moving average and instead transmit the power every 3s-60s. 3s is the minimum, 60s is the maximum and if the power changes +- 5W, then it transmits more frequently. I recognize the above is a bit convoluted, but compared to the default YAML it seems to increase accuracy (compared to another sensor I compared with) and reduce traffic.
|
Beta Was this translation helpful? Give feedback.
-
1.) I have 24 circuits to monitor but only the 16 clamps. Is it OK to wrap a CT over multiple wires (if their phase is the same) and monitoring them together?
2.) The manual explicitly says to only use one CT for multi breakers and use multiplier of 2. But why is that? I don't think that's accurate since it's not guaranteed that the same current flows (that would only be the case is both wires are connected to the same load, basically having two thinner wires instead of one thick one). Can someone explain the background?
Now I do have multi breakers and most of them use both phases (e.g. branch circuits going to another sub panel). Especially in this case I must monitor their individual wires with separate CTs, right?
3.) For the multi breakers in (2), is there a config option to automatically together the contributions of two circuits? Example: One tandem breaker goes to the dryer. Of course, it includes both phases. I have one CT over each. But I am really interested in the dryer power/energy as a whole and not per phase. While I can add them outside ESPhome (e.g. in Home Assistant) it would be much cleaner if I could define those in ESPhome directly.
Beta Was this translation helpful? Give feedback.
All reactions