Band 2 Development–A First Try…

For a while, I’ve had it on my list to look at developing on the Microsoft Band 2. I skipped the first generation of the Band but I did jump in with the 2nd generation back in December and I like the device although I don’t use it to its full capacity, mainly using it for;

    • Sleep tracking
    • Workout tracking
    • Calendar, Mails, Text
    • Steps, Calendar, Calories

and it’s been working pretty well for me in that I get decent battery life from the device and it’s proved to be quite wearable once I’ve got used to its position on my wrist.

I’ll admit that I had a few ‘challenges’ with the device when I first got it in that some of the functionality didn’t work with my original Lumia 1020 phone for a reason that I never got to the bottom of and I found that quite frustrating. That said, I’ve since paired it with a Lumia 950XL and it’s worked fine with that device.

One of the things that I think causes some confusion around the Band 2 is the type of device that it is. I’ve talked to Microsoft folks who are convinced that the Band is a Windows 10 device and that it runs the Windows 10 OS and Universal Windows Platform (UWP) apps and, as you probably know if you’re reading this blog site, it isn’t and it doesn’t.

As an aside, it would be quite something to put the “UWP” onto a device like this. As has been said before, the Windows 10 app platform is made up of platforms (UWP, Mobile, Desktop, etc) and those platforms are made up contracts which are sets of APIs. The “UWP” is a contract and it contains most of the available APIs and so to put all of that functionality onto a device like a Band 2 would be “quite a piece of work”. I’m not saying it’s “impossible” but…

For me, I see the Band 2 as more of a ‘smart peripheral’. That is, it’s really a companion device for a piece of software running on another device (generally, a phone) but it does have some level of built-in functionality in that it can do things like;

    1. Guide you through a pre-defined workout.
    2. Run a stopwatch or a timer.
    3. Record details of your sleep or a run.
    4. Keep track of your activity like steps, calories, heart rate.

in isolation which is a good thing otherwise you’d have to take both the phone and the band with you any time you wanted to use the band.

When it comes to developing for the Band 2, there are 3 options outlined on the website.

    1. Use the Band SDK – write code to talk to the Band 2 over bluetooth from a device like a phone or PC (there’s an iOS, Android and Windows SDK).
    2. Make a ‘Web Tile’ – provide a URL that can be polled for data to display on a tile. The resulting tile can be manually installed to a Band 2 or it can be submitted to the gallery for a user to install themselves via the Health app on the Band 2.
    3. Work with the back-end data that’s ultimately gathered from a Band 2 and available over a REST API.

I think all of these have their place but it’s probably the SDK that’s of most interest to me and so I opened it up and tried out a few scenarios.

A big note – I’m not doing anything here that hasn’t been done many times before. I’ve watched developer sessions on the Band/Band 2. I’m mainly working through it here and writing it up in order to add to that weight of material but also so that it sticks in my head – I tend to find that if I haven’t written the code myself then I can’t remember how things work.

Installing the SDK

Installing the Band 2 SDK is as easy as installing the NuGet package Microsoft.Band which I did from the package manager console in Visual Studio and seemed to get ‘success’ !

image

At the time of writing, I always have a tense “Will it install?” moment when grabbing SDKs from Nuget and trying to install them into UWP projects so I was happy that this one seemed to “just work”.

The SDK docs are then a big PDF document that seems to do a pretty good job of explaining things but I’ll add my own little experiments below.

Connecting to a Band 2

The SDK walks you through getting a connection to a Band which I think can be summarised as;

      var bands = await BandClientManager.Instance.GetBandsAsync();
      
      if (bands?.Count() > 0)
      {
        var client = 
          await BandClientManager.Instance.ConnectAsync(bands.First());

        // Do something...
      }

and that was a piece of code that worked the first time I wrote it and ran it on my phone (which is paired with my Band 2). I should say that I’d modified my manifest to include;

image

but I didn’t manually hack the manifest XML file as suggested in the SDK, I just used the manifest designer here.

So, I’m connected to my Band 2. What can I now do?

Reading from Sensors

There are a bunch of sensors on the Band 2 and accessing them seems pretty simple. There’s even a ‘Sensor Manager’ that handles it for you although I’ve often been told off for naming classes “XYZManager” in the past Smile

If you imagine that the code below is from a code-behind class in XAML and that there is a TextBlock on screen called txtStatus;

      var bands = await BandClientManager.Instance.GetBandsAsync();
      
      if (bands?.Count() > 0)
      {
        var client = 
          await BandClientManager.Instance.ConnectAsync(bands.First());

        var contact = client.SensorManager.Contact;

        // Do something...
        DispatchedHandler handler = async () =>
        {
          var status = 
            await contact.GetCurrentStateAsync();

          this.txtStatus.Text = status.State !=
            BandContactState.Worn ? "Put your band back on now!" : "Glad you are wearing Band";
        };
        handler();

        contact.ReadingChanged += (s, e) =>
        {
          this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, handler);
        };
      }

then this code does what you expect – it monitors the user as they put their Band 2 on/off.

I found that code worked pretty well given that I’d spend maybe 1-2 minutes on it and apologies for the slightly obtuse (i.e. lazy) way that I’ve written and invoked the event handler inline inside of this function.

If I wanted to read from another sensor like the heart rate sensor then that’s pretty easy but it requires a couple of extra steps – I set up an interval for the reading to occur and I also have to ask for permission;

      var bands = await BandClientManager.Instance.GetBandsAsync();

      if (bands?.Count() > 0)
      {
        var client =
          await BandClientManager.Instance.ConnectAsync(bands.First());

        var hr = client.SensorManager.HeartRate;

        var allowed = (hr.GetCurrentUserConsent() == UserConsent.Granted);

        if (!allowed)
        {
          allowed = await hr.RequestUserConsentAsync();
        }
        if (allowed)
        {
          // choose the smallest interval
          hr.ReportingInterval = hr.SupportedReportingIntervals.OrderBy(i => i).First();

          await hr.StartReadingsAsync();

          hr.ReadingChanged += (s, e) =>
          {
            this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
              () =>
              {
                this.txtStatus.Text = e.SensorReading.HeartRate.ToString();
              });
          };
        }

but it’s still pretty easy and it looks like the other sensors including Accelerometer, Altimeter, Barometer, Calories, Distance, Gsr, Gyroscope, Pedometer, RRInterval, SkinTemperature and UV all follow a very similar model meaning that it’s a “learn once, repeat many” type process which I like a lot.

For an application that wants to run on my Phone/PC in either the foreground or the background and talk to my Band 2 for a while, gather some data from it and then “do something with it” like sending it to the cloud it doesn’t feel like it should be too difficult to do.

What’s not so clear to me is how the Band 2 might trigger something on the Phone/PC in a scenario where the Phone/PC wasn’t already “listening” for that action short of having the Phone/PC app running all the time which often isn’t a viable option. I’ll need to come back to that.

What else can be done? There are some other ‘manager’ objects hanging off that IBandClient interface that I’ve got hold of…

Notifications (Part 1)

I can send notifications from code running on my device (Phone/PC) to my Band 2. Here’s my first attempt;

      var bands = await BandClientManager.Instance.GetBandsAsync();

      if (bands?.Count() > 0)
      {
        using (var client =
          await BandClientManager.Instance.ConnectAsync(bands.First()))
        {
          await client.NotificationManager.VibrateAsync(VibrationType.OneToneHigh);
        }
      }

and that worked out fine so I thought that I’d be able to follow it up by sending a message or showing a dialog but both of those need a tile ID – where does that come from?

Tiles

The band displays tiles on its Start Strip;

image

and those can be of two different types;

    • Messaging – just have messages (titles/bodies) behind them.
    • Custom – can have more complex content behind them managed as a set of pages.

I figured that I’d create a messaging tile and see how that worked for me. The code’s pretty simple and so I tried to combine it with some code which then displayed a dialog before tidying up the tile by removing it again;

     var bands = await BandClientManager.Instance.GetBandsAsync();

      if (bands?.Count() > 0)
      {
        using (var client =
          await BandClientManager.Instance.ConnectAsync(bands.First()))
        {
          var tileSpace = await client.TileManager.GetRemainingTileCapacityAsync();

          if (tileSpace > 0)
          {
            var iconFile = await StorageFile.GetFileFromApplicationUriAsync(
              new Uri("ms-appx:///Assets/tileicon.png"));

            var smallIconFile = await StorageFile.GetFileFromApplicationUriAsync(
              new Uri("ms-appx:///Assets/smalltileicon.png"));

            using (var stream = await iconFile.OpenReadAsync())
            {
              using (var smallStream = await smallIconFile.OpenReadAsync())
              {
                var largeBitmap = new WriteableBitmap(48, 48);
                largeBitmap.SetSource(stream);
                var largeIcon = largeBitmap.ToBandIcon();

                var smallBitmap = new WriteableBitmap(24, 24);
                smallBitmap.SetSource(smallStream);
                var smallIcon = smallBitmap.ToBandIcon();

                var guid = Guid.NewGuid();

                var added = await client.TileManager.AddTileAsync(
                  new BandTile(guid)
                  {
                    Name = "Test",
                    TileIcon = largeIcon,
                    SmallIcon = smallIcon
                  }
                );
                if (added)
                {
                  // NB: This call will return back to us *before* the
                  // user has acknowledged the dialog on their device -
                  // we don't get to know their answer here.
                  await client.NotificationManager.ShowDialogAsync(
                    guid, "check-in", "are you ok?");

                  await client.TileManager.RemoveTileAsync(guid);
                }
              }
            }
          }
        }
      }
    }

Note that while this code works, I think it’s a little suspect as I actually make a call to RemoveTileAsync which is likely to run before the user has actually seen the dialog associated with that tile on the screen. The Band 2 (or the SDK) seems to forgive me for this but it’s perhaps not the best of ideas.

I can switch the call to ShowDialogAsync to ShowMessageAsync by just changing that piece of code to;

              await client.NotificationManager.SendMessageAsync(
                    guid, "title", "body", DateTimeOffset.Now, MessageFlags.None);

                  await Task.Delay(20000);

                  await client.TileManager.RemoveTileAsync(guid);

and the difference here is in how the Band 2 displays a message versus a dialog.

For the dialog case it pops a dialog for me to dismiss whereas for the message case I end up with a tile with a badge on it that I can then tap to see the details of the message behind the tile.

That’s why I added the 20 second delay to the code for the message example in order to give me enough time to see the message behind the tile before it was removed. Again, the way in which I’m removing the tile here probably isn’t a great thing to be doing!

Once I’ve got a tile, I can know when the user taps on the tile to “enter” my app within the Band 2 and when they “leave” by using the back button but, again, this is only going to be while I have code with an active connection to the Band 2.

It was time to shake up my “UI” a little so I made 3 buttons for Create/Register Handlers/Remove and put this code behind it;

  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();
    }
    async void OnCreateTile(object sender, RoutedEventArgs args)
    {
      var bands = await BandClientManager.Instance.GetBandsAsync();

      if (bands?.Count() > 0)
      {
        this.client = await BandClientManager.Instance.ConnectAsync(bands.First());

        var tileSpace = await this.client.TileManager.GetRemainingTileCapacityAsync();

        if (tileSpace > 0)
        {
          var iconFile = await StorageFile.GetFileFromApplicationUriAsync(
            new Uri("ms-appx:///Assets/tileicon.png"));

          var smallIconFile = await StorageFile.GetFileFromApplicationUriAsync(
            new Uri("ms-appx:///Assets/smalltileicon.png"));

          using (var stream = await iconFile.OpenReadAsync())
          {
            using (var smallStream = await smallIconFile.OpenReadAsync())
            {
              var largeBitmap = new WriteableBitmap(48, 48);
              largeBitmap.SetSource(stream);
              var largeIcon = largeBitmap.ToBandIcon();

              var smallBitmap = new WriteableBitmap(24, 24);
              smallBitmap.SetSource(smallStream);
              var smallIcon = smallBitmap.ToBandIcon();

              this.tileGuid = Guid.NewGuid();

              var added = await this.client.TileManager.AddTileAsync(
                new BandTile(this.tileGuid)
                {
                  Name = "Test",
                  TileIcon = largeIcon,
                  SmallIcon = smallIcon
                }
              );
            }
          }
        }
      }
    }
    async void OnRemove(object sender, RoutedEventArgs e)
    {
      await this.client.TileManager.StopReadingsAsync();
      await this.client.TileManager.RemoveTileAsync(this.tileGuid);
    }

    void OnRegister(object sender, RoutedEventArgs e)
    {
      this.client.TileManager.TileOpened += OnTileOpened;
      this.client.TileManager.TileClosed += OnTileClosed;
      this.client.TileManager.StartReadingsAsync();
    }
    void OnTileClosed(object sender, BandTileEventArgs<IBandTileClosedEvent> e)
    {
      if (e.TileEvent.TileId == this.tileGuid)
      {
        // My tile!
      }
    }
    void OnTileOpened(object sender, BandTileEventArgs<IBandTileOpenedEvent> e)
    {
      if (e.TileEvent.TileId == this.tileGuid)
      {
        // My tile!
      }
    }
    IBandClient client;
    Guid tileGuid;
  }

and, sure enough, the 2 event handlers that I left empty above get called when I tap on the new tile on the band and when I use the “back” button to leave that tile.

From the SDK docs, these events are handled using intents on Android which would mean that the events can be received whether the app on the phone is running or not which is not something that the Windows SDK here surfaces (although, naturally, it’s possible to do that type of thing on Windows UWP).

Pages, UI Elements

Beyond that, tiles can have pages within them which can contain UI pieces like buttons and text blocks, icons and barcodes within layout containers that may scroll etc.

I found this slightly more tricky to get working in the first instance as there’s a definite ‘pattern’ that’s in use here where you need to;

    1. Create your tile that specifies the page layouts that it contains and the UI elements within them which are given identifiers.
    2. Dynamically “talk” to the tile at a later point to set content for the elements that you earlier identified.

and I didn’t get this ‘2 step dance’ quite right for the first few attempts but it makes sense and is fairly easy once you’ve got the idea of it.

I left my UI as being 3 buttons for create/register/remove and I altered my tile such that it had a single page with a single button on it;

  using Microsoft.Band;
  using Microsoft.Band.Tiles;
  using Microsoft.Band.Tiles.Pages;
  using System;
  using System.Linq;
  using Windows.Storage;
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;
  using Windows.UI.Xaml.Media.Imaging;
  public sealed partial class MainPage : Page
  {
    public MainPage()
    {
      this.InitializeComponent();
      this.buttonElementId = 1;
    }
    async void OnCreateTile(object sender, RoutedEventArgs args)
    {
      var bands = await BandClientManager.Instance.GetBandsAsync();

      if (bands?.Count() > 0)
      {
        this.client = await BandClientManager.Instance.ConnectAsync(bands.First());

        var tileSpace = await this.client.TileManager.GetRemainingTileCapacityAsync();

        if (tileSpace > 0)
        {
          var iconFile = await StorageFile.GetFileFromApplicationUriAsync(
            new Uri("ms-appx:///Assets/tileicon.png"));

          var smallIconFile = await StorageFile.GetFileFromApplicationUriAsync(
            new Uri("ms-appx:///Assets/smalltileicon.png"));

          using (var stream = await iconFile.OpenReadAsync())
          {
            using (var smallStream = await smallIconFile.OpenReadAsync())
            {
              var largeBitmap = new WriteableBitmap(48, 48);
              largeBitmap.SetSource(stream);
              var largeIcon = largeBitmap.ToBandIcon();

              var smallBitmap = new WriteableBitmap(24, 24);
              smallBitmap.SetSource(smallStream);
              var smallIcon = smallBitmap.ToBandIcon();

              this.tileGuid = Guid.NewGuid();
              this.pageGuid = Guid.NewGuid();

              var panel = new FilledPanel()
              {
                BackgroundColor = new BandColor(0xFF, 0x00, 0x00),
                Rect = new PageRect(0, 0, 245, 102),
                BackgroundColorSource = ElementColorSource.BandBase
              };
              panel.Elements.Add(
                new Microsoft.Band.Tiles.Pages.TextButton()
                {
                  ElementId = this.buttonElementId,
                  Rect = new PageRect(0, 0, 245, 102),
                  VerticalAlignment = Microsoft.Band.Tiles.Pages.VerticalAlignment.Bottom,
                  HorizontalAlignment = Microsoft.Band.Tiles.Pages.HorizontalAlignment.Center
                }
              );
              var pageLayout = new PageLayout(panel);

              var bandTile = new BandTile(this.tileGuid)
              {
                Name = "Test",
                TileIcon = largeIcon,
                SmallIcon = smallIcon
              };
              bandTile.PageLayouts.Add(pageLayout);

              var added = await this.client.TileManager.AddTileAsync(bandTile);
            }
          }
        }
      }

      this.pageGuid = Guid.NewGuid();

      // The hard-coded 0 here means 'layout 0'
      await this.client.TileManager.SetPagesAsync(
        this.tileGuid,
        new PageData(
          this.pageGuid,
          0, 
          new TextButtonData(this.buttonElementId, "Click Me")));
    }
    async void OnRemove(object sender, RoutedEventArgs e)
    {
      await this.client.TileManager.StopReadingsAsync();
      this.client.TileManager.TileOpened -= OnTileOpened;
      this.client.TileManager.TileClosed -= OnTileClosed;
      this.client.TileManager.TileButtonPressed -= OnTileButtonPressed;
      await this.client.TileManager.RemoveTileAsync(this.tileGuid);
    }

    void OnRegister(object sender, RoutedEventArgs e)
    {
      this.client.TileManager.TileOpened += OnTileOpened;
      this.client.TileManager.TileClosed += OnTileClosed;
      this.client.TileManager.TileButtonPressed += OnTileButtonPressed;
      this.client.TileManager.StartReadingsAsync();
    }

    void OnTileButtonPressed(object sender,
      BandTileEventArgs<IBandTileButtonPressedEvent> e)
    {
      if ((e.TileEvent.TileId == this.tileGuid) &&
        (e.TileEvent.PageId == this.pageGuid) &&
        (e.TileEvent.ElementId == this.buttonElementId))
      {
        // My button!
      }
    }

    void OnTileClosed(object sender, BandTileEventArgs<IBandTileClosedEvent> e)
    {
      if (e.TileEvent.TileId == this.tileGuid)
      {
        // My tile!
      }
    }
    async void OnTileOpened(object sender, BandTileEventArgs<IBandTileOpenedEvent> e)
    {
      if (e.TileEvent.TileId == this.tileGuid)
      {
        // My Tile
      }
    }
    IBandClient client;
    Guid tileGuid;
    Guid pageGuid;
    short buttonElementId;
    BandTile bandTile;
  }

and the OnCreateTile handler here creates a tile on the band with a single page with a single button on it whose data is dynamically set to say “Click Me”.

The OnRegister handler sets up event handlers for when that tile is entered/left and also for when the button is clicked – the OnTileButtonPressed handler.

That’s all quite nice and not too difficult at all. What else can the Band 2 do for me?

Personalisation

The last bit of obvious functionality in the Band SDK is the personalisation that lets you write code to;

    • Change the ‘Me Tile’
    • Change the theme

and so it feels like this is the set of APIs that underpin some (or all) of the Health app’s capability to play around with the Band 2’s settings which you might want to duplicate in your own app.

What’s Missing?

The one thing I’d really like to see from the Band 2 SDK is a means by which an action initiated on the Band 2 can cause something to happen on a paired phone or PC even if that phone or PC isn’t actively waiting for it.

I’m thinking of some kind of feature whereby an app on the phone or PC can register a background trigger (possibly on a bluetooth socket) so that the system can quietly wait for an action on the Band 2 (like a button press) and run some background code when that occurs.

From the SDK docs, it looks like that’s do-able on Android and it’s probably do-able on Windows too if you go spelunking into the bluetooth protocol that underpins the SDK but, without that, it’s the one most obvious thing that I’d like to see added.

Otherwise, the SDK’s really easy to pick up and code against – I enjoyed having a quick play with the Band 2.

3 thoughts on “Band 2 Development–A First Try…

    • Yup, that’s great – in talking about background handling here though what I’m really looking for is a way for the Band to initiate action on the Phone/PC rather than using the background infrastructure (i.e. triggers, conditions and tasks) in order to periodically talk to the Band just in case there’s something “interesting” going on 🙂

      So…I want more of a “push” from the Band rather than a “pull” from the PC.

      • That’s one thing I found to be missing from the SDK that I wanted to use. My app takes backups from the Band, and I wanted to be able to initiate the backup from a button inside my apps tile on the band.

        Closest I got to pushing an action from the band to the app on the phone was to implement a Cortana voice command that launched my app and performed the backup action.

        I was hoping the Band 2 / newer SDK in general would allow us to trigger our app on the phone via a button, but I guess the UWP version of the app will have to forgo that for now.

Comments are closed.