installedSmartAppId not consistent across events - Workaround?

Hi Everyone,

I apologize for any errors here as I am new to the platform, but hopefully my research thus far or this discussion will help others.

I have been working on an app that I want to restore light settings (on/off/level) to the last known external (not set by my app) values. Looking for .isPhysical on events gets part way there, but does not cover if the last light value was set by another app. It seemed like looking for the last event with a installedSmartAppId that did not match app.id would work, but not all events are properly tagged with the triggering appid it seems. So this code does not work:

swEvents.find {(it.name == "level" || it.name == "switch") && (it.installedSmartAppId != app.id}

When I looked into the list of events after issuing switches.setLevel(100) in my app (excuse my sloppy debug format):

ID: 4a60d368-ae72-11e5-8b20-22000aef9668 | AppID: null | Description: TESTDIM1 level is 100 | Name: TESTDIM1 (level) | App: KODI Playback Controls | Value: 100 | Source: DEVICE | Desc:
ID: 4a665cee-ae72-11e5-8b20-22000aef9668 | AppID: null | Description: TESTDIM1 set level is 100 | Name: TESTDIM1 (switch.setLevel) | App: KODI Playback Controls | Value: 100 | Source: DEVICE | Desc:
ID: 4a3283d7-ae72-11e5-8b20-22000aef9668 | AppID: d8b42bac-a8e5-4373-b0bf-4a566a2f72a3 | Description: null | Name: KODI Playback Controls (null) | App: KODI Playback Controls | Value: setLevel | Source: APP_COMMAND | Desc: KODI Playback Controls sent setLevel command to TESTDIM1

There are three events for the single setLevel command, the two “useful” ones (level and switch.setLevel) both have a null AppID even though they were triggered by a specific app. The third event seems to actually log the fact that the application initiated the event and correctly has an AppID. The problem here is that I want to exclude the events with null AppIDs when searching through events, but there is nothing particularly distinct to distinguish them from other app events.

Then I noticed a pattern in the event IDs. The first 8 digits are unique, but the remaining 4 sets always seem to match for any given action taken (in this case the single setLevel command issued by the app). Using this I created a nested search to see if each level event had a corresponding “app event” (with matching AppID). This looks like so:

def lastState = swEvents.find {
    (it.name == "level" || it.name == "switch") && (swEvents.find{it2 -> it2.id.toString().substring(8) == it.id.toString().substring(8) && it2.installedSmartAppId == app.id} == null)
    }

So far this seems to work correctly in finding me the last level or switch event not triggered by my app. I have some concerns about this being a totally undocumented workaround based on an assumption about event ID formats, but I don’t see a better way at the moment.

Does any one know of a better way to accomplish this or know why installedSmartAppId is not corrctly listed on all events? I did search through the forums for answers and only found a dead end discussion on the same topic from a few months back (How to tell when an event was triggered by the current app instance).

Appreciate any feedback or discussion!

Good question! I messed with this several months ago and became frustrated and gave up. You’ve done a great job of drilling into it.

@Jim, this whole topic is not documented as far as I can tell.

1 Like

No, it is not documented.

I’ve added a backlog item to work with engineering to see if this is something that is possible/supported, and document it if it is.

By chance do you know what app triggered the first two with null appId’s? I see the event SOURCE is DEVICE in those two.

All three were triggered by calling this once:

 switches.setLevel(100)

in an app I am working on (KODI Playback Controls). In theory they should have the same AppId as the third event.

Does that help?

Best way to see this is to create a virtual switch (or in this case dimmer switch). Create an app that issues a setLevel command to that switch and pull a log of all events on that switch. Assuming this app is the only thing that has “touched” that virtual switch all of the events should in theory have that app’s ID as their installedSmartAppId. What you will quickly see is this is not the case at all =(

What I’m confused by the event source. In the example logs above, the event source is DEVICE. So if the event that triggered your handler to be called was not an event from the app itself, it makes sense to me that the installedSmartAppId would be null.

For example - if I subscribe to an event like this:

subscribe(lights, "level", levelHandler)

def lightHandler(evt) {
   log.debug "smartApp ID: $evt.installedSmartAppId"
   log.debug "event source: $evt.source"
}

Then if the switch level changes from physical actuation, mobile app actuation, etc., it makes sense that the SmartApp ID is not set.

But I am seeing that even though my app is calling setLevel(), the event source is DEVICE and there is no SmartApp ID on the event.

I’ll open an internal ticket for this, but it wouldn’t hurt to open a ticket with support as well.

1 Like

Yup, that is exactly the issue. Why/how is an app command creating a DEVICE event. Thanks for looking into it, I can certainly open a support case =)

I think this has always happened this way. First there is an app command, then at some point the device itself responds with a DEVICE event, because it has turned on or off. This is what I was trying to filter, so I didn’t respond to the device that was simply echoing what my app had done. I’m pretty sure that in this case, the device did not send isPhysical with that event. But, still, I couldn’t figure it out.

1 Like

You may be right about it always being this way. In my case, when I execute setLevel() in a SmartApp, I get a DEVICE event only - no APP event.

1 Like

I am new to the platform, so it probably has always been that way. I would argue that adding the triggering appID to those device events would be at least a valuable enhancement if not actually a bug. Unless there is some other more supported way to find the last device settings not set by your app. Having the ability to restore back to previous settings once an app is “done” is a very valuable feature.

1 Like

Using my workaround code in a Kodi lighting control endpoint if anyone wants to look over an example:

1 Like

Hmm. Found some oddities with this tonight. Usually the last digits of the ID are the same, but sometimes they are not. Welcome to the joys of trying to find undocumented workarounds. I will keep poking around and see if I can find why until @Jim finds us an official answer =/

In the mean time, add detection based on timestamp within 6 seconds seems to work pretty well:

def last = devEvents.find {
    (it.name == "level" || it.name == "switch") && (devEvents.find{it2 -> it2.installedSmartAppId == app.id && (it2.id.toString().substring(8) == it.id.toString().substring(8) || Math.sqrt((it2.date.getTime() - it.date.getTime())**2) < 6000 )} == null)
    }
2 Likes

Thanks for digging into this. Would love to know Smart Things gets this documented.

@jim any official updates on this topic?