In my last post about my Slide Wear project, I mentioned that I was having trouble coming up with any kind or practical application for my Zenwatch 2 in conjunction with the Digital Dash project. Well, I still haven't found anything practical, but I did come up with something kind of fun.
The other day when I was browsing G+ communities related to my watch, I came across a custom face that some guy had created based on his fondness for BMW motorcycles. The logo caught my eye and got me thinking that I could probably make my own themed watchface as well. He mentioned that he had made his in an app called "Watchmaker". I started doing a bit of research into that app and found some truly amazing faces that people had created. The app not only has incredible flexibility in design, but also includes a focused version of the Lua programming language; it's pretty much like Tasker for your watch.
I went to the Google Play Store and saw the app was currently 50% off. That pretty much clinched the deal right there, but when i saw this in the feature list:
• Tasker - Full tasker integration to set watchface, change variables, run tasks
I was hooked. I had the app on my phone within minutes.
The watchfaces in this picture (shown in both their bright and dimmed states) are the first things I did with Watchmaker.
Can you guess where I'm going with this?
If you're one of the 26 people who read my post entitled "A Few More Changes" you know that I added a feature to the Digital Dash that allows it to know which vehicle it's connected to and display the appropriate logo on the tablet's main interface. Given that I already had that code in place, it wasn't hard to add a single line that sent an AutoRemote direct message to the phone with the formal XXX=:=Face (where XXX is either BMW or JAG).
A Tasker profile on the phone recognizes this command and uses the parameter to call an appropriate task that sets my watch to full brightness and activates the proper face so that my watch is themed appropriately to the vehicle I'm driving.
I thought about adding an exit routine that would automatically revert back to my regular watchface when the Digital Dash shut down, but decided against it. I'd rather have the watch stay themed if I stop for lunch or something during a roadtrip and shut the system down.
Instead, I used Watchmaker's "Tap" function to set up a manual reversion. If I touch the vehicle logo on the watchface, it sends a command to run a Tasker task. That task uses AutoWear to create a simple confirmation screen:
If I truly do want to revert to my normal watchface, I have five seconds to touch the AutoWear icon. In that case, it runs another task that sets my display brightness back to normal and loads my regular watchface.
If, however, I don't touch the icon, the screen will dismiss itself automatically after five seconds without making any changes. I did it this way to avoid accidentally changing face with an inadvertent touch.
The one other thing I did was extend the system's brightness control to include the watch. Now if I dim the tablet screen for night driving, it also dims the watch as well as the phone. Setting the tablet back to full brightness restores that setting to the other devices as well.
I realize that this is a pretty pointless enhancement, but it's also kind of fun and I'm glad I did it.
This is a place for me to write about a lot of different things, but I will focus on small-format video, educational technology, and boardgames.
Showing posts with label Android. Show all posts
Showing posts with label Android. Show all posts
Friday, March 11, 2016
Monday, January 11, 2016
Using Android's BATTERY_CHANGED Intent in Tasker
Monitoring the status of my tablet's battery has always been part of the Digital Dash project (http://mikesgeneralblog.blogspot.com/2015/06/digital-dash-documentation-part-4.html). And later the function was expanded to bring in the phone's battery information as well. That part of the project has always worked pretty well and I haven't changed the code in a long time.
Now, however, I've discovered a more efficient way to gather this information and have rewritten that part of the system to take advantage of it. It allows me to replace what had been two profiles and three tasks, with a single profile with one task attached to it.
The key is another Android Broadcast Intent, similar to the TIME_TICK intent I wrote about in my last post. (http://mikesgeneralblog.blogspot.com/2016/01/using-androids-timetick-intent-in-tasker.html) The main difference is that where the TIME_TICK intent didn't provide any real information (it was just a synchronizing pulse), the BATTERY_CHANGED intent that we'll be using has a payload that contains a lot of info about the device's battery.
Setup to monitor this intent is nearly identical to the TIME_TICK: Create a new Event profile and choose "Intent Received" from the "System" category. In the "Action" field enter "android.intent.action.BATTERY_CHANGED" (without quotes). Then link to the task you want to run from this profile.
Here's what mine looks like:
Profile: V3_ BatteryTracker (472)
Cooldown: 10
Event: Intent Received [ Action:android.intent.action. BATTERY_CHANGED Cat:None Cat:None Scheme:* Mime Type:* ]
State: Variable Value [ %V3_DrivingMode Set ]
Enter: V3_BattMon (483)
A1: Variable Set [ Name:%V3_BatteryDisplay To:%level% Do Maths:Off Append:Off ] If [ %plugged = 0 ]
A2: Variable Set [ Name:%V3_BatteryDisplay To:<u>%level%</u> Do Maths:Off Append:Off ] If [ %plugged > 0 ]
(Note that, as usual, my profile has a second context, %V3_DrivingMode Set, to keep it from firing unless my Digital Dash system is running. You don't need that context just to monitor the battery.)
Although this is pretty simple arrangement, there are a couple of points to keep in mind if you're thinking of using this intent. First of all, notice that I've put a 10-second cooldown on the profile to limit it's maximum firing rate. That's because this intent will change any time one of several battery conditions changes. It's not quite like Tasker's built-in "Battery Changed" event that only fires when the battery level changes. The BATTERY_CHANGED intent (which Tasker is undoubtedly monitoring behind the scenes) puts out new information not only when the battery level changes, but also when the powered status changes, when the battery health changes, and when the the battery temperature changes, along with several other triggers.
The upshot is that this intent can be updated very frequently and since I don't need or want to have that kind of granularity, I've restricted the profile to firing only once every 10 seconds, at a maximum.
The second things to take note of are the names of the Tasker variables that I'm using: %plugged and %level. They're obviously local variables, but I didn't make up the names; they are the ones created by Tasker and based on the names provided by the intent itself. Since there are a lot of other variables associated with this intent (and no real documentation about how they translate to Tasker) it's probably worth a few minutes to lay it out. (You can't just choose these variables from Tasker's drop-down list because they are generated dynamically at runtime.)
The Android system documentation contains a section on the "Battery Manager" class, which provides the details of this intent. You can find it here: http://developer.android.com/reference/android/os/BatteryManager.html
There you'll find a list of all the constants used by this class and the information they can contain. This document is the key to sorting out the Tasker names and knowing what values to look for.
For example, let's take the %plugged variable. As you can see in the code above, if this variable is not 0, I wrap the battery level in HTML underline tags before displaying it. This gives me a visual indicator of the power state on the main screen.
If you look at the Battery Manager documentation, you'll see this entry:
Now, however, I've discovered a more efficient way to gather this information and have rewritten that part of the system to take advantage of it. It allows me to replace what had been two profiles and three tasks, with a single profile with one task attached to it.
The key is another Android Broadcast Intent, similar to the TIME_TICK intent I wrote about in my last post. (http://mikesgeneralblog.blogspot.com/2016/01/using-androids-timetick-intent-in-tasker.html) The main difference is that where the TIME_TICK intent didn't provide any real information (it was just a synchronizing pulse), the BATTERY_CHANGED intent that we'll be using has a payload that contains a lot of info about the device's battery.
Setup to monitor this intent is nearly identical to the TIME_TICK: Create a new Event profile and choose "Intent Received" from the "System" category. In the "Action" field enter "android.intent.action.BATTERY_CHANGED" (without quotes). Then link to the task you want to run from this profile.
Here's what mine looks like:
Profile: V3_ BatteryTracker (472)
Cooldown: 10
Event: Intent Received [ Action:android.intent.action.
State: Variable Value [ %V3_DrivingMode Set ]
Enter: V3_BattMon (483)
A1: Variable Set [ Name:%V3_BatteryDisplay To:%level% Do Maths:Off Append:Off ] If [ %plugged = 0 ]
A2: Variable Set [ Name:%V3_BatteryDisplay To:<u>%level%</u> Do Maths:Off Append:Off ] If [ %plugged > 0 ]
(Note that, as usual, my profile has a second context, %V3_DrivingMode Set, to keep it from firing unless my Digital Dash system is running. You don't need that context just to monitor the battery.)
Although this is pretty simple arrangement, there are a couple of points to keep in mind if you're thinking of using this intent. First of all, notice that I've put a 10-second cooldown on the profile to limit it's maximum firing rate. That's because this intent will change any time one of several battery conditions changes. It's not quite like Tasker's built-in "Battery Changed" event that only fires when the battery level changes. The BATTERY_CHANGED intent (which Tasker is undoubtedly monitoring behind the scenes) puts out new information not only when the battery level changes, but also when the powered status changes, when the battery health changes, and when the the battery temperature changes, along with several other triggers.
The upshot is that this intent can be updated very frequently and since I don't need or want to have that kind of granularity, I've restricted the profile to firing only once every 10 seconds, at a maximum.
The second things to take note of are the names of the Tasker variables that I'm using: %plugged and %level. They're obviously local variables, but I didn't make up the names; they are the ones created by Tasker and based on the names provided by the intent itself. Since there are a lot of other variables associated with this intent (and no real documentation about how they translate to Tasker) it's probably worth a few minutes to lay it out. (You can't just choose these variables from Tasker's drop-down list because they are generated dynamically at runtime.)
The Android system documentation contains a section on the "Battery Manager" class, which provides the details of this intent. You can find it here: http://developer.android.com/reference/android/os/BatteryManager.html
There you'll find a list of all the constants used by this class and the information they can contain. This document is the key to sorting out the Tasker names and knowing what values to look for.
For example, let's take the %plugged variable. As you can see in the code above, if this variable is not 0, I wrap the battery level in HTML underline tags before displaying it. This gives me a visual indicator of the power state on the main screen.
If you look at the Battery Manager documentation, you'll see this entry:
public static final String EXTRA_PLUGGED
Added in API level 5
Extra for
ACTION_BATTERY_CHANGED: integer indicating whether the device is plugged in to a power source; 0 means it is on battery, other constants are different types of power sources.
Constant Value: "plugged"
The "Constant Value" gives you the name of the Tasker variable (once you add the leading %). It also give you a hint about what the variable might contain, but there's more information available.
If you scroll up a bit, you'll find these entries, all with the word "plugged" in their names:
public static final int BATTERY_PLUGGED_AC
Added in API level 1
Power source is an AC charger.
Constant Value: 1 (0x00000001)
public static final int BATTERY_PLUGGED_USB
Added in API level 1
Power source is a USB port.
Constant Value: 2 (0x00000002)
public static final int BATTERY_PLUGGED_WIRELESS
Added in API level 17
Power source is wireless.
Constant Value: 4 (0x00000004)
These are the other values that might be present in the %plugged variable. Knowing that you can test for any state and act accordingly.
Likewise, you can find the names for other variables, such as the ones for health, voltage, status, and so on. By linking the names back to the constants, you can determine what type of information can be retrieved.
Some variables, like temperature, require a little more digging, however. If you set up the profile and flash the %temperature value, you might see something like: 223 Don't worry; it doesn't mean your battery is at the boiling point. The intent reports the battery temperature in tenths of a degree Celsius. So, a value of 223 means 23.3 degrees Celsius or about 72.14 degrees Fahrenheit. So, basically room temperature.
The fact that this intent contains the battery temperature is the main reason I put the cooldown period on the profile. When my Digital Dash project begins ramping up, the battery temperature changes rapidly and I didn't want to have the profile firing every time the temperature changed a tenth of a degree.
This single intent could be a starting point for a pretty comprehensive Tasker-based battery monitoring system. Take a look at the documentation and see just how much information you can gather with just one profile.
Friday, January 08, 2016
Using Android's TIME_TICK intent in Tasker
I keep coming back to the TimeAndTorque task. Not because it's all that important, but because I keep finding better ways to execute it.
It started off as a looping task (http://mikesgeneralblog.blogspot.com/2015/06/digital-dash-documentation-part-9.html) and then moved to being triggered by a Schrödinger's Profile, (http://mikesgeneralblog.blogspot.com/2015/12/schrodingers-profile.html) and now it's being driven by Android's TIME_TICK Broadcast Intent.
If you don't know what a Broadcast Intent is, you can think of it as an olde tyme town crier. The town crier would wander the streets calling out the time and any relevant news. The Broadcast Intent is basically the same thing; it's just a message that the Android system sends out. Like the town crier, the system doesn't really know or care it anyone is listening; it's job is just to send out the message.
There are a lot of Broadcast Intents that the system sends out, but the one we are interested in here is called the TIME_TICK. This message is broadcast at the top of every minute and can be used for triggering events or synchronizing information.
Other apps can broadcast intents as well. I use one generated by PowerAmp to grab and display music track and artist information (http://mikesgeneralblog.blogspot.com/2015/06/digital-dash-documentation-part-5-music.html).
Fortunately, Tasker makes it easy to listen for these intents and harness them for our own uses. To begin listening for the TIME_TICK, create a new Event profile and choose "Intent Received" from the System category. In the Action field enter "android.intent.action. TIME_TICK" (without the quotes). You can leave all the other fields blank. Then, just exit the event configuration and choose the task that you want to run when this intent is received.
Here's what the code looks like for my TimeAndTorque profile and task:
Profile: V3_TimeTick (484)
Event: Intent Received [ Action:android.intent.action. TIME_TICK Cat:None Cat:None Scheme:* Mime Type:* ]
State: Variable Value [ %V3_DrivingMode Set ]
Enter: V3_TimeAndTorque (336)
A1: Variable Split [ Name:%TIME Splitter:. Delete Base:Off ]
A2: Variable Subtract [ Name:%TIME1 Value:12 Wrap Around:0 ] If [ %TIME1 > 12 ]
A3: Variable Set [ Name:%V3_DispTime To:%TIME1:%TIME2 Do Maths:Off Append:Off ]
A4: Run Shell [ Command:/data/data/burrows. apps.busybox/app_busybox/tail -1 /storage/emulated/0/ torqueLogs/trackLog.csv Timeout (Seconds):0 Use Root:Off Store Output In:%obd_log Store Errors In: Store Result In: Continue Task After Error:On ]
A5: Variable Split [ Name:%obd_log Splitter:, Delete Base:Off ]
A6: Test Element [ Scene Name:V3_LH Element:LowFuel Test:Element Visibility Store Result In:%lowdistanceindicator Continue Task After Error:On ]
A7: If [ %obd_log6 < 60 & %lowdistanceindicator ~ false ]
A8: Element Visibility [ Scene Name:V3_LH Element Match:LowFuel Set:True Animation Time (MS):0 ]
A9: Say [ Text:Warning. Range limit under sixty miles. Engine:Voice:default:default Stream:3 Pitch:5 Speed:4 Respect Audio Focus:On Network:Off Continue Task Immediately:Off Continue Task After Error:On ]
A10: End If
A11: If [ %obd_log6 > 60 & %lowdistanceindicator ~ true ]
A12: Element Visibility [ Scene Name:V3_LH Element Match:LowFuel Set:False Animation Time (MS):0 ]
A13: End If
Once every minute, the profile will become active and run the TimeAndTorque task, grabbing the time (and synchronizing my on-screen display) and running the low-fuel check (If you want more detail about the task itself, check the documentation in the first link, above.)
This method is simple, clean, and reliable. I doubt that I'll find a better way to run this task, but who knows; I learn something new about Tasker amost every day.
It started off as a looping task (http://mikesgeneralblog.blogspot.com/2015/06/digital-dash-documentation-part-9.html) and then moved to being triggered by a Schrödinger's Profile, (http://mikesgeneralblog.blogspot.com/2015/12/schrodingers-profile.html) and now it's being driven by Android's TIME_TICK Broadcast Intent.
If you don't know what a Broadcast Intent is, you can think of it as an olde tyme town crier. The town crier would wander the streets calling out the time and any relevant news. The Broadcast Intent is basically the same thing; it's just a message that the Android system sends out. Like the town crier, the system doesn't really know or care it anyone is listening; it's job is just to send out the message.
There are a lot of Broadcast Intents that the system sends out, but the one we are interested in here is called the TIME_TICK. This message is broadcast at the top of every minute and can be used for triggering events or synchronizing information.
Other apps can broadcast intents as well. I use one generated by PowerAmp to grab and display music track and artist information (http://mikesgeneralblog.blogspot.com/2015/06/digital-dash-documentation-part-5-music.html).
Fortunately, Tasker makes it easy to listen for these intents and harness them for our own uses. To begin listening for the TIME_TICK, create a new Event profile and choose "Intent Received" from the System category. In the Action field enter "android.intent.action.
Here's what the code looks like for my TimeAndTorque profile and task:
Profile: V3_TimeTick (484)
Event: Intent Received [ Action:android.intent.action.
State: Variable Value [ %V3_DrivingMode Set ]
Enter: V3_TimeAndTorque (336)
A1: Variable Split [ Name:%TIME Splitter:. Delete Base:Off ]
A2: Variable Subtract [ Name:%TIME1 Value:12 Wrap Around:0 ] If [ %TIME1 > 12 ]
A3: Variable Set [ Name:%V3_DispTime To:%TIME1:%TIME2 Do Maths:Off Append:Off ]
A4: Run Shell [ Command:/data/data/burrows.
A5: Variable Split [ Name:%obd_log Splitter:, Delete Base:Off ]
A6: Test Element [ Scene Name:V3_LH Element:LowFuel Test:Element Visibility Store Result In:%lowdistanceindicator Continue Task After Error:On ]
A7: If [ %obd_log6 < 60 & %lowdistanceindicator ~ false ]
A8: Element Visibility [ Scene Name:V3_LH Element Match:LowFuel Set:True Animation Time (MS):0 ]
A9: Say [ Text:Warning. Range limit under sixty miles. Engine:Voice:default:default Stream:3 Pitch:5 Speed:4 Respect Audio Focus:On Network:Off Continue Task Immediately:Off Continue Task After Error:On ]
A10: End If
A11: If [ %obd_log6 > 60 & %lowdistanceindicator ~ true ]
A12: Element Visibility [ Scene Name:V3_LH Element Match:LowFuel Set:False Animation Time (MS):0 ]
A13: End If
Once every minute, the profile will become active and run the TimeAndTorque task, grabbing the time (and synchronizing my on-screen display) and running the low-fuel check (If you want more detail about the task itself, check the documentation in the first link, above.)
This method is simple, clean, and reliable. I doubt that I'll find a better way to run this task, but who knows; I learn something new about Tasker amost every day.
Tuesday, December 22, 2015
Physical Controls for the Digital Dash, Part 3
After careful thought and consideration (Yes, I'm serious.) I've decided to retire my gamepad side-arm controller in favor of my original scheme of using a Flic to control app access on the Digital Dash. There are a couple of reasons for that:
1) The Flic is simply more reliable and easier to use. It doesn't require recharging, connects automatically, and handles it's sleep mode more elegantly. It also responds more quickly than the gamepad.
2) Many of the functions that the gamepad controls are either no longer necessary or are otherwise handled. I don't need to download A-GPS data since I'm using the "Device Only" mode for GPS and I've brought the external temperature reading to the main screen, eliminating the need to pull up the Weather Channel app just to get that information. And, of course, by installing a Flic dedicated to music control, I no longer need those functions on the gamepad.
3) Finally, on our last few drives I kept closer track of just exactly how I was interacting with the system. By far, the thing I did most often was to switch over to Google Maps to see the next turn information. And that didn't happen very often. I used to switch over more often to see the ETA information, but since bringing that to the main screen as well, it's available without any interaction. The second most common thing was to check MyRadar to see incoming weather, but again, that was relatively rare. Other things, such as switching music sources or setting a navigation destination simply didn't happen during the drive; I tended to do that at the beginning of a trip and never needed to change it en-route.
Taking all that into account, I decided to set up a second "App Control" Flic with the following functions.
When the main screen is showing:
When on the Google Maps screen:
1) The Flic is simply more reliable and easier to use. It doesn't require recharging, connects automatically, and handles it's sleep mode more elegantly. It also responds more quickly than the gamepad.
2) Many of the functions that the gamepad controls are either no longer necessary or are otherwise handled. I don't need to download A-GPS data since I'm using the "Device Only" mode for GPS and I've brought the external temperature reading to the main screen, eliminating the need to pull up the Weather Channel app just to get that information. And, of course, by installing a Flic dedicated to music control, I no longer need those functions on the gamepad.
3) Finally, on our last few drives I kept closer track of just exactly how I was interacting with the system. By far, the thing I did most often was to switch over to Google Maps to see the next turn information. And that didn't happen very often. I used to switch over more often to see the ETA information, but since bringing that to the main screen as well, it's available without any interaction. The second most common thing was to check MyRadar to see incoming weather, but again, that was relatively rare. Other things, such as switching music sources or setting a navigation destination simply didn't happen during the drive; I tended to do that at the beginning of a trip and never needed to change it en-route.
Taking all that into account, I decided to set up a second "App Control" Flic with the following functions.
When the main screen is showing:
- Short Click - Launch Google Maps
- Long Click - Launch MyRadar
- Double Click - Launch GasBuddy
- Short Click - Return to the main screen
When on the Google Maps screen:
- Long Click - Toggle Voice Navigation Guidance on or off
That seems like a pretty short list, but it represents all the things I'm likely to do while driving. Of course, if I need to I can map a few other functions to the button, either by manipulating profiles (adding a triple click, for example) or by taking more contexts into account (such as a double click launching the navigation panel only when the Maps screen is showing). I also have three other Flics that I could dedicate to controlling certain subsystems.
Time will tell what, if any, changes need to be made.
Labels:
Android,
Bluetooth button,
BMW,
Car Dock,
Digital Dash,
Tasker,
Z3
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.
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.
Friday, November 20, 2015
Another Bluetooth Button Control Scheme
My post on adding button control to my Digital Dash project (http://mikesgeneralblog.blogspot.com/2015/08/physical-controls-for-digital-dash.html) has turned out to be one of the most popular ones that I've written. (Around here that only means about 550 views, but it's something.) It's been linked to in a couple of places and over on the AutoApps forum someone asked me if there was a way to make a button continually perform a task as long as it was held down. It turned out to be pretty easy, so here's an example that controls the Media Volume of a device.
I chose to use the "Media Rewind" keycode to lower the system's media volume because it made sense in the physical layout of the Bluetooth Gamepad I was using, but you can, of course, use any key you want. To make a companion way to increase the volume, just copy the profiles and tasks and rename them with "Up" instead of "Down" in the names and change the "-1" in line A1 of the first task to a "+1". You'll also need to modify the IF clause in line A3 so that it checks for a maximum value rather than a minimum.
Profile: Vo!umeDownStart (437)
Event: AutoInput Key [ Configuration:Keys: Media Rewind
Key Action: Key Down ]
Enter: VolDown (436)
A1: Media Volume [ Level:%VOLM-1 Display:Off Sound:Off ]
A2: Wait [ MS:250 Seconds:0 Minutes:0 Hours:0 Days:0 ]
A3: Goto [ Type:Action Number Number:1 Label: ] If [ %VOLM > 0 ]
Profile: VolumeDownStop (438)
Priority: 11
Event: AutoInput Key [ Configuration:Keys: Media Rewind
Key Action: Key Up ]
Enter: VolDownStop (439)
A1: Stop [ With Error:Off Task:VolDown ]
A couple of things to note: The first profile triggers on the Key Down event and the second one triggers on Key Up. Be sure you bump up the priority for launched tasks in the second profile; it needs to be greater than the the task it's trying to stop. You'll also need to run AutoInput's KeySupress function before you use these profiles and then disable key suppression when you're done. The 250 ms delay in line A2 seems to give a nice response ramp, but if you want something a little faster, just decrease that value. Finally, you'll probably need to disable "Restore Settings" in the first profile to make the volume changes stick.
Hope this helps someone.
I chose to use the "Media Rewind" keycode to lower the system's media volume because it made sense in the physical layout of the Bluetooth Gamepad I was using, but you can, of course, use any key you want. To make a companion way to increase the volume, just copy the profiles and tasks and rename them with "Up" instead of "Down" in the names and change the "-1" in line A1 of the first task to a "+1". You'll also need to modify the IF clause in line A3 so that it checks for a maximum value rather than a minimum.
Profile: Vo!umeDownStart (437)
Event: AutoInput Key [ Configuration:Keys: Media Rewind
Key Action: Key Down ]
Enter: VolDown (436)
A1: Media Volume [ Level:%VOLM-1 Display:Off Sound:Off ]
A2: Wait [ MS:250 Seconds:0 Minutes:0 Hours:0 Days:0 ]
A3: Goto [ Type:Action Number Number:1 Label: ] If [ %VOLM > 0 ]
Profile: VolumeDownStop (438)
Priority: 11
Event: AutoInput Key [ Configuration:Keys: Media Rewind
Key Action: Key Up ]
Enter: VolDownStop (439)
A1: Stop [ With Error:Off Task:VolDown ]
A couple of things to note: The first profile triggers on the Key Down event and the second one triggers on Key Up. Be sure you bump up the priority for launched tasks in the second profile; it needs to be greater than the the task it's trying to stop. You'll also need to run AutoInput's KeySupress function before you use these profiles and then disable key suppression when you're done. The 250 ms delay in line A2 seems to give a nice response ramp, but if you want something a little faster, just decrease that value. Finally, you'll probably need to disable "Restore Settings" in the first profile to make the volume changes stick.
Hope this helps someone.
Wednesday, November 18, 2015
Another (Very) Minor Tweak
I have a control on the main screen that allows me to toggle the tablet's AutoBrightness on and off, but it doesn't get used very much. 99% of the time we're driving in the sun with the top down and I want the tablet at full brightness. About the only time I use it is on those rare occasions when we are traveling at night.
It occurred to me today, though, that if I do dim the screen on the tablet, the phone will remain at full brightness. That would be annoying and since I don't really have a control interface on that unit, changing the brightness would be distracting and annoying, requiring me to go into settings and fumble around.
I don't really want to put controls on the phone, so instead I used AutoRemote. Now when I toggle the screen brightness on the tablet, it sends a direct message to the phone, which causes it to toggle as well.
As I said, a very minor thing, but when I need to use it, it will be handy.
It occurred to me today, though, that if I do dim the screen on the tablet, the phone will remain at full brightness. That would be annoying and since I don't really have a control interface on that unit, changing the brightness would be distracting and annoying, requiring me to go into settings and fumble around.
I don't really want to put controls on the phone, so instead I used AutoRemote. Now when I toggle the screen brightness on the tablet, it sends a direct message to the phone, which causes it to toggle as well.
As I said, a very minor thing, but when I need to use it, it will be handy.
Wednesday, November 11, 2015
Torque and Tasker
If you're interested in getting data from the Torque app into Tasker, take a look at the video I posted on YouTube:
https://www.youtube.com/watch?v=bjFLbbYakBo
It begins by showing you how to set up Torque, then steps through gathering the information and tools that you need, and finishes up with some actual Tasker code.
https://www.youtube.com/watch?v=bjFLbbYakBo
It begins by showing you how to set up Torque, then steps through gathering the information and tools that you need, and finishes up with some actual Tasker code.
Thursday, October 29, 2015
Some More On-Screen Information
I've added a couple more pieces of information to the on-screen display. Nothing major, just some more things I like to be able to see at a glance without having to interact with the system.
When I first made the Digital Dash, I was using the WeatherAce plugin to query a server and return the nearest local environment temperature. It worked fine, but when I decided to mount the phone as a secondary screen dedicated to running Waze, I realized that it would be exposed to approximately the same temperature environment as the people in the car would. So, I set up a task on the phone that would send an AutoRemote message containing data from the phone's built-in temperature sensor over to the tablet about every 30 seconds. This gave me hyper-localized data, and I figured that if I wanted the surrounding area temp, I could just pull up the Weather Channel app.
But the more I thought about it, the more I realized that I wanted to see both values at any time; essentially an interior and exterior temperature display. This time, however, instead of using WeatherAce, I decided to just use AutoNotification to scrape the temperature from the Weather Channel's notification.
Because I didn't want to make another display element (that I didn't really have room for) I just stacked the two temperatures into a single variable and linked it to the existing display. In the screenshot, below, the interior (phone temp) is on top and the environmental temp (from the Weather Channel notification) is on the bottom. There was enough extra room in the element that I didn't even need to change the font size.
The other piece of data I'm now sending to the tablet is the phone's battery level, along with its charging status.
Since the phone is mounted on the driver's "wing" window, and I don't like stringing cables across the car from the utility port, I normally just plug it into my Anker battery pack, which rides in a door pocket right below the phone. The only problem is that on more than one occasion I've forgotten to push the button on the pack that starts the charging process. I thought the phone was plugged in and didn't realize I was draining the battery until the phone put up its low battery warning.
Now, however, I can see the battery level and charging status for both the phone and the tablet right on the main screen. In the screenshot, above, the tablet is the top entry, and the phone is the bottom one. This also shows that the phone is plugged in and charging because the data is underlined. (The tablet entry has the same capability.)
To make this work, I set up two new profiles on the phone. One is a Power ANY profile with both an entry and exit task, and the other is a Battery Changed profile, which only has an entry task.
Here's the Powered profile:
Profile: V3_PoweredCheck (240)
State: Power [ Source:Any ]State: Variable Value [ %TabletConnected Set ]
Pretty simple. The profile just watches for a change in charging status. The secondary context keeps it from firing unless the Digital Dash system is actually running. The %TabletConnected variable is set when the phone project runs its startup sequence after it receives a command to start Bluetooth tethering from the tablet. It's the equivalent of the tablet's %V3_DrivingMode.
A1: Variable Set [ Name:%V3_PowerState To:-1 Do Maths:On Append:Off ]
A2: Perform Task [ Name:V3_SendBatt Priority:%priority Parameter 1 (%par1): Parameter 2 (%par2): Return Value Variable: Stop:Off ]
Exit: V3_Unplugged (242)
A1: Variable Set [ Name:%V3_PowerState To:1 Do Maths:On Append:Off ]
A2: Perform Task [ Name:V3_SendBatt Priority:%priority Parameter 1 (%par1): Parameter 2 (%par2): Return Value Variable: Stop:Off ]
The entry and and exit tasks are nearly identical. They both adjust the value of the %V3_PowerState variable and then call the task that actually sends the AutoRemote message to the tablet.
Because I actually need to send two pieces of information in each message, I use a little simple math to concatenate the data. As you can see, the entry tasks sets the %V3_PoweredState to "-1", while the exit task sets it to just "1". Before the data gets sent, I multiply this variable by Taskers built-in %BATT (which contains the actual battery charge level). So, when the phone is plugged in, the data sent is a negative number, and when the phone isn't charging, it sends a positive number.
Over on the tablet, I make a check for these conditions and use the result to decide whether or not to wrap the battery value in html underline tags.
(This seems a bit convoluted, and in theory AutoRemote can send multiple pieces of data in a single message, but I was never able to get that to work. The messages just stopped going out, with no error indications. So I went with this scheme instead.)
Profile: V3_BattTracker (244)
Event: Battery Changed
State: Variable Value [ %TabletConnected Set ]
Event: Battery Changed
State: Variable Value [ %TabletConnected Set ]
The battery tracking profile is just as simple. Any time the battery level changes and the tablet is connected, it executes the task that sends the AutoRemote message. That's the same task called by the charging/not charging profile.
Enter: V3_SendBatt (243)
A1: Variable Set [ Name:%mybatt To:%V3_PowerState*%BATT Do Maths:On Append:Off ]
A2: Variable Set [ Name:%tempmessage To:https://autoremotejoaomgcd.
A3: HTTP Post [ Server:Port:%tempmessage Path: Data / File: Cookies: User Agent: Timeout:10 Content Type: Output File: Trust Any Certificate:Off Continue Task After Error:On ] If [ %TabletConnected Set ]
The messaging task does the multiplication I mentioned above and prepares an AutoRemote message. The preparation must be done in a separate Variable Set action because if you try to add variables directly to the HTTP Post step, they won't be interpreted and only the variable names themselves will be sent.
This task, like the one that sends the phone's temperature, uses AutoRemotes direct messaging option. I prefer this, because it doesn't need an internet connection to function.
That's it. Pretty minor modifications, but nice to have.
Tuesday, October 27, 2015
Navigation ETA Information Revisited - Revisited
I had hoped that my solution to the missing information in the Google Maps navigation notification would be a temporary one. While it does work, it's definitely a kludge and I'm happy to be able to go back to gathering that content via AutoNotification.
Thanks to a user in one of the AutoApps forums, I learned that the ETA information is available in the variable %antextsbig4 under Lollipop. (Formerly it had just been in %antexts.) With that knowledge I was able to re-do my profile and task and get them working again, so I thought I would post the solution here.
Profile: V3_ETAScrape (109)
Cooldown: 30
Event: AutoNotification Intercept [ Configuration:Event Behaviour: true
Persistency Type: Both
Notification Apps: Maps
Get All Fields : true ]
State: Variable Value [ %V3_DrivingMode Set ]
This is the profile itself. It triggers when Google Maps puts up a notification AND the %V3_DrivingMode variable is set. AutoNotification is set as the event handler and configured to look at both created and persistent notifications. I've also set the flag so that AN will get all the fields in a notification.
The other thing to note is that there is a 30-second Cooldown set on the profile. This keeps it from constantly firing as Maps updates the notification. While it does reduce the granularity of the information somewhat, it keeps this profile from blocking other tasks and may make it a bit more stable.
When the profile fires, it gathers the information from the notification and populates a number of AN variables. However, for this job we only need one of them: %antextsbig4. It will contain data that looks something like this:
3.2 mi - Continue onto US 20
2 hr 52 min (165 mi) to destination
Estimated arrival at 2:39 PM
If I had enough room on my main screen, I could just display that info in a text box, but I really don't have that kind of real estate available to me, so I need to take it apart and shorten it up.
Enter: V3_NotificationQuery (102)
A1: Variable Set [ Name:%ret To:
Do Maths:Off Append:Off ]
The first thing the entry task does is define the %ret variable. Blogger doesn't show it, but the variable is filled simply by hitting the "Enter" key when you create the variable. This is necessary because we need to split %antextsbig4 based on line returns and Tasker doesn't allow you to enter a return directly in the Variable Split action.
A2: Variable Split [ Name:%antextsbig4 Splitter:%ret Delete Base:Off ]
As promised, we first use the %ret variable to split %antextsbig4 into its individual lines. After the split, %antextsbig41 will contain the turn-by-turn info and %antextsbig42 will be a blank line. I don't care about either of those, so they remain unused.
A3: Variable Split [ Name:%antextsbig43 Splitter:) Delete Base:Off ]
%antextsbig43 contains the first line of the ETA information. By splitting it with a parenthesis I can extract just the first part of the line and ignore the "to destination".
A4: Variable Split [ Name:%antextsbig44 Splitter:at Delete Base:Off ]
Now I split %antextsbig44, which contains the second line of the ETA info. I use the word "at" as the splitter, which isolates the first part of the line,"Estimated arrival", and leaves me with just the arrival time. (Splitters never become part of a variable, so the "at" is gone as well.)
A5: Variable Set [ Name:%V3_ETA To:%antextsbig431)
ETA: %antextsbig443 Do Maths:Off Append:Off ]
The last step is to populate the %V3_ETA variable, which serves as the source for my onscreen text display. It's simply a matter of taking the parts of the lines I've split and concatenating them. Note that I had to add the ) back in because it was used as the splitter in an earlier step and was, therefore, discarded out of the variable.
This is a pretty simple, but very useful addition to the Digital Dash project and I'm glad to have it working properly again.
Thanks to a user in one of the AutoApps forums, I learned that the ETA information is available in the variable %antextsbig4 under Lollipop. (Formerly it had just been in %antexts.) With that knowledge I was able to re-do my profile and task and get them working again, so I thought I would post the solution here.
Profile: V3_ETAScrape (109)
Cooldown: 30
Event: AutoNotification Intercept [ Configuration:Event Behaviour: true
Persistency Type: Both
Notification Apps: Maps
Get All Fields : true ]
State: Variable Value [ %V3_DrivingMode Set ]
This is the profile itself. It triggers when Google Maps puts up a notification AND the %V3_DrivingMode variable is set. AutoNotification is set as the event handler and configured to look at both created and persistent notifications. I've also set the flag so that AN will get all the fields in a notification.
The other thing to note is that there is a 30-second Cooldown set on the profile. This keeps it from constantly firing as Maps updates the notification. While it does reduce the granularity of the information somewhat, it keeps this profile from blocking other tasks and may make it a bit more stable.
When the profile fires, it gathers the information from the notification and populates a number of AN variables. However, for this job we only need one of them: %antextsbig4. It will contain data that looks something like this:
3.2 mi - Continue onto US 20
2 hr 52 min (165 mi) to destination
Estimated arrival at 2:39 PM
If I had enough room on my main screen, I could just display that info in a text box, but I really don't have that kind of real estate available to me, so I need to take it apart and shorten it up.
Enter: V3_NotificationQuery (102)
A1: Variable Set [ Name:%ret To:
Do Maths:Off Append:Off ]
The first thing the entry task does is define the %ret variable. Blogger doesn't show it, but the variable is filled simply by hitting the "Enter" key when you create the variable. This is necessary because we need to split %antextsbig4 based on line returns and Tasker doesn't allow you to enter a return directly in the Variable Split action.
A2: Variable Split [ Name:%antextsbig4 Splitter:%ret Delete Base:Off ]
As promised, we first use the %ret variable to split %antextsbig4 into its individual lines. After the split, %antextsbig41 will contain the turn-by-turn info and %antextsbig42 will be a blank line. I don't care about either of those, so they remain unused.
A3: Variable Split [ Name:%antextsbig43 Splitter:) Delete Base:Off ]
%antextsbig43 contains the first line of the ETA information. By splitting it with a parenthesis I can extract just the first part of the line and ignore the "to destination".
A4: Variable Split [ Name:%antextsbig44 Splitter:at Delete Base:Off ]
Now I split %antextsbig44, which contains the second line of the ETA info. I use the word "at" as the splitter, which isolates the first part of the line,"Estimated arrival", and leaves me with just the arrival time. (Splitters never become part of a variable, so the "at" is gone as well.)
A5: Variable Set [ Name:%V3_ETA To:%antextsbig431)
ETA: %antextsbig443 Do Maths:Off Append:Off ]
The last step is to populate the %V3_ETA variable, which serves as the source for my onscreen text display. It's simply a matter of taking the parts of the lines I've split and concatenating them. Note that I had to add the ) back in because it was used as the splitter in an earlier step and was, therefore, discarded out of the variable.
This is a pretty simple, but very useful addition to the Digital Dash project and I'm glad to have it working properly again.
Saturday, October 03, 2015
Physical Controls for the Digital Dash , Part 2
Well, it took a bit longer than I expected (about six months longer) but I finally got my Flic Bluetooth buttons. And I like them; see my full review here:
http://mikesgeneralblog.blogspot.com/2015/09/flic-bluetooth-button-review.html
I'm not quite sure what I'm going to do with all of them (I have five), but I have added one on the right-hand side of the center console in the Z3 as a music control.
I also thought about mirroring its functions to another button on the back of the steering wheel, but I've found that the car's cabin is small enough that I can reach the button while driving without even shifting position, so I've dropped that idea for the moment.
Here are the functions the button performs.
When the main screen is showing:
Short Click - Next (random) track when PowerAmp is the music source. When Pandora is playing instead, Skip the current track.
Double Click - Replay the Previous Track (PowerAmp only; no function for Pandora).
Long Click - Display the Favorite Songs menu with a cursor used to highlight selections.
Triple Click - Toggles pause for the currently playing track.
When the Favorite Songs Menu is showing:
Short Click - Cause the music cursor to step down the list to the next entry. If the bottom of the list is reached, the cursor wraps around back to the top of the list.
Long Click - Select the song under the cursor and have PowerAmp begin playing the track. This causes PowerAmp to display the album art for the song. After about 5 seconds, the system returns to the main Digital Dash screen automatically.
Double Click - Cancels the Favorite Song menu and removes it from the screen without selecting any track.
If you read the first part of the physical controls documentation, you might recognize those functions as also being performed by the Bluetooth Gamepad that I've re-purposed into a sidearm controller.
In fact, I just reused the same code I wrote for that. In order to make the Flic perform those functions, all I had to do was create the profiles that caught the various activations. Once I had done that, I just linked to the existing tasks. I don't think that it took me 10 minutes to get everything working.
We've taken a few drives and tried out the Flic and it has worked beautifully. It's much easier (and safer) to click the button than it is to try hit the on-screen controls.
http://mikesgeneralblog.blogspot.com/2015/09/flic-bluetooth-button-review.html
I'm not quite sure what I'm going to do with all of them (I have five), but I have added one on the right-hand side of the center console in the Z3 as a music control.
I also thought about mirroring its functions to another button on the back of the steering wheel, but I've found that the car's cabin is small enough that I can reach the button while driving without even shifting position, so I've dropped that idea for the moment.
Here are the functions the button performs.
When the main screen is showing:
Short Click - Next (random) track when PowerAmp is the music source. When Pandora is playing instead, Skip the current track.
Double Click - Replay the Previous Track (PowerAmp only; no function for Pandora).
Long Click - Display the Favorite Songs menu with a cursor used to highlight selections.
Triple Click - Toggles pause for the currently playing track.
Short Click - Cause the music cursor to step down the list to the next entry. If the bottom of the list is reached, the cursor wraps around back to the top of the list.
Long Click - Select the song under the cursor and have PowerAmp begin playing the track. This causes PowerAmp to display the album art for the song. After about 5 seconds, the system returns to the main Digital Dash screen automatically.
Double Click - Cancels the Favorite Song menu and removes it from the screen without selecting any track.
If you read the first part of the physical controls documentation, you might recognize those functions as also being performed by the Bluetooth Gamepad that I've re-purposed into a sidearm controller.
In fact, I just reused the same code I wrote for that. In order to make the Flic perform those functions, all I had to do was create the profiles that caught the various activations. Once I had done that, I just linked to the existing tasks. I don't think that it took me 10 minutes to get everything working.
We've taken a few drives and tried out the Flic and it has worked beautifully. It's much easier (and safer) to click the button than it is to try hit the on-screen controls.
Labels:
Android,
Bluetooth button,
Car Dock,
Digital Dash,
Tasker,
Z3
Wednesday, September 23, 2015
Navigation ETA Information Revisited
The display of ETA information that I talked about in my last entry about the Digital Dash project was (I thought) pretty neat, since it expanded on the philosophy of bringing as much useful information to one screen as possible. It was also, unfortunately, very short-lived.
I got to use the feature on exactly one trip. Then, after we returned home, I made the mistake of upgrading my tablet to Lollipop. I figured it had been out awhile and had had a couple of point releases, so it was probably safe. Wrong.
In the first place, it completely messed up my GPS. After upgrading it took more than five minutes to get a lock, and even when it did, it had an error of up to 900 feet and would drop out every couple of minutes. I was on the verge of flashing back to KitKat when I saw a post where someone mentioned the "GPS Status Test & Fix - No Ads" app as a possible solution. Fortunately, installing it did the trick and my GPS is working normally again. At the same time, the original "GPS Status" app that I was using wasn't working at all, so I've uninstalled that one.
The other thing that Lollipop broke was Google Maps notifications. Well, maybe not broke, exactly, but it did change things to the point where AutoNotification can't return the ETA data any more. The information is still there, and AN can actually respond to it, but it can't report it back to Tasker as a variable. I've reached out to the AN developer and he's investigating, but I haven't heard anything back from him yet.
There's no guarantee, of course, that he'll be able to fix it or that it won't break again, so rather than waiting, I decided to see if there was any other way to get the information I wanted.
Turns out, there is.
I originally started out looking to see if there was an intent that I could call or monitor for this information. I didn't find one, but I did find plenty of discussions from developers wanting to create different types of navigation apps, particularly for geocaching. It seemed like they were always directed to check out something called the "Google Maps DistanceMatrix API".
A little more research turned up that this was a web-based service that can be called via a simple HTTP Get command. You simply create a query string with a starting point, a destination point, and few settings and ship it off to the server. You get back a data block (either xml or json) that contains, among other things, the distance to the target and the estimated travel time. Just what I needed.
I played around with it a bit and came up with the code I needed. I added a couple of lines to my V3_Compass routine to save the current latitude and longitude, added one line to the V3_StartNavigation task that saved my chosen destination to a new global variable, and added one more line to the V3_TimeAndTorque task to call my new routine so that it gets executed about every 30 seconds.
Here's that new task:
APIQuery (113)
A1: Variable Set [ Name:%dayhalf To:AM Do Maths:Off Append:Off ]
We start off by assuming that we will be arriving at our destination during the morning hours. Later, we'll test this assumption and modify this variable if necessary.
A2: Variable Set [ Name:%uri To:/maps/api/distancematrix/ xml?origins=%V3_Lat+%V3_Long& destinations=%V3_ CurrentDestination&mode= driving&units=imperial&key=My Google Key Do Maths:Off Append:Off ]
A3: HTTP Get [ Server:Port:https://maps. googleapis.com Path:%uri Attributes: Cookies: User Agent: Timeout:10 Mime Type: Output File: None Trust Any Certificate:Off Continue Task After Error:On ]
This is the heart of the routine. We use variables from other parts of the system to construct a properly-formatted query string and ship it off to the Google Maps DistanceMatrix API. Although anyone can call the non-secure version of the API, I signed up for a free Google Developer Account and obtained a Key so that I could use a secure connection and be able to monitor performance. This query will return a consistently formatted block of xml data containing the information I need and put it in Tasker's built-in %HTTPD variable. Much of the rest of this task is devoted to extracting the relevant pieces.
A4: Variable Split [ Name:%HTTPD Splitter:text> Delete Base:Off ]
A5: Variable Split [ Name:%HTTPD2 Splitter:< Delete Base:Off ]
A6: Variable Set [ Name:%timeleft To:%HTTPD21 Do Maths:Off Append:Off ]
A7: Variable Search Replace [ Variable:%timeleft Search:hours Ignore Case:Off Multi-Line:Off One Match Only:Off Store Matches In: Replace Matches:On Replace With:hr ]
A8: Variable Search Replace [ Variable:%timeleft Search:mins Ignore Case:Off Multi-Line:Off One Match Only:Off Store Matches In: Replace Matches:On Replace With:min ]
The first thing I obtain is the travel time remaining. It's almost in the format I want, but to shorten it up a bit for display, I replace "hours" with "hr" and "mins" with "min".
A9: Variable Split [ Name:%HTTPD4 Splitter:< Delete Base:Off ]
A10: Variable Set [ Name:%distance To:%HTTPD41 Do Maths:Off Append:Off ]
The next piece is the distance to the destination. This is already formatted as I want, so I just have to split it out.
A11: Variable Split [ Name:%HTTPD1 Splitter:value> Delete Base:Off ]
A12: Variable Split [ Name:%HTTPD12 Splitter:< Delete Base:Off ]
A13: Variable Set [ Name:%durationvalue To:%HTTPD121 Do Maths:Off Append:Off ]
For the first two pieces of information above, I extracted the "text" versions. That is, the information as presented in a human-readable format. However, the xml contains "values" for this information as well: the distance is also sent as meters, and the travel time is also sent in seconds. Because the API does not return an ETA, I need to calculate it myself, and it's easier to do by manipulating a single block of seconds rather than individual hours and minutes. Accordingly, I grab the travel time value to use in that calculation.
A14: Variable Split [ Name:%TIME Splitter:. Delete Base:Off ]
This line grabs the system time from Tasker's built-in variable (which is in 24-hour format) and splits it into separate hours and minutes variables.
A15: Variable Set [ Name:%eta To:((%TIME1*3600)+(%TIME2*60)) +%durationvalue Do Maths:On Append:Off ]
The line above converts the hours and minutes into a single variable that represents the seconds since midnight. It then adds the number of seconds left to travel to that value. The result is the ETA expressed in seconds.
A16: Variable Set [ Name:%hours To:floor(%eta/3600) Do Maths:On Append:Off ]
A17: Variable Set [ Name:%minutes To:floor(((%eta/3600)-%hours)* 60+.5) Do Maths:On Append:Off ]
The above two lines simply convert the seconds back into hours and minutes.
A18: Variable Set [ Name:%dayhalf To:PM Do Maths:Off Append:Off ] If [ %hours > 11 & %hours < 24 ]
Now it's time to revisit that %dayhalf variable. If %hours is greater than 11, then we will be arriving at our destination in the afternoon and need to set %dayhalf to "PM"; unless %hours is also greater than 23. In that case, we will be arriving sometime after midnight of the next day, which is morning again so we leave %dayhalf alone.
A19: Variable Subtract [ Name:%hours Value:12 Wrap Around:0 ] If [ %hours > 12 ]
A20: Variable Subtract [ Name:%hours Value:12 Wrap Around:0 ] If [ %hours > 12 ]
I prefer the 12-hour time format, so these two lines take care of converting 24-hour time back to what I want. The same action is used twice just in case our trip will take us past midnight.
A21: Variable Set [ Name:%V3_ETA To:%timeleft (%distance)ETA: %hours:%minutes %dayhalf Do Maths:Off Append:Off ]
The final step is to take all the information we've derived and put it into a variable which is linked to a text box display on the main screen.
Once again, here's what that looks like:
I got to use the feature on exactly one trip. Then, after we returned home, I made the mistake of upgrading my tablet to Lollipop. I figured it had been out awhile and had had a couple of point releases, so it was probably safe. Wrong.
In the first place, it completely messed up my GPS. After upgrading it took more than five minutes to get a lock, and even when it did, it had an error of up to 900 feet and would drop out every couple of minutes. I was on the verge of flashing back to KitKat when I saw a post where someone mentioned the "GPS Status Test & Fix - No Ads" app as a possible solution. Fortunately, installing it did the trick and my GPS is working normally again. At the same time, the original "GPS Status" app that I was using wasn't working at all, so I've uninstalled that one.
The other thing that Lollipop broke was Google Maps notifications. Well, maybe not broke, exactly, but it did change things to the point where AutoNotification can't return the ETA data any more. The information is still there, and AN can actually respond to it, but it can't report it back to Tasker as a variable. I've reached out to the AN developer and he's investigating, but I haven't heard anything back from him yet.
There's no guarantee, of course, that he'll be able to fix it or that it won't break again, so rather than waiting, I decided to see if there was any other way to get the information I wanted.
Turns out, there is.
I originally started out looking to see if there was an intent that I could call or monitor for this information. I didn't find one, but I did find plenty of discussions from developers wanting to create different types of navigation apps, particularly for geocaching. It seemed like they were always directed to check out something called the "Google Maps DistanceMatrix API".
A little more research turned up that this was a web-based service that can be called via a simple HTTP Get command. You simply create a query string with a starting point, a destination point, and few settings and ship it off to the server. You get back a data block (either xml or json) that contains, among other things, the distance to the target and the estimated travel time. Just what I needed.
I played around with it a bit and came up with the code I needed. I added a couple of lines to my V3_Compass routine to save the current latitude and longitude, added one line to the V3_StartNavigation task that saved my chosen destination to a new global variable, and added one more line to the V3_TimeAndTorque task to call my new routine so that it gets executed about every 30 seconds.
Here's that new task:
APIQuery (113)
A1: Variable Set [ Name:%dayhalf To:AM Do Maths:Off Append:Off ]
We start off by assuming that we will be arriving at our destination during the morning hours. Later, we'll test this assumption and modify this variable if necessary.
A2: Variable Set [ Name:%uri To:/maps/api/distancematrix/
A3: HTTP Get [ Server:Port:https://maps.
This is the heart of the routine. We use variables from other parts of the system to construct a properly-formatted query string and ship it off to the Google Maps DistanceMatrix API. Although anyone can call the non-secure version of the API, I signed up for a free Google Developer Account and obtained a Key so that I could use a secure connection and be able to monitor performance. This query will return a consistently formatted block of xml data containing the information I need and put it in Tasker's built-in %HTTPD variable. Much of the rest of this task is devoted to extracting the relevant pieces.
A4: Variable Split [ Name:%HTTPD Splitter:text> Delete Base:Off ]
A5: Variable Split [ Name:%HTTPD2 Splitter:< Delete Base:Off ]
A6: Variable Set [ Name:%timeleft To:%HTTPD21 Do Maths:Off Append:Off ]
A7: Variable Search Replace [ Variable:%timeleft Search:hours Ignore Case:Off Multi-Line:Off One Match Only:Off Store Matches In: Replace Matches:On Replace With:hr ]
A8: Variable Search Replace [ Variable:%timeleft Search:mins Ignore Case:Off Multi-Line:Off One Match Only:Off Store Matches In: Replace Matches:On Replace With:min ]
The first thing I obtain is the travel time remaining. It's almost in the format I want, but to shorten it up a bit for display, I replace "hours" with "hr" and "mins" with "min".
A9: Variable Split [ Name:%HTTPD4 Splitter:< Delete Base:Off ]
A10: Variable Set [ Name:%distance To:%HTTPD41 Do Maths:Off Append:Off ]
The next piece is the distance to the destination. This is already formatted as I want, so I just have to split it out.
A11: Variable Split [ Name:%HTTPD1 Splitter:value> Delete Base:Off ]
A12: Variable Split [ Name:%HTTPD12 Splitter:< Delete Base:Off ]
A13: Variable Set [ Name:%durationvalue To:%HTTPD121 Do Maths:Off Append:Off ]
For the first two pieces of information above, I extracted the "text" versions. That is, the information as presented in a human-readable format. However, the xml contains "values" for this information as well: the distance is also sent as meters, and the travel time is also sent in seconds. Because the API does not return an ETA, I need to calculate it myself, and it's easier to do by manipulating a single block of seconds rather than individual hours and minutes. Accordingly, I grab the travel time value to use in that calculation.
A14: Variable Split [ Name:%TIME Splitter:. Delete Base:Off ]
This line grabs the system time from Tasker's built-in variable (which is in 24-hour format) and splits it into separate hours and minutes variables.
A15: Variable Set [ Name:%eta To:((%TIME1*3600)+(%TIME2*60))
The line above converts the hours and minutes into a single variable that represents the seconds since midnight. It then adds the number of seconds left to travel to that value. The result is the ETA expressed in seconds.
A16: Variable Set [ Name:%hours To:floor(%eta/3600) Do Maths:On Append:Off ]
A17: Variable Set [ Name:%minutes To:floor(((%eta/3600)-%hours)*
The above two lines simply convert the seconds back into hours and minutes.
A18: Variable Set [ Name:%dayhalf To:PM Do Maths:Off Append:Off ] If [ %hours > 11 & %hours < 24 ]
Now it's time to revisit that %dayhalf variable. If %hours is greater than 11, then we will be arriving at our destination in the afternoon and need to set %dayhalf to "PM"; unless %hours is also greater than 23. In that case, we will be arriving sometime after midnight of the next day, which is morning again so we leave %dayhalf alone.
A19: Variable Subtract [ Name:%hours Value:12 Wrap Around:0 ] If [ %hours > 12 ]
A20: Variable Subtract [ Name:%hours Value:12 Wrap Around:0 ] If [ %hours > 12 ]
I prefer the 12-hour time format, so these two lines take care of converting 24-hour time back to what I want. The same action is used twice just in case our trip will take us past midnight.
A21: Variable Set [ Name:%V3_ETA To:%timeleft (%distance)ETA: %hours:%minutes %dayhalf Do Maths:Off Append:Off ]
The final step is to take all the information we've derived and put it into a variable which is linked to a text box display on the main screen.
Once again, here's what that looks like:
To test this, we took the Z3 out for a little drive this weekend. We drove over a neighboring town and when we got ready to come back, I called up my navigation panel and chose "Home" as the destination. In about 15 seconds, the ETA display showed up on the main screen and began tracking the trip. I watched it pretty closely and it seemed to work perfectly. The ETA that it showed turned out to be exactly right and it counted down time and distance very accurately.
Later, I logged on to my Google Developer account and checked the stats for usage on the Distance Matrix API. It showed 44 calls, which was exactly correct for the 22 minute trip, and every call was successful. I also checked my cellular data usage and showed that my Bluetooth tethering app went through just under 8Mb for the day. I have no way of knowing just how much of that was for Distance Matrix calls, but even it accounted for all of it, I'm no danger of running over my 10GB monthly allowance even if I use navigation heavily.
Update: I tried this again today.(The weather here has been beautiful this weekend.) Same idea, but this time it was a longer test. On a 49-minute trip, I can see that the system handled 98 successful events and the info tracked perfectly. My phone showed data usage of about 5MB, so it is using even less than I had previously thought. It looks like I could run this system 24 hours per day all month and not even use half of my monthly data allowance.
Subscribe to:
Posts (Atom)




