[RELEASE] Emporia Vue driver for Hubitat

@ke7lvb: Thank you for doing this. There is much in the above string I don't understand, but I surmise
a. You figured out how to get Emporia Vue data into Hubitat, but the authentication expires every hour.
b. You wrote an external program on something other than HE to overcome this, which requires more hardware and knowledge than just Hubitat.

I share your hope that someone can figure this out. The lack of integration with Hubitat is the one thing keeping me from investing in the Emporia Vue hardware and install.

This looks really awesome, and I would love to be able to use your driver, but not sure where to start with using pyemvue and maker api to get and pass the token to HE.

Would it be possible for someone to post a step-by-step "for dummies" wiki on how to set this up on a PC or Pi?

1 Like

This is what I did on Ubuntu. You would need to run the Debian equivalents for Pi.

Intall PyEmVue

sudo pip install pyemvue

Create a directory for the authentication script

mkdir ~/pyemvue

Create a .py file in a text editor

nano ~/pyemvue/login.py

Copy the contents below into login.py - replace items in angle brackets <> as needed (remove the angle brackets)

import pyemvue
import json
import requests

vue = pyemvue.PyEmVue()
#create the auth token and store it in keys.json
auth = vue.login(username='<emporia_username>', password='<emporia_password>', token_storage_file='keys.json')

#retrieve the auth_token from keys.json
with open('keys.json')as f:
        data = json.load(f)

#pass token to hubitat via Maker API
token_id = data['id_token']
requests.get("http://<url_or_ip_to_hubitat>/apps/api/<maker_api_id>/devices/<emporia_device_id>/authToken/{}?access_token=<maker_api_access_token>".format(token_id))

Test the script and confirm the token updated in Hubitat

python3 ~/pyemvue/login.py

Create a cron job with whatever user profile should run the script

sudo crontab -u <username> -e

add to cron file

0 * * * * python3 ~/pyemvue/login.py
2 Likes

Thank you very much. I'm sure I'll get stuck somewhere, but very helpful to get started.

Thank you again for your help.

I was able to install pyemvue and run login.py on an AWS EC2 ubuntu instance.

I can get python code to run, and if I just print(token_id) and copy paste to "update authtoken" button in the Emporia device, it works and updates all my circuits as sub-devices.

So, I think I can get this working, but need to find the best way to communicate from EC2 instance to maker API (requests.get("http://<url_or_ip_to_hubitat>...))

If I enter the cloud URL link from maker API with https (https://cloud.hubitat.com/api/ff3xx...xxxx), python script executes without error, but does not update emporia device on hubitat. Is https a problem for request.get command, or some other reason this isn't working?

Any suggestion on easiest way to set up communication between EC2 instance and hubitat without opening up major network security issue?

update:

requests.get() syntax seems to work and https in requests.get works when I redirect to a google forms input (thanks to LibraSun's, HOW-TO Store data in Google Sheets directly from RM5),

requests.get("https://docs.google.com/forms/d/e/1FAxxxxxxxxxxxxxxxxxxmQ/formResponse?entry.87xxxxxx42={}".format(token_id))

works, with key posted to google sheets. Cron job also working with new post to sheet every hour.

When I print() everything in the request.get(), it also seems to output key correctly in the expected way for "secondary value" of the Maker API send device comand syntax:

https://cloud.hubitat.com/api/ff3xxxxxx...xxxxxxxa29/apps/api/134/devices/104/authToken/eyJraWQixxxxxx...really_long_key...xxxxxeiQGQ?access_token=be8xxxx5-2xx2-4xx8-9xx8-exxxxxxxa

(134) is number from maker API sample requests, and (104) is device number of emporia from get all devices command.

Hmm, just noticed syntax is different for local vs. cloud versions of maker api syntax. Local version has .../apps/api/ before <maker_api_id>, but cloud version just has .../apps/.

Could it be so simple? will report result soon...

2 Likes

Yes. It works. Key uploaded to Emporia device successfully.

My fault for assuming syntax of local/cloud versions matched exactly after the /api phrase. RTFM!

Thanks again for the driver and the detailed how-to on the pyemvue part!

1 Like

Still working after hours with 1 min scale + 1 min refresh.

I see no need to log/graph, as I'm happy with Emporia app and website for these, but I am anxious to start making rules based on different circuits power/duration triggers!

Can't thank you enough ke7lvb!

Glad to hear you were able to get it working!

Yes, thanks again. Really appreciate the driver. It is awesome.

Was thinking about how to do this locally, but since need to interact with Emporia web anyway, this seemed like a good opportunity to try to do something with an AWS free tier cloud instance. Just need to mind the syntax difference between local and cloud maker API call.

Still working!

Seemed pretty wasteful to keep my EC2 instance running 24/7 just to execute every hour, and assuming it's still needed after my year of free tier runs out, don't care to pay $4/month estimated cost.

There are probably other ways to cut the cost, but I managed to get login.py running as an AWS Lamda function with a couple modifications (uses EventBridge to trigger every hour):

import pyemvue
import json
import requests
import os

def lambda_handler(event, context):
os.chdir('/tmp')
vue = pyemvue.PyEmVue()
#create the auth token and store it in keys.json
auth = vue.login(username='xxxxxx', password='xxxxxx', token_storage_file='keys.json')
#retrieve the auth_token from keys.json
with open('keys.json')as f:
data = json.load(f)
#pass token to hubitat via Maker API
token_id = data['id_token']
requests.get("https://cloud.hubitat.com/api/xxxxxxxxxxxxx/apps/###/devices/###/authToken/{}?access_token=yyyyyyyyyyyyyyyy".format(token_id))

Used this tutorial to install pyemview as a layer:

Should be very cheap, to keep this running after free tier runs out, but haven't estimated cost yet.

Sorry to keep spamming your thread with this, in case no one else finds it useful. I do hope someone figures out how to do this in Groovy, or with WebCore/Rules.

1 Like

Hi @ke7lvb.

Thanks again for awesome driver. It's been humming along fine for better part of the month, and enjoying benefits of triggering actions when washer/dryer done, TV turned on/off, and still finding more ways it's useful.

Just one quick question. After updating HE firmware today, I noticed lastUpdate date/time seems to be reporting GMT instead of local PST time. Everything else seems to be working fine. Did I just not notice this difference before, or is this unusual?

I am seeing the same behavior, only after the update. I'll look into it this weekend.

I believe the timestamp from Emporia is in UTC. I use the native Hubitat function to convert it to your hub's timezone. If that's the case, it may be a bug on the Hubitat side.

This is not a problem at all for me, just information - glad it's not just me. If anything I can do to help troubleshoot, I'm happy to help.

I have had some past confusion with how HE handles GMT vs local time in Webcore. I hope whatever happened in latest ver. didn't goof up my automations in webcore too...

I updated the driver to get the current timestamp in local time. It does not rely on the native Hubitat functions to convert the timezone anymore. I was not able to figure out why the conversion suddenly stopped working.

I asked ChatGPT to create a groovy app for this. I reckon it's missing some stuff it needs to make it work, but figure it's worth taking a stab in the dark and trying it!

translate pyemvue from python to groovy app code for hubitat.

Here is an example of translating a Python Pyemvue code to equivalent Groovy code for a Hubitat app:

Python:

pythonCopy code

import requests

def update_emporia_api_token(new_api_token):
    headers = {
        'Authorization': f"Token {new_api_token}"
    }
    response = requests.post("https://api.emporia-energy.com/v2/accounts/me", headers=headers)
    if response.status_code == 200:
        print("Emporia API token updated successfully")
    else:
        print("Failed to update Emporia API token")

Groovy:

pythonCopy code

import groovyx.net.http.RESTClient

def updateEmporiaApiToken(newApiToken) {
    def client = new RESTClient("https://api.emporia-energy.com/v2")
    def headers = [
        Authorization: "Token ${newApiToken}"
    ]
    def response = client.post(path: "/accounts/me", headers: headers)
    if (response.status == 200) {
        log.debug "Emporia API token updated successfully"
    } else {
        log.debug "Failed to update Emporia API token"
    }
}

This code defines a function updateEmporiaApiToken that takes in a parameter newApiToken and uses the RESTClient class from the Groovy HTTP Builder library to make a POST request to the Emporia API with the updated token value in the Authorization header. The function logs a message indicating whether the API token was updated successfully or not based on the HTTP response status code.

Create groovy app for Hubitat using login.py and pyemvue python code described here: [RELEASE] Emporia Vue driver for Hubitat - #9 by calinatl

Here is an example of a Groovy app for Hubitat using the login.py and pyemvue.py code described in the link:

javaCopy code

import groovyx.net.http.RESTClient

def updateEmporiaApiToken(newApiToken) {
    def client = new RESTClient("https://api.emporia-energy.com/v2")
    def headers = [
        Authorization: "Token ${newApiToken}"
    ]
    def response = client.post(path: "/accounts/me", headers: headers)
    if (response.status == 200) {
        log.debug "Emporia API token updated successfully"
    } else {
        log.debug "Failed to update Emporia API token"
    }
}

def login(username, password) {
    def client = new RESTClient("https://api.emporia-energy.com/v2/accounts/login")
    def response = client.post(
        contentType: "application/json",
        body: [
            username: username,
            password: password
        ]
    )
    if (response.status == 200) {
        def json = response.json
        return json.token
    } else {
        log.debug "Login failed with status ${response.status}"
        return null
    }
}

This code defines two functions, updateEmporiaApiToken and login. The updateEmporiaApiToken function is the same as the one in the previous answer and updates the Emporia API token with a new value passed as a parameter. The login function takes in a username and a password and uses the RESTClient class from the Groovy HTTP Builder library to make a POST request to the Emporia login API with the provided credentials. The function returns the API token obtained from the response if the login is successful, or null if the login fails. The function also logs a message indicating the status of the login attempt.

3 Likes

Brilliant! If that turns out to work….. well, lets just say that would be a huge step for the community.

1 Like

Not holding my breath, and I lack the skills in this to troubleshoot much if it doesn't work.

Just a lark.

2 Likes

So @nrus2222, any update? Did it work?

1 Like

Sorry, had other priorities bump this hobby, and haven't tested yet. Been long enough I'd probably ask ChatGPT again and see if different answer before testing. 99% sure it won't work without some tweaks by someone who actually knows what they're doing, but still willing to give it a go.

Pyremvue on AWS Lamda function has been bullet-proof and still zero cost, so has not a high priority for me.

2 Likes