Best laundry alerts for stackable units?

I use Mike’s dth that another member of community modified two include two buttons (#1one button is the dryer,#2 button is ‘washer’ or however you adapt the energy clamps) combined with webcore, it’s working solid.

Hardware You need: DSB09104 find it in link here:

/*
In-progress update of Mike Maxwell's HEM Laundry monitoring device for Aeon HEM V1. Includes customizations 
from Ogiewon and MEarly. 
SmartThings thread: https://community.smartthings.com/t/aeon-home-energy-meter-v1-read-clamps-separately/25480/100?u=danabw
Status: 
1. Done: Ogiewon - Fix reported number of buttons so both are announced/available in SmartApps  
2. Done: MEarly - Set Washer/Dryer On=pushed event; Washer/Dryer Off=held event. Allows notifications at start/end of cycle, & start/stop logging under Recent Activity.     
2. Done: Danabw - All Preferences entries labeled 
3. Need help: 1) Add reporting frequency setting to preferences (currently only managed in Configuration section, not available in preferences
4. Need help: 2) ID what KWhDelay and detailDelay settings control, and remove them from Preferences if user control not necessary  
*/
metadata {
	definition (name: "In-progress update of Maxwell's Aeon HEM V1 Laundry DTH", namespace:	"MikeMaxwell", author: "Mike Maxwell") 
	{
		capability "Configuration"
		capability "Switch"
        capability "Button"
        capability "Energy Meter"
        capability "Power Meter"
        capability "Actuator"
		capability "Holdable Button"
		capability "Sensor"
         capability "Refresh"
        capability "Polling"
        attribute "washerWatts", "string"
    attribute "dryerWatts", "string"
    attribute "washerState", "string"
    attribute "dryerState", "string"
        command "configure"
		fingerprint deviceId: "0x2101", inClusters: " 0x70,0x31,0x72,0x86,0x32,0x80,0x85,0x60"
	}
    preferences {
    input name: "c1Name", type: "text", title: "Clamp 1 device (e.g., Washer)", description: "", required: true
   	input name: "washerRW", type: "number", title: "Minimum watts device draws when running:", description: "", required: true
    input name: "c2Name", type: "text", title: "Clamp 2 device (e.g., Dryer)", description: "", required: true
    input name: "dryerRW", type: "number", title: "Minimum watts device draws when running:", description: "", required: true
    input name: "voltageValue", type: "number", title: "Line voltage: 120 or 240", description: "", required: true
    input name: "kWhCost", type: "number", title: "Cost per kWh", description: "", required: true
//	Not sure what kWhDelay is or does. Not sure how to remove it from the Preferences screen - it displaye w/no label before I created this entry.  
		input name: "kWhDelay", type: "number", title: "kWh Delay", description: "", required: true  
//	Not sure what detailDelay is or does. Not sure how to remove it from the Preferences screen - it displaye w/no label before I created this entry. 
		input name: "detailDelay", type: "number", title: "Detail Delay", description: "", required: true  
//	Would like to add an entry in Preferences to set the HEM reporting frequency (it is included in the Configuration  
//	section at the end of the code, but I don't know how to do that. :)
    }

//simulator {
//nothing currently in In-Progress
//}

	// simulator metadata
	simulator {
		for (int i = 0; i <= 10000; i += 1000) {
			status "power  ${i} W-ZZ": new physicalgraph.zwave.Zwave().meterV1.meterReport(
				scaledMeterValue: i, precision: 3, meterType: 33, scale: 2, size: 4).incomingMessage()
		}
		for (int i = 0; i <= 100; i += 10) {
			status "energy  ${i} kWh-ZZ": new physicalgraph.zwave.Zwave().meterV1.meterReport(
				scaledMeterValue: i, precision: 3, meterType: 33, scale: 0, size: 4).incomingMessage()
		}
        // TODO: Add data feeds for Volts and Amps
	}



}// end of metadata
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(physicalgraph.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 = "2Ulices Total\n${formattedValue}\nkWh"		// total kWh label
                sendEvent(name: "energyDisp", value: dispValue as String, unit: "", descriptionText: "Display Energy: ${newValue} kWh", displayed: true)
                state.energyValue = newValue
                [name: "energy", value: newValue, unit: "kWh", descriptionText: "Ulices1 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 = "Power \n"+newValue+"\nWatts"	// Total watts label
                sendEvent(name: "powerDisp", value: dispValue as String, unit: "", descriptionText: "Display Power: ${newValue} Watts", displayed: true)
                state.powerValue = newValue
                [name: "power", value: newValue, unit: "W", descriptionText: "TPower: ${formattedValue}W"]
           }
		}
 	}     
}


tiles(scale: 1) {
	multiAttributeTile(name:"laundryState", type: "generic", width: 6, height: 4, canChangeIcon: false){
    	tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
//            	attributeState "on", label:'Laundry Running', icon:"st.Appliances.appliances1", backgroundColor:"#53a7c0"
//            	attributeState "off", label:'Laundry Done', icon:"st.Appliances.appliances1", backgroundColor:"#ffffff"
            	attributeState "on", label:'', icon:"st.samsung.da.dryer_ic_dryer", backgroundColor:"#79b821"
            	attributeState "off", label:'', icon:"st.samsung.da.dryer_ic_dryer", backgroundColor:"#ffffff"
        	}
            tileAttribute("device.switch", key: "SECONDARY_CONTROL") {
             	attributeState "on", label:'Laundry Running'
            	attributeState "off", label:'Laundry Done'
    		}
        }   
/*        
        valueTile("washerState", "device.washerState", width: 3, height: 2, canChangeIcon: true) {
        	state "default", label:'Washer\n${currentValue}'        
        }
        valueTile("dryerState", "device.dryerState", width: 3, height: 2, canChangeIcon: true) {
        	state "default", label:'Dryer\n${currentValue}'        
        }
*/
		   
	
        standardTile("washerState", "device.washerState", width: 3, height: 3, canChangeIcon: true) {
        	state "off", label:'${name}', icon: "st.samsung.da.washer_ic_washer", backgroundColor:"#ffffff"
            state "on", label:'${name}', icon: "st.samsung.da.washer_ic_washer", backgroundColor:"#79b821"
        }
        standardTile("dryerState", "device.dryerState", width: 3, height: 3, canChangeIcon: true) {
        	state "off", label:'${name}', icon: "st.samsung.da.dryer_ic_dryer", backgroundColor:"#ffffff"
            state "on", label:'${name}', icon: "st.samsung.da.dryer_ic_dryer", backgroundColor:"#79b821"
        }
    	valueTile("washer", "device.washerWatts", width: 3, height: 2, decoration: "flat") {
        state("default", label:'Washer\n${currentValue} Watts', foregroundColor: "#000000")
    }

	valueTile("dryer", "device.dryerWatts", width: 3, height: 2, decoration: "flat") {
        state("default", label:'Dryer\n${currentValue} Watts', foregroundColor: "#000000")
    }

   
   	valueTile("power", "device.dryerWatts") {
			state (	"default", label:'${currentValue}W', foregroundColor: "#000000", backgroundColor:"#79b821" )
		}
   
   
	standardTile("configure", "device.configure", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
		state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
	}

	main (["power"])
	//main "laundryState"
	details(["laundryState","washerState","dryerState","washer","dryer","configure", "powerDisp"])
}





def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
	//log.info "mc3v cmd: ${cmd}"
	if (cmd.commandClass == 50) {  
    	def encapsulatedCommand = cmd.encapsulatedCommand([0x30: 1, 0x31: 1])
        if (encapsulatedCommand) {
        	def scale = encapsulatedCommand.scale
        	def value = encapsulatedCommand.scaledMeterValue
            def source = cmd.sourceEndPoint
            def str = ""
            def name = ""
        	if (scale == 2 ){ //watts
            	str = "watts"
                if (source == 1){
                	name = "washerWatts"
                    if (value >= settings.washerRW.toInteger()){
                    	//washer is on
                        sendEvent(name: "washerState", value: "on", displayed: true)
                    //button event
                    if (!state.washerIsRunning)
                    	sendEvent(name: "button", value: "pushed", data: [buttonNumber: 1], descriptionText: "Washer has started.", isStateChange: true)                        
                        state.washerIsRunning = true
                    } else {
                    	//washer is off
                        if (state.washerIsRunning == true){
                        	//button event
                            sendEvent(name: "button", value: "held", data: [buttonNumber: 1], descriptionText: "Washer has finished.", isStateChange: true)
                        }
                        sendEvent(name: "washerState", value: "off", displayed: false)
                        state.washerIsRunning = false
                    }
                } else {
                	name = "dryerWatts"
                    if (value >= settings.dryerRW.toInteger()){
                    	//dryer is on
                        sendEvent(name: "dryerState", value: "on", displayed: false)
                    //button event
                    if (!state.dryerIsRunning)
                    	sendEvent(name: "button", value: "pushed", data: [buttonNumber: 2], descriptionText: "Dryer has started.", isStateChange: true)                        
                        state.dryerIsRunning = true
                    } else {
                    	//dryer is off
                        if (state.dryerIsRunning == true){
                        	//button event
                            sendEvent(name: "button", value: "held", data: [buttonNumber: 2], descriptionText: "Dryer has finished.", isStateChange: true)
                        }
                        sendEvent(name: "dryerState", value: "off", displayed: false)
                        state.dryerIsRunning = false
                    }
                }
                if (state.washerIsRunning || state.dryerIsRunning){
                	sendEvent(name: "switch", value: "on", descriptionText: "Washer and/or Dryer running...", displayed: true)
                } else {
                	sendEvent(name: "switch", value: "off", displayed: false)
                }
                //log.debug "mc3v- name: ${name}, value: ${value}, unit: ${str}"
            	return [name: name, value: value.toInteger(), unit: str, displayed: false]
            }
        }
    }
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
	// Handles all Z-Wave commands we aren't interested in
    //log.debug "Unhandled event ${cmd}"
	[:]
}
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
}
def installed() {
	configure()
}
def updated() {
	configure()
}
def initialize() {
	sendEvent(name: "numberOfButtons", value: 2)
}
2 Likes