Aeon Home Energy Meter & Xively logging Integration

Hello SmartThinkers,

I’ve just wrote a small program (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 ), that will log the Volts, Power, Amps, and Energy to an Xively account. See below. Enjoy.

 /**
 *  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://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
                iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")

preferences {
    section("Log devices...") {
        input "energymeters", "capability.EnergyMeter", title: "Energy Meter", 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")] )
        state[t.displayName + ".energy"] = t.latestValue("energy")
    }
    for (t in settings.energymeters) {
        logitems.add([t.displayName, "energymeter.power", t.latestValue("power")] )
        state[t.displayName + ".power"] = t.latestValue("power")
    }
    for (t in settings.energymeters) {
        logitems.add([t.displayName, "energymeter.volts", t.latestValue("volts")] )
        state[t.displayName + ".volts"] = t.latestValue("volts")
    }
    for (t in settings.energymeters) {
        logitems.add([t.displayName, "energymeter.amps", t.latestValue("amps")] )
        state[t.displayName + ".amps"] = t.latestValue("amps")
    }
    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
}

Cool! Thanks for sharing!

Was looking to do the same thing. I assume it will log just as well from the original AEON home energy V1?

I assume it will work w/ v1 as well. Give it a try.

Will do… I will let you know when I have the first chance.

Well, this is what I get for being new to Xively. Ok really new to Github as well.

I setup the Github site, forked the Xively code to my account,
Modified the Index
Waited for the page to be created
During this time started up the app
Put in the key and ID
Done there.
Went to the Xively page, put the API Key and the ID at the top.
Request a location for the device, so set that.
Then I tried a simple 6 hour time frame. Just showed loading.

I see that the device is being polled, I see get’s coming up with responses on the Xively page.
So it appears the connection is there.

I was not expecting much, but at least some data after the first couple of pulls.

Obviously still learning here, and getting used to all the new tools. So someone who has this working, please guide me oh great one (SMILE)>

And thank you ahead of time.

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