Zwave Power Strip from AEON

@reevesjm

I know exactly what you’re saying! Now that you’ve done it, it seems so simple and straight forward, but before you started it seemed a quite complex! :slight_smile: Glad you were able to get everything up and running. I’ll take credit for the SmartApp, and some minor changes in the devise type, but most of the kudos for the device type go to @bigpunk6 as its based on his device type.

One thing I’m sorta thinking about is setting up an alarm with it… just some sort of simple device siren device (like this one: http://www.ebay.com/itm/Wired-Mini-Strobe-External-Siren-With-Red-Strobe-Light-for-Security-Alarm-System-/290974501674?pt=LH_DefaultDomain_0&var=&hash=item43bf6e8b2a)

If I’m thinking correctly I could just connect the lead wires to each other so that the siren is “always on.” Then plug it into one of the outlets and turn in on/off via SmartThings.

@thrash99er

I did get the power sensing and reporting to work by building on the code @chrisb posted. The really neat thing is that the strip actually reports total power usage and also breaks out numbers for individual outlets, including always on ones. You can find the device type code @
https://github.com/jialong/smartthings/blob/master/device-types/aeon_smartstrip
.

Numbering of the power and energy reports corresponds to the numbering of the switches for the Zwave-controlled outlets (switch1 -> energy1, power1). The always on outlets are numbered 5 and 6. Total power and energy usage are reported through the usual “power” and “energy” parameters. You can also change the reporting frequency by changing the parameter number 111 in configure function.

1 Like

@chrisb @jwb - i am trying to adapt above code for a Dual Relay Insert module from TKB. Switches work endpoint 0 being switch1 and triggers both relays, switch2 triggers Relay1, switch3 triggers relay2. Metering works for switch1 and two bot not switch 3 (scaling may be out though). I new to groovy and ST and would really appreciate help in getting this to work. Documentation of the dual relay insert is located here.
http://www.z-wave.ru/podderzhka/skachat/category/82-tkbhome.html?download=200:tz04-manual

@jjhamb

Can you post the response you get when you query switch 3 for meter report ?

def test() {
  def cmds = []
  cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format()
  cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format()
  delayBetween(cmds, 1000)
}

@jwb

def test() {
 def cmds = []
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format()
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format()
	log.debug "Sending ${cmds.inspect()}"
delayBetween(cmds, 1000)
}

results in

12:40:42 am IST: debug Sending ['600D0103320100', '600D0103320110']

– I am not sure if I did what was expected :frowning:

regards

@jjhamb

Did you enable the debug logging in zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) block ? I’m curious to see if you are getting the meter report for switch 3, which I assume is using endpoint 3. You can try to debug this issue by changing the endpoint in the test method to 1 or 2 and compare the responses you get after enabling the debug logging.

@jww

Enabled debug in

def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd) {
//	  [50, 37, 32], dynamic: false, endPoint: 1, genericDeviceClass: 16, specificDeviceClass: 1)
    log.debug "multichannelv3.MultiChannelCapabilityReport $cmd"
}

I called testA()

def testA() {
 log.debug "testA"
 def cmds = []
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format()
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format()
 delayBetween(cmds, 1000) 
 }

I resulted in :

11:39:09 PM: debug testA

However there was no output from

zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd)

tried destinationEndPoint:3, destinationEndPoint:2, destinationEndPoint:1

As per docs, Endpoint1 (default) represents both relays, 2 represents relay1, 3 represents relay 2.
Device handler displays data in

        valueTile("power", "device.power", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy", "device.energy", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}

and


        valueTile("power1", "device.power1", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy1", "device.energy1", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}

but not in other tiles… further data in first set (larger values) is not same as data in second set, it should be because endpoint1 is supposed to represent both relays.

I apologise for being thick, my experience with st and groovy is just beginning :frowning:

regards

Here is the device handler code that I am using. I made changes in:

        zwaveEvent((cmd.sourceEndPoint > 3) ? (cmd.sourceEndPoint-3) : (cmd.sourceEndPoint+4), zwave.parse(desc, [ 0x60:3, 0x25:1, 0x32:1, 0x70:1 ]))

maybe the problem lies above. ??

/**
 *  Aeon Smartstrip
 */
 // for the UI
metadata {
	simulator {
		// TODO: define status and reply messages here
	}

	tiles {
        valueTile("power", "device.power", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy", "device.energy", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}
		standardTile("switch1", "device.switch1",canChangeIcon: true) {
                        state "on", label: "switch1", action: "off1", icon: "st.switches.switch.on", backgroundColor: "#79b821"
                        state "off", label: "switch1", action: "on1", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
                }
        standardTile("switch2", "device.switch2",canChangeIcon: true) {
                        state "on", label: "switch2", action: "off2", icon: "st.switches.switch.on", backgroundColor: "#79b821"
                        state "off", label: "switch2", action: "on2", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
                }
        standardTile("switch3", "device.switch3",canChangeIcon: true) {
                        state "on", label: "switch3", action: "off3", icon: "st.switches.switch.on", backgroundColor: "#79b821"
                        state "off", label: "switch3", action:"on3", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
                }
        standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
                        state "default", label:"", action:"refresh", icon:"st.secondary.refresh"
                }
        standardTile("reset", "device.switch", inactiveLabel: false, decoration: "flat") {
        				state "default", label:"reset kWh", action:"reset"
                }
        standardTile("configure", "device.switch", inactiveLabel: false, decoration: "flat") {
        				state "default", label:"", action:"configure", icon:"st.secondary.configure"
                }
                
        valueTile("power1", "device.power1", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy1", "device.energy1", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}
        valueTile("power2", "device.power2", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy2", "device.energy2", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}
        valueTile("power3", "device.power3", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy3", "device.energy3", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}
        

        main(["switch1", "power", "energy"])
        details(["power", "energy", "reset", "switch1","switch2","switch3","refresh","configure",
        "power1", "energy1","power2", "energy2","power3", "energy3"])
	}
}

// 0x25 0x32 0x27 0x70 0x85 0x72 0x86 0x60 0xEF 0x82

// 0x25: switch binary
// 0x32: meter
// 0x27: switch all
// 0x70: configuration
// 0x85: association
// 0x86: version
// 0x60: multi-channel
// 0xEF: mark
// 0x82: hail

// parse events into attributes
def parse(String description) {
	// log.debug "Parsing desc => '${description}'"

    def result = null
    def cmd = zwave.parse(description, [0x60:3, 0x25:1, 0x32:1, 0x70:1])
    if (cmd) {
        result = createEvent(zwaveEvent(cmd))
    }
    
    return result
}

//Reports

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
        [name: "switch", value: cmd.value ? "on" : "off", type: "physical"]
}

def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
        [name: "switch", value: cmd.value ? "on" : "off", type: "digital"]
}

def zwaveEvent(physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
	def map = []
    
	if (cmd.scale == 0) {
    	map = [ name: "energy", value: cmd.scaledMeterValue, unit: "kWh" ]
    }
    else if (cmd.scale == 2) {
    	map = [ name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W" ]
    }
    
    map
}

def zwaveEvent(int endPoint, physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
	// MeterReport(deltaTime: 1368, meterType: 1, meterValue: [0, 3, 29, 17], precision: 3, previousMeterValue: [0, 3, 29, 17], rateType: 1, reserved02: false, scale: 0, scaledMeterValue: 204.049, scaledPreviousMeterValue: 204.049, size: 4)
	 log.debug "EndPoint $endPoint, MeterReport $cmd"
    def map = []
    
    if (cmd.scale == 0) {
    	map = [ name: "energy" + endPoint, value: cmd.scaledMeterValue, unit: "kWh" ]
    }
    else if (cmd.scale == 2) {
    	map = [ name: "power" + endPoint, value: Math.round(cmd.scaledMeterValue), unit: "W" ]
    }
    
    map
}

def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
	 log.debug "MultiChannelCmdEncap $cmd"
    
    def map = [ name: "switch$cmd.sourceEndPoint" ]
    if (cmd.commandClass == 37){
    	if (cmd.parameter == [0]) {
        	map.value = "off"
        }
        if (cmd.parameter == [255]) {
            map.value = "on"
        }
        map
    }
    else if (cmd.commandClass == 50) {
    	// bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 100, 0, 0, 0, 0, 0, 94, 0, 0, 0, 0], res01: false, sourceEndPoint: 1
        def hex1 = { n -> String.format("%02X", n) }
        def desc = "command: ${hex1(cmd.commandClass)}${hex1(cmd.command)}, payload: " + cmd.parameter.collect{hex1(it)}.join(" ")
        // Re-assign source end point 3-6 to 1-4 and 1-2 to 5-6 to sync up with the switch end points. 
        // Source end point in the message refers to always-on sockets.
        zwaveEvent((cmd.sourceEndPoint > 3) ? (cmd.sourceEndPoint-3) : (cmd.sourceEndPoint+4), zwave.parse(desc, [ 0x60:3, 0x25:1, 0x32:1, 0x70:1 ]))
    }
}

def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd) {
//	  [50, 37, 32], dynamic: false, endPoint: 1, genericDeviceClass: 16, specificDeviceClass: 1)
    log.debug "multichannelv3.MultiChannelCapabilityReport $cmd"
}

def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
	log.debug "Configuration Report for parameter ${cmd.parameterNumber}: Value is ${cmd.configurationValue}, Size is ${cmd.size}"
}

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

// handle commands
def refresh() {
	def cmds = []
 
 	for ( i in 1..3 )
    	cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:37, command:2).format()
    	
    for ( i in 1..3 ) {
        cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:50, command:1, parameter:[0]).format()
        cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:50, command:1, parameter:[16]).format()
    }
    
    cmds << zwave.meterV2.meterGet(scale:0).format()
    cmds << zwave.meterV2.meterGet(scale:2).format()
    
    delayBetween(cmds)
}

def on(value) {
	log.debug "value $value"
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:1, parameter:[255]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:2).format()
	])
}

def off(value) {
	log.debug "value $value"
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:1, parameter:[0]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:2).format()
	])
}

def poll() {
	refresh()
}

def configure() {
	log.debug "Executing 'configure'"
    delayBetween([
    	zwave.configurationV1.configurationSet(parameterNumber:101, size:4, configurationValue: [ 0, 0, 127, 127 ]).format(),	// Report meter on all channels (6 sockets + master)
        zwave.configurationV1.configurationSet(parameterNumber:111, size:4, scaledConfigurationValue: 120).format(),		// 120s interval for meter reports
        zwave.configurationV1.configurationSet(parameterNumber:4, configurationValue: [0]).format()				// Report reguarly
    ])
}

def on1() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[255]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
	])
}

def off1() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[0]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
	])
}

def on2() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:1, parameter:[255]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
	])
}

def off2() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:1, parameter:[0]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
	])
}

def on3() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:1, parameter:[255]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:2).format()
	])
}

def off3() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:1, parameter:[0]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:2).format()
	])
}

//test
def test() {
	def cmds = []
        // cmds << zwave.multiChannelV3.multiChannelCapabilityGet(endPoint:1).format()
        // cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format()
        // cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format()
	    // cmds << zwave.multiChannelV3.multiChannelEndPointFind(genericDeviceClass:32).format()
	    // cmds << zwave.multiChannelV3.multiChannelEndPointGet().format()
        // cmds << zwave.meterV2.meterGet(scale:0).format()
        // cmds << zwave.meterV2.meterGet(scale:2).format()
        cmds << zwave.configurationV1.configurationGet(parameterNumber:101).format() 
        cmds << zwave.configurationV1.configurationGet(parameterNumber:4).format()
        cmds << zwave.configurationV1.configurationGet(parameterNumber:90).format()
	log.debug "Sending ${cmds.inspect()}"
	delayBetween(cmds, 2300)
}

def testA() {
 log.debug "testA"
 def cmds = []
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:3, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format()
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:3, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format()
 delayBetween(cmds, 1000) 
 }

//reset
def reset() {
	def cmds = []
    cmds << zwave.meterV2.meterReset().format()
    cmds << zwave.meterV2.meterGet(scale:0).format()
    for ( i in 1..6 ) 
    	cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:50, command:1, parameter:[0]).format()
        
	delayBetween(cmds)
}

That line is specifically for Aeon Smartstrip. For your device, you can pass the end point directly:

zwaveEvent(cmd.sourceEndPoint, zwave.parse(desc, [ 0x60:3, 0x25:1, 0x32:1, 0x70:1 ]))

@jwu

I tried this:

// parse events into attributes
def parse(String description) {
	 log.debug "Parsing desc => '${description}'"

    def result = null
    def cmd = zwave.parse(description, [0x60:3, 0x25:1, 0x32:1, 0x70:1])
    if (cmd) {
        result = createEvent(zwaveEvent(cmd))
    }
    log.debug "Parsing result => '${result}'"
    return result
}

But I am not able to generate any output… To be honest, I am clueless on what I must do or expect, I know you are trying to help.

regards

@jjhamb

Can you replace the code for zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) with the following:

def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
    log.debug "MultiChannelCmdEncap $cmd"
    
    def map = [ name: "switch$cmd.sourceEndPoint" ]
    if (cmd.commandClass == 37){
    	if (cmd.parameter == [0]) {
        	map.value = "off"
        }
        if (cmd.parameter == [255]) {
            map.value = "on"
        }
        map
    }
    else if (cmd.commandClass == 50) {
        def hex1 = { n -> String.format("%02X", n) }
        def desc = "command: ${hex1(cmd.commandClass)}${hex1(cmd.command)}, payload: " + cmd.parameter.collect{hex1(it)}.join(" ")
        zwaveEvent(cmd.sourceEndPoint, zwave.parse(desc, [ 0x60:3, 0x25:1, 0x32:1, 0x70:1 ]))
    }
}

Also in your testA function, can you change sourceEndPoint values from 3 to 1 ?

You should see debug messages for MultiChannelCmdEncap events if the command messages for requesting meter reports are formulated correctly. If you are getting nothing, most likely the commands you are sending are not correct. Let’s see if the above changes would at least generate some debug messages we can use.

@jwu

amended the device handler as suggested (amended code is copied in the end). on1-3,off1-3 work, on refresh tiles for power/energy/power1/energy1 get updated latter ones with lesser value, though power1/energy1 should have same data as main tiles as endpoint1 should give combined data.
… No debug messages are printed though from zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) :frowning:

Manual for the device is here:
http://www.z-wave.ru/podderzhka/skachat/category/82-tkbhome.html?download=200:tz04-manual

changed code:

/**
 *  Aeon Smartstrip
 */
 // for the UI
metadata {
	simulator {
		// TODO: define status and reply messages here
	}

	tiles {
        valueTile("power", "device.power", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy", "device.energy", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}
		standardTile("switch1", "device.switch1",canChangeIcon: true) {
                        state "on", label: "switch1", action: "off1", icon: "st.switches.switch.on", backgroundColor: "#79b821"
                        state "off", label: "switch1", action: "on1", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
                }
        standardTile("switch2", "device.switch2",canChangeIcon: true) {
                        state "on", label: "switch2", action: "off2", icon: "st.switches.switch.on", backgroundColor: "#79b821"
                        state "off", label: "switch2", action: "on2", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
                }
        standardTile("switch3", "device.switch3",canChangeIcon: true) {
                        state "on", label: "switch3", action: "off3", icon: "st.switches.switch.on", backgroundColor: "#79b821"
                        state "off", label: "switch3", action:"on3", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
                }
        standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
                        state "default", label:"", action:"refresh", icon:"st.secondary.refresh"
                }
        standardTile("reset", "device.switch", inactiveLabel: false, decoration: "flat") {
        				state "default", label:"reset kWh", action:"reset"
                }
        standardTile("configure", "device.switch", inactiveLabel: false, decoration: "flat") {
        				state "default", label:"", action:"configure", icon:"st.secondary.configure"
                }
                
        valueTile("power1", "device.power1", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy1", "device.energy1", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}
        valueTile("power2", "device.power2", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy2", "device.energy2", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}
        valueTile("power3", "zwaveEvent(cmd.sourceEndPoint, zwave.parse(desc, [ 0x60:3, 0x25:1, 0x32:1, 0x70:1 ]))device.power3", decoration: "flat") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy3", "device.energy3", decoration: "flat") {
			state "default", label:'${currentValue} kWh'
		}
        

        main(["switch1", "power", "energy"])
        details(["power", "energy", "reset", "switch1","switch2","switch3","refresh","configure",
        "power1", "energy1","power2", "energy2","power3", "energy3"])
	}
}

// 0x25 0x32 0x27 0x70 0x85 0x72 0x86 0x60 0xEF 0x82

// 0x25: switch binary
// 0x32: meter
// 0x27: switch all
// 0x70: configuration
// 0x85: association
// 0x86: version
// 0x60: multi-channel
// 0xEF: mark
// 0x82: hail

// parse events into attributes
def parse(String description) {
	 log.debug "Parsing desc => '${description}'"

    def result = null
    def cmd = zwave.parse(description, [0x60:3, 0x25:1, 0x32:1, 0x70:1])
    if (cmd) {
        result = createEvent(zwaveEvent(cmd))
    }
    log.debug "Parsing result => '${result}'"
    return result
}

//Reports

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
        [name: "switch", value: cmd.value ? "on" : "off", type: "physical"]
}

def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
        [name: "switch", value: cmd.value ? "on" : "off", type: "digital"]
}

def zwaveEvent(physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
	def map = []
    
	if (cmd.scale == 0) {
    	map = [ name: "energy", value: cmd.scaledMeterValue, unit: "kWh" ]
    }
    else if (cmd.scale == 2) {
    	map = [ name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W" ]
    }
    
    map
}

def zwaveEvent(int endPoint, physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
	// MeterReport(deltaTime: 1368, meterType: 1, meterValue: [0, 3, 29, 17], precision: 3, previousMeterValue: [0, 3, 29, 17], rateType: 1, reserved02: false, scale: 0, scaledMeterValue: 204.049, scaledPreviousMeterValue: 204.049, size: 4)
	 log.debug "EndPoint $endPoint, MeterReport $cmd"
    def map = []
    
    if (cmd.scale == 0) {
    	map = [ name: "energy" + endPoint, value: cmd.scaledMeterValue, unit: "kWh" ]
    }
    else if (cmd.scale == 2) {
    	map = [ name: "power" + endPoint, value: Math.round(cmd.scaledMeterValue), unit: "W" ]
    }
    
    map
}

/**
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
	 log.debug "MultiChannelCmdEncap $cmd"
    
    def map = [ name: "switch$cmd.sourceEndPoint" ]
    if (cmd.commandClass == 37){
    	if (cmd.parameter == [0]) {
        	map.value = "off"
        }
        if (cmd.parameter == [255]) {
            map.value = "on"
        }
        map
    }
    else if (cmd.commandClass == 50) {
    	// bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 100, 0, 0, 0, 0, 0, 94, 0, 0, 0, 0], res01: false, sourceEndPoint: 1
        def hex1 = { n -> String.format("%02X", n) }
        def desc = "command: ${hex1(cmd.commandClass)}${hex1(cmd.command)}, payload: " + cmd.parameter.collect{hex1(it)}.join(" ")
        // Re-assign source end point 3-6 to 1-4 and 1-2 to 5-6 to sync up with the switch end points. 
        // Source end point in the message refers to always-on sockets.
        zwaveEvent((cmd.sourceEndPoint > 3) ? (cmd.sourceEndPoint-3) : (cmd.sourceEndPoint+3), zwave.parse(desc, [ 0x60:3, 0x25:1, 0x32:1, 0x70:1 ]))
    }
}
*/
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
    log.debug "MultiChannelCmdEncap $cmd"
    
    def map = [ name: "switch$cmd.sourceEndPoint" ]
    if (cmd.commandClass == 37){
    	if (cmd.parameter == [0]) {
        	map.value = "off"
        }
        if (cmd.parameter == [255]) {
            map.value = "on"
        }
        map
    }
    else if (cmd.commandClass == 50) {
        def hex1 = { n -> String.format("%02X", n) }
        def desc = "command: ${hex1(cmd.commandClass)}${hex1(cmd.command)}, payload: " + cmd.parameter.collect{hex1(it)}.join(" ")
        zwaveEvent(cmd.sourceEndPoint, zwave.parse(desc, [ 0x60:3, 0x25:1, 0x32:1, 0x70:1 ]))
    }
}

def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCapabilityReport cmd) {
//	  [50, 37, 32], dynamic: false, endPoint: 1, genericDeviceClass: 16, specificDeviceClass: 1)
    log.debug "multichannelv3.MultiChannelCapabilityReport $cmd"
}

def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
	log.debug "Configuration Report for parameter ${cmd.parameterNumber}: Value is ${cmd.configurationValue}, Size is ${cmd.size}"
}

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

// handle commands
def refresh() {
	def cmds = []
 
 	for ( i in 1..3 )
    	cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:37, command:2).format()
    	
    for ( i in 1..3 ) {
        cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:50, command:1, parameter:[0]).format()
        cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:50, command:1, parameter:[16]).format()
    }
    
    cmds << zwave.meterV2.meterGet(scale:0).format()
    cmds << zwave.meterV2.meterGet(scale:2).format()
    
    delayBetween(cmds)
}

def on(value) {
	log.debug "value $value"
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:1, parameter:[255]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:2).format()
	])
}

def off(value) {
	log.debug "value $value"
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:1, parameter:[0]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint: value, commandClass:37, command:2).format()
	])
}

def poll() {
	refresh()
}

def configure() {
	log.debug "Executing 'configure'"
    delayBetween([
    	zwave.configurationV1.configurationSet(parameterNumber:101, size:4, configurationValue: [ 0, 0, 127, 127 ]).format(),	// Report meter on all channels (6 sockets + master)
        zwave.configurationV1.configurationSet(parameterNumber:111, size:4, scaledConfigurationValue: 120).format(),		// 120s interval for meter reports
        zwave.configurationV1.configurationSet(parameterNumber:4, configurationValue: [0]).format()				// Report reguarly
    ])
}

def on1() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[255]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
	])
}

def off1() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:1, parameter:[0]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:37, command:2).format()
	])
}

def on2() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:1, parameter:[255]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
	])
}

def off2() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:1, parameter:[0]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:37, command:2).format()
	])
}

def on3() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:1, parameter:[255]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:2).format()
	])
}

def off3() {
	delayBetween([
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:1, parameter:[0]).format(),
		zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:37, command:2).format()
	])
}

//test
def test() {
	def cmds = []
        // cmds << zwave.multiChannelV3.multiChannelCapabilityGet(endPoint:1).format()
        // cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[0]).format()
        // cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:3, commandClass:50, command:1, parameter:[16]).format()
	    // cmds << zwave.multiChannelV3.multiChannelEndPointFind(genericDeviceClass:32).format()
	    // cmds << zwave.multiChannelV3.multiChannelEndPointGet().format()
        // cmds << zwave.meterV2.meterGet(scale:0).format()
        // cmds << zwave.meterV2.meterGet(scale:2).format()
        cmds << zwave.configurationV1.configurationGet(parameterNumber:101).format() 
        cmds << zwave.configurationV1.configurationGet(parameterNumber:4).format()
        cmds << zwave.configurationV1.configurationGet(parameterNumber:90).format()
	log.debug "Sending ${cmds.inspect()}"
	delayBetween(cmds, 2300)
}

def testA() {
 log.debug "testA"
 def cmds = []
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:50, command:1, parameter:[0]).format()
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:50, command:1, parameter:[16]).format()
 delayBetween(cmds, 1000) 
 }

//reset
def reset() {
	def cmds = []
    cmds << zwave.meterV2.meterReset().format()
    cmds << zwave.meterV2.meterGet(scale:0).format()
    for ( i in 1..6 ) 
    	cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:i, commandClass:50, command:1, parameter:[0]).format()
        
	delayBetween(cmds)
}

Do you get meter reporting from endpoint 2 ? If yes, do the numbers from endpoint 1 & 2 add to the number reported in power / energy tiles ? I’m suspecting there’s no endpoint 3 in meter reporting if endpoint 1 and 2 are already reporting individual energy usage for each switch.

@jwu

I get data in tiles for power and power1, energy and energy1 but not in power2, power3, energy2, energy3.
on1/off1 work as expected , they trigger both relays on or off. on2/off2 only triggers relay1, on3/off3 triggers only relay2.
This is expected as per docs as Endpoint1 (default) triggers both relays and gives combined power. Endpoint2 controls relay1 and endpoint3 is for relay2. Data displayed in power/energy tile is sum of relay1 and relay2. switching the load on/off using on2/off2 or on3/off3 changes data in main power and energy tiles.

AFTER attaching dissimilar loads noticed that power1/energy1 tile displays data for relay2 (endpoint3). the main power/energy tiles display combined data. data for relay1 (endpoint2) is not being displayed at all. Note that relay1 (endpoint2) can be switched on or off using on2/off2.

Ok, just curious did anyone get this working w/ the virtual switches to control individual outlets and the metering working? Or does the device types from @chrisb work but just w/ the virtual switches and not metering of the power?

I was just curious, I read through this, but there are a lot of device types and it is a little hard to understand how to hook this all up, and if the metering is working as well.

@jjhamb did you get the metering working? What custom commands and attributes to i need to use for it?

@Morgan - I am still struggling with the metering parameters. Also note that I am trying to use this device handler code for a dual relay insert from TKBHOME. So far switching works but metering needs tweaking.

@chrisb I set up using your device type, and add 4 virtual switches and then used the binder, but I wasn’t getting the virtual switches to be able to control the individual plugs. It is suppose to do that correct?

@thrash99er, Yep… it should.

You’re using my DeviceType code? And you’re checking the power strip itself, right? With my device type I never got the tiles to work correctly on in the mobileapp. They’d never turn on or off in there, but using the virtual tiles and the binder program the outlets on the strip itself would turn on and off.

@jwu

For some reason log.debug wasn’t working for me… but today it did.
Copied below is output for testA() set for destinationEndPoint:1 then destinationEndPoint:2 and destinationEndPoint:3.
I also tried destinationEndPoint:0 & destinationEndPoint:4 … There was no response

Hope it tells you something. Iam also wondering why log.debug works some times and not at other times.

Regards

def testA() {
 log.debug "testA"
 def cmds = []
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:50, command:1, parameter:[0]).format()
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:1, commandClass:50, command:1, parameter:[16]).format()
 delayBetween(cmds, 1000) 
 }

9:19:20 PM: debug Parsing result => '[name:power1, value:113, unit:W, isStateChange:false, displayed:false, linkText:Kids Room, descriptionText:Kids Room power1 is 113 W]'
9:19:20 PM: debug EndPoint 1, MeterReport MeterReport(meterType: 33, meterValue: [0, 0, 4, 110], precision: 1, scale: 2, scaledMeterValue: 113.4, size: 4)
9:19:20 PM: debug MultiChannelCmdEncap MultiChannelCmdEncap(bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 52, 0, 0, 4, 110, 0, 0, 0, 0, 0, 0], res01: false, sourceEndPoint: 1)
9:19:20 PM: debug Parsing desc => 'zw device: 0A, command: 600D, payload: 01 01 32 02 21 34 00 00 04 6E 00 00 00 00 00 00 '
9:19:19 PM: debug Parsing result => '[name:energy1, value:0.60, unit:kWh, isStateChange:false, displayed:false, linkText:Kids Room, descriptionText:Kids Room energy1 is 0.60 kWh]'
9:19:19 PM: debug EndPoint 1, MeterReport MeterReport(meterType: 33, meterValue: [0, 0, 0, 60], precision: 2, scale: 0, scaledMeterValue: 0.60, size: 4)
9:19:19 PM: debug MultiChannelCmdEncap MultiChannelCmdEncap(bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 68, 0, 0, 0, 60, 0, 33, 0, 0, 0, 60], res01: false, sourceEndPoint: 1)
9:19:19 PM: debug Parsing desc => 'zw device: 0A, command: 600D, payload: 01 01 32 02 21 44 00 00 00 3C 00 21 00 00 00 3C '
9:19:18 PM: debug testA

def testA() {
 log.debug "testA"
 def cmds = []
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[0]).format()
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[16]).format()
 delayBetween(cmds, 1000) 
 }

9:21:41 PM: debug Parsing result => '[name:power2, value:29, unit:W, isStateChange:false, displayed:false, linkText:Kids Room, descriptionText:Kids Room power2 is 29 W]'
9:21:41 PM: debug EndPoint 2, MeterReport MeterReport(meterType: 33, meterValue: [0, 0, 1, 29], precision: 1, scale: 2, scaledMeterValue: 28.5, size: 4)
9:21:41 PM: debug MultiChannelCmdEncap MultiChannelCmdEncap(bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 52, 0, 0, 1, 29, 0, 0, 0, 0, 0, 0], res01: false, sourceEndPoint: 2)
9:21:41 PM: debug Parsing desc => 'zw device: 0A, command: 600D, payload: 02 01 32 02 21 34 00 00 01 1D 00 00 00 00 00 00 '
9:21:41 PM: debug Parsing result => '[name:energy2, value:0.38, unit:kWh, isStateChange:false, displayed:false, linkText:Kids Room, descriptionText:Kids Room energy2 is 0.38 kWh]'
9:21:41 PM: debug EndPoint 2, MeterReport MeterReport(meterType: 33, meterValue: [0, 0, 0, 38], precision: 2, scale: 0, scaledMeterValue: 0.38, size: 4)
9:21:41 PM: debug MultiChannelCmdEncap MultiChannelCmdEncap(bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 68, 0, 0, 0, 38, 1, 162, 0, 0, 0, 38], res01: false, sourceEndPoint: 2)
9:21:41 PM: debug Parsing desc => 'zw device: 0A, command: 600D, payload: 02 01 32 02 21 44 00 00 00 26 01 A2 00 00 00 26 '
9:21:39 PM: debug testA

def testA() {
 log.debug "testA"
 def cmds = []
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[0]).format()
 cmds << zwave.multiChannelV3.multiChannelCmdEncap(sourceEndPoint:1, destinationEndPoint:2, commandClass:50, command:1, parameter:[16]).format()
 delayBetween(cmds, 1000) 
 }

9:22:59 PM: debug Parsing result => '[name:power3, value:0, unit:W, isStateChange:false, displayed:false, linkText:Kids Room, descriptionText:Kids Room power3 is 0 W]'
9:22:59 PM: debug EndPoint 3, MeterReport MeterReport(meterType: 33, meterValue: [0, 0, 0, 0], precision: 1, scale: 2, scaledMeterValue: 0.0, size: 4)
9:22:59 PM: debug MultiChannelCmdEncap MultiChannelCmdEncap(bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], res01: false, sourceEndPoint: 3)
9:22:59 PM: debug Parsing desc => 'zw device: 0A, command: 600D, payload: 03 01 32 02 21 34 00 00 00 00 00 00 00 00 00 00 '
9:22:58 PM: debug Parsing result => '[name:power3, value:0, unit:W, isStateChange:true, displayed:true, linkText:Kids Room, descriptionText:Kids Room power3 is 0 W]'
9:22:58 PM: debug EndPoint 3, MeterReport MeterReport(meterType: 33, meterValue: [0, 0, 0, 0], precision: 1, scale: 2, scaledMeterValue: 0.0, size: 4)
9:22:58 PM: debug MultiChannelCmdEncap MultiChannelCmdEncap(bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], res01: false, sourceEndPoint: 3)
9:22:58 PM: debug Parsing desc => 'zw device: 0A, command: 600D, payload: 03 01 32 02 21 34 00 00 00 00 00 00 00 00 00 00 '
9:22:58 PM: debug Parsing result => '[name:energy3, value:0.23, unit:kWh, isStateChange:true, displayed:true, linkText:Kids Room, descriptionText:Kids Room energy3 is 0.23 kWh]'
9:22:58 PM: debug EndPoint 3, MeterReport MeterReport(meterType: 33, meterValue: [0, 0, 0, 23], precision: 2, scale: 0, scaledMeterValue: 0.23, size: 4)
9:22:58 PM: debug MultiChannelCmdEncap MultiChannelCmdEncap(bitAddress: false, command: 2, commandClass: 50, destinationEndPoint: 1, parameter: [33, 68, 0, 0, 0, 23, 1, 239, 0, 0, 0, 22], res01: false, sourceEndPoint: 3)
9:22:58 PM: debug Parsing desc => 'zw device: 0A, command: 600D, payload: 03 01 32 02 21 44 00 00 00 17 01 EF 00 00 00 16 '
9:22:55 PM: debug testA