Skip to content

Instantly share code, notes, and snippets.

@flaviut
Last active March 12, 2024 00:27
Show Gist options
  • Save flaviut/93a1212c7b165c7674693a45ad52c512 to your computer and use it in GitHub Desktop.
Save flaviut/93a1212c7b165c7674693a45ad52c512 to your computer and use it in GitHub Desktop.
Setting up Emporia Vue 2 with ESPHome
@Jens5395
Copy link

Jens5395 commented Aug 9, 2022

Ha!!!! That did the trick, it compiled! Now to see if it fixes things at midnight ....
Thanks!

@stephenjamieson
Copy link

@clowrey sure, my point is that these issues are not related to the Emporia Vue.

@flaviut
Copy link
Author

flaviut commented Aug 9, 2022

Please keep all discussion here

Hey y'all, I've had my own gripes with Gist as a discussion platform as well.

Any further comments here will be deleted and recreated in https://github.com/emporia-vue-local/esphome/discussions. This will be the last comment on this gist.

@doronazl
Copy link

@rjchu, Yes mqtt is working quite well - I use FHEM (https://fhem.de/) hence mqtt is my choice of integration in this case.

Btw, unlike the emporia app which only calculates apparent power on the 16 CTs, this implementation is correctly measuring real power - thank you @flaviut !

sudo docker run --rm -v "${PWD}":/config -it esphome/esphome version
Version: 2022.6.0

here is my draft config - still making design decisions but working quite well already. the send times are temporarily set too low, I plan on every 10 secs for instant and every 5 mins for totals, etc

esphome:
  name: emporiavue2

external_components:
  - source: github://flaviut/esphome@emporia-vue-2022.4.0
    components: [ emporia_vue ]

esp32:
  board: esp32dev
  framework:
    type: esp-idf
    version: recommended

# Enable Home Assistant API
#api: {"password": "<ota password>"}
ota: {"password": "<xyz>"}

# Enable logging
logger:
  level: WARN

wifi:
  ssid: "myssid"
  password: "mypassword"

mqtt:
  broker: 1.2.3.4
  client_id: vue2
  username: myuser
  password: mypassword
  port: 1883
  topic_prefix: vue2
  discovery: false
  log_topic: null


i2c:
  sda: 21
  scl: 22
  scan: false
  frequency: 200kHz  # recommended range is 50-200kHz
  id: i2c_a

# these are called references in YAML. They allow you to reuse
# this configuration in each sensor, while only defining it once
.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
  - &invert
    # 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
    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.0229  # 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 A Voltage"
          internal: true
          id: phase_a_voltage
          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.022  # 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"
          internal: true
          id: phase_b_voltage
          filters: [*moving_avg, *pos]
      - id: phase_c  # Verify that this specific phase/leg is connected to correct input wire color on device listed below
        input: BLUE  # Vue device wire color
        calibration: 0.022193  # 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:
          internal: true
          name: "Phase C Voltage"
          id: phase_c_voltage
          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"
          internal: true
          id: phase_a_power
          device_class: power
          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"
          internal: true
          id: phase_b_power
          device_class: power
          filters: [*moving_avg, *pos]
      - phase_id: phase_c
        input: "C"  # Verify the CT going to this device input also matches the phase/leg
        power:
          name: "Phase C Power"
          internal: true
          id: phase_c_power
          device_class: power
          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
      - { phase_id: phase_b, input:  "1", power: { name: "Front Steps (Camera, Lights)",                            internal: true, id:  cir1, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_b, input:  "2", power: { name: "Kitchen Left (Fridge, Oven, Microwave)",                  internal: true, id:  cir2, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_b, input:  "3", power: { name: "Kitchen Right (Dishwasher, Coffee Machine)",              internal: true, id:  cir3, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_b, input:  "4", power: { name: "4 ??",                                                       internal: true, id:  cir4, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_c, input:  "5", power: { name: "Kitchen Stove",                                           internal: true, id:  cir5, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_b, input:  "6", power: { name: "Downstairs Right (Study, Hall, Bathroom, Guest, Server)", internal: true, id:  cir6, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_c, input:  "7", power: { name: "Downstairs Left (Living, Dining Room)",                   internal: true, id:  cir7, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_b, input:  "8", power: { name: "Upstairs (Master Bedroom)",                               internal: true, id:  cir8, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_a, input:  "9", power: { name: "Upstairs (incl. Attic lights, except Master Bedroom)",    internal: true, id:  cir9, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_c, input: "10", power: { name: "Cellar, Washing Room, Power Room (incl. Freezer)",        internal: true, id: cir10, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_a, input: "11", power: { name: "Doorbell",                                                internal: true, id: cir11, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_a, input: "12", power: { name: "Shutters",                                                internal: true, id: cir12, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_a, input: "13", power: { name: "Cellar (Dryer, Washer)",                                  internal: true, id: cir13, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_a, input: "14", power: { name: "Cellar Right, Workshop Play Room, Heating Room",          internal: true, id: cir14, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_a, input: "15", power: { name: "Outdoors Lights",                                         internal: true, id: cir15, filters: [ *moving_avg, *pos ] } }
      - { phase_id: phase_a, input: "16", power: { name: "Cellar Left, Washing Room (Plants Lights, Garage)",       internal: true, id: cir16, filters: [ *moving_avg, *pos ] } }





  - platform: template
    internal: true
    name: "Total Circuits Power"
    lambda: return 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: 1s
    id: total_power_circuits
    unit_of_measurement: "W"
  - platform: template
    internal: true
    name: "Total Power"
    lambda: return id(phase_a_power).state + id(phase_b_power).state + id(phase_c_power).state;
    update_interval: 1s
    id: total_power
    unit_of_measurement: "W"
  - platform: total_daily_energy
    name: "Total Daily Energy"
    internal: true
    power_id: total_power
    id: total_daily_power
    accuracy_decimals: 0
    min_save_interval: 10s
    unit_of_measurement: "W"
  - { power_id:  cir1, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir1 }
  - { power_id:  cir2, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir2 }
  - { power_id:  cir3, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir3 }
  - { power_id:  cir4, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir4 }
  - { power_id:  cir5, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir5 }
  - { power_id:  cir6, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir6 }
  - { power_id:  cir7, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir7 }
  - { power_id:  cir8, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir8 }
  - { power_id:  cir9, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir9 }
  - { power_id: cir10, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir10 }
  - { power_id: cir11, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir11 }
  - { power_id: cir12, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir12 }
  - { power_id: cir13, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir13 }
  - { power_id: cir14, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir14 }
  - { power_id: cir15, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir15 }
  - { power_id: cir16, platform: total_daily_energy, accuracy_decimals: 0, internal: true, id: total_daily_power_cir16 }

time:
  - platform: sntp
    id: my_time
    timezone: Europe/Berlin
    servers: 
      - 192.168.178.99
      - 0.pool.ntp.org
      - 1.pool.ntp.org
    on_time_sync:
      then:
        - logger.log: "Synchronized system clock"
    on_time:
      - seconds: /10
        then:
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir01_power");
              payload: !lambda |-
                return to_string(id(cir1).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir02_power");
              payload: !lambda |-
                return to_string(id(cir2).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir03_power");
              payload: !lambda |-
                return to_string(id(cir3).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir04_power");
              payload: !lambda |-
                return to_string(id(cir4).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir05_power");
              payload: !lambda |-
                return to_string(id(cir5).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir06_power");
              payload: !lambda |-
                return to_string(id(cir6).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir07_power");
              payload: !lambda |-
                return to_string(id(cir7).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir08_power");
              payload: !lambda |-
                return to_string(id(cir8).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir09_power");
              payload: !lambda |-
                return to_string(id(cir9).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir10_power");
              payload: !lambda |-
                return to_string(id(cir10).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir11_power");
              payload: !lambda |-
                return to_string(id(cir11).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir12_power");
              payload: !lambda |-
                return to_string(id(cir12).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir13_power");
              payload: !lambda |-
                return to_string(id(cir13).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir14_power");
              payload: !lambda |-
                return to_string(id(cir14).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir15_power");
              payload: !lambda |-
                return to_string(id(cir15).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir16_power");
              payload: !lambda |-
                return to_string(id(cir16).state);
          - mqtt.publish:
              topic: "vue2/circuits/total_power_circuits"
              payload: !lambda |-
                return to_string(id(total_power_circuits).state);
          - mqtt.publish:
              topic: "vue2/mains/total/total_power"
              payload: !lambda |-
                return to_string(id(total_power).state);

      - seconds: /10
        then:
          - mqtt.publish:
              topic: "vue2/mains/phase_a_voltage"
              payload: !lambda |-
                return to_string(id(phase_a_voltage).state);
          - mqtt.publish:
              topic: "vue2/mains/phase_b_voltage"
              payload: !lambda |-
                return to_string(id(phase_b_voltage).state);
          - mqtt.publish:
              topic: "vue2/mains/phase_c_voltage"
              payload: !lambda |-
                return to_string(id(phase_c_voltage).state);
          - mqtt.publish:
              topic: "vue2/mains/phase_a_power"
              payload: !lambda |-
                return to_string(id(phase_a_power).state);
          - mqtt.publish:
              topic: "vue2/mains/phase_b_power"
              payload: !lambda |-
                return to_string(id(phase_b_power).state);
          - mqtt.publish:
              topic: "vue2/mains/phase_c_power"
              payload: !lambda |-
                return to_string(id(phase_c_power).state);

      - seconds: 0
        minutes: /5
        then:
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir01_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir1).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir02_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir2).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir03_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir3).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir04_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir4).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir05_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir5).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir06_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir6).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir07_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir7).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir08_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir8).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir09_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir9).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir10_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir10).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir11_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir11).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir12_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir12).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir13_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir13).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir14_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir14).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir15_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir15).state);
          - mqtt.publish:
              topic: !lambda |-
                return std::string("vue2/circuits/cir16_total_daily_power");
              payload: !lambda |-
                return to_string(id(total_daily_power_cir16).state);


      - seconds: 0
        minutes: /5
        then:
          - mqtt.publish:
              topic: "vue2/mains/total/total_daily_power"
              payload: !lambda |-
                return to_string(id(total_daily_power).state);

      - seconds: 0
        minutes: /10
        then:
          - mqtt.publish:
              topic: "vue2/circuits/cir01_name"
              payload: !lambda |-
                return id(cir1).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir02_name"
              payload: !lambda |-
                return id(cir2).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir03_name"
              payload: !lambda |-
                return id(cir3).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir04_name"
              payload: !lambda |-
                return id(cir4).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir05_name"
              payload: !lambda |-
                return id(cir5).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir06_name"
              payload: !lambda |-
                return id(cir6).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir07_name"
              payload: !lambda |-
                return id(cir7).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir08_name"
              payload: !lambda |-
                return id(cir8).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir09_name"
              payload: !lambda |-
                return id(cir9).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir10_name"
              payload: !lambda |-
                return id(cir10).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir11_name"
              payload: !lambda |-
                return id(cir11).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir12_name"
              payload: !lambda |-
                return id(cir12).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir13_name"
              payload: !lambda |-
                return id(cir13).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir14_name"
              payload: !lambda |-
                return id(cir14).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir15_name"
              payload: !lambda |-
                return id(cir15).get_name();
          - mqtt.publish:
              topic: "vue2/circuits/cir16_name"
              payload: !lambda |-
                return id(cir16).get_name();

Hi. How is your setup working with 3 phase?
Just bought a couple of emporia and noticed the yaml is setup for 2 phase, not 3.

@cangaroo82
Copy link

Good morning
VUE 2 modified with ESPHOME.
same as VUE2.yaml. on home assistant virtual machine works well.
on ihost modified Home assistant returns:

`- Configuring incomplete, errors occurred!
See also "/data/build/vue2-1/.pioenvs/vue2-1/CMakeFiles/CMakeOutput.log".
See also "/data/build/vue2-1/.pioenvs/vue2-1/CMakeFiles/CMakeError.log".

fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
CMake Error at /data/cache/platformio/packages/tool-cmake/share/cmake-3.16/Modules/CMakeTestCCompiler.cmake:60 (message):
The C compiler

"/data/cache/platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-gcc"

is not able to compile a simple test program.

It fails with the following output:

Change Dir: /data/build/vue2-1/.pioenvs/vue2-1/CMakeFiles/CMakeTmp

Run Build Command(s):/data/cache/platformio/packages/tool-ninja/ninja cmTC_22cff && [1/2] Building C object CMakeFiles/cmTC_22cff.dir/testCCompiler.c.obj
[2/2] Linking C executable cmTC_22cff
FAILED: cmTC_22cff 
: && /data/cache/platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-gcc -mlongcalls -Wno-frame-address   CMakeFiles/cmTC_22cff.dir/testCCompiler.c.obj  -o cmTC_22cff   && :
/data/cache/platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: /data/cache/platformio/packages/toolchain-xtensa-esp32/bin/../libexec/gcc/xtensa-esp32-elf/8.4.0/liblto_plugin.so: error loading plugin: /data/cache/platformio/packages/toolchain-xtensa-esp32/bin/../libexec/gcc/xtensa-esp32-elf/8.4.0/liblto_plugin.so: cannot open shared object file: No such file or directory
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

CMake will not be able to correctly generate this project.
Call Stack (most recent call first):
/data/cache/platformio/packages/framework-espidf/tools/cmake/project.cmake:298 (__project)
CMakeLists.txt:3 (project)

========================= [FAILED] Took 26.35 seconds =========================`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment