OK, so what I think is going on here is related to this sequence (this from on()
, but similar things happen elsewhere):
sendEvent(name: "switch", value: "on", isStateChange: true)
switchvalue = device.currentValue("switch", true)
My guess is that there is not enough time for the event to be "committed" (i.e., to the database), so when you attempt to read the current state immediately after, you will still see the previous state. If you just waited some number of milliseconds (10? 100? I don't know, but it's likely to vary, and this isn't really how I'd approach solving this problem), then you'd likely see different results. You can test this by using pauseExecution()
, but again, that's not really how I'd solve this.
Here's how I'd actually approach this problem. For the on()
and off()
commands, you are setting the (virtual) device to a specific state. Thus, you don't really need to read it after sending the event; you already know what it is. So instead of this:
def on() {
// ...
sendEvent(name: "switch", value: "on", isStateChange: true)
switchvalue = device.currentValue("switch", true)
// ...
}
you could do this:
def on() {
// ...
String switchValue = "on"
sendEvent(name: "switch", value: switchValue, isStateChange: true)
// ...
}
(or a variety of other ways you could write that, the idea being that you don't need to retrieve that value--you just set it and know what it is, or at least what it will be when the event actually happens in a few milliseconds.) In your original code, it looks like you have some sort of toggle thing going on, so if you need to read the current state in that case, you should just be able to do so before changing it, and then you'll know you're going to set it to and again don't need to retrieve the value afterwards (assuming your toggle just does an on if off or an off if on--maybe that's not what you're going for).
PS - Some unrelated notes on logging, if you care to follow convention. 
Generally, the "Enable descriptionText logging" does a log.info
with information from events you send (as the name suggests, the descriptionText
, though you aren't explicitly setting that here; something like "{device.displayName} switch is ${switchValue}"
is pretty common). Whether the driver checks to see if the device is already in that state before logging the event or not varies from driver to driver (I like to do it myself since there is a better chance it will match when events actually happen, though "Events" is the authoritative source for that regardless).
The "Enable debug logging" setting normally does a log.debug
with data coming in from the device, which is common for Z-Wave and Zigbee but not really a thing for virtual devices, but a newer trend (which I happen to like) is to also do debug logging when commands are executed. So, at the beginning of your on()
command, it would be more conventional to do something like this:
if (logEnable) log.debug "on()"
instead of this:
if (txtEnable) log.info "ON button pushed."
But, of course, that is totally up to you.
PPS - You likely don't need isStateChange: true
, though that really depends on the behavior you want. If you do a sendEvent(name: "switch", value: "on")
and the switch
attribute (name) is already on
(value), then the platform will just filter that out instead of generating another event. This is how most real-world devices work and usually reflects what users want to happen in apps that subscribe to these devices (usually nothing). But if you want an event to be generated each time regardless, you can keep the isStateChange: true
. It isn't related to your issue, and I know I mentioned this above--just wanted to bring it up again.