Aeon Home Energy Meter & Xively logging Integration

Ed, are you able to see the graphs on Xively? Should look something like this:

Yes, sorry to say it was me thinking I had the right ID in the APP. I copied and pasted into an emulator that I use on the computer to manage ST (SMILE) at least untl they have a web app for us.

Anyway, I missed by one digit… (hitting self over head). Sorry to bother you. Dialysis day today, so I guess I am just not as certain (SMILE)

I did note that I have 4 channels 2 of which are Null, and no Cost yet. I can work with the app and figure that out. Do you mind if I make the modifications and repost here for those who have v1 of AEON HEM?

I don’t think the HEMv1 provides the Volts or Amps, just kWh and current Watts.

Here is another version, this time it only logs Aeon HEMv2 volts and watts, along with the thermostat state (0=idle, 1=cooling, 2=heating). This way it’s easier to plot the relationship between thermostat states and energy consumption. I’ll add this to Github as well: https://github.com/d6nab/Smartthings-Aeon-HEMv2-Thermostat-log-to-Xively

  /**
 *  Aeon HEM - Xively
 *
 *  Copyright 2014 Dan Anghelescu
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License  for 
 *  the specific language governing permissions and limitations under the License.
 *
 *
 *
 *  Genesys: Based off of Aeon Smart Meter Code sample provided by SmartThings (2013-05-30), Aeon Home Energy Meter v2 by Barry A. Burke, 
 *  and Xively Logger by Patrick Stuart  Built on US model may also work on international versions (currently reports total values only)
 */


// Automatically generated. Make future change here.
definition (
                name: "Aeon HEM + Temp - Xively",
                namespace: "smartthings",
                author: "Dan Anghelescu",
                description: "Aeon HEM + Temp - Xively",
                category: "My Apps",
                iconUrl: "https://graph.api.smartthings.com/api/devices/icons/st.Electronics.electronics13-icn?displaySize",
                iconX2Url: "https://graph.api.smartthings.com/api/devices/icons/st.Electronics.electronics13-icn?displaySize=2x")

preferences {
    section("Log devices...") {
        input "energymeters", "capability.EnergyMeter", title: "Energy Meter", required: false, multiple: true
        input "thermostats", "capability.thermostat", title: "Thermostat", required: false, multiple: true
    }

    section ("Xively Info") {
        input "xi_apikey", "text", title: "Xively API Key"
        input "xi_feed", "number", title: "Xively Feed ID"
    }
}

def installed() {
    initialize()
}

def updated() {
    unsubscribe()

    initialize()
}

def initialize() {
    state.clear()
        unschedule(checkSensors)
        schedule("0 */15 * * * ?", "checkSensors")
        subscribe(app, appTouch)
}

def appTouch(evt) {
    log.debug "appTouch: $evt"
    checkSensors()
}



def checkSensors() {

    def logitems = []
    for (t in settings.energymeters) {
        logitems.add([t.displayName, "power", t.latestValue("power")] )
        state[t.displayName + ".power"] = t.latestValue("power")
    }

    for (t in settings.thermostats) {

//        logitems.add([t.displayName, "temperature", t.latestValue("temperature")] )
//        state[t.displayName + ".temperature"] = t.latestValue("temperature")
        
        if ( t.currentValue("thermostatOperatingState")  ==  "cooling") {
                logitems.add([t.displayName, "thermostatState", 1 ])
                state[t.displayName + ".thermostatOperatingState"] = 1
         }

        if ( t.currentValue("thermostatOperatingState")  ==  "heating") {
                logitems.add([t.displayName, "thermostatState", 2 ])
                state[t.displayName + ".thermostatOperatingState"] = 2
        }
        
        if ( t.currentValue("thermostatOperatingState")  ==  "idle") {
                logitems.add([t.displayName, "thermostatState", 0 ])
                state[t.displayName + ".thermostatOperatingState"] = 0
        }
    }

    for (t in settings.energymeters) {
        logitems.add([t.displayName, "volts", t.latestValue("volts")] )
        state[t.displayName + ".volts"] = t.latestValue("volts")
    }

//   for (t in settings.thermostats) {
//       logitems.add([t.displayName, "coolingSetpoint", t.latestValue("coolingSetpoint")] )
//        state[t.displayName + ".coolingSetpoint"] = t.latestValue("coolingSetpoint")
// 
//        logitems.add([t.displayName, "heatingSetpoint", t.latestValue("heatingSetpoint")] )
//        state[t.displayName + ".heatingSetpoint"] = t.latestValue("heatingSetpoint")

//   }

    logField2(logitems)

}

private getFieldMap(channelInfo) {
    def fieldMap = [:]
    channelInfo?.findAll { it.key?.startsWith("field") }.each { fieldMap[it.value?.trim()] = it.key }
    return fieldMap
}


private logField2(logItems) {
    def fieldvalues = ""
    log.debug logItems


    def xivelyinfo = ""
    logItems.eachWithIndex() { item, i ->
    def channelname = item[0].replace(" ","_") + "_" + item[1]
    xivelyinfo += "{\"id\":\"${channelname}\",\"current_value\":\"${item[2]}\"}"
    if (i.toInteger() + 1 < logItems.size())
    {
    xivelyinfo += ","
    }

    }
    log.debug xivelyinfo
    def uri = "https://api.xively.com/v2/feeds/${xi_feed}.json"
    def json = "{\"version\":\"1.0.0\",\"datastreams\":[${xivelyinfo} ]}"

    def headers = [
        "X-ApiKey" : "${xi_apikey}"
    ]

    def params = [
        uri: uri,
        headers: headers,
        body: json
    ]
    log.debug params.body
    httpPutJson(params) {response -> parseHttpResponse(response)}
}

def parseHttpResponse(response) {
    log.debug "HTTP Response: ${response}"
}

def captureState(theDevice) {
    def deviceAttrValue = [:]
    for ( attr in theDevice.supportedAttributes ) {
        def attrName = "${attr}"
        def attrValue = theDevice.currentValue(attrName)
        deviceAttrValue[attrName] = attrValue
    }
    return deviceAttrValue
}

Ed, please feel free to make whatever changes you find fit. I’ve also used others’ code as well. That’s what makes Smartthings so much fun.

In the spirit of crowdsourced continuous improvements, here is a newer version for the HEMv2 that posts inside and outside temperatures along with the appropriate unit labels and symbols for Xively (what a pain it was to figure out the syntax for that!).

    /**
 *  Aeon HEM - Xively
 *
 *  Copyright 2014 Dan Anghelescu
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License  for the specific language governing permissions and limitations under the License.
 *
 *
 *
 *  Genesys: Based off of Aeon Smart Meter Code sample provided by SmartThings (2013-05-30), Aeon Home Energy Meter v2 by Barry A. Burke, and Xively Logger by Patrick Stuart  Built on US model
 *           may also work on international versions (currently reports total values only)
 */


// Automatically generated. Make future change here.
definition (
                name: "Aeon HEM - Xively",
                namespace: "smartthings",
                author: "Dan Anghelescu",
                description: "Aeon HEM - Xively Logger",
                category: "My Apps",
                iconUrl: "https://graph.api.smartthings.com/api/devices/icons/st.Electronics.electronics13-icn?displaySize",
                iconX2Url: "https://graph.api.smartthings.com/api/devices/icons/st.Electronics.electronics13-icn?displaySize=2x")

preferences {
    section("Log devices...") {
        input "energymeters", "capability.EnergyMeter", title: "Energy Meter", required: false, multiple: true
        input "thermostats", "capability.thermostat", title: "Thermostat", required: false, multiple: true
        input "weatherstations", "capability.temperatureMeasurement", title: "Outside Temperature", required: false, multiple: true 

    }

    section ("Xively Info") {
        input "xi_apikey", "text", title: "Xively API Key"
        input "xi_feed", "number", title: "Xively Feed ID"
    }
}

def installed() {
    initialize()
}

def updated() {
    unsubscribe()

    initialize()
}

def initialize() {
    state.clear()
        unschedule(checkSensors)
        schedule("0 */15 * * * ?", "checkSensors")
        subscribe(app, appTouch)
}

def appTouch(evt) {
    log.debug "appTouch: $evt"
    checkSensors()
}



def checkSensors() {

    def logitems = []
    for (t in settings.energymeters) {
        logitems.add([t.displayName, "energymeter.energy", t.latestValue("energy"), "KilowattHours", "kWh"] )
        state[t.displayName + ".energy"] = t.latestValue("energy")
    }
    for (t in settings.thermostats) {
        logitems.add([t.displayName, "inside.temperature", t.latestValue("temperature"), "Farenheight", "°F"] )
        state[t.displayName + ".temperature"] = t.latestValue("temperature")

/*        
        if ( t.currentValue("thermostatOperatingState")  ==  "cooling") {
                logitems.add([t.displayName, "thermostatState", 1 ])
                state[t.displayName + ".thermostatOperatingState"] = 1
         }

        if ( t.currentValue("thermostatOperatingState")  ==  "heating") {
                logitems.add([t.displayName, "thermostatState", 2 ])
                state[t.displayName + ".thermostatOperatingState"] = 2
        }

        if ( t.currentValue("thermostatOperatingState")  ==  "idle") {
                logitems.add([t.displayName, "thermostatState", 0 ])
                state[t.displayName + ".thermostatOperatingState"] = 0
        }
*/
    }
    for (t in settings.energymeters) {
        logitems.add([t.displayName, "energymeter.power", t.latestValue("power"), "Watts", "W"] )
        state[t.displayName + ".power"] = t.latestValue("power")
    }
    for (t in settings.energymeters) {
        logitems.add([t.displayName, "energymeter.volts", t.latestValue("volts"), "Volts", "V"] )
        state[t.displayName + ".volts"] = t.latestValue("volts")
    }
    for (t in settings.energymeters) {
        logitems.add([t.displayName, "energymeter.amps", t.latestValue("amps"), "Amps", "A"] )
        state[t.displayName + ".amps"] = t.latestValue("amps")
    }
    for (t in settings.weatherstations) {
        logitems.add([t.displayName, "outside.temperature", t.latestValue("temperature"), "Farenheight", "°F"] )
        state[t.displayName + ".temperature"] = t.latestValue("temperature")
    }
    logField2(logitems)

}

private getFieldMap(channelInfo) {
    def fieldMap = [:]
    channelInfo?.findAll { it.key?.startsWith("field") }.each { fieldMap[it.value?.trim()] = it.key }
    return fieldMap
}


private logField2(logItems) {
    def fieldvalues = ""
    log.debug logItems


    def xivelyinfo = ""
    logItems.eachWithIndex() { item, i ->
    def channelname = item[0].replace(" ","_") + "_" + item[1]
    xivelyinfo += "{\"id\":\"${channelname}\",\"current_value\":\"${item[2]}\",\"unit\":{\"label\":\"${item[3]}\",\"symbol\":\"${item[4]}\"}}"
    if (i.toInteger() + 1 < logItems.size())
    {
    xivelyinfo += ","
    }

    }
    log.debug xivelyinfo
    def uri = "https://api.xively.com/v2/feeds/${xi_feed}.json"
    def json = "{\"version\":\"1.0.0\",\"datastreams\":[${xivelyinfo} ]}"

    def headers = [
        "X-ApiKey" : "${xi_apikey}"
    ]

    def params = [
        uri: uri,
        headers: headers,
        body: json
    ]
    log.debug params.body
    httpPutJson(params) {response -> parseHttpResponse(response)}
}

def parseHttpResponse(response) {
    log.debug "HTTP Response: ${response}"
}

def captureState(theDevice) {
    def deviceAttrValue = [:]
    for ( attr in theDevice.supportedAttributes ) {
        def attrName = "${attr}"
        def attrValue = theDevice.currentValue(attrName)
        deviceAttrValue[attrName] = attrValue
    }
    return deviceAttrValue
}

Nice work!

I’m now trying to figure out how to plot more than one measurement per graph. I’m trying to get the thermostat states on top of the watts diagram, to have an easy cause-n-effect plot, using the Xively / Github example.

I’ll post if I make any progress on that.

Indeed, I have a modified version of the AEON HEMv2 code, that works with HEM v1. Well honestly just gives me the KWh, Watts, and Current projected Cost.

The problem that I have run into now, is that the Cost value has special characters. Which causes the Xively log to fail on sending the Cost information to Xively.

This is where I need to learn Groovy to understand how it handles strings etc.

I just want to make it clear. You guys here in the community make ST work for me. I used to do quite a bit of coding, but have been out of that area for over 10 years. So, I am playing catch up with all that you guys are doing.

Thank you all so much for all the help you give us here in the community.

Here is a device type handler for the HEM v1 for anyone interested that provides a nice real-time view to pair with Xively data graphs and timelines.

3 Likes

How do you pair the HEM with smartthings. did you need to use a minimote?

If memory serves, the mounting plate on the back slides off and there’s a button under there somewhere.

2 Likes

Thanks, I have it working now beautifully.

I made some changes to the code to fix the date and display on android devices. I submitted a pull request to the original author, but if you have a hem v1, here is the code

1 Like

Hi guys,

Does this code still work for Version one? When I try to add the code as a SmartApp “From Code” I get the following errors when hitting save:

“No signature of method: script14294058998262129033250.metadata() is applicable for argument types: (script14294058998262129033250$_run_closure1) values: [script14294058998262129033250$_run_closure1@48d29040] Possible solutions: getMetadata(), getState(), setState(java.lang.Object), metaClass(groovy.lang.Closure)”

This is the Ver 1 code on GitHub:

Any help would be greatly appreciated!
Derek

Hi Edward,

Did you ever get this working for the Aeon HEM V1? I tried running the Code for V1 and ran into a bunch of errors.

Any advice you might have would be greatly appreciated!

Thanks,
Derek

I actually ended up using a better logging solution Grovestream, Much nicer interface and more options to process the data, Although due to more options it is more complicated.

Here is the link to get you started

@Slowfoxtrot thanks for the v1 code! its all working great except the “since date time” in the top left is updating the first two digits of the date to match the minute of reset. for example today 02/02/16 @ 7:54pm if I reset it shows “Since 54/2/16 7:54PM” I just cant seem to find the problem…

Do you solve this errors, i just try the same with the same errors.

looks great. But where or what do I enter under api?