Emporia Vue Utility Connect

Very nice investigation! Sucks that the MGM firmware update put it in a bad state… but good luck!

Emporia refunded me for the purchase price. They were going to send a new one but they don’t do that for Canadian customers, So a refund instead. Think I’ll buy the kingkony AliExpress 16xCT clamp board instead of replacing the vue connect.

Haven’t had time to try and fix the device I have. The “run” and “ebl info” options seem interesting, deff want to take a look at that if I can.

It never occurred to me to check AliExpress for a multi-channel CT board, but I could only find this 30 channel model. Can you link the 16 channel one?

Same price basically, it’s confusing to decipher what they’re selling lol

Kincony KC868-M16 ESP32 16 Channel CT Clamp Energy Monitor no APP control For esphome home assistant
https://a.aliexpress.com/_mtbNM8a

[definitely off topic]

I see it uses the audio jack type of plug and would not recommend it. I have the Emporia Vue 2 (as well as the Utility Connect haha) and I’ve had to open up my panel multiple times due to finding out one of the sensors got loose. It’s just a terrible connector for reliability and is probably why the Vue 3 switched to those “Phoenix” plugs.

At least Kincony’s 30 channel model also uses a non-audio-jack connection and should be very reliable. I would go with either the Vue 3 or Kincony’s 30 channel model… both are supported by ESPHome, though the Vue 3 looks like it’s much more of a pain to flash ESPHome onto.

[/definitely off topic]

Thats some good advice, I was looking at the vue gen 3 and saw it has an ethernet port as well, no point in it being PoE as mains power is right there. Im guessing a person could also spend the time to desolder and resolder terminal blocks or similar?

A happy surprise on my end, they refunded me and also sent a unit out, when I let them know, they said to just keep the old and new units, very nice!

So, the upgrade process failed again but this time, I didnt delete the device from the emporia app, power cycled it and I just left it plugged in. It took a bit, but the esp32 did push a FW update to the MGM chip. My thoughts are that the esp32 downloads the mgm firmware and stores it, then randomly updates it.

I now have a working updated device and the old device stuck in bootloader mode. I havent had time to play with the old device and see if I can get any useful info out of it or fix it. Would be nice if I could get an unencrypted firmware dump, but im not holding my breath.

I now have a spare esp32-WROOM and MGM111 board I can play with at least. When I do try and play around with the old device, I will update this thread with the outcome and any juicy details.

Not sure what’s wrong, but I am not able to connect to the ESP32 chip. I am pretty certain that I selected the right voltage (3.3v) for my USB-Serial adapter, and connected the wires in the correct way. I also tried both plugging directly to USB port for power, as well as connect the 5v power from the USB-Serial adapter to the 5v pin. It always complains about some unexpected header value. I can see the stick entered a different mode when trying to read the firmware (all three lights turn solid green). Any suggestion/help would be appreciated. I am using a Apple chip MacBook Pro if that matters.

Looks like I need the --before usb_reset argument to esptool.

Hi everyone, original author here. Unfortunately, my current employer prohibits me from working on open source projects. If someone want’s to take over ownership of this I’ll edit the top post URLs to point to their repo.

2 Likes

When I get a bit of time, my plan is to convert it into the newer esphome add-on format. Currently, they dont want to offer ANY support if it is a deprecated external component like it is in its current form.

@monkeyst Has been reverse engineering the v7+ protocol, I am just about to go down that rabbit hole myself as I think I am getting higher than actual watts reported but unsure.

Anyways, thank you for your work @jrouvier , it is greatly appreciated!

@monkeyst I got a new emporia vue connect and it has been upgraded to v 8 MGM firmware, so I am using your .h file. I am seeing a watts reading that seems to be higher than what the original emporia app did and I also see lots of extra data coming through the debug logs. I also see that some meter read messages arent caught because the first char isn’t a ‘$’ → [16:40:34][D][esp-idf:000]: E (22338618) Vue: 0x3ffb2bc4 34 0d 0a 24 01 72 2c |4..$.r,|

I havent had a chance to look at the .h file and go througyh it yet. I did have a different meter div value than the original repo code had and had to write some custom code to get the correct values. I am, assuming my meter div is throwing the reported watts off.

Ill try and take some time this week or this coming weekend to go over the v7 code and see if I can add anything. I appreciate your advice, work and input!

Thanks!

Some of the extra output I am seeing in debug logs (including missed meter reading msg)

16:40:08][D][Vue:319]: Parsing V7+ Payload
[16:40:08][I][Vue:079]: Watts = 1770.00
[16:40:08][D][sensor:094]: 'House Watts': Sending state 1770.00000 W with 2 decimals of accuracy
[16:40:08][D][sensor:094]: 'House Watts': Sending state 1770.00000 W with 2 decimals of accuracy
[16:40:08][D][sensor:094]: '': Sending state 123094352.00000  with 0 decimals of accuracy
[16:40:08][D][sensor:094]: '': Sending state 0.00000  with 0 decimals of accuracy
[16:40:08][I][Vue:119]: Consumed kWh = 123094.352
[16:40:08][D][sensor:094]: 'House Consumed kWh': Sending state 123094.35156 kWh with 3 decimals of accuracy
[16:40:08][D][sensor:094]: 'House Consumed kWh': Sending state 123094.35156 kWh with 3 decimals of accuracy
[16:40:08][D][sensor:094]: '': Sending state 0.00000  with 0 decimals of accuracy
[16:40:08][D][sensor:094]: '': Sending state 123094352.00000  with 0 decimals of accuracy
[16:40:08][I][Vue:101]: kWh = 123094.352
[16:40:08][D][sensor:094]: 'House Net kWh': Sending state 123094.35156 kWh with 3 decimals of accuracy
[16:40:08][D][sensor:094]: 'House Net kWh': Sending state 123094.35156 kWh with 3 decimals of accuracy
[16:40:08][W][component:232]: Component <unknown> took a long time for an operation (109 ms).
[16:40:08][W][component:233]: Components should block for at most 30 ms.
[16:40:10][D][sensor:094]: 'Wifi signal': Sending state -67.00000 dBm with 0 decimals of accuracy
[16:40:20][D][sensor:094]: 'Wifi signal': Sending state -68.00000 dBm with 0 decimals of accuracy
[16:40:30][D][sensor:094]: 'Wifi signal': Sending state -68.00000 dBm with 0 decimals of accuracy
[16:40:34][D][Vue:613]: Sending request for meter reading
[16:40:34][E][Vue:203]: Invalid input at position 1: 0x53
[16:40:34][E][Vue:182]: Skipped input:
[16:40:34][D][esp-idf:000]: E (22338190) Vue: 0x3ffb2bc4   53 65 49 68 64                                    |SeIhd|

[16:40:34][E][Vue:203]: Invalid input at position 1: 0xd
[16:40:34][E][Vue:182]: Skipped input:
[16:40:34][D][esp-idf:000]: E (22338261) Vue: 0x3ffb2bc4   0d 0a 54 30 30 30 30 30  30 30 30 3a 52 58 20 6c  |..T00000000:RX l|

[16:40:34][D][esp-idf:000]: E (22338274) Vue: 0x3ffb2bd4   65 6e 20 34 34 2c 20 65  70 20 30 31 2c 20 63 6c  |en 44, ep 01, cl|

[16:40:34][D][esp-idf:000]: E (22338287) Vue: 0x3ffb2be4   75 73 20 30 78 30 37 30  32 20 28 53 69 6d 70     |us 0x0702 (Simp|

[16:40:34][E][Vue:203]: Invalid input at position 1: 0x65
[16:40:34][E][Vue:182]: Skipped input:
[16:40:34][D][esp-idf:000]: E (22338323) Vue: 0x3ffb2bc4   65 20 4d 65 74 65 72 69  6e 67 29 20 46 43 20 31  |e Metering) FC 1|

[16:40:34][D][esp-idf:000]: E (22338336) Vue: 0x3ffb2bd4   38 20 73 65 71 20 33 36  20 63 6d 64 20 30 31 20  |8 seq 36 cmd 01 |

[16:40:34][D][esp-idf:000]: E (22338349) Vue: 0x3ffb2be4   70 61 79 6c 6f 61 64 5b  30 30 20 30 30 20 30     |payload[00 00 0|

[16:40:34][E][Vue:203]: Invalid input at position 1: 0x20
[16:40:34][E][Vue:182]: Skipped input:
[16:40:34][D][esp-idf:000]: E (22338386) Vue: 0x3ffb2bc4   20 32 35 20 36 30 20 34  35 20 35 36 20 30 37 20  | 25 60 45 56 07 |

[16:40:34][D][esp-idf:000]: E (22338399) Vue: 0x3ffb2bd4   30 30 20 30 30 20 30 31  20 30 30 20 30 30 20 32  |00 00 01 00 00 2|

[16:40:34][D][esp-idf:000]: E (22338412) Vue: 0x3ffb2be4   35 20 30 30 20 30 30 20  30 30 20 30 30 20 30     |5 00 00 00 00 0|

[16:40:34][E][Vue:203]: Invalid input at position 1: 0x20
[16:40:34][E][Vue:182]: Skipped input:
[16:40:34][D][esp-idf:000]: E (22338449) Vue: 0x3ffb2bc4   20 30 30 20 30 31 20 30  33 20 30 30 20 32 32 20  | 00 01 03 00 22 |

[16:40:34][D][esp-idf:000]: E (22338462) Vue: 0x3ffb2bd4   30 33 20 30 30 20 30 30  20 30 32 20 30 33 20 30  |03 00 00 02 03 0|

[16:40:34][D][esp-idf:000]: E (22338475) Vue: 0x3ffb2be4   30 20 32 32 20 38 38 20  31 33 20 30 30 20 30     |0 22 88 13 00 0|

[16:40:34][E][Vue:203]: Invalid input at position 1: 0x20
[16:40:34][E][Vue:182]: Skipped input:
[16:40:34][D][esp-idf:000]: E (22338511) Vue: 0x3ffb2bc4   20 30 34 20 30 30 20 32  41 20 43 44 20 30 36 20  | 04 00 2A CD 06 |

[16:40:34][D][esp-idf:000]: E (22338524) Vue: 0x3ffb2bd4   30 30 20 5d 0d 0a 2d 2d  20 47 6f 74 20 4d 65 74  |00 ]..-- Got Met|

[16:40:34][D][esp-idf:000]: E (22338536) Vue: 0x3ffb2be4   65 72 20 52 73 70 2c 20  6c 65 6e 3d 34 34 2c     |er Rsp, len=44,|

[16:40:34][E][Vue:203]: Invalid input at position 1: 0x35
[16:40:34][E][Vue:182]: Skipped input:
[16:40:34][D][esp-idf:000]: E (22338571) Vue: 0x3ffb2bc4   35 34 20 3d 20 35 34 3f  0d 0a 73 4d 52 42 20 6c  |54 = 54?..sMRB l|

[16:40:34][D][esp-idf:000]: E (22338584) Vue: 0x3ffb2bd4   65 6e 20                                          |en |

[16:40:34][E][Vue:203]: Invalid input at position 1: 0x34
[16:40:34][E][Vue:182]: Skipped input:
[16:40:34][D][esp-idf:000]: E (22338618) Vue: 0x3ffb2bc4   34 0d 0a 24 01 72 2c                              |4..$.r,|

[16:40:34][E][Vue:203]: Invalid input at position 1: 0x36
[16:40:34][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x1
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x25
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x60
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x45
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x56
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x7
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x1
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x25
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x1
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x3
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x22
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x3
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x2
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x3
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x22
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x88
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x13
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x4
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x2a
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0xcd
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x6
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0x0
[16:40:35][E][Vue:182]: Skipped input:
[16:40:35][E][Vue:203]: Invalid input at position 1: 0xd
[16:40:35][E][Vue:182]: Skipped input:

@visualage Did the “--before usb_reset” unblock you?

@jrouvier Thanks a lot for doing the initial heavy lifting! I can take ownership (for now at least) at GitHub - nekorevend/esphome-emporia-vue-utility: Alternative ESPHome firmware for the Emporia Vue Utility Connect

@baudneo Not sure about the your “Invalid input at position 1” issue, but regarding the watt reading looking too high, do you have any clue about how much higher it is than it should be? Something that comes to mind is that the V2 firmware had a MeterDiv field where the watt field (and watt-hour?) had to be divided by MeterDiv to get the correct wattage. Oops just read your message more carefully.

If MeterDiv is still a thing in V7+, then for me it is a 1. According to what I had documented, MeterDiv might be at byte 2, 13, 23, or 27 since those have a value of 1 for me. Are any of those bytes possibly not 1 for you?

Ive been kind of poking at it,

            Meter Response Bytes   0 to   3: 18 8a 01 00
            Meter Response Bytes   4 to   7: 00 00 25 7e
            Meter Response Bytes   8 to  11: 83 56 07 00
            Meter Response Bytes  12 to  15: 00 01 00 00
            Meter Response Bytes  16 to  19: 25 00 00 00
            Meter Response Bytes  20 to  23: 00 00 00 01
            Meter Response Bytes  24 to  27: 03 00 22 03
            Meter Response Bytes  28 to  31: 00 00 02 03
            Meter Response Bytes  32 to  35: 00 22 88 13
            Meter Response Bytes  36 to  39: 00 00 04 00
            Meter Response Bytes  40 to  43: 2a 23 05 00

Index 27 is 0x3, So I thought that may be it (my meter div was 3 in v2 struct as well), im going to flash back and forth to get some watt readings to compare. meter div 3 output seems to be lower than expected watts, so I am unsure.

The MGM chip is sending weird log messages, it seems they have some logging component spitting some logs out over that UART as well. Here is some of the message that keeps being repeated.

0x45 0x4d 0x42 0x45 0x52 0x5f 0x4e 0x45 0x54 0x57 0x4f 0x52 0x4b 0x5f 0x44 0x4f 0x57 0x4e 0x0d
E M B E R _ N E T W O R K _ D O W N

0x0a 0x53 0x65 0x74 0x74 0x69 0x6e 0x67 0x20 0x74 0x72 0x75 0x73 0x74 0x20 0x63 0x65 0x6e 0x74 0x65 0x72 0x20 0x6b 0x65 0x65 0x70 0x61 0x6c 0x69 0x76 0x65 0x20 0x69 0x6e 0x61 0x63 0x74 0x69 0x76 0x65 0x2e 0x0d
Setting trust center keepalive inactive.

0x0a 0x53 0x65 0x74 0x74 0x69 0x6e 0x67 0x20 0x74 0x72 0x75 0x73 0x74 0x20 0x63 0x65 0x6e 0x74 0x65 0x72 0x20 0x6b 0x65 0x65 0x70 0x61 0x6c 0x69 0x76 0x65 0x20 0x69 0x6e 0x61 0x63 0x74 0x69 0x76 0x65 0x2e 0x0d
Setting trust center keepalive inactive.

0x0a 0x3d 0x3d 0x20 0x53 0x54 0x41 0x43 0x4b 0x20 0x53 0x54 0x41 0x54 0x55 0x53 0x20 0x30 0x78 0x39 0x31 0x20 0x3d 0x3d 0x0d
== STACK STATUS 0x91 ==

0x0a 0x45 0x4d 0x42 0x45 0x52 0x5f 0x4e 0x45 0x54 0x57 0x4f 0x52 0x4b 0x5f 0x55 0x50 0x20 0x30 0x78 0x36 0x30 0x33 0x32 0x0d
EMBER_NETWORK_UP 0x6032

The logging is drowning the meter readings out as $ is not the first char. I am trying to implement something that will adjust for that.

It would be great if we could get this code switched over to the external component format, that way we can get support from the esphome team. ATM they refuse to help with any issues because this uses the deprecated includes component.

I had an issue upgrading to newer versions of esphome and needed to downgrade to get this code working again. I am stuck on esphome 2024.5.4, unsure if others are experiencing the same.

Yes. Adding --before usb_reset unblocked me.

At least for mine, the display on the actual utility meter periodically shows the current watt usage. Is that not the case for yours? I imagine you could have the ESPHome firmware installed and just stand at your meter and compare it on your phone.

Regarding the log outputs, have you identified a consistent header or byte that it always sends before a log? Could hopefully make the code ignore messages that start with that header. The first line of your example messages doesn’t have the 0x0a, but the rest do… Ideally the header also provides the expected length to read :crossed_fingers:

I did briefly look at converting to the external component format some months ago but dropped the idea at the time due to no familiarity with that format (and admittedly no familiarity with the deprecated format. I only added some C++ to jrouvier’s code.) Just curious, what problems were you hoping the ESPHome team would help with? Is it related to you saying you’re stuck on 2024.5.4?

It seems to me it’s roughly around half of the reported value - ish.

My reason for wanting to switch to the external component for esphome support is, I’ve had some minor problems when upgrading. The ::time issue I put a PR in for and then this newer issue of not being able to upgrade to x.5.

Every issue I’ve opened with esphome is closed immediately and I am told because this code is using deprecated custom_component, esphome devs won’t even look at the issue.

I am the main/only ML dev for ZoneMinder so I don’t have a lot of spare time, but I will try and get this code switched over to an external component format when I get some time.

I tried comparing your meter response values to mine and the differing bytes are at 27, 34, 35, but nothing obvious is jumping out at me about them to suggest “divide by 2”.

(Also noticed my documentation was missing two static bytes at 16 and 40 so I’ve pushed an update for that, not that they help here though)

As far as I can tell, 2024.5.4 is the latest version (which works for me). I guess you’re on some sort of beta release track?

If a breakage is impending then maybe I’ll revisit trying the external component conversion as well. :face_with_monocle:
EDIT: I’ve created a branch with stubbed external component stuff. Will look at it more later.

@baudneo Upon reading more of the V2 documentation, I realize that EnergyCostUnit probably also exists at bytes 34 and 35 in V7. For me the value equates to 1000 since I am billed per kWh. I see for your 0x8813 → 0x1388 → 5000, could you confirm if you are billed for every 5 kWh? Thanks!

I am billed per kWh on my energy bill. I don’t think it is exactly /2, but def in that area.

I misspoke, I’m stuck on 2024.3.2, so I think it was 2024.4.x maybe that has issues. When I upgraded, it was only the vue utility that didn’t work any longer. Downgrading back to 2024.3.2 gets the code working again. Esphome guys told me to kick rocks and get the maintainer to upgrade to an external component before they will even consider looking into the issue.

I’ve reached a minimally viable #WorksOnMyMachine conversion to external component at this branch the main branch, leaving the original implementation as unmodified as possible.

It is certainly not idiomatic to external components and I have no idea how update() is actually supposed to work. My understanding is the system should call update() at the stated update interval (30s in the sensor.py or 15s in my yaml override), but instead it calls incessantly The implementation skips using the update() function for now. (solved :slight_smile: needed to call the parent set_update_interval())

Anyway, these are the important parts of my yaml:

external_components:
  source:
    type: git
    url: https://github.com/nekorevend/esphome-emporia-vue-utility

substitutions:
  name: Meter

uart:
    id: emporia_uart
    rx_pin: GPIO21
    tx_pin: GPIO22
    baud_rate: 115200

sensor:
  - platform: emporia_vue_utility
    uart_id: emporia_uart
    update_interval: 15s
    power:
      name: "${name} Watts"
    power_export:
      name: "${name} Watts Returned"
    power_import:
      name: "${name} Watts Consumed"
    energy:
      name: "${name} Wh Net"
    energy_export:
      name: "${name} Wh Returned"
    energy_import:
      name: "${name} Wh Consumed"

I’ve listed out all 6 sensors you can choose from, but you probably don’t need all of them.

If you try it out, I would give the name some value like Test so potentially incorrect values don’t infect your sensor history.

1 Like