Aeon Labs Energy Meter clamps

@johnconstantelo I used your device drive for my HEM v1. I have not claimed the device to power line yet.
I saw these errors in the Live log:

I

1 Like

I was getting the same error so I changed line 113 to the following and it worked fine.

statusTextmsg = "Home is currently using ${device.currentState('powerDisp')?.value}.\nMin was ${device.currentState('powerOne')?.value}.\nMax was ${device.currentState('powerTwo')?.value}."
1 Like

Thanks @cuboy29, I updated that line in th code on Github just now.

Good catch @binhton. I actually didn’t watch the live logs all that close to see this earlier. Thanks!

How long does it take before the Min value gets updated? I have mine running all day today and it’s still showing NULL. Are you guys getting the Min value?

Edit: NVM… it started updating the min value right after I did the reset must have not done it for awhile.

1 Like

@JSCGS350 - thanks for this device type! I’m having an issue though - If I hit ā€œrefreshā€ everything updates as expected, but I get no other updates… whether I wait hours or a day… it only updates the when I press ā€œrefreshā€ - any thoughts? I have have changed the intervals enabled/disabled selective reporting, etc… but my HEM (USB powered) will only send out an update when prodded.

Hi @stevesell, that’s really odd. You don’t happen to have the batteries installed along with USB power? I’ve not seen your issue before, so you may want to exclude and reinclude the HEM just in case. Set your device type settings back to what I have in the Github source before doing that.

@johnconstantelo - one thing I noticed in your code - parameter 8 is size 1 according to the documentation I have, but you have it as size 2. Obviously, this isn’t causing you an issue, but thought I should let you know.

1 Like

@johnconstantelo, I am using the battery powered one with ur device type…Is there way to get battery level reporting?

Thanks

Just realized for severely degraded the performance is with running on batteries, need to figure out how to wire this inside the circuit breaker!!

If anyone here has done this, then any sort of guidance will be helpful and a picture will be great…Reading through the install documentation provided in one the above posts in the mean time!

I’ve made some modifications to this great device type by @johnconstantelo including the parameter 8 size fix.

DrShaw changes:

  • Added two new tiles to display max and min values as a tile, (works better for iOS or at least my iPhone 6)
  • Updated and fixed the configuration settings so that it reports every 15 seconds , added some additional information in the comments for user manipulation. (After applying this device type, or making configuration adjustments, remember you need to push the ā€œConfigurationā€ tile in the app after publishing to apply any configuration changes.)
  • Added a tile to reset just the max/min values, keeping total kwh and cost in tact
  • Disabled the report group 3 configuration settings which were causing some unhandled zwave events.

If you’re interested:

/**
 *  Aeon HEM12
 *
 *  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.
 *
 *  Aeon Home Energy Meter v1 (US)
 *
 *  DrShaw Modifications: Updated configuration settings and tiles
 */

metadata {

	definition (name: "My Aeon Home Energy Monitor v2", namespace: "jscgs350", author: "SmartThings") 
    {
        capability "Energy Meter"
        capability "Power Meter"
        capability "Configuration"
        capability "Sensor"
        capability "Refresh"
        capability "Polling"
        capability "Battery"
        
        attribute "energy", "string"
        attribute "energyDisp", "string"
        attribute "energyOne", "string"
        attribute "energyTwo", "string"
        
        attribute "power", "string"
        attribute "powerDisp", "string"
        attribute "powerOne", "string"
        attribute "powerTwo", "string"
        
        command "reset"
        command "configure"
        command "resetmaxmin"
        
        fingerprint deviceId: "0x2101", inClusters: " 0x70,0x31,0x72,0x86,0x32,0x80,0x85,0x60"

    }

// tile definitions
    
	tiles(scale: 2) {
		multiAttributeTile(name:"powerDisp", type: "lighting", width: 6, height: 4, decoration: "flat", canChangeIcon: true){
			tileAttribute ("device.powerDisp", key: "PRIMARY_CONTROL") {
				attributeState "default", action: "refresh", label: '${currentValue}', icon: "st.switches.light.on", backgroundColor: "#79b821"
			}
            tileAttribute ("statusText", key: "SECONDARY_CONTROL") {
           		attributeState "statusText", label:'${currentValue}'       		
            }
		}    
       
// Power row
    
        valueTile("energyDisp", "device.energyDisp", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state("default", label: '${currentValue}', backgroundColor:"#ffffff")
        }
        
        valueTile("energyOne", "device.energyOne", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state("default", label: '${currentValue}', backgroundColor:"#ffffff")
        }        
        
        valueTile("energyTwo", "device.energyTwo", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state("default", label: '${currentValue}', backgroundColor:"#ffffff")
        }
        
// Controls row
    

        standardTile("reset", "device.energy", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
			state "default", label:'Reset All', action:"reset", icon:"st.secondary.refresh-icon"
		}
        standardTile("refresh", "device.power", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
        }
        standardTile("configure", "device.power", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "configure", label:'', action:"configure", icon:"st.secondary.configure"
        }
        
        valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
            state "battery", label:'${currentValue}% battery', unit:""
        }
        
        valueTile("statusText", "statusText", width: 3, height: 2, inactiveLabel: false) {
			state "statusText", label:'${currentValue}', backgroundColor:"#ffffff"
		}
        
        valueTile("min", "powerOne", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
			state "default", label:'Min:\n${currentValue}', backgroundColor:"#ffffff"
		}

		valueTile("max", "powerTwo", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
			state "default", label:'Max:\n${currentValue}', backgroundColor:"#ffffff"
		}

        standardTile("resetmaxmin", "device.energy", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
			state "default", label:'Reset Max/Min', action:"resetmaxmin", icon:"st.secondary.refresh-icon"
		}

        main (["powerDisp"])
		details(["powerDisp", "energyDisp", "energyOne", "energyTwo", "min", "max", "refresh", "resetmaxmin", "reset", "configure"])
    	}
    
        preferences {
            input "kWhCost", "string", title: "\$/kWh (0.16)", defaultValue: "0.16" as String
        }
}


def parse(String description) {
//    log.debug "Parse received ${description}"
    def result = null
    def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3])
    if (cmd) {
        result = createEvent(zwaveEvent(cmd))
    }
//    if (result) log.debug "Parse returned ${result}"
           
    def statusTextmsg = ""
    statusTextmsg = "Home is currently using ${device.currentState('powerDisp').value}.\nMin was ${device.currentState('powerOne').value}.\nMax was ${device.currentState('powerTwo').value}."
    sendEvent("name":"statusText", "value":statusTextmsg)
//    log.debug statusTextmsg
    
    return result
}

def zwaveEvent(physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
    //log.debug "zwaveEvent received ${cmd}"
    def dispValue
    def newValue
    def timeString = new Date().format("yyyy-MM-dd h:mm a", location.timeZone)
    if (cmd.meterType == 33) {
        if (cmd.scale == 0) {
            newValue = cmd.scaledMeterValue
            if (newValue != state.energyValue) {
                dispValue = String.format("%5.2f",newValue)+"\nkWh"
                sendEvent(name: "energyDisp", value: dispValue as String, unit: "")
                state.energyValue = newValue
                BigDecimal costDecimal = newValue * ( kWhCost as BigDecimal)
                def costDisplay = String.format("%3.2f",costDecimal)
                sendEvent(name: "energyTwo", value: "Cost\n\$${costDisplay}", unit: "")
                [name: "energy", value: newValue, unit: "kWh"]
            }
        } else if (cmd.scale == 1) {
            newValue = cmd.scaledMeterValue
            if (newValue != state.energyValue) {
                dispValue = String.format("%5.2f",newValue)+"\nkVAh"
                sendEvent(name: "energyDisp", value: dispValue as String, unit: "")
                state.energyValue = newValue
                [name: "energy", value: newValue, unit: "kVAh"]
            }
        }
        else if (cmd.scale==2) {                
            newValue = Math.round( cmd.scaledMeterValue )       // really not worth the hassle to show decimals for Watts
            if (newValue != state.powerValue) {
                dispValue = newValue+"w"
                sendEvent(name: "powerDisp", value: dispValue as String, unit: "")
                
                if (newValue < state.powerLow) {
                    dispValue = newValue+"w"+" on "+timeString
                    sendEvent(name: "powerOne", value: dispValue as String, unit: "")
                    state.powerLow = newValue
                }
                if (newValue > state.powerHigh) {
                    dispValue = newValue+"w"+" on "+timeString
                    sendEvent(name: "powerTwo", value: dispValue as String, unit: "")
                    state.powerHigh = newValue
                }
                state.powerValue = newValue
                [name: "power", value: newValue, unit: "W"]
            }
        }
    }
}

def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
    def map = [:]
    map.name = "battery"
    map.unit = "%"
    if (cmd.batteryLevel == 0xFF) {
        map.value = 1
        map.descriptionText = "${device.displayName} has a low battery"
        map.isStateChange = true
    } else {
        map.value = cmd.batteryLevel
    }
//    log.debug map
    return map
}

def zwaveEvent(physicalgraph.zwave.Command cmd) {
    // Handles all Z-Wave commands we aren't interested in
    log.debug "Unhandled event ${cmd}"
    [:]
}

def refresh() {

    delayBetween([
        zwave.meterV2.meterGet(scale: 0).format(),
        zwave.meterV2.meterGet(scale: 2).format()
    ])
}

def poll() {
    refresh()
}
    
def reset() {
    log.debug "${device.name} reset all values"

    state.powerHigh = 0
    state.powerLow = 99999

    def timeString = new Date().format("yyyy-MM-dd h:mm a", location.timeZone)
    sendEvent(name: "energyOne", value: "Since:\n"+timeString, unit: "")
    sendEvent(name: "powerOne", value: "", unit: "")    
    sendEvent(name: "powerDisp", value: "", unit: "")    
    sendEvent(name: "energyDisp", value: "", unit: "")
    sendEvent(name: "energyTwo", value: "Cost\n--", unit: "")
    sendEvent(name: "powerTwo", value: "", unit: "")    
    
// No V1 available
    def cmd = delayBetween( [
        zwave.meterV2.meterReset().format(),
        zwave.meterV2.meterGet(scale: 0).format()
    ])
    
    cmd
}

def resetmaxmin() {
    log.debug "${device.name} reset max/min values"

    state.powerHigh = 0
    state.powerLow = 99999

    sendEvent(name: "powerOne", value: "", unit: "")    
    sendEvent(name: "powerTwo", value: "", unit: "")    
    
    refresh()
}

def configure() {

    def cmd = delayBetween([
        zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: 0).format(),      // Disable selective reporting, so always update based on schedule below <set to 1 to reduce network traffic>
        zwave.configurationV1.configurationSet(parameterNumber: 4, size: 2, scaledConfigurationValue: 50).format(),     // (DISABLED by first option) Don't send unless watts have changed by 50 <default>
        zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: 10).format(),     // (DISABLED by first option) Or by 10% <default>
        zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 4).format(),   // Combined energy in Watts
        zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 15).format(),   // Every 15 Seconds (for Watts)
        zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 8).format(),    // Combined energy in kWh
        zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 60).format(),  // every 60 seconds (for kWh)
        zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 0).format(),    // Disable report 3
        zwave.configurationV1.configurationSet(parameterNumber: 113, size: 4, scaledConfigurationValue: 0).format()   // Disable report 3
    ])
    log.debug cmd

    cmd
}
1 Like

Well done @drshaw, thanks for your contributions! That’s what this community is all about. Too bad iOS and Android differences needed to be addressed, but none the less I’ll update my code on Github with yours.

I see in the description that you mention the v2 of the device so you also updated it to work with both HEM v1 and v2 or only v2?

This is version 2 of the device type for version 1 of the HEM that uses the new attributeTile code and tile formatting. It really should say Aeon HEM1 instead of Aeon HEM12, but that’s how I found the original way back when I first got the device last year. It does say Aeon Home Energy Meter v1 (US) further into the code’s comments.

1 Like

I’m so glad the v1 of HEM is getting love via this thread!

Just in case anyone is searching for help on this in the future… somehow during the process of moving my HEM from hub v1 to hub v2 (and simultaneously switching the device type from one I hacked earlier to this one by John) the thing just stopped reporting. The only way to get it to report was to send it a ā€œrefreshā€ command. I finally had to do a factory reset and re-add it my system. The factory reset was hard for me to find how to do So here’s how:

  1. Open the battery compartment
  2. Press and hold the programming button for 10s and release.

Also - Here is the only place I could find what appears to be the correct technical specification document (for programming). Engineering Spec - Aeon Labs Home Energy Monitor.pdf - And even at that it appears to be cached by google.

3 Likes

I put the meter clamps in my main box over the weekend. The device works and displays all of the numbers. I don’t know how accurate it is. One thing I notice so far. Since I put this device in the Garage Room in ST app, it floods the Recently tab with all of the messages when it reports the numbers. I may have to create a separate room just to put this device in. However, it will still fill the Activity Feed tab in the Notification page. Is there a way not to display all of these messages? If we increase how often it reports, let say 1min, then it becomes less accurate in measurement of total kWh and cost? Does it report every 15 seconds now?

You can adjust the reporting frequency by adjusting these lines of the code. After adjusting, you need to ā€œPublish For Meā€ in the IDE, then open up the device in the ST mobile app, and press ā€œConfigureā€ which will apply your changes to the HEM device.

If you’re using my version, then yes, right now it reports every 15 seconds. By changing the first parameter below to 1, you can make it only report based on certain threshold changes (open 2 and 3), or just change option 5 for frequency on total watts, and Option 7 for frequency on KWH.

I’m not positive, but I think it works like this: If you adjust them to, say once per minute or even once every 5 minutes, it won’t be ā€œless accurateā€. It will just push the information to you less frequently. But I think the device stores the running total of kwh (up until you reset it). And the total watts information, at a glance, may be 1-5 minutes old unless you manually refresh it by clicking the green light-icon, or pushing the refresh tile. The reason you do it every 15 seconds, like I do, is if you are pulling that information out to run data analytic type stuff on the energy usage of you home. Say to something like PlotWatt (as we’ve talked about earlier in this thread, or you can search the forums for cool smart apps that will push your HEM readings to it) or GroveStreams etc… where pushing them your readings as frequently as you can helps plot and track usage, patterns, spikes, etc… so you can start to understand where energy is going in your house.

But maybe there is a way that I’m not aware of (I’m new to this), that lets you get readings, without flooding the Activity Feed? @johnconstantelo may know.

def configure() {
    def cmd = delayBetween([
    zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: 0).format(),      // Disable selective reporting, so always update based on schedule below <set to 1 to reduce network traffic>
    zwave.configurationV1.configurationSet(parameterNumber: 4, size: 2, scaledConfigurationValue: 50).format(),     // (DISABLED by first option) Don't send unless watts have changed by 50 <default>
    zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: 10).format(),     // (DISABLED by first option) Or by 10% <default>
    zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 4).format(),   // Combined energy in Watts
    zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 15).format(),   // Every 15 Seconds (for Watts)
    zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 8).format(),    // Combined energy in kWh
    zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 60).format(),  // every 60 seconds (for kWh)
    zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 0).format(),    // Disable report 3
    zwave.configurationV1.configurationSet(parameterNumber: 113, size: 4, scaledConfigurationValue: 0).format()   // Disable report 3
])

Thanks. I adjusted the frequency on Watts to 60 seconds, and KHW to 180 seconds. I checked the Activity Feed and saw correct update frequency. However, for every Watt update, there are 3 entries in the Feed: 1 for statusText, other 2 are power disp. Are they from sendEvent?

Just picked up an Aeon HEM and was going to install it, but I want to make sure I do it correctly. When I removed the cover from my breaker panel, I was expecting to see some shielded wires to attach to, but I don’t have any. Would I hook a clamp on the two metal pieces I have marked in the following image? Obviously after I shut off the main breaker right above them.

Thanks for your help.

Looks like that’s the right location but Can’t tell how high the bars are, it may be impossible to fit a rigid CT there. Otherwise you’d have to put them on the feeders under the guard, which are always live, and that’s a fine way to electrocute yourself.

Do you have a meter to check your conditions? Don’t assume switches grounds breakers.