Device handler not executing switch.on/off command during runIn scheduler

I have tried most everything in the forums to be able to switch on a z-wave device using the example code. I want to add a few additional buttons that turn on the device for a set amount of time and then switch them off. For example: 5/15/30/60/90/180 tiles within the switch/dimmer device. I do not want to use a smart app as I would like to flexibility to have a varying time duration to have the device operating from the control panel. I was able to modify the tile button to use the switch.on action which does turn the device on, but once the runIn code executes a few seconds during debugging, no physical action takes place; log code does execute just fine. Another thread that I found to be similar [Schedule() runs but is not sending commands to device] is stating to use the method sendHubCommand, however, I get a response is null in the logs. Ideally, I want a few tiles to call separate methods with a runIn delay to shut off the device.

Edit: current DTH

metadata {
	definition (name: "My Timer Z-Wave Switch", namespace: "smartthings", author: "Nate", ocfDeviceType: "oic.d.switch") {
capability "Actuator"
capability "Indicator"
capability "Switch"
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Health Check"
capability "Light"
    
    attribute "timerOff", "string"

    command "min1"
    command "min5"
    command "min10"
    command "min15"
    command "min30"
    command "min45"
    command "min60"
    command "min90"
    command "min120"//2hr
    command "min180"//3hr
    command "min240"//4hr
    command "min300"//5hr
    command "min360"//6hr
    
		fingerprint mfr:"0063", prod:"4952", deviceJoinName: "GE Wall Switch"
		fingerprint mfr:"0063", prod:"5257", deviceJoinName: "GE Wall Switch"
		fingerprint mfr:"0063", prod:"5052", deviceJoinName: "GE Plug-In Switch"
		fingerprint mfr:"0113", prod:"5257", deviceJoinName: "Z-Wave Wall Switch"
	}

	// simulator metadata
	simulator {
		status "on":  "command: 2003, payload: FF"
		status "off": "command: 2003, payload: 00"

		// reply messages
		reply "2001FF,delay 100,2502": "command: 2503, payload: FF"
		reply "200100,delay 100,2502": "command: 2503, payload: 00"
	}

	preferences {
		input "ledIndicator", "enum", title: "LED Indicator", description: "Turn LED indicator... ", required: false, options:["on": "When On", "off": "When Off", "never": "Never"], defaultValue: "off"
	}

	// tile definitions
	tiles(scale: 2) {
		multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
			tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
				attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC"
				attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
			}
		}

		standardTile("indicator", "device.indicatorStatus", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
			state "when off", action:"indicator.indicatorWhenOn", icon:"st.indicators.lit-when-off"
			state "when on", action:"indicator.indicatorNever", icon:"st.indicators.lit-when-on"
			state "never", action:"indicator.indicatorWhenOff", icon:"st.indicators.never-lit"
		}
		standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
			state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
		}

		valueTile("timeRemaining", "device.timeRemaining", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "default", label:'${currentValue}', backgroundColor:"#ffffff"
		}
		
    standardTile("min1_button", "device.min1", width: 2, height: 2) {
        state "off", label: "1 Min", backgroundColor: "#ffffff", nextState: "on", action:"min1"
        state "on", label: "1min Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    standardTile("min5_button", "device.min5", width: 2, height: 2) {
        state "off", label: "5 Min", backgroundColor: "#ffffff", nextState: "on", action:"min5"
        state "on", label: "5min Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    
    standardTile("min10_button", "device.min10", width: 2, height: 2) {
        state "off", label: "10 Min", backgroundColor: "#ffffff", nextState: "on", action:"min10"
        state "on", label: "10min Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    
    standardTile("min15_button", "device.min15", width: 2, height: 2) {
        state "off", label: "15 Min", backgroundColor: "#ffffff", nextState: "on", action:"min15"
        state "on", label: "15min Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    
    standardTile("min30_button", "device.min30", width: 2, height: 2) {
        state "off", label: "30 Min", backgroundColor: "#ffffff", nextState: "on", action:"min30"
        state "on", label: "30min Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    
    standardTile("min45_button", "device.min45", width: 2, height: 2) {
        state "off", label: "45 Min", backgroundColor: "#ffffff", nextState: "on", action:"min45"
        state "on", label: "45min Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    
    standardTile("min60_button", "device.min60", width: 2, height: 2) {
        state "off", label: "1 Hr", backgroundColor: "#ffffff", nextState: "on", action:"min60"
        state "on", label: "1 Hr Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    
    standardTile("min90_button", "device.min90", width: 2, height: 2) {
        state "off", label: "1.5 Hr", backgroundColor: "#ffffff", nextState: "on", action:"min90"
        state "on", label: "1.5 Hr Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    
    standardTile("min120_button", "device.min120", width: 2, height: 2) {
        state "off", label: "2 Hr", backgroundColor: "#ffffff", nextState: "on", action:"min120"
        state "on", label: "2 Hr Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    
    standardTile("min180_button", "device.min180", width: 2, height: 2) {
        state "off", label: "3 Hr", backgroundColor: "#ffffff", nextState: "on", action:"min180"
        state "on", label: "3 Hr Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    standardTile("min240_button", "device.min240", width: 2, height: 2) {
        state "off", label: "4 Hr", backgroundColor: "#ffffff", nextState: "on", action:"min240"
        state "on", label: "4 Hr Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    standardTile("min300_button", "device.min300", width: 2, height: 2) {
        state "off", label: "5 Hr", backgroundColor: "#ffffff", nextState: "on", action:"min300"
        state "on", label: "5 Hr Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }
    standardTile("min360_button", "device.min360", width: 2, height: 2) {
        state "off", label: "6 Hr", backgroundColor: "#ffffff", nextState: "on", action:"min360"
        state "on", label: "6 Hr Active", backgroundColor: "#00a0dc", nextState: "off", action:"switch.off"
    }

		main "switch"
		details(["switch","refresh", "timeRemaining", "min1_button", "min5_button", "min10_button", "min15_button", 
    			"min30_button", "min45_button", "min60_button", "min90_button", "min120_button",
                "min180_button", "min240_button", "min300_button", "min360_button"])
	}
}

def installed() {
	// Device-Watch simply pings if no device events received for 32min(checkInterval)
	sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}

def updated(){
		// Device-Watch simply pings if no device events received for 32min(checkInterval)
		sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
  switch (ledIndicator) {
    case "on":
        indicatorWhenOn()
        break
    case "off":
        indicatorWhenOff()
        break
    case "never":
        indicatorNever()
        break
    default:
        indicatorWhenOn()
        break
}
}

def parse(String description) {
	def result = null
	def cmd = zwave.parse(description, [0x20: 1, 0x70: 1])
	if (cmd) {
		result = createEvent(zwaveEvent(cmd))
	}
	if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) {
		result = [result, response(zwave.basicV1.basicGet())]
		log.debug "Was hailed: requesting state update"
	} else {
		log.debug "Parse returned ${result?.descriptionText}"
	}
	return result
}

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

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet 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.configurationv1.ConfigurationReport cmd) {
	def value = "when off"
	if (cmd.configurationValue[0] == 1) {value = "when on"}
	if (cmd.configurationValue[0] == 2) {value = "never"}
	[name: "indicatorStatus", value: value, displayed: false]
}

def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) {
	[name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false]
}

def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
	log.debug "manufacturerId:   ${cmd.manufacturerId}"
	log.debug "manufacturerName: ${cmd.manufacturerName}"
	log.debug "productId:        ${cmd.productId}"
	log.debug "productTypeId:    ${cmd.productTypeId}"
	def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
	updateDataValue("MSR", msr)
	updateDataValue("manufacturer", cmd.manufacturerName)
	createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
}


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

def on() {
	delayBetween([
		zwave.basicV1.basicSet(value: 0xFF).format(),
		zwave.switchBinaryV1.switchBinaryGet().format()
	])
}

def off() {
	delayBetween([
		zwave.basicV1.basicSet(value: 0x00).format(),
		zwave.switchBinaryV1.switchBinaryGet().format()
	])
}

def poll() {
	delayBetween([
		zwave.switchBinaryV1.switchBinaryGet().format(),
		zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
	])
}

/**
  * PING is used by Device-Watch in attempt to reach the Device
**/
def ping() {
		refresh()
}

def refresh() {
	delayBetween([
		zwave.switchBinaryV1.switchBinaryGet().format(),
		zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
	])
}

void indicatorWhenOn() {
	sendEvent(name: "indicatorStatus", value: "when on", displayed: false)
	sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format()))
}

void indicatorWhenOff() {
	sendEvent(name: "indicatorStatus", value: "when off", displayed: false)
	sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format()))
}

void indicatorNever() {
	sendEvent(name: "indicatorStatus", value: "never", displayed: false)
	sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format()))
}

def invertSwitch(invert=true) {
	if (invert) {
		zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format()
	}
	else {
		zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format()
	}
}

def min1events() {
	def min = 1
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)

}
def min1() {
		def min = 1
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min1events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

def min5events() {
	def min = 5
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min5() {
		def min = 5
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min5events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

def min10events() {
	def min = 10
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min10() {
		def min = 10
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min5events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

def min15events() {
	def min = 15
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min15() {
		def min = 15
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min15events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

def min30events() {
	def min = 30
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min30() {
		def min = 30
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min30events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

def min45events() {
	def min = 45
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min45() {
		def min = 45
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min45events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

def min60events() {
	def min = 60
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min60() {
		def min = 60
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min60events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

def min90events() {
	def min = 90
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min90() {
		def min = 90
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min90events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

def min120events() {
	def min = 120
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min120() {
		def min = 120
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min120events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

def min180events() {
	def min = 180
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min180() {
		def min = 180
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min180events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}
def min240events() {
	def min = 240
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min240() {
		def min = 240
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min240events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}
def min300events() {
	def min = 300
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min300() {
		def min = 300
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min300events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}
def min360events() {
	def min = 360
	log.debug "Turning off ${device.displayName} now via min${min}_button"
sendEvent(name: "min${min}", value: "off", isStateChange: true)
}
def min360() {
		def min = 360
    
    log.debug "Turning on ${device.displayName} now via min${min}_button"
    sendEvent(name: "min${min}", value: "on", isStateChange: true)

   	runIn(min*60, min360events) // 60*X execute handler in X minutes from now

    delayBetween([
            on(),
            off()
    ], min*60000) //in ms:   60000 is 1 minute, 5*60000 is 5 mins
}

I thought there is a delay in ms you can specify on the command, something like switch.off [delay : 5000]

1 Like

I’m not sure if that is a blocking delay and also wonder if there can be an
override to cancel if desired. That is why the runIn method appears best.
It just seems odd that there is no physical control of that device if
scheduling a task to switch on/off.

Have you tried using response to encapsulate the entire delayBetween or using the response to encapsulate the off().
It is entire possible that it may not work since zWave command are typically executed when the action is initiated with the user (tile press) or a the system (poll).

I am using runIn in a DTH myself to have a delay in between commands, but my sendHubActions are are located in the smartapp.

I was able to get the switch to turn on and off with the delay method,
however, I would like to be able to cancel the event. Would anyone happen
to provide a working snippet of code that either shows a working runIn
method or the encapsulated response? I have tried a few examples from the
web and not seeing any results. I appreciate the assistance thus far gents!

In device domoticzBlinds, but the hubactions are done in the app domoticz server

I took a look through these files; I don’t see how this is relevant due to my devices are Z-wave, not LAN connected requiring sockets. I have recently made headway using two methods and the delay function; this enables me to get the timer activity I desire from within the “things” instance.

The problem is now that the delay cannot be cancelled and will run if the timer is removed (e.g., manually turned back on for indefinite time, the previous delay will switch it off undesirably). I could really use some insight as to how specifically use the runIn method to schedule the device to be turned off and actually doing it. As I mentioned before, the logs show all debug messages print successfully it’s just that the physical control is not possible. I can’t find any documentation on how to use more elaborate methods to send the zwave commands. I am surprised that the timer functionality is not native in device handlers (or at least to choose it) since you can buy them with said behavior. I appreciate your continued support.

Scheduled methods can send commands to the device so there’s no need to send both commands together with a delay between them.

The on method shown below uses runIn to call the autoOff method and that method uses sendHubCommand to send the device the commands returned by the off method.

def on() {
	runIn(5, autoOff)
	[
		zwave.basicV1.basicSet(value: 0xFF).format(),
		zwave.basicV1.basicGet().format()
	]
}

void autoOff() {
	def cmds = []
	off()?.each {
		cmds << new physicalgraph.device.HubAction(it)
	}
	if (cmds) {
		sendHubCommand(cmds)
	}
}

def off() {
	[
		zwave.basicV1.basicSet(value: 0x00).format(),
		zwave.basicV1.basicGet().format()
	]
}

Thank you so much, this completed my objective!

1 Like