Another switch / virtual switch synchronization question

There have been several threads on the problem of having a virtual switch stay in synch with a physical switch. But I haven’t quite solved the problem.

In my case, I have legacy push-button GE RR8 low-voltage relays all over my home. I have built a sensor (Thanks to st_anything!) that properly reports that status of the PHYSICAL switch. But now I want to be able to display the real physical switch status on the virtual switch icon (just on and off) whether or not the status was changed by the SmartThings/Arduino or by the wall switch.

The “obvious” approach is to detect the change and then do a sendevent(). I don’t like this one so much. Here’s what happens.

  1. Person pushes the wall switch

  2. SmartApp is informed of the change in the relay status because it is subscribed to the arduino sensor’s device handler ON event.

  3. That SmartApp It says, “wow, the status of the relay changed” and sends an on() command to the virtual switch device handler.

  4. Of course, the Virtual Switch then says, well, I just got an on() command, so it send does a sendevent() to indicate that it is now on.

  5. So the smartapp that is subscribed to listen to the Virtual switch says… oh the user wanted to turn off the light from his phone, so it sends a on() command to the arduino device handler which pulsed the relay a second time.

What I’d like to be able to do is to send a message to the Virtual Switch to say, "Change your state, but don’t fire off the “I changed my state” message.

I’ve tried setting an attribute value, but I’m having trouble accessing that attribute… mostly syntax errors, I think.

Anyone have an approach to this they have liked?

Paul

I’ve not played with SmartApps yet so it will be interesting to see if I have the right idea if someone who really knows what they are talking about also responds.

You seem to be able to subscribeToCommand() which is supposed to mean your handler is only called when a particular command is run e.g. on(). So that means you don’t have to subscribe to events from the virtual device, you can just get told when on() gets run manually, as it were.

I also see that sendEvent() can take a device argument when used in SmartApps. I’m guessing that might mean at step 3) you can send the virtual switch an event to change its status so you never actually run the on() command.

I guess there might be a catch that the docs don’t make clear.

Devices in ST are event driven; the current value of an attribute (I.e. “switch") is the value of the last event, so you can’t change an attribute without an event. (Although note commands don’t have to create events and events can be created without executing commands.)

If the aurduino can report changes to the the current state and receive on/off commands, it would be better to have a switch device where the on/off command methods send commands to arduino, but do not create events for the device. The events (change attribute) should be triggered by the arduino sending notification of the change (after receiving and executing the command).

1 Like

If the aurduino can report changes to the the current state and receive on/off commands, it would be better to have a switch device where the on/off command methods send commands to arduino, but do not create events for the device

Thanks @TonyFleisher. The problem is that the “switch device” is a legacy switch from the 1990s without a way to send a software message/event in the way you imply.

Paul

@orangebucket, the problem here is that a VirtualSwitch is basically a switch. So the on() method does pretty standard stuff even if you don’t want it to do so.

metadata {
    definition (name: "Virtual Switch 2", namespace: "paulmullen", author: "Paul Mullen") {
    capability "switch"
    
    command stateOn
    command stateOff
    command remoteOn
    command remoteOff
    }

// simulator metadata
simulator {
}

// UI tile definitions
tiles {
	standardTile("Switch", "device.switch", width: 2, height: 2, canChangeIcon: false) {
		state "on", label: 'On', action: "switch.off", icon: "st.Lighting.light13", backgroundColor: "#00AA00"
		state "off", label: 'Off', action: "switch.on", icon: "st.Lighting.light13", backgroundColor: "#AAAAAA"
	}
	standardTile("OnButton", "device.button", width: 1, height: 1, canChangeIcon: false) {
		state "pushed", label: 'On', action: "switch.on", icon: "st.Kids.kid10"
	}
	standardTile("OffButton", "device.button", width: 1, height: 1, canChangeIcon: false) {
		state "pushed", label: 'Off', action: "switch.off", icon: "st.Kids.kid10"
	}
	main "Switch"
	details(["Switch","OnButton","OffButton"])
    
}

}

However, I’ve been considering creating a VirtualSwitch that does not inherit its methods from a device type of “switch”. Then I can create distinct commands/methods in the device handler that react differently to a user press on the app and event from the arduino. That’s the approach tonight if others don’t have better ideas.

The “switch device” I was referring to in the section quoted is the dth, not the physical device. Sorry for confusion.

What I was proposing is two things:

  1. That the SmartApp doesn’t call the on() or off() method on the virtual switch but does something different to communicate the updated status. If it doesn’t call on() or off() then there can’t be any confusion.

One way this could be done is to have the SmartApp call e.g. sendEvent(device, [name: “switch”, value: “on”]) where ‘device’ is your virtual switch. This doesn’t seem to be documented beyond it’s actual existence and doesn’t seem to do what I rather assumed it would. It actually does something far more sensible which is to call the virtual switch’s parse() method with the event Map as the argument, which you can then use to do a createEvent/sendEvent to change the status. I find this rather elegant as it is what the parse() method is there for.

The alternative would be to add custom command methods to the virtual switch, such as ‘syncOn()’ and ‘syncOff()’ that do the same as ‘on()’ and ‘off()’ without actually being them.

  1. Either of the above will still end up with on/off events being generated, so don’t subscribe to them in the SmartApp. Instead use subscribeToCommand() to listen for the on() and off() commands being called. This does, of course, rely on subscribeToCommand() working as described.

OK. The solution was embarrassingly simple. Simple, but not intuitive.

I created two new states (virtualOn and virtualOff). So when the sendEvent() is happens, the state will change, but anything else subscribed to on() and off() will not respond.

Here’s the final code.

metadata {
    definition (name: "Virtual Switch 2", namespace: "paulmullen", author: "Paul Mullen") {
    capability "switch"
    
    command stateOn
    command stateOff
    }

// simulator metadata
simulator {
}

// UI tile definitions
tiles {
	standardTile("Switch", "device.switch", width: 2, height: 2, canChangeIcon: false) {
		state "on", label: 'On', action: "switch.off", icon: "st.Lighting.light13", backgroundColor: "#00AA00"
		state "off", label: 'Off', action: "switch.on", icon: "st.Lighting.light13", backgroundColor: "#AAAAAA"
		state "virtualOn", label: 'On', action: "switch.off", icon: "st.Lighting.light13", backgroundColor: "#00AA00"
		state "virtualOff", label: 'Off', action: "switch.on", icon: "st.Lighting.light13", backgroundColor: "#AAAAAA"
      
	}
	standardTile("OnButton", "device.button", width: 1, height: 1, canChangeIcon: false) {
		state "pushed", label: 'On', action: "switch.on", icon: "st.Kids.kid10"
	}
	standardTile("OffButton", "device.button", width: 1, height: 1, canChangeIcon: false) {
		state "pushed", label: 'Off', action: "switch.off", icon: "st.Kids.kid10"
	}
	main "Switch"
	details(["Switch","OnButton","OffButton"])
    
}
}

def parse(String description) {
log.debug "Virtual switch 2 parse description: $description"
}


def on() {
 	sendEvent(name: "Switch", value: "on", isStateChange: "true")
    }

def off() {
   sendEvent(name: "Switch", value: "off", isStateChange: "true")
}

def stateOn() {
  // note that nothing subscribes to the device.virtualOn event, so this will trigger nothing other than the icon change in the app.
   sendEvent(name: "Switch", value: "virtualOn", isStateChange: "true")
}

def stateOff() {
  // note that nothing subscribes to the device.virtualOn event, so this will trigger nothing other than the icon change in the app.
   sendEvent(name: "Switch", value: "virtualOff", isStateChange: "true")
}
1 Like