Wednesday, December 09, 2015

Schrödinger's Profile

How do you define when a Tasker profile is active?

One way, is to say it's active when all of its contexts are true.  In practice, though, that can sometimes be difficult to monitor directly, so we tend to use another method.  Commonly, we assume a profile is active when it runs its Entry task.

Normally, these two metrics align exactly and can be considered equivalent.  However, there are some profiles where that's not true; where the contexts are active, but the Entry task hasn't been run.

Since, depending on which definition we use, the profile can be considered both active and inactive, we call these Schrödinger's profiles.  (Yeah, okay.  Fine.  I'm the only one that calls them that, but I like it, and it's my blog.)

So what makes a profile behave like that, and what is it good for?

Creating a Schrödinger's profile is very easy; all you need to do is go into the profile properties and configure the Cooldown time.  Cooldown is a little-used parameter in Tasker that keeps a profile from running it's entry task until the the Cooldown time has elapsed.  If you set a Cooldown of 20 seconds, the profile can't activate any more often than that, even if all the explicit contexts for it are true.

One thing this type of profile is good for is ignoring some triggers, while allowing others of the same type to be acted on.

For example, in the Digital Dash project, I have a profile that watches for changes in the Google Maps navigation notification so that I can display ETA information on the main screen.
(As described here: http://mikesgeneralblog.blogspot.com/2015/10/navigation-eta-information-revisited.html.)

That notification can update many times a minute, particularly when there are a lot of navigation events happening close together, and when it starts counting down distance in tenths of a mile.  I'm not displaying the navigation instructions at all, and I don't really need second-by-second updates, so I've added a 30-second Cooldown to the profile that monitors the notification.  That way I still get data with the granularity I need, but don't waste a lot a system resources processing useless information; I get updates every 30 seconds even though the notification itself updates much more often than that.

The other thing you can do with a Schrödinger's profile is use it to replace a looping task.

I've never been conceptually happy with the Time and Torque task in the system.  I've previously described it as a "rogue task" because it's the only one that isn't triggered directly by some action. (http://mikesgeneralblog.blogspot.com/2015/06/digital-dash-documentation-part-9.html)  Instead, it gets kicked off by the Digital Dash startup task and simply loops about every 30 seconds until the system kills it via the shutdown task.  And that's always bothered me.  I'd prefer that it not run constantly in the background, and a profile with a Cooldown allows me trigger it to run regularly and on-demand without a loop.

Let's take a look at how to get that set up.  Let's say that, for some ungodly reason, you want Tasker to beep at you every five seconds.  We can make that happen.

The first thing to do is base a profile on a context that is always true.  For example, set up a global variable and Set it to something; doesn't matter what it is.  Then, you use a context of "Variable IS Set" in your profile.

Here's what that might look like (Assuming you've Set the %MyLoop variable elsewhere.)

Profile: Looper (468)
        Cooldown: 5
        State: Variable Value [ %MyLoop Set ]
Enter: Action (466)
        A1: Beep [ Frequency:8000 Duration:1000 Amplitude:12 Stream:3 ]


Enter this into Tasker and back all the way out.  After five seconds, you'll get a beep.  And after another five seconds you'll get...nothing.  And every five seconds after that, you'll get nothing.  You get one beep, and that's it.  Clearly, this isn't working.

That's because the Cooldown is really just a timer.  It gets triggered when the profile becomes active and starts counting down.  Once it reaches the end, the profile is allowed to run its Entry task.  The problem is, Cooldown doesn't automatically reset itself.  It only starts when the profile becomes active, and since, in the example above, the profile never becomes inactive, the Cooldown timer won't fire again.

So, we need to make the profile go inactive and then activate it again so the timer can restart itself.

We can easily add a line to the Entry task to accomplish the first part:

Profile: Looper (468)
        Cooldown: 5
        State: Variable Value [ %MyLoop Set ]
Enter: Action (466)
        A1: Variable Clear [ Name:%MyLoop Pattern Matching:Off ]
        A2: Beep [ Frequency:8000 Duration:1000 Amplitude:12 Stream:3 ]



By clearing the %MyLoop variable, we've made the profile go inactive because its context is no longer true.  Of course, we're still not going to get repeating beeps, because all we've done at this point is turn off the profile.  Now we need to activate it again.  We can do that by adding an Exit task.

Profile: Looper (468)
        Cooldown: 5
        State: Variable Value [ %MyLoop Set ]
Enter: Action (466)
        A1: Variable Clear [ Name:%MyLoop Pattern Matching:Off ]
        A2: Beep [ Frequency:8000 Duration:1000 Amplitude:12 Stream:3 ]

Exit: Reaction (467)
        A1: Variable Set [ Name:%MyLoop To:True Do Maths:Off Append:Off ]



So, what happens is, when the profile runs its Entry task (after the Cooldown period has expired) it gets turned off by line A1 in the Entry Task.  This causes it to immediately run its Exit task, which resets the %MyLoop variable, making the context True again, and restarting the Cooldown period.  Note that deactivating the profile does not stop the entry task from running to completion, so we get our beep.  And every five seconds after that, we'll get another beep until we go into Tasker and either disable the profile with its On/Off switch or explicitly clear the %MyLoop variable on the Var panel.

This is the technique I used to convert the Time and Torque loop to a triggered task rather than a loop, and it's working quite well.  I still get regular updates, but nothing is sitting in a Wait state.  And I'm happier with that.


No comments: