Aeon HEM V1 Separate clamp readings

Picking up where this took off.

THIS DOES NOT WORK YET!!! It's In progress

CURRENT CODE PARENT:

/**
 * SmartThings Device Type for Aeon Home Energy Monitor v1 (HEM v1)
 * (Goal of project is to) Displays individual values for each clamp (L1, L2) for granular monitoring
 * Example: Individual circuits (breakers) of high-load devices, such as HVAC or clothes dryer
 *
 * Original Author: Copyright 2014 Barry A. Burke
 *
 **/

metadata {
	// Automatically generated. Make future change here.
	definition (
		name: 		"AEON HEM v1 Separate Clamp",
		namespace: 	"jrfarrar",
		category: 	"HEM",
		author: 	"J.R. Farrar"
	)
	{
	capability "Energy Meter"
		capability "Power Meter"
		capability "Configuration"
		capability "Sensor"
    capability "Refresh"
    capability "Polling"
    // capability "Battery"
    
    attribute "energy", "string"
    attribute "energyOne", "string"
    attribute "energyTwo", "string"
		attribute "power", "string"
    attribute "powerOne", "string"
    attribute "powerTwo", "string"
    attribute "volts", "string"
    attribute "voltage", "string"		// We'll deliver both, since the correct one is not defined anywhere
    
    attribute "energyDisp", "string"
		attribute "energy1Disp", "string"
    attribute "energy2Disp", "string"
    attribute "powerDisp", "string"
    attribute "power1Disp", "string"
    attribute "power2Disp", "string"
    

    command "reset"
    command "configure"
    command "refresh"
    command "poll"
    command "recreateChildDevices"
    command "deleteChildren"
    
		fingerprint deviceId: "0x2101", inClusters: " 0x70,0x31,0x72,0x86,0x32,0x80,0x85,0x60"

//		fingerprint deviceId: "0x3101", inClusters: "0x70,0x32,0x60,0x85,0x56,0x72,0x86"
	}
preferences {
  		input "voltageValue", "number",
    	title: "Voltage being monitored",
        defaultValue: 120,
        range: "110..240",
        required: false,
        displayDuringSetup: true
  		input "c1Name", "string",
    	title: "Clamp 1 Name",
        defaultValue: "Clamp 1" as String,
        required: false,
        displayDuringSetup: true
    input "c2Name", "string",
    	title: "Clamp 2 Name",
        defaultValue: "Clamp 2" as String,
        required: false,
        displayDuringSetup: true
		input "kWhCost", "string",
			title: "Enter your cost per kWh (or just use the default, or use 0 to not calculate):",
			defaultValue: 0.06,
			required: false,
			displayDuringSetup: true
    input "reportType", "number",
			title: "ReportType: Send watt/kWh data on a time interval (0), or on a change in wattage (1)? Enter a 0 or 1:",
			defaultValue: 1,
			range: "0..1",
			required: false,
			displayDuringSetup: true
		input "wattsChangedWhole", "number",
			title: "For ReportType = 1, Don't send unless watts have changed by this many watts Whole Meter (range 0 - 32,000W)",
			defaultValue: 50,
			range: "0..32000",
			required: false,
			displayDuringSetup: true
    input "wattsChangedOne", "number",
			title: "For ReportType = 1, Don't send unless watts have changed by this many watts Clamp 1: (range 0 - 32,000W)",
			defaultValue: 50,
			range: "0..32000",
			required: false,
			displayDuringSetup: true
    input "wattsChangedTwo", "number",
			title: "For ReportType = 1, Don't send unless watts have changed by this many watts Clamp 2: (range 0 - 32,000W)",
			defaultValue: 50,
			range: "0..32000",
			required: false,
			displayDuringSetup: true
		input "wattsPercent", "number",
			title: "For ReportType = 1, Don't send unless watts have changed by this percent(whole meter): (range 0 - 99%)",
			defaultValue: 10,
			range: "0..99",
			required: false,
			displayDuringSetup: true
    input "wattsPercent1", "number",
			title: "For ReportType = 1, Don't send unless watts have changed by this percent(Clamp1): (range 0 - 99%)",
			defaultValue: 10,
			range: "0..99",
			required: false,
			displayDuringSetup: true
    input "wattsPercent2", "number",
			title: "For ReportType = 1, Don't send unless watts have changed by this percent(Clamp2): (range 0 - 99%)",
			defaultValue: 10,
			range: "0..99",
			required: false,
			displayDuringSetup: true
		input "secondsWatts", "number",
			title: "For ReportType = 0, Send Watts data every how many seconds? (range 0 - 65,000 seconds)",
			defaultValue: 15,
			range: "0..65000",
			required: false,
			displayDuringSetup: true
		input "secondsKwh", "number",
			title: "For ReportType = 0, Send kWh data every how many seconds? (range 0 - 65,000 seconds)",
			defaultValue: 60,
			range: "0..65000",
			required: false,
			displayDuringSetup: true
		input "secondsBattery", "number",
			title: "If the HEM has batteries installed, send battery data every how many seconds? (range 0 - 65,000 seconds)",
			defaultValue: 900,
			range: "0..65000",
			required: false,
			displayDuringSetup: true
}

}

def installed() {
	reset()						// The order here is important
createChildDevices()
	configure()					// Since reports can start coming in even before we finish configure()
	refresh()
}

def updated() {
log.debug "updated"

if (!childDevices) {
    createChildDevices()
}
else if (device.label != state.oldLabel) {
    childDevices.each {
        def newLabel = "$device.displayName (CH${channelNumber(it.deviceNetworkId)})"
        it.setLabel(newLabel)
    }
  
    state.oldLabel = device.label
}

	configure()
	resetDisplay()
	refresh()
}

def parse(String 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?.descriptionText}"
		return result
	} else {
	}
}


def zwaveEvent(hubitat.zwave.commands.meterv1.MeterReport cmd) {
def dispValue
def newValue
def formattedValue

	//def timeString = new Date().format("h:mm a", location.timeZone)

if (cmd.meterType == 33) {
		if (cmd.scale == 0) {
    	newValue = Math.round(cmd.scaledMeterValue * 100) / 100
    	if (newValue != state.energyValue) {
    		formattedValue = String.format("%5.2f", newValue)
			dispValue = "Total\n${formattedValue}\nkWh"		// total kWh label
            sendEvent(name: "energyDisp", value: dispValue as String, unit: "", descriptionText: "Display Energy: ${newValue} kWh", displayed: false)
            state.energyValue = newValue
            [name: "energy", value: newValue, unit: "kWh", descriptionText: "Total Energy: ${formattedValue} kWh"]

        }
		}
		else if (cmd.scale==2) {
    	newValue = Math.round(cmd.scaledMeterValue*10)/10
        formattedValue = String.format("%5.1f", newValue)
    	//newValue = Math.round(cmd.scaledMeterValue)		// really not worth the hassle to show decimals for Watts
    	if (newValue != state.powerValue) {
			dispValue = "Total\n"+newValue+"\nWatts"	// Total watts label
            sendEvent(name: "powerDisp", value: dispValue as String, unit: "", descriptionText: "Display Power: ${newValue} Watts", displayed: false)
            state.powerValue = newValue
            [name: "power", value: newValue, unit: "W", descriptionText: "Total Power: ${formattedValue} Watts"]
       }
		}
 	}
}

def zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
	def dispValue
	def newValue
	def formattedValue

   	if (cmd.commandClass == 50) {
   		def encapsulatedCommand = cmd.encapsulatedCommand([0x30: 1, 0x31: 1]) // can specify command class versions here like in zwave.parse
		if (encapsulatedCommand) {
			if (cmd.sourceEndPoint == 1) {
				if (encapsulatedCommand.scale == 2 ) {
					newValue = Math.round(encapsulatedCommand.scaledMeterValue*10)/10
                formattedValue = String.format("%5.1f", newValue)
					//dispValue = "${c1Name}\n${formattedValue}\nWatts"	// L1 Watts Label
                dispValue = "${formattedValue}"	// L1 Watts Label
					if (newValue != state.powerL1) {
						state.powerL1 = newValue
						[name: "powerOne", value: dispValue, unit: "", descriptionText: "L1 Power: ${formattedValue} Watts"]
					}
				}
				else if (encapsulatedCommand.scale == 0 ){
					newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100
					formattedValue = String.format("%5.2f", newValue)
					//dispValue = "${c1Name}\n${formattedValue}\nkWh"		// L1 kWh label
                dispValue = "${formattedValue}"
					if (newValue != state.energyL1) {
						state.energyL1 = newValue
						[name: "energyOne", value: dispValue, unit: "", descriptionText: "L1 Energy: ${formattedValue} kWh"]
					}
				}
			}
			else if (cmd.sourceEndPoint == 2) {
				if (encapsulatedCommand.scale == 2 ){
					newValue = Math.round(encapsulatedCommand.scaledMeterValue*10)/10
                formattedValue = String.format("%5.1f", newValue)
                //dispValue = "${c2Name}\n${formattedValue}\nWatts"	// L2 Watts Label
                dispValue = "${formattedValue}"	// L2 Watts Label
					if (newValue != state.powerL1) {
						state.powerL2 = newValue
						[name: "powerTwo", value: dispValue, unit: "", descriptionText: "L2 Power: ${formattedValue} Watts"]
					}
				}
				else if (encapsulatedCommand.scale == 0 ){
					newValue = Math.round(encapsulatedCommand.scaledMeterValue * 100) / 100
					formattedValue = String.format("%5.2f", newValue)
					// dispValue = "${c2Name}\n${formattedValue}\nkWh"		// L2 kWh label
                dispValue = "${formattedValue}"
					if (newValue != state.energyL2) {
						state.energyL2 = newValue
						[name: "energyTwo", value: dispValue, unit: "", descriptionText: "L2 Energy: ${formattedValue} kWh"]
					}
				}
			}
		}
	}
}

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

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

def refresh() {			// Request HEMv2 to send us the latest values for the 4 we are tracking
	log.debug "refresh()"

	delayBetween([
		zwave.meterV2.meterGet(scale: 0).format(),		// Change 0 to 1 if international version
		zwave.meterV2.meterGet(scale: 2).format(),
	])
resetDisplay()
}

def poll() {
	log.debug "poll()"
	refresh()
}

def resetDisplay() {
	log.debug "resetDisplay()"
	
sendEvent(name: "powerDisp", value: "Total\n" + state.powerValue + "\nWatts", unit: "W")
sendEvent(name: "energyDisp", value: "Total\n" + state.energyValue + "\nkWh", unit: "kWh")
sendEvent(name: "power1Disp", value: c1Name + "\n" + state.powerL1 + "\nWatts", unit: "W")
sendEvent(name: "energy1Disp", value: c1Name + "\n" + state.energyL1 + "\nkWh", unit: "kWh")
sendEvent(name: "power2Disp", value: c2Name + "\n" + state.powerL2 + "\nWatts", unit: "W")
sendEvent(name: "energy2Disp", value: c2Name + "\n" + state.energyL2 + "\nkWh", unit: "kWh")
}

def reset() {
	log.debug "reset()"
	
state.energyValue = ""
state.powerValue = ""
state.energyL1 = ""
state.energyL2 = ""
state.powerL1 = ""
state.powerL2 = ""
	
resetDisplay()

	return [
		zwave.meterV2.meterReset().format(),
		zwave.meterV2.meterGet(scale: 0).format()
	]

configure()
}

def configure() {
	log.debug "configure()"

	Long kwhDelay = settings.kWhDelay as Long
Long wDelay = settings.wDelay as Long

if (kwhDelay == null) {		// Shouldn't have to do this, but there seem to be initialization errors
		kwhDelay = 15
	}

	if (wDelay == null) {
		wDelay = 15
	}

	def cmd = delayBetween([

	// Perform a complete factory reset. Use this all by itself and comment out all others below.
	// Once reset, comment this line out and uncomment the others to go back to normal
//	zwave.configurationV1.configurationSet(parameterNumber: 255, size: 4, scaledConfigurationValue: 1).format()
	
    zwave.configurationV1.configurationSet(parameterNumber: 1, size: 2, scaledConfigurationValue: voltageValue).format(),		// assumed voltage
		zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: reportType).format(),			// Disable (=0) selective reporting
		zwave.configurationV1.configurationSet(parameterNumber: 4, size: 2, scaledConfigurationValue: wattsChangedWhole).format(),			// Don't send whole HEM unless watts have changed by 1
		zwave.configurationV1.configurationSet(parameterNumber: 5, size: 2, scaledConfigurationValue: wattsChangedOne).format(),			// Don't send L1 Data unless watts have changed by 1
		zwave.configurationV1.configurationSet(parameterNumber: 6, size: 2, scaledConfigurationValue: wattsChangedTwo).format(),			// Don't send L2 Data unless watts have changed by 1
		zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: wattsPercent).format(),			// Or by 5% (whole HEM)
		zwave.configurationV1.configurationSet(parameterNumber: 9, size: 1, scaledConfigurationValue: wattsPercent1).format(),			// Or by 5% (L1)
	    zwave.configurationV1.configurationSet(parameterNumber: 10, size: 1, scaledConfigurationValue: wattsPercent2).format(),			// Or by 5% (L2)

		zwave.configurationV1.configurationSet(parameterNumber: 100, size: 4, scaledConfigurationValue: 1).format(),		// reset to defaults
		zwave.configurationV1.configurationSet(parameterNumber: 110, size: 4, scaledConfigurationValue: 1).format(),		// reset to defaults
		zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 772).format(),		// watt (don't change)
		zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: secondsWatts).format(), 	// every %Delay% seconds
		zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 6152).format(),   	// kwh (don't change)
		zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: secondsKwh).format(), // Every %Delay% seconds
		zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 1).format(),		// battery (don't change)
		zwave.configurationV1.configurationSet(parameterNumber: 113, size: 4, scaledConfigurationValue: secondsBattery).format() 		// every hour
    
    
	], 2000)
	

	log.debug cmd
	cmd
}

def recreateChildDevices() {
log.debug "recreateChildDevices"
deleteChildren()
createChildDevices()
}

def deleteChildren() {
	log.debug "deleteChildren"
	def children = getChildDevices()

children.each {child->
  		deleteChildDevice(child.deviceNetworkId)
}
}

private void createChildDevices() {
log.debug "createChildDevices"

for (i in 1..2) {
    addChildDevice("jrfarrar", "AEON HEM V1 (Child)", "$device.deviceNetworkId-$i", [name: "ch$i", label: "$device.displayName $i", isComponent: true])
}
}

CURRENT CODE CHILD:

/*
 *  AEON HEM V1 Child Device Driver
 */
metadata {
    definition (name: "AEON HEM V1 (Child)", namespace: "jrfarrar", author: "jrfarrar") {
        capability "Energy Meter"
		capability "Power Meter"
		capability "Refresh"
		capability "Sensor"
        
        attribute "power", "string"
        attribute "energy", "string"        
    }
}

Yes, those readings are working fine. Those 'Watts' readings are what is used to compare against the thresholds for washer and dryer cycle complete logic. What I do not know is how accurate they are. I am just using them as a trigger, so accuracy does not matter as much as consistency.

How often are those "washerWatts" updating for you?

I believe every 30 seconds?

Looks like Mike Maxwell even left a comment regarding the 30 seconds in the code... Look near bottom of the CONFIGURE command.

def configure() {
	log.debug "configure()"
    initialize()
	def cmd = delayBetween([
    	//zwave.configurationV1.configurationSet(parameterNumber: 100, size: 4, scaledConfigurationValue:1).format(),	//reset if not 0
        //zwave.configurationV1.configurationSet(parameterNumber: 110, size: 4, scaledConfigurationValue: 1).format(),	//reset if not 0
        
    	zwave.configurationV1.configurationSet(parameterNumber: 1, size: 2, scaledConfigurationValue: 120).format(),		// assumed voltage
		zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: 0).format(),			// Disable (=0) selective reporting
		zwave.configurationV1.configurationSet(parameterNumber: 9, size: 1, scaledConfigurationValue: 10).format(),			// Or by 10% (L1)
      	zwave.configurationV1.configurationSet(parameterNumber: 10, size: 1, scaledConfigurationValue: 10).format(),		// Or by 10% (L2)
		zwave.configurationV1.configurationSet(parameterNumber: 20, size: 1, scaledConfigurationValue: 1).format(),			//usb = 1
		zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 6912).format(),   	
		zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 30).format() 		// Every 30 seconds
	], 2000)

	return cmd
}

Ok, I must have something going on here with mine then. I can talk to it and get all my configuration parameters. I see what the other code is doing and I'm even using the other code and not getting updates at all....so something is a miss. If I post some code here can you tell me if you are getting the readings updated?

I am away from home... So I probably wouldn't be able to help until the weekend.

I do recall having to pair, unpair/exclude, and hit configure a few times to get the readings to start transferring... It was a little bit of trial and error. Once it started working, I left it alone! :wink:

That sir was the key! I've got the reports coming in now....I can see now, the right way to do this is going to be to make 2 child devices from the parent. One for each clamp. That might be just above my pay grade as far as hacking code goes. Would love some help because I think this is close. Will post above.

1 Like

@mike.maxwell care to lend a hand? :smiley:

Adding the two child devices shouldn't be too difficult. I might have some time this weekend to lend a hand if you need it. You simply need a virtual power/energy child driver, two calls to addChildDevice(), and then keep the child attributes updated in the parent's parse routine.

How about I do my best effort....and hopefully get mostly there (if not all the way there) and then I'll ping you.

1 Like

Just a side note. I believe the update interval is way slower if you are using battery power instead of the adapter.

Yes! I have an Aeon HemV1. I came from Vera and am only on day 3. New to all the groovy and smartthings migrations. I'm trying to get current watts for each clamp. Was hoping there was a more universal driver like Vera's where there are three devices, both clamps Current Watts, Both Clamps KWH count. Each clamp current Watts, KWH Count. Bonus for resetable KWH count (so i can count per month and reset on my powerbill date). I can then schedule a pushover to send the KWH value for each clamp before the value reset. Haven't seen it yet on these forums, but i would put a bounty out there for this driver. Is that type of the solicitation allowed here?

I started it but got as far as I knew how. I have a fix that works for what I'M doing by using the WATO app here (at least check that out) as you can do things with the individual attributes from the main DH. I still think the best answer for this solution is two child devices that report like energy meters for each clamp. I think you should be able to do your reset using a custom command in RM. Haven't tried it,but that should work. Pretty sure between WATO and pushover you can do your reporting also.

the child one does NOT work...but I got the shell of creating the children. The other DH should work for what I said above. It does for me. Also, do read down below in the DH. At one point on ST I heavily documente what I could find about all the settings in the HEM1

Did you ever manage to get this working?

I tried using it with my Aussie 2 clamp HEM, and got this error:

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.