Moving Simple Garage Door App from ST

HE Gang,
My total transformation from 70+ ST devices into the great world of HE is almost complete! Loving the change so far.

Need some help, though. This particular App from ST doesn't seem to be working for me in HE. The code doesn't show any errors. Is there anything glaring in this where those more experienced than me can look at it and say, "oh well that will never work, you big dumb animal"!

The driver from ST works just fine. I updated them from physicalgraph to hubitat, etc. I have confirmed the driver is reporting in the logs and acting just as it should when pushing the buttons, etc. However, the app seems to be where everything drops. It seems weird to me though that what I thought was a super simple app doesn't seem to be working as intended...

The reason I want to use this app is because it avoids the need to add a virtual switch, it simply uses the contact sensor as the button. Anyone have any ideas on this one? Any help is appreciated!

/**
 *  Virtual Garage Triggers Outlet
 *
 *  Author: chrisb
 */

// Automatically generated. Make future change here.
definition(
    name: "Virtual Garage triggers outlet",
    namespace: "sirhcb",
    author: "ChrisB",
    description: "Tapping a SmartSense Virtual Garage Sensor Button triggers and outlet connected to a relay to open/close a garage door, and then shuts off the outlet after 4 seconds.",
    category: "My Apps",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png",
    oauth: true)

preferences {
	section("When a Virtual Garage Door is tapped..."){
		input "GarageSensor1", "capability.contactSensor", title: "Which?"
	}
	section("Trigger which outlet?"){
		input "switches", "capability.switch"
	}
}


def installed()
{
	subscribe(GarageSensor1, "buttonpress.true", contactOpenHandler)
}

def updated()
{
	unsubscribe()
	subscribe(GarageSensor1, "buttonpress.true", contactOpenHandler)
}

def contactOpenHandler(evt) {
	log.debug "$evt.value: $evt, $settings"
	log.trace "Turning on switches: $switches"
	switches.on()
}

I’m not sure if your goal is converting code from ST or just getting a garage door working.

I suppose in either case it may be helpful to know that Zooz has a virtual garage door opener app which is published in HPM and works well.

I can only seem to find apps that require an additional virtual switch. This code simply uses the sensor itself as a button so you don’t have to introduce that third variable. At least that’s the way it was in ST.

I have the driver working just fine and doing everything it’s supposed to. For some reason this app doesn’t appear to be playing nicely, though.

Does Hubitat have one like this where it uses only the relay and sensor, not a third variable (the virtual switch)? I’ll check out the prebuilt Zooz apps to see if they work with GoControl relays as well.

I guess I’m not even sure what you mean.

The zooz app has an input of a sensor, either tilt or contact. This is how it detects the door state. It has an output of a relay control, which is wired into the bell wire button system of the opener.

The app then creates a virtual garage door, which has all the features of a smart-ish garage door.

Optionally you can configure some secondary switching and delaying to make it more consistent with the physical door. For example how long it takes to open or close the door, or sounding a siren before moving the door.

Would you mind posting the code from the driver too? Button devices work differently in Hubitat from SmartThings, and I think that may be why the app isn’t working for you right now.

That could be it. The driver code is below. Have been messing with this for hours. Appreciate the extra set of eyes.

metadata {
	definition (name: "Natty - Z-Wave Garage Door Sensor Button", namespace: "Natty", author: "Chris LeBlanc") {
		capability "Garage Door Control"
		capability "Contact Sensor"
		capability "Actuator"
		capability "Sensor"
		capability "Battery"
		capability "Door Control"
        capability "Switch"
        capability "Refresh"
        capability "Temperature Measurement"

		attribute "status", "string"
		attribute "buttonPress", "string"

		command "actuate"
	}

	tiles {
		standardTile("status", "device.status", width: 2, height: 2) {
			state("open", label:'${name}', icon:"st.doors.garage.garage-open", action: "actuate", backgroundColor:"#ffa81e", nextState:"closing")
			state("closed", label:'${name}', icon:"st.doors.garage.garage-closed", action: "actuate", backgroundColor:"#ffffff", nextState:"opening")
			state("opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
			state("closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")
		}
		standardTile("contact", "device.contact") {
			state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
			state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
		}
	}
		valueTile("temperature", "device.temperature", width: 2, height: 2) {
			state("temperature", label:'${currentValue}°',
				backgroundColors:[
					[value: 31, color: "#153591"],
					[value: 44, color: "#1e9cbb"],
					[value: 59, color: "#90d2a7"],
					[value: 74, color: "#44b621"],
					[value: 84, color: "#f1d801"],
					[value: 95, color: "#d04e00"],
					[value: 96, color: "#bc2323"]
				]
			)
		}
		valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
			state "battery", label:'${currentValue}% battery', unit:""
		}
		main(["status","contact", "acceleration"])
		details(["status","contact", "acceleration"])
	}


def parse(String description) {
	def result = null
	if (description.startsWith("Err 106")) {
		if (state.sec) {
			log.debug description
		} else {
			result = createEvent(
				descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.",
				eventType: "ALERT",
				name: "secureInclusion",
				value: "failed",
				isStateChange: true,
			)
		}
	} else if (description != "updated") {
		def cmd = zwave.parse(description, [0x20: 1, 0x25: 1, 0x30: 1, 0x31: 5, 0x80: 1, 0x84: 1, 0x71: 3, 0x9C: 1])
		if (cmd) {
			result = zwaveEvent(cmd)
		}
	}
	log.debug "parsed '$description' to $result"
	return result
}

def updated() {
	def cmds = []
	if (!state.MSR) {
		cmds = [
			zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
			"delay 1200",
			zwave.wakeUpV1.wakeUpNoMoreInformation().format()
		]
	} else if (!state.lastbat) {
		cmds = []
	} else {
		cmds = [zwave.wakeUpV1.wakeUpNoMoreInformation().format()]
	}
	response(cmds)
}

def configure() {
	delayBetween([
		zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
		batteryGetCommand()
	], 6000)
}

def sensorValueEvent(value) {
	def results = []
	if (value) {
		results << createEvent(name: "contact", value: "open", descriptionText: "$device.displayName sensor is open")
        results << createEvent(name: "status", value: "open", unit: "")
        results << createEvent(name: "door", value: "open", unit: "")
	} else {
		results << createEvent(name: "contact", value: "closed", descriptionText: "$device.displayName is closed")
        results << createEvent(name: "status", value: "closed", unit: "")
        results << createEvent(name: "door", value: "closed", unit: "")
	}
    
    results
}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd)
{
	sensorValueEvent(cmd.value)
}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicSet cmd)
{
	sensorValueEvent(cmd.value)
}

def zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd)
{
	sensorValueEvent(cmd.value)
}

def zwaveEvent(hubitat.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd)
{
	sensorValueEvent(cmd.sensorValue)
}

def zwaveEvent(hubitat.zwave.commands.sensoralarmv1.SensorAlarmReport cmd)
{
	sensorValueEvent(cmd.sensorState)
}

def zwaveEvent(hubitat.zwave.commands.notificationv3.NotificationReport cmd)
{
	def result = []
	if (cmd.notificationType == 0x06 && cmd.event == 0x16) {
		result << sensorValueEvent(1)
	} else if (cmd.notificationType == 0x06 && cmd.event == 0x17) {
		result << sensorValueEvent(0)
	} else if (cmd.notificationType == 0x07) {
		if (cmd.v1AlarmType == 0x07) {  // special case for nonstandard messages from Monoprice door/window sensors
			result << sensorValueEvent(cmd.v1AlarmLevel)
		} else if (cmd.event == 0x01 || cmd.event == 0x02) {
			result << sensorValueEvent(1)
		} else if (cmd.event == 0x03) {
			result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
			result << response(zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId))
			if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
		} else if (cmd.event == 0x05 || cmd.event == 0x06) {
			result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
		} else if (cmd.event == 0x07) {
			if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
			result << createEvent(name: "motion", value: "active", descriptionText:"$device.displayName detected motion")
		}
	} else if (cmd.notificationType) {
		def text = "Notification $cmd.notificationType: event ${([cmd.event] + cmd.eventParameter).join(", ")}"
		result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, displayed: false)
	} else {
		def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive"
		result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, displayed: false)
	}
	result
}

def zwaveEvent(hubitat.zwave.commands.wakeupv1.WakeUpNotification cmd)
{
	def event = createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
	def cmds = []
	if (!state.MSR) {
		cmds << zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId).format()
		cmds << zwave.manufacturerSpecificV2.manufacturerSpecificGet().format()
		cmds << "delay 1200"
	}
	if (!state.lastbat || now() - state.lastbat > 53*60*60*1000) {
		cmds << batteryGetCommand()
	} else {
		cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
	}
	[event, response(cmds)]
}

def zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd) {
	def map = [ name: "battery", unit: "%" ]
	if (cmd.batteryLevel == 0xFF) {
		map.value = 1
		map.descriptionText = "${device.displayName} has a low battery"
		map.isStateChange = true
	} else {
		map.value = cmd.batteryLevel
	}
	state.lastbat = now()
	[createEvent(map), response(zwave.wakeUpV1.wakeUpNoMoreInformation())]
}

def zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
	def result = []

	def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
	log.debug "msr: $msr"
	updateDataValue("MSR", msr)

	result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false)

	if (msr == "011A-0601-0901") {  // Enerwave motion doesn't always get the associationSet that the hub sends on join
		result << response(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
	} else if (!device.currentState("battery")) {
		if (msr == "0086-0102-0059") {
			result << response(zwave.securityV1.securityMessageEncapsulation().encapsulate(zwave.batteryV1.batteryGet()).format())
		} else {
			result << response(batteryGetCommand())
		}
	}

	result
}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
	def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1])
	// log.debug "encapsulated: $encapsulatedCommand"
	if (encapsulatedCommand) {
		state.sec = 1
		zwaveEvent(encapsulatedCommand)
	}
}

def zwaveEvent(hubitat.zwave.Command cmd) {
	createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)
}

def batteryGetCommand() {
	def cmd = zwave.batteryV1.batteryGet()
	if (state.sec) {
		cmd = zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd)
	}
	cmd.format()
}


 
 def actuate() {
	sendEvent(name: "buttonPress", value: "true", isStateChange: true, unit: "")
}
def open() {
	if (device.currentValue("status") != "open") {
		log.debug "Sending button press event to open door"
		actuate()
	}
	else {
		log.debug "Not opening door since it is already open"
	}
}

def close() {
	if (device.currentValue("status") != "closed") {
		log.debug "Sending button press event to close door"
		actuate()
	}
	else {
		log.debug "Not closing door since it is already closed"
	}
}

def on()
{
open()
}

def off()
{
close()
}

Ok, nothing out of the ordinary there. It’s presenting a custom attribute for the button press, but that shouldn’t be a problem.

Can you confirm that the logs include the following?

Sending button press event to open door

Or this?

Sending button press event to close door

That’ll help confirm that the driver is actually sending the event correctly. Also check the Events page for the device to confirm that the event is shown there.

If that’s all working, then it’s possible that it’s getting confused by using true as a string instead of a boolean. But we can try that after we’ve confirmed that the events are getting sent from the device.

Yessir! See below from me just pressing the button. It’s recognizing when we come and go otherwise too (that’s what the other messages are).

I have tried about 5 other drivers and apps for this similar functionality, but none seem to be quite as good as these IMO.

I honestly appreciate this, I thought it’d be working just fine. Thought maybe I was overlooking something easy...

Ok, perfect. That eliminates that possibility. Let’s focus on the app. Can you go to the app page and press the gear icon:

I want to see the Settings and the Event Subscriptions sections, like this:

See below:

Dang it, I should’ve noticed this sooner. In the driver, the attribute is buttonPress, but the app is subscribing to buttonpress. The capitalization is different.

In the app code, change these lines (there are 2 of them):

subscribe(GarageSensor1, "buttonpress.true", contactOpenHandler)

to this:

subscribe(GarageSensor1, "buttonPress.true", contactOpenHandler)

Then open up the app itself and press Done. Then try pressing the Actuate button in the device and see if it works.

The app settings page should now look like this:

Mike!!!! I knew it was something simple. Had to be. I’ve honestly looked at this for like 4+ hours and love that it was capitalization. That has tripped me up once before in Hubitat and I didn’t even think of it this time. Everything I was seeing, I would have sworn everything was good. You’re the man! Thank you!!