Coding switches to keep doors in synch with Echo

The standard “Simulated Switch” has separate commands for “physical.on” vs. regular, and that is how it can handle syncing SmartApps without looping. I keep thinking there is a better way to do this, but this works…

/**
 *  Copyright 2015 Terry Gauchat
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 */
definition(
	name: "Screen Down Up Virtual Switch App",
	namespace: "CosmicPuppy",
	author: "Terry Gauchat",
	description: "Turns on (down) and off (up) movie screen linking it to Virtual Switch and ThingShield Projector Device",
	category: "My Apps",
	iconUrl: "http://thumb1.shutterstock.com/display_pic_with_logo/1943207/159822338/stock-vector--projector-screen-159822338.jpg",
	iconX2Url: "http://thumb1.shutterstock.com/display_pic_with_logo/1943207/159822338/stock-vector--projector-screen-159822338.jpg",
    iconX3Url: "http://thumb1.shutterstock.com/display_pic_with_logo/1943207/159822338/stock-vector--projector-screen-159822338.jpg"
)

preferences {
	section("When this Virtual Screen Switch is turned on / off :") {
		input "virtualSwitch", "device.projectorVirtualScreen", title: "VirtualSwitch", multiple: false, required: true
	}
	section("Turn on (down) / off (up) Projector Screen:") {
		input "projector", "device.projectorScreenShield", multiple: false, required: true
	}
}

def installed()
{   
	subscribeToCommand(virtualSwitch, "switch.on", onHandler)
	subscribeToCommand(virtualSwitch, "switch.off", offHandler)
	subscribe(projector, "screen.on", screenPhysicalOnHandler)
	subscribe(projector, "screen.off", screenPhysicalOffHandler)
}

def updated()
{
	unsubscribe()
	subscribeToCommand(virtualSwitch, "on", onHandler)
	subscribeToCommand(virtualSwitch, "off", offHandler)
  	subscribe(projector, "screen.on", screenPhysicalOnHandler)
	subscribe(projector, "screen.off", screenPhysicalOffHandler)
}

def logHandler(evt) {
	log.debug evt.value
}

def onHandler(evt) {
    log.debug "Handler caught On request from virtualSwitch."
	projector.screenOn()
}

def offHandler(evt) {
    log.debug "Handler caught On request from virtualSwitch."
	projector.screenOff()
}

def screenPhysicalOnHandler(evt) {
    log.debug "Handler Caught screen Physical On. Should not move screen."
	virtualSwitch.onPhysical()
}

def screenPhysicalOffHandler(evt) {
    log.debug "Handler Caught screen Physical Off. Should not move screen."
	virtualSwitch.offPhysical()
}


/* =========== */
/* End of File */
/* =========== */
1 Like

I took your idea as a guide and made a small app (my first one, actually), that allows me to synchronize up to 4 virtual switches with four doors. I can now open/close my garage doors with Echo by having each virtual switch exposed to Echo and put in a suitable named group (e.g. Garage Door 1). I can even create a group for “All Garage Doors” to open/close them all at once.

/**
 *  Copyright 2015 Peter Freese
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 */
definition(
	name: "Virtual Slave Switch for Door",
	namespace: "PeterFreese",
	author: "Peter Freese",
	description: "Opens/closes a (garage) door from a virtual switch, keeping the switch in sync with the state of the door",
	category: "My Apps",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png"    
)

preferences
{
	section("Synced Switch/Door 1")
    {
		paragraph "some text"
		input name: "switch1", type: "device.simulatedSwitch", title: "Virtual Switch", multiple: false, required: true
		input name: "door1", type: "capability.doorControl", title: "Door Control", multiple: false, required: true
	}
	section("Synced Switch/Door 2")
    {
		input name: "switch2", type: "device.simulatedSwitch", title: "Virtual Switch", required: false
		input name: "door2", type: "capability.doorControl", title: "Door Control", required: false
	}
	section("Synced Switch/Door 3")
    {
		input name: "switch3", type: "device.simulatedSwitch", title: "Virtual Switch", required: false
		input name: "door3", type: "capability.doorControl", title: "Door Control", required: false
	}
	section("Synced Switch/Door 4")
    {
		input name: "switch4", type: "device.simulatedSwitch", title: "Virtual Switch", required: false
		input name: "door4", type: "capability.doorControl", title: "Door Control", required: false
	}
}

def initialize()
{
	log.debug "initialize"
//	subscribe([switch1,switch2,switch3,switch4], "switch", logHandler)
	subscribe([switch1,switch2,switch3,switch4], "switch.on", onHandler)
	subscribe([switch1,switch2,switch3,switch4], "switch.off", offHandler)
	subscribe([door1,door2,door3,door4], "door.open", doorOpenedHandler)
	subscribe([door1,door2,door3,door4], "door.closed", doorClosedHandler)
}

def installed()
{   
	log.debug "Installed with settings: ${settings}"
	initialize()
}

def updated()
{
	log.debug "Updated with settings: ${settings}"
	unsubscribe()
    initialize()
}

def logHandler(evt)
{
	log.debug "event device: ${evt.device}"
    log.debug "event id:     ${evt.id}"    
    log.debug "event name    ${evt.name}"    
	log.debug "event value:  ${evt.value}"
	log.debug "event data:   ${evt.data}"
}

def String eventDump(evt)
{
	String s = "Event: { "
    s += "device: ${evt.device}, "
    s += "deviceId: ${evt.deviceId}, "
    s += "displayName: ${evt.displayName}, "
    s += "}"
    return s;
}

// TODO: figure out how to persist these
def getPairs()
{
	return [ [switch1,door1], [switch2, door2], [switch3,door3], [switch4,door4]]
}

def onHandler(evt)
{
	log.debug "Handler caught On request from virtual Switch: $evt.device"
    for (v in getPairs())
	{
		if ( (v[0] != null) && (v[0].id == evt.device.id) && (v[1] != null))
		{
   			log.debug "Opening door ${v[1]} in response to switch ${v[0]}"
        	v[1].open()
		}
	}
}

def offHandler(evt)
{
	log.debug "Handler caught Off request from virtual Switch: $evt.device"
    for (v in getPairs())
	{
		if ( (v[0] != null) && (v[0].id == evt.device.id) && (v[1] != null))
		{
   			log.debug "Closing door ${v[1]} in response to switch ${v[0]}"
        	v[1].close()
		}
	}
}

def doorOpenedHandler(evt)
{
	log.debug "Handler caught door opened event on door: $evt.device"
    for (v in getPairs())
	{
		if ( (v[1] != null) && (v[1].id == evt.device.id) && (v[0] != null))
		{
   			log.debug "Setting switch ${v[0]} ON in response to door ${v[1]}"
        	v[0].onPhysical()
		}
	}
}

def doorClosedHandler(evt)
{
	log.debug "Handler caught door closed event on door: $evt.device"
    for (v in getPairs())
	{
		if ( (v[1] != null) && (v[1].id == evt.device.id) && (v[0] != null))
		{
   			log.debug "Setting switch ${v[0]} OFF in response to door ${v[1]}"
        	v[0].offPhysical()
		}
	}
}
2 Likes

Yes… mine is responding to open/close

2 Likes

Why not just put them in a state variable?

1 Like

Why not just put them in a state variable?

I’m still learning Groovy and the nuances of SmartThings app development. Is a state variable the right way to persist that kind of data?

I admit that I struggled for quite a while trying to figure out how to declare a simple structure (e.g., class SwitchDoorPair { def switch, door } ) in Groovy before realizing that I couldn’t with SmartThings, and I ended up creating those two arrays as a fallback to avoid lots of duplicate if code. There’s probably a better way of doing it still, but I’m a Groovy neophyte.

Also, is it known that subscribe( [array], eventName, handler) works, but subscribeToCommand( [array], eventName, handler) doesn’t (i.e, the array overload behavior is missing)?

1 Like

You mean command as the second parameter, right?

But for the first pparameter, indeed the documentation says Device or Devices. You get no signature error?

Changing the function call to subscribeToCommand results in the following error:

 error groovy.lang.MissingMethodException: No signature of method: script14454222442391295081637.subscribeToCommand() is applicable for argument types: (java.util.ArrayList, java.lang.String, java.lang.String) values: [[Virtual Switch for Garage Door 1, Virtual Switch for Garage Door 2, ...], ...]
Possible solutions: subscribeToCommand(physicalgraph.app.DeviceWrapper, java.lang.String, java.lang.String), subscribeToCommand(physicalgraph.app.DeviceWrapper, java.lang.String), subscribeToCommand(physicalgraph.app.DeviceWrapper, java.lang.String, java.lang.String, java.util.Map) @ line 53

It took me quite a while to figure out what I was doing wrong, and in the end I decided that it probably wasn’t something I was doing wrong after all – the subscribeToCommand version is just wonky.

1 Like

Be sure to raise it to Support@SmartThings.com and tag @Slagle.

Moved to new topic so it doesn’t clutter the Echo thread with groovy syntax discussions.

1 Like

Thanks Tim!

Any word on the bug described above by @Peter_Freese?