How to: Ask Amazon Alexa for your Microsoft Teams Presence
Introduction
We’re all working and living at home and with our loved ones more. As we struggle to maintain the balance between work and life, it can be hard to separate time focused on work, time spent in meetings and calls, and time that could be free. For many, Microsoft Teams presence is a good indicator of how “disturbable” someone is, and this can extend to be useful information to those we live with.
There are lots of solutions out there already, using presence lights. Back in the day, I wrote something to expose Skype for Business presence for Phillips Hue. More recently, Isaac Levin has open-sourced an updated version for Teams. And, there are marvelous hardware solutions out there like Kuando BusyLight (of which I’m a big fan).
But.
For whatever reason, in our household, after an initial grace period, these passive solutions don’t seem to work, and I wanted to try something different.
We’re an Amazon Alexa house, using it for music, timers, calendar, and lighting. We like it.
At Build 2020, Microsoft announced GA support for Amazon Alexa in the Bot Framework SDK. This made me think:Â what if I could expose Microsoft Teams presence in Alex?
That’s what this blog post is about, here’s the code if you want to do it yourself, and here’s the very first time it happened:
If you want to do this yourself and want to follow along, the video at the top of this post is a 90 min demo of starting from nothing and creating with all the code and config. Enjoy
The “design”
In my head, it was all fairly easy: create a simple Bot Framework bot to get Teams presence when asked, then layer on the Alexa skill part.
Once I’d achieved the goal of surfacing presence, there were some other ideas – maybe a way for someone to use Alex to send a voice memo, which the bot could then send as an Adaptive Card in an IM.
Getting Teams Presence
On the face of it, getting Microsoft Teams presence is pretty straightforward – it’s part of the /beta branch of Microsoft Graph:
But, there’s a problem. Right now, there’s no support for Application permission type. What does this mean? It means that you can’t call this API as a headless application without user interaction – exactly what you need if you’re writing bot code. Instead, the only way to call the API is once you’ve authenticated as a user. If you’re writing a web page or desktop application this is fine, you can ask the user to authenticate and then call the API as that user (delegated permission) but that doesn’t really work for bots. (Yes, there are nasty workarounds to this, such as using a non-MFA service user, but it was 10pm at night and I didn’t want to trouble our internal IT for another user account just to get my presence.)
This is a pain point, and I’ve love to see it resolved in the future. For now, though, here’s my workaround: A separate application, which I run on my desktop, which polls for my presence (using delegated permissions) and writes it to Azure Table Storage. That means that my bot code can read the table storage without needing to work directly with the API.
This approach actually also gave me some better data, because I also have the time-stamps of when presence changes, so I can say things like “Tom is on a call, and has been for 15 mins”.
Writing the local presence polling app
I wrote the app to collect presence locally as a console application, because I’m lazy. All the code is on GitHub, but here are the important bits:
I’m using the excellent Graph SDK to make authentication a breeze. I’m using the InteractiveAuthenticationProvider, passing a RedirectUri which matches one I’ve set in the application definition:
public GraphClient() { IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder .Create(clientId) .WithRedirectUri("https://324exxxx-xxxx-xxxx-xxxx-xxxx49cbf85c") .Build(); InteractiveAuthenticationProvider authProvider = new InteractiveAuthenticationProvider(publicClientApplication); theGraphClient = new GraphServiceClient(authProvider); }
The SDK doesn’t have built-in support for the presence API calls yet (because they’re new and in beta still), but you can still use the authentication provider it provides and call URLs directly, like this:
public async Task<PresenceInfo> GetPresence() { string requestUrl = "https://graph.microsoft.com/beta/me/presence"; // Create the request message and add the content. HttpRequestMessage hrm = new HttpRequestMessage(HttpMethod.Get, requestUrl); // Authenticate (add access token) our HttpRequestMessage await theGraphClient.AuthenticationProvider.AuthenticateRequestAsync(hrm); // Send the request and get the response. HttpResponseMessage response = await theGraphClient.HttpProvider.SendAsync(hrm); var content = await response.Content.ReadAsStringAsync(); PresenceInfo presence = theGraphClient.HttpProvider.Serializer.DeserializeObject<PresenceInfo>(content); return presence; }
I’m de-serializing into a basic PresenceInfo class, which matches the JSON that comes back. Notice though, the TableEntity class it’s inheriting from, that’s for later:
public class PresenceInfo: TableEntity { public Guid id { get; set; } public string availability { get; set; } public string activity { get; set; } }
I have this code running on a 1-minute timer getting presence and comparing it the previously collected value. If it’s changed, then I write it to Table Storage – this way I’m not writing to Table Storage every minute with lots of repeated values (and it makes dealing with the data later easier on). I’m using the WindowsAzure.Storage NuGet package to write the data to table.
When writing to Table Storage, you have to specify a partition key and row key (the combination of which needs to be unique). Choosing these wisely can make your calls more performant and cheaper in the long run. I’ve set the partition key to be a representation of today’s date and the row key to be a Guid. This means that in the bot I can easily get all the presence for today (which is really all I care about).
Writing the Bot
Now that my presence info is in a Storage Table, the Bot part is quite easy. I started with creating the Web App Bot in Azure, choosing the C# Echo Bot sample. Then, I added the WindowsAzure.Storage NuGet package to get all the presence information, using today’s partition key.
At this stage I didn’t bother doing any sort of parsing of the input – the bot only supports a single activity so regardless of the input it will always throw back the same answer.
The Presence API provides both availability and activity states, with the activity one being more detailed – there are 15 different states. I converted those into English, did some datetime calculations to work out how long I’d been in my current state, and, voila:
Making it work with Amazon Alexa
Everything up to this point I already knew how to do. Now, I was heading into uncharted territory. How to take an existing Bot Framework bot and enable it for Alexa?
The Alexa Adapter is new into GA, but it’s been around a while. It was original written as a BotBuilder Community contribution, and I found that following their instructions worked really well.
You need an Amazon Developer Account, but there’s no cost to this, and you can create skills which work on any device that’s logged in with the same account without needing to publish, which is ideal for my purposes. The instructions include sample code to create a new Bot Framework adapter for Alex, which plugs straight into the standard EchoBot codebase without any wrinkles. It honestly was one of the easiest things I’ve done – within about 10 mins I was testing my bot out in the Amazon Dashboard:
At this point, I found that I can also talk to any of my devices, by saying “Ask Presence Helper…”. Anything I add after that is passed through to the Bot Framework to parse and deal with. Remember I said before that at the moment I’m always returning the presence, regardless of the input? That’s just as well, because I found that Amazon doesn’t seem to do a great job of the speech to text. It’s strange because for all the other skills it’s been pretty good, so maybe there’s something about it being in Development mode, or a new app or something. It’s not a huge problem at the moment, but I don’t know how well LUIS will deal with it if I decide to give the bot more functionality.
Futures
I’m really pleased with the results. It’s really cool that you can now ask Alexa if I’m free, busy, in a meeting or whatever. I’ve got some ideas for making things better though:
- I’d like to be able to send a message via Alexa and have it turn up as a card message from the bot.
- Ryan on LinkedIn had a good idea as well – triggering the Alexa status alerts to match presence state
And finally, I’m excited to see what you can do with it. All the code is on GitHub – please fork it and do something cool with it.