Mobile Corner
Build Apps That Talk with the Microsoft Band
The Microsoft Band is one of the most sophisticated fitness and health trackers available today. In this article, Nick walks through building a Windows Phone app that can communicate with it.
- By Nick Randolph
- 03/01/2016
The SDK that's available for developers to build applications that communicate with the Microsoft Band (or, simply, the Band) allows for a number of simple scenarios out of the box, such as adding, removing and updating tiles on the Band, reading sensor data and prompting the user. However, most of these scenarios rely on the application running in the foreground, or staying alive in the background. The issue with this is that this excludes some of the most interesting scenarios that are only achievable by tracking or monitoring sensor data over an extended period of time.
In this article I'll walk through creating an application that has a background task that will remain active in the background, and as sensor data changes, it will update a live tile with the most recent data value.
The application consists of two parts, the application itself, which in this case will be a Windows Phone application, called HealthMonitor, based on the Blank App (Windows Phone) project template (see Figure 1), along with a Windows Runtime Component, called HealthMonitor.Background, which will house the background task (see Figure 2).
After creating a solution with these two projects, I need to add a HealthMonitor.Background reference to the HealthMonitor project. The next reference to add is to the Microsoft Band SDK, which can be found in NuGet, as shown in Figure 3.
In order to make updating the contents of a live tile easier, I'll also add a NuGet reference to the NotificationsExtensions package (see Figure 4).
The interface for the application will contain three buttons: Pin, Register and Unregister. The Pin button creates a new SecondaryTile that will be used to display the current heart rate that the background task will read from the Band. Listing 1 shows the code for this.
Listing 1: Creating a Secondary Tile
private const string BandTileId = "HeartRate";
private async void PinTileClick(object sender, RoutedEventArgs e)
{
var square = new Uri("ms-appx:///Assets/Square150x150Logo.scale-200.png");
var wide = new Uri("ms-appx:///Assets/Wide310x150Logo.scale-200.png");
var secondaryTile = new SecondaryTile(BandTileId,
"Current heart rate",
"heartrate",
square,
TileSize.Wide310x150);
secondaryTile.VisualElements.Wide310x150Logo = wide;
secondaryTile.VisualElements.ShowNameOnSquare150x150Logo = true;
secondaryTile.VisualElements.ShowNameOnWide310x150Logo = true;
secondaryTile.VisualElements.ForegroundText = ForegroundText.Light;
await secondaryTile.RequestCreateAsync();
}
Before writing the event handlers for the Register or Unregister button I need to create the background task within the Windows Runtime Component. The code in Listing 2 obtains a deferral that will allow the task to continue running for a period of time in the background. It also subscribes to the Canceled event, to ensure that any references to the Band are disposed of before the task completes.
Listing 2: Creating the Background Task
public sealed class MonitorTask : IBackgroundTask
{
private BackgroundTaskDeferral deferral;
public async void Run(IBackgroundTaskInstance taskInstance)
{
deferral = taskInstance.GetDeferral();
taskInstance.Canceled += OnTaskCanceled;
var bandConnected = true;
try
{
bandConnected = await ConnectToBand();
}
catch (Exception)
{
bandConnected = false;
}
if (!bandConnected)
{
await CompleteDeferral();
}
}
private async void OnTaskCanceled(IBackgroundTaskInstance sender,
BackgroundTaskCancellationReason reason)
{
await CompleteDeferral();
}
private async Task CompleteDeferral()
{
if (this.bandClient != null)
{
await StopHeartRateMonitor();
bandClient.Dispose();
bandClient = null;
}
deferral.Complete();
}
}
The ConnectToBand method is where the core functionality of the background task starts (see Listing 3). It begins with acquiring a reference to the Band and connecting to it. It also checks to make sure that the user has given consent for the application to reference the HeartRate sensor -- the user will be prompted for this prior to registering the background task.
Listing 3: Connecting to the Band
private async Task<bool> ConnectToBand()
{
bandInfo = (await BandClientManager.Instance.GetBandsAsync()).FirstOrDefault();
if (bandInfo == null)
{
return false;
}
bandClient = await BandClientManager.Instance.ConnectAsync(bandInfo);
if (bandClient?.SensorManager.HeartRate.GetCurrentUserConsent() != UserConsent.Granted)
{
return false;
}
await StartHeartRateMonitor();
return true;
}
The StartSensorRunning method starts by making sure the sensors are off, before it wires up an event handler for the HeartRate sensor and starts reading data from the sensor, as shown in Listing 4.
Listing 4: Starting the HeartRateSensor
private bool isHeartRateOn;
private async Task StartHeartRateMonitor()
{
await StopHeartRateMonitor();
try
{
bandClient.SensorManager.HeartRate.ReadingChanged += OnHeartRateChanged;
await bandClient.SensorManager.HeartRate.StartReadingsAsync();
isHeartRateOn = true;
}
catch (Exception)
{
isHeartRateOn = false;
}
}
private async Task StopHeartRateMonitor()
{
if (isHeartRateOn)
{
try
{
await bandClient.SensorManager.HeartRate.StopReadingsAsync();
}
catch (Exception)
{
}
finally
{
bandClient.SensorManager.HeartRate.ReadingChanged -= OnHeartRateChanged;
isHeartRateOn = false;
}
}
}
The event handler for the ReadingChanged event on the HeartRate sensor creates a new badge update and applies it to the tile created by the Pin button (the BandTileId of the secondary tile created earlier matches the BandTileId specified in call to the CreateBadgeUpdaterForSecondaryTile method:
private const string BandTileId = "HeartRate";
private void OnHeartRateChanged(object sender,
BandSensorReadingEventArgs<IBandHeartRateReading> e)
{
var content = new BadgeNumericNotificationContent
{
Number = (uint)e.SensorReading.HeartRate
};
BadgeUpdateManager.CreateBadgeUpdaterForSecondaryTile(BandTileId)
.Update(new BadgeNotification(content.GetXml()));
}
Now that I've completed the code for the background task, it's necessary to register it to be invoked. There are two parts to this: First, the MonitorTask class has to be registered in the Package.appxmanifest file as a Background task (Figure 5).
Next, when the user clicks the Register button he'll be prompted to grant access to the HeartRate sensor on the Band. The user will also be prompted to grant permission for the application to execute in the background. Once granted, the background task will be registered and a DeviceUseTrigger assigned to it. The DeviceUseTrigger is used to trigger the background task when the Band (which is identified using the FindAllAsync method on the DeviceInformation class) communicates with the phone, as shown in Listing 6.
Listing 6: Registering the Background Task
private const string BandDataTaskId = "BandDataTask";
public static IBackgroundTaskRegistration ActiveMonitorTask
{
get
{
var task = BackgroundTaskRegistration.AllTasks.FirstOrDefault(
t => t.Value.Name == BandDataTaskId).Value;
return task;
}
}
private async void RegisterClick(object sender, RoutedEventArgs e)
{
var bandInfo = (await BandClientManager.Instance.GetBandsAsync()).FirstOrDefault();
using (var bandClient = await BandClientManager.Instance.ConnectAsync(bandInfo))
{
bool? consentGranted;
if (bandClient.SensorManager.HeartRate.GetCurrentUserConsent() != UserConsent.Granted)
{
consentGranted =
await bandClient.SensorManager.HeartRate.RequestUserConsentAsync();
}
else
{
consentGranted = true;
}
if (consentGranted.HasValue && consentGranted.Value)
{
var device =
(await
DeviceInformation.FindAllAsync(
RfcommDeviceService.GetDeviceSelector(
RfcommServiceId.FromUuid(
new Guid("A502CA9A-2BA5-413C-A4E0-13804E47B38F")))))
.FirstOrDefault(x => x.Name == bandInfo.Name);
var access = await BackgroundExecutionManager.RequestAccessAsync();
if ((access == BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity)
|| (access == BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity))
{
var taskBuilder = new BackgroundTaskBuilder
{
Name = BandDataTaskId,
TaskEntryPoint = typeof(MonitorTask).FullName
};
var deviceUseTrigger = new DeviceUseTrigger();
taskBuilder.SetTrigger(deviceUseTrigger);
taskBuilder.Register();
await deviceUseTrigger.RequestAsync(device.Id);
}
}
}
}
The remaining code to write is the Unregister event handler, which simply unregisters the background task, assuming one exists:
private void UnregisterClick(object sender, RoutedEventArgs e)
{
ActiveMonitorTask?.Unregister(true);
}
When this application is run the user can pin a secondary tile and register the background task. If the Band is within range of the phone, the background task will be triggered and events will be raised whenever the heart rate changes. This will in turn be used to update the badge on the secondary tile. This is just one of many sensors on the Microsoft Band that can be monitored and displayed on tiles on the Start screen.
Alternatively, there are many other ways to interact with users via the Microsoft Band either by creating tiles, or prompting them for a response. These are all possible using the SDK that's available for Windows, iOS and Android applications.
Reference: Credit goes to James Croft who originally posted this article on how to trigger a background task using the DeviceUseTrigger for Microsoft Band.
About the Author
Nick Randolph runs Built to Roam, a consulting company that specializes in training, mentoring and assisting other companies build mobile applications. With a heritage in rich client applications for both the desktop and a variety of mobile platforms, Nick currently presents, writes and educates on the Windows Phone platform.