Category Archives: Microsoft Teams

Blog Series – Power’ing up your Home Office Lights: Part 10 – Subscribe to Graph and Teams Presence to automatically set Hue Lights based on my Teams Presence!

This blog post is part of the Blog Series: Power’ing up your Home Office Lights with Power Platform. See introduction post for links to the other articles in the series:
https://gotoguy.blog/2020/12/02/blog-series—powering-up-your-home-office-lights-using-power-platform—introduction/

In this last part of the blog series, we will build on the previous blog post where we could get the presence status from Teams to the Hue Power App. Now we will use Microsoft Graph and subscribe to change notifications for presence, so that both the Power App and my Hue Lights will automatically change status based on the presence.

In this blog post I will reuse the Custom Connector I created for managing Microsoft Graph Subscriptions in this previous blog post: Managing Microsoft Graph Change Notifications Subscriptions with Power Platform | GoToGuy Blog

To follow along, you will need at least to create the App Registration and the Custom Connector referred to in that blog post. You can also import the Custom Connector OpenAPI Swagger from this link: <MY GITHUB URL TO HERE>

Adding the Custom Connector to the PowerApp

In the Hue Power App, go to the View menu, and under Data click to “+ Add data source”. Add the MSGraph Subscription Connector as shown below. This way we can refer to this in the Hue Power App.

We are going to add the logic to creating the subscription based on this toggle button and the selected light source:

But before that we have somethings to prepare first. When creating a Graph Subscription, we will need to prepare a Webhook Url for where Graph will send its notifcations when the Teams presence changes. This will be handled in a Power Automate Flow, so lets create that first.

Creating the Flow for Graph Notifications and Hue Lights changes

Since I have built this all before, ref. https://gotoguy.blog/2020/10/24/managing-microsoft-graph-change-notifications-subscriptions-with-power-platform/, I will make a copy of the “Graph Notification – Presence Change” flow for this use case.

If you want to follow along, you will need to follow the instructions in that blog post first. After saving a copy, or if you created a new flow from blank using HTTP request webhook, you will need to copy the HTTP POST URL shown below, as that will be the “notification url” we will refer to later:

Next set some value that only you know for the secret client state:

The next part of the Flow are used to do a first-time validation of adding the change subscription, with Content-Type text/plain and request query containing a validation token. Microsoft Graph expects a response of status code 200 with the validation token back. If that is returned, Microsoft Graph will successfully create the subscription.

For subsequent requests, we must return a 202 Accepted response, and in the next step I parse the notification request body, so that we can look into what change we have been notified for:

Following the change notification, we can start looking into the change value. Firstly, I have added a verification of the secret client state I specified earlier, this is prevent misuse of the notification Url if that become known by others or used in the wrong context. After doing a simple test of the client state, where I do nothing if the client state don’t match, I can start building the logic behind the changes in presence status:

Inside the Switch Presence Status action, I will based on the availability status change, do a different case for each of the possible Teams Presence status values (see blog series post part 9 for explaining the different presence availability values):

Inside each of these cases I will define the light settings and colors, and after that call the Remote Hue API for setting the light. As you saw in part 8 of this blog series, in order to access the Hue API remotely I will need the following:

  • An Access Token to be included as a Bearer token in the Authorization Header
  • A username/whitelist identifier
  • The actual lightnumber to set the state for
  • A request body containing the light state, for example colors, brightness, on/off etc.

Remember, this Flow will be triggered from Microsoft Graph whenever there is a presence state change in Teams. So I need to be able to access/retreive the access token, username and for which lightnumber I created the subscription. This is how I will get it:

  • Access Token will be retreived via the Logic App I created in part 4 of the blog series.
  • Username/whitelist identifier will be retrieved via the SharePoint List i created in part 6, see below image.
  • However, I do need to store the lightnumber I will create the change notification for, and for this I will add a couple more columns in this list:

Customize the Configuration List for storing Subscription Details

I add the following two single-line-of-text columns to my List:

  • Subscribe Presence LightNumber. This will be the chosen Hue light I want to change when change notifications occur for Teams presence status.
  • Change Notification Subscription Id. This will be the Id I can refer back to when adding and removing the subscription when needed.

Customize the Flow for getting User Configuration and preparing Light States

Back to the Flow again, we need to add some actions. First, add a initialize variable action after the initialize SecretClientState action like here:

I set the type to Object and using the json function to create an empty json object. This variable will later be used for changing the light state.

Next, in the Flow after the “Parse Notification Body” action, add a “Get Items” action from SharePoint connector, and configure it to your site, list name and the following Filter Query:

My filter query:
first(body('Parse_Notification_Body')?['value'])?['subscriptionId']
will be used to find what username and light that have been set up for presence changes.

Next, lets set the variable for LigthState. Inside each of the Switch Cases, add the action Set Variable, and then set the variable to the chosen xy color code, as a json object like the following. This is for the color green:

Do the same for all of the other case, these are the values I have been using:

STATECOLORJSON VARIABLE
AwayYellowjson(‘{
“on”: true,
“bri”: 254,
“xy”: [ 0.517102, 0.474840 ],
“transitiontime”: 0
}’)
AvailableGreenjson(‘{
“on”: true,
“bri”: 254,
“xy”: [ 0.358189, 0.556853 ],
“transitiontime”: 0
}’)
AvailableIdleGreen (10% bright)json(‘{
“on”: true,
“bri”: 25,
“xy”: [ 0.358189, 0.556853 ],
“transitiontime”: 0
}’)
BusyRedjson(‘{
“on”: true,
“bri”: 254,
“xy”: [ 0.626564, 0.256591 ],
“transitiontime”: 0
}’)
BusyIdleRed (10% bright)json(‘{
“on”: true,
“bri”: 25,
“xy”: [ 0.626564, 0.256591 ],
“transitiontime”: 0
}’)
BeRightBackYellowjson(‘{
“on”: true,
“bri”: 254,
“xy”: [ 0.517102, 0.474840 ],
“transitiontime”: 0
}’)
DoNotDisturbRedjson(‘{
“on”: true,
“bri”: 254,
“xy”: [ 0.626564, 0.256591 ],
“transitiontime”: 0
}’)
OfflineGrey (10% bright)json(‘{
“on”: true,
“bri”: 25,
“xy”: [ 0.3146, 0.3303 ],
“transitiontime”: 0
}’)
PresenceUnknownWhitejson(‘{
“on”: true,
“bri”: 254,
“xy”: [ 0.3146, 0.3303 ],
“transitiontime”: 0
}’)

PS! The transitiontime is to get as close to realtime updates as possible. Add the colors similarly for rest of the states:

Get the Access Token and call Hue Remote API

Now we are ready to get the Access Token, and call the Hue Remote API with the selected light state.

First, add a HTTP action below the Switch Presence Status action, calling the Logic App previously created in part 4 of the blog series:

Next, add another HTTP request after, and this will call the Hue Remote API to set the lights:

When constructing Hue API URI above, I retreive the whitelist identifier for username with the following expression:

first(body('Get_My_Hue_User_Subscription')?['Value'])?['WhitelistIdentifier']

And then which light number with this expression:

first(body('Get_My_Hue_User_Subscription')?['Value'])?['SubscribePresenceLightNumber']

That should be the completion of this Flow. Remember to turn it on if needed. We are now ready for the last step.

Adding the Flow for Creating the Graph Subscription

In the beginning of this blog post I referred to the toggle “Sync Hue Lights with Teams” in the Power App. Now that we have prepared the Flow handling the notification Url, we need to add a new Flow that would handle the creating, or deletion, of Graph Subscriptions. As this is a toggle control, setting it to “On” should create a Graph subscription for the selected lights, and setting it to “Off” should remove that subscription again.

Create a new instant Flow with PowerApps as trigger:

Next, add an action for initialize variable for getting the UserDisplayName:

Use the following expression for getting the user display name:

triggerOutputs()['headers']['x-ms-user-name']

Next, add another initialize variable for getting the light number. After renaming the action, click on “Ask in PowerApps”:

Add another initialize variable, this time a boolean value, and for getting the value of the toggle sync control:

Next, we will retrieve our user config source from the SharePoint list again, this time filtered by the UserDisplayName variable:

We now need some logic to the next steps for the flow, lets start by the toogle button which will be either true or false:

If yes, that should mean that we either should create a new graph subscription, or update any exisiting ones.

Under Yes, add a new action. This action will call the Custom Connector I created in another blog post (https://gotoguy.blog/2020/10/24/managing-microsoft-graph-change-notifications-subscriptions-with-power-platform/). This Custom Connector should have the following actions:

Click on the Get Subscription action to add that. For the subscriptionId parameter I will refer to the first returned instance of any exisiting Graph Subscriptions I have in the SharePoint list returned earlier.

Here is the expression for your reference:

first(body('Get_My_Hue_User')?['Value'])?['ChangeNotificationSubscriptionId']

When running the Flow this action will either:

  • Return a 200 OK if the subscription is found, or if the SharePoint List has a blank value for ChangeNotificationSubscriptionId.
  • Return a 404 ResourceNotFound if the subscription is not found, this will happen if the subscription is expired. This is an error that will halt the Flow, so we need to handle it.

So lets start by getting the Status Code. I’ll do that by adding a compose action with the following expression:

outputs('Get_Graph_Subscription')['statusCode']

It’s also important to set that this action should run even if the Get Graph Subscription action fails:

Lets add another condition, where the outputs of the status code should be 200 (OK):

A status code of 200 will either indicate that either we found an existing Graph Subscription matching the configured subscription id from the SharePoint List, or that the list is blank and the Get Graph Subscription will return an empty array or any other Graph Subscriptions you might have. In the first case, we will just update the existing subscription, in the latter case we will create a new subscription. Lets start by adding another compose action:

Adding the Flow to the Hue Power App

Back in the Hue Power App, we can now link this Flow to the toggle control. With the sync toggle control selected, go to the Action menu, and then click on Power Automate. From there you should see the “Hue – Create Update Remove Graph Subscription” Flow, click to add it. On the OnChange event, add the Run parameters where the lightnumber value is read from the dropdown list and selected items lightnumber, and the toggle button value like this:

Blog Series – Power’ing up your Home Office Lights: Part 9 – Using Microsoft Graph to get Teams Presence and show state in PowerApp.

This blog post is part of the Blog Series: Power’ing up your Home Office Lights with Power Platform. See introduction post for links to the other articles in the series:
https://gotoguy.blog/2020/12/02/blog-series—powering-up-your-home-office-lights-using-power-platform—introduction/

In this part 9 we will use Microsoft Graph to get the logged in user Teams Presence, and show that state in the PowerApp.

I have previously written another post on Teams Presence, Microsoft Graph and requirements here: Subscribing to Teams Presence with Graph API using Power Platform | GoToGuy Blog. If you want to dig deeper into that I would recommend that you read that post, but for now in this article I will show how you can get your Teams Presence into the Hue Power App.

Teams Presence is currently available in the beta endpoint of Microsoft Graph here: https://graph.microsoft.com/beta/me/presence

If you quickly want to check your own Teams Presence via the Microsoft Graph you can try the following. Just click this link that will launch in Graph Explorer: https://developer.microsoft.com/en-us/graph/graph-explorer?request=me%2Fpresence&method=GET&version=beta&GraphUrl=https://graph.microsoft.com

Just remember to consent to the Presence.Read permission as shown below:

As always when calling Microsoft Graph, we need to authenticate to Azure AD and authorize to Graph API to get an access token for quierying resources. And if we want to do that from Power Platform we need to create an app registration for that in Azure AD.

App Registration in Azure AD

This step might be dependent on if your tenant administrator has restricted the users’ right to create app registrations. If so, you will need to log into your tenant as a Global Administrator or Application Administrator, or get help from your IT admin to create the following App Registration in Azure AD.

If not, the following operations don’t require admin consent or permissions, so you can go ahead and create the App Registration. At the Azure AD Portal, go to https://aad.portal.azure.com, App Registrations and add a new like below:

Just leave the Redirect URI blank for now and click register.

Next, click on API Permissions, and click add a permission and select Microsoft Graph at the top, click on Delegated permissions, and add the Presence.Read permission as shown below:

You should now have the following permissions:

Next, go to Certificates & secrets, add a new client secret with a description, and select your chosen expiry:

Click Add and copy the secret value which will showed only this once. Save this secret for now, we will need it later. Also, go back to overview and copy the Application (Client) Id for later. We will need that as well.

There is just one thing left in this app registration, but for now we need to switch over to Power Platform for creating the Custom Connector.

Custom Connector in Power Platform for Microsoft Graph

We will now create a custom connector in Power Platform to reference this App Registration and get the Presence. Log either into make.powerapps.com, or flow.microsoft.com, for this next step.

Under the Data menu, select Custom Connectors. Select to add new connector from blank, and give it a name:

Select Continue, and on the General page, type graph.microsoft.com as host. You can also upload an icon and a description:

On the Security page, select OAuth 2.0 as type, and Azure Active Directory for Identity Provider. Client Id and Secret is the App Id and Secret from the App Registration earlier. Resource Url is https://graph.microsoft.com, and specify the scope to be Presence.Read:

After that, click on “Create Connector”, and the the “Redirect URL” will be populated:

Copy this URL and add it as a Web platform Redirect URI back in the Azure AD App Registration:

Back in the Custom Connector, go to Step 3. Definition, and click New Action. Type in a Summary “Get Presence” and Operation ID “GetPresence”, and under Request click Import from sample. Specify Get as verb, and URL to https://graph.microsoft.com/beta/me/presence, like below, and click Import:

Go to the Response section, and click on the Default response. Click on Import from sample and specify Content-Type application/json for Header response, and for Body, paste in the response you got when you tried the presence query in Graph Explorer in the beginning of this blog post:

The action should now look like this:

We can now proceed to Test. Click on Update Connector and under 4. Test click on “New connection”, and then Create:

Sign in and then accept the application to read your presence information and profile as shown below:

I can now test the GetPresence action with the signed in connection, and verify a successful response. In my case my availability just now is “Away”:

With the Custom Connector now ready, I can proceed to add this status to my PowerApp.

Customizing the Hue Power App to get Presence

Back in my Power App i created in earlier parts of this blog series, I want this icon to reflect my Teams Presence status. I will start simple by adding an OnSelect event to this icon, that will get my Presence status using the Custom Connector.

Under View menu, and Data, select to add the custom connector as a new connection to the PowerApp:

On the OnSelect event for the presence icon, I will use Set function and a variable called MyPresence, where I run the Custom connector and GetPresence operation like below:

Set(MyPresence,MSGraphPresenceConnector.GetPresence())

This is how it looks:

Holding down ALT button, I can now click on the Icon to run the OnSelect event, and after that I can go to the View menu again, then under variables I will find the MyPresence variable. When looking into that record, I can verify that I indeed have received my presence status:

The next part would be to update the color of the Icon to reflect the status. I also, for now at least want an extra label that specifies the status as a text value. Lets start by that. I add a label next to the Icon and then set the Text property to “MyPresence.availability”, as shown under:

You should now be able to change the Teams Presence and then click on the Icon in the Hue Power App to update presence status text:

From the Graph Documentation, presence resource type – Microsoft Graph beta | Microsoft Docs, the following values are possible for presence availability, and I have added the suggested colors for these statuses:

  • Away (Yellow)
  • Available (Green)
  • AvailableIdle (Green)
  • Busy (Red)
  • BusyIdle (Red)
  • BeRightBack (Yellow)
  • DoNotDisturb (Red)
  • Offline (Light Grey)
  • PresenceUnknown (White)

So what remaining is that I want to update the color of the Teams Presence Icon also to reflect the status. And for this I chose to use the Switch function, where I evaluate the MyPresence.availability variable, and have different results:

Switch( MyPresence.availability, "Away", "Result1", "Available", "Result2", "AvailableIdle", "Result3", "Busy", "Result4", "BusyIdle", "Result5", "BeRightBack", "Result6", "DoNotDisturb", "Result7", "Offline", "Result8", "PresenceUnknown", "Result9", "DefaultResult" )

I will use that Switch formula to set the Fill property of the Icon, which now is manually set to Red like this:

So after picking the colors, I end up with this formula:

Switch( MyPresence.availability, "Away", RGBA(253, 185, 19, 1), "Available", RGBA(146, 195, 83, 1), "AvailableIdle", RGBA(146, 195, 83, 1), "Busy", RGBA(196, 49, 75, 1), "BusyIdle", RGBA(196, 49, 75, 1), "BeRightBack", RGBA(253, 185, 19, 1), "DoNotDisturb", RGBA(196, 49, 75, 1), "Offline", RGBA(128, 130, 133, 1), "PresenceUnknown", RGBA(255, 255, 255, 1), RGBA(0, 0, 0, 0) )

Adding this to the Fill property of the Icon:

After this you should be able to change your Teams Presence status, and then click on the Icon to update the status in the PowerApp:

One last ting remains before I conclude this blog post, and that is that I want to update the presence status everytime I navigate to this screen in my PowerApp. I’ll just add the following line to the OnSelect event for the Control Lights button on the main screen:

Summary & Next Steps

In this blog post I have shown how you can get the Teams Presence status into the Hue Power App, and for now the status is manually updated either by clicking on the status Icon, or when navigating to the lights screen.

In the next, and last part, of this blog series, I will show how you can subscribe to Microsoft Graph changes, so that you can automatically get status updates.

Thanks for reading so far, see you in the last part 10 of this blog series!

Subscribing to Teams Presence with Graph API using Power Platform

Since the support for getting Teams Presence via Microsoft Graph API came into public preview earlier this year, https://docs.microsoft.com/en-us/graph/api/presence-get?view=graph-rest-beta&tabs=http, many where seeing interesting scenarios for automating Microsoft 365 using this presence status. In fact many, myself included, created different solutions for setting light bulbs etc to reflect the Teams presence. If busy, then red lights, if available green lights, and so on. I have this setup myself using Philips Hue lights, and have created a PowerApp using Flows to control my lights:

There has been one problem though, getting Teams Presence has only been possible by continuously querying the API to check if the status has changed. This can be scheduled of course, but running this every minute or so seems like an excessive way to achieve the goal: being able to see if the Teams Presence has changed!

Until just a few days ago that is, where I noticed that Teams Presence now is in Preview for subscribing to change notifications in Microsoft Graph: https://docs.microsoft.com/en-us/graph/webhooks#supported-resources

In this blog post I wlll show how to create a subscription for change notifications for Teams Presence for a specified user, and the requirements for doing this. And I will use Power Platform and Power Automate Flows to achieve this.

Let’s start with the Requirements..

First of all, to be able to set up a change notification for a resource, you need the same API permission as the resource require itself, for Teams Presence this is one of the following:

  • Presence.Read, for being able to read your own users presence status.
  • Presence.Read.All, for being able to read all users in your organization’s presence status.

Currently in the Beta endpoint, getting presence is only supported for Delegated permissions using work account, so you will have to to this with a logged in user.

The other important requirement is that you need a webhook uri for where the change notification can send a POST request for the resource that has been changed. This webhook uri can be on your preferred platform, many are using Azure Functions for this, but it can also be a third party platform. I will do it using Power Automate, another alternative could be using Azure Logic Apps also.

This webhook uri need to be able to:

  • Process the initial creation of the change notification subscription. Microsoft Graph expects a text response with a validation token for it to be successful.
  • Process every change to the resource that you subscribe to, in this example a Teams Users Presence Status.

We will get into the details on this, but for now, we will start explore using Graph Explorer.

Explore Presence and Subscriptions with Graph Explorer

Go to the Microsoft Graph Docs site and find Graph Explorer there, or just go to https://aka.ms/ge, and log in with your work account.

Type the following GET query: https://graph.microsoft.com/beta/me/presence

You need to be a Teams user to run this, but if permissions are consented, you should be able to see your own presence status like my example below. If you are getting a permission error, make sure that one of the permissions for Presence.Read or Presence.Read.All er consented. This can already been done by your administrator, if not you need to consent to them yourself.

Great, let’s check another users’ presence status. PS! The permissions don’t require admin consent, and using delegated permissions every user can check other users presence status, but you need Presence.Read.All to do that.

You need the other users id property, so you can check that first using for example <a href=”https://graph.microsoft.com/v1.0/users/https://graph.microsoft.com/v1.0/users/<someotherusersUpn>/?$select=id.

And then query for the other user’s presence: <a href=”https://graph.microsoft.com/beta/users/https://graph.microsoft.com/beta/users/<userid>/presence

Another way to query for your or other users presences, that are more aligned to the change notifications is using the /beta/communications/presences endpoint: <a href=”https://graph.microsoft.com/beta/communications/presences/https://graph.microsoft.com/beta/communications/presences/<userid&gt;:

If you can run all these queries successfully, then you are ready to proceed to change notifications subscriptions.

You can check if you have any active subscriptions for change notifications using a GET query to: https://graph.microsoft.com/v1.0/subscriptions or https://graph.microsoft.com/beta/subscriptions, depending on if you are working with preview resources. If you have no active subscriptions this should return a successful but empty response like below:

The next part would be to create a subscription for change notifications for presence, but first we need to prepare the Flow for receiving the webhook uri.

Create a Power Automate Flow for Change Notification Webhook

Log in with your work account to flow.microsoft.com, and create a new flow of type automated and blank, skip the first part of using pre-selected triggers. Search for the Request type trigger that is called “When a HTTP request is received”:

Next add a Initialize Variable action for setting the ClientState which we will use later for the creating the subscription. Set the variable to anything you want, give a name to the Flow and then save. When saved the HTTP POST URL will show the value you later will use as the webhook uri for the change notifications:

The next part is the most important one, and that require a little knowledge of the creation process for change notifications subscriptions. This is documented here https://docs.microsoft.com/en-us/graph/webhooks#managing-subscriptions, but the high level steps are:

  1. When the Subscription is created, a POST request is sent with Content-Type text/plain and a query parameter that contains a validationToken. Microsoft Graph expects a 200 OK response and a text/plain body that contains the validationToken to be able to successfully create the subscription.
  2. Immediately after creation, and for every change notification, a POST request is sent with Content-Type application/json. Microsoft Graph expects a 202 Accepted response to this, if not it will continuously try to send the same change notification. You should also check the ClientState for verification, and of course include your own business logic for what to do in the Flow for when changes occur.

So your Flow needs to address these requirements, and there can be different ways to achieve this, but this is how I did it in my setup:

After the initialize variable, I created a Switch control action, using Content-Type from the HTTP Request trigger. In this way I can separate the logic in my flow for the initial creation and validation, and for the updates.

Validating the Subscription

If the Content-Type is text/plain; charset=utf-8, I will try to get the validation token sent by Microsoft Graph, and create a response that returns status 200 OK and the validation token back in a text/plain body.

I used the Compose data operation, and a custom expression that retrieves the validationToken from the query parameters. The expression I use is: @triggerOutputs()[‘queries’][‘validationToken’].

The Request Response action returns a status code of 200, set the Content-Type to text/plain and for Body select the Output from the Compose Validation Token.

The above steps should be sufficient for creating the subscription, it is recommended to validate the ClientState also, in my case I have done that in the next step.

Processing and Responding to Change Notifications

The change notifications will come as Content-Type “application/json; charset=utf-8”, so I add a case for that under my Switch. Microsoft Graph expects a 202 Accepted Response, it is recommended to respond this early in your flow, so that any later actions or your own business logic generates errors that prevents this response.

In the Response action I just return 202 Accepted. I have then added a Parse Json action to make it easier to reference the values later in the flow:

You can generate the schema from an existing sample, from the Microsoft Graph docs, referred above, or you can just use the following sample from me here:

{  “value”: [    
{      “subscriptionId”: “<subscriptionid>”,      
“clientState”: “MyGraphExplorerSecretClientState”,      
“changeType”: “updated”,      
“resource”: “communications/presences(‘<userid>’)”,      
“subscriptionExpirationDateTime”: “2020-07-11T20:04:40.9743268+00:00”,      
“resourceData”: {        
“@odata.type”: “#Microsoft.Graph.presence”,        
“@odata.id”: “communications/presences(‘<userid>’)”,        
“id”: null,        
“activity”: “Away”,        
“availability”: “Away”      },      
“tenantId”: “<tenantid>”    }  
]}

Now I can begin my business logic. As the value from the notification body is of type array, an Apply to each block will be needed, it will be automatically added if you select any of the attributes in the resulting actions. I will first verify that the clientState is matching the variable i defined in the beginning of the flow. This is a safestep that only the Graph subscription I created can call this Flow.

In the next part I have added a new Switch control, where I retrieve the presence status that have changed.

And this is where you add your own business logic, so I will stop here. In my own example (see PowerApp for Hue Lights above) I will control my lights based on presence updates, but this is a theme for a upcoming blog post.

With the Flow ready, we can now create the Subscription.

Creating Microsoft Graph Change Notification for Teams Presence

I will now create the Subscription in Graph Explorer, using a POST request to https://graph.microsoft.com/beta/subscriptions. In this POST request you will need a request body that contains the following:

Here is a quick explanation of the above values:

  • changeType: can be “updated”, “created”, “deleted”. Only “updated” is relevant for presence changes.
  • clientState: For verification in the webhook that the call is coming from the expected source, consider this a sensitive value.
  • notificationUrl: This is the webhook uri, and for my Flow this will be HTTP POST URL shown earlier.
  • resource: This is the user I want to get presence updates from. Note that you can get multiple user presences also using /communications/presences?$filter=id in ({id},{id}…) (https://docs.microsoft.com/en-us/graph/api/resources/webhooks?view=graph-rest-beta)
  • expirationDateTime: Every subscription only lasts for a specific time depending on resource type. The subscription needs to be renewed before expiry. For Teams Presence subscriptions only last one hour, same as chatMessage. See details for different types of resources: https://docs.microsoft.com/en-us/graph/api/resources/subscription?view=graph-rest-beta#properties. If you select an expiry time that is larger than supported, it will reset to one hour max.

Let’s run this query to create the subscription, if everything works, I will now get a 201 response that the subscription is created. Note the expiration time that is one hour from now:

This wouldn’t work if my Flow had failed, so let’s look into the run history on that. I see that I have 2 recent successful runs:

Let’s look into the first one. I see that the request is coming in as a text/plain with a validationToken, which I then return to Microsoft Graph:

Let’s look into the second run. This returns the first presence update, I can see that the user is “away”:

Now, let’s test this. With my user I go to Teams client, it should automatically change my presence from away to available or busy depending on my calendar, or I can set the status manually myself:

Checking the Flow runs again, I can indeed see that the Flow has been triggered via the Change Notification:

If I look into the details, the status update is Available:

If I bring up the Flow in a bigger picture I can indeed see that my logic ends up where the switch case checks for available status:

My Flow will now be triggered for every change as long as the subscription don’t expire.

Summary

In this blog post I have shown how you can create a Microsoft Graph Subscription for Change Notifications to selected users Teams Presence, using Power Automate to validate subscriptions and process the changes.

There are more work to do, as I need some logic for renewing and managing my subscriptions. But that will be the topic for the next blog post coming soon!

Thanks for reading, hope it has been useful 🙂

How I as a Soccer Coach….

…..moved trainings and meetings online using the modern collaboration tools I know and love!

When I’m not working or fulfilling my community activities as MVP, I spend many evenings and weekends at the soccer field, where I’m coaching and managing a soccer team consisting of 14 year old boys.

Due to the Coronavirus situation, as in many countries also Norway has closed it schools, many businesses are either closed or working from home, and in general we are all following the rules of isolating to make sure the virus doesn’t spread. And of course, this has also resulted in closing all grass roots football, for the time being at least to the end of April. I quickly understood that I needed to think in new ways..

So I decided to use the tools I have at hand, and I created virtual follow ups using tools like Microsoft Teams, Forms, Power Automate and SharePoint Online among a few to help support the boys doing self practice and have Virtual team Meetings..

This blog post is a technical version of a LinkedIn article I wrote in Norwegian, https://www.linkedin.com/pulse/hvordan-jeg-som-fotballtrener-jan-vidar-elven/, explaining more the reasons and why I set this up. In this post I will go more into the technical setup, and share some resources for those that want to learn or maybe do something similar themselves. Some of the screenshot images are in Norwegian, but you should be able to understand from my comments.

It all started with Microsoft Teams.. and a Form!

I knew already from before that Microsoft Teams would be more central in my daily work, but I also observed that my son and his classmates, that also plays on the aforementioned soccer team, use Teams themselves now for digital schooling at home. They have daily Teams meetings, as well as some online classes and homework delivery.

So I thought that I wanted to set up a player meeting on Teams, and later a meeting with their parents. This way we could have a social and digital arena to meet each other, as well as talk with the boys how we wanted them to do training with self practice at home. So the first thing was to invite to online meetings.

I knew that the boys already used Teams in school, but I needed to collect their school e-mail addresses. I also suspected several of the parents use Teams at their workplace, but not everyone, so I also needed to get an overview on that. So I decided to create a questionnaire in Microsoft Forms:

How I set it up:

I created a Form with the following inputs:

  • Name (Text). The person filling in the form.
  • Using Teams from before? (Choice). Yes, No and Don’t Know option for parents to answer if they use Teams already.
  • Parents, Teams e-mail address (Text). Their existing Teams work e-mail address or personal e-mail address.
  • Players, Teams e-mail address (Text). The Teams e-mail address they use at school.

In addition I used the club logo and customized the theme colors for the Form. Then I selected Share settings and selected so that “Anyone with the link can respond”. This will mean that all responses are anonymous, so that’s why I require that they type their name in the first input. Then I distributed the Forms link to the parents.

After I received all the responses, I created a Teams meeting invite to the players for the players online meeting, and a Teams meeting invite to the parents for the parents online meeting.

We were now ready for our inaugural online meetings!

Organizing Self Practice Trainings

Normally our soccer team practice at least 3 times a week, in addition to playing games or other activities. So before we had the online player meeting, a self practice training plan was created, focusing on 3 weekly trainings:

  • Conditioning (interval runs, with or without ball, dribbles, jumps and obstacles etc.)
  • Technique, Agility and Strength (ball possession, passing, runs, sprints, physical strength)
  • Endurance (long hikes and outdoor activity with family)

These training should be completed after plan where the boys will get approved attendance for each training they complete. To get a training approved they needed to self register a training form, as well as document by sharing pictures, video, screenshots of activity etc.

The players were shown examples of activities and drills they could complete themselves or with help of family. With this organization and training plan, the only thing that was missing was a system for registering self practice and how I could follow up on that.

I decided to create another Form. In this Form which the boys got a shared link to, they could register their name, date, self assessment, what kind of activity they did and type, provide a description and optionally register number of minutes and kilometres. They were also asked to send in documentation with video, pictures, etc.

This worked really great and next week we already had a lot of responses for completed trainings:

And from the media I received I could really see that the boys were doing their self practice:

How I set it up:

I created a Form with the following inputs:

  • Name (Text). The player name filling in the form.
  • Activity Type (Choice). Conditioning, Technique + Strength, or Endurance.
  • Date of Activity (Date).
  • Self Assessment (Rating). 1-5 stars where they could evaluate their own session.
  • Type of Condition (Choice). If they did conditioning work, what kind of interval (4×4, 60×60, 15×15).
  • Technique + Strength (Text, Long). Comment field for explaining how they did technique and strength work.
  • Endurance (Text, Long). Comment field for explaining what kind of long activity they did.
  • Number of Kilometres (Text, Restricted to Number). Optionally how many kilometres they practiced.
  • Number of Minutes (Text, Restricted to Number). Optionally how many minutes the activity lasted.
  • Sent media of activity (Choice). Yes or No for if they have sent picture, video or screenshot to our team e-mail address.

Also in this Form I used the club logo and customized the theme colors. Then I selected Share settings and selected so that “Anyone with the link can respond”.

PS! I was looking into a File Upload response in the Form, but this cannot be added to a Form that are shared externally. That is why I needed the boys, or their parents, to send their media files to our e-mail address in addition to the Form registration.

Following up on Registered Self Practice Trainings

With all those great responses coming in, I could look through the responses in Office Forms, and download an Excel copy of the responses, but I needed something more to follow up the trainings. So I created a private Team in Microsoft Teams:

Next I wanted to get all the responses from Forms into a SharePoint List. I created the List into the Teams SharePoint Site so that I could get all the registered self practice trainings in one place, and be able to do edits if needed. I also added some extra columns for approve the training and if there was sent media documentation:

Now the only thing I needed was some kind of automation that could bring every response from Forms over to this list: Enter Power Automate!

With the help of Power Automate I created the following Flow to automate that every time a new response is submitted in the Form, this would trigger the Flow:

Next I needed to do some magic on the number inputs, I’ll get back to that in the “How I did it:” section, but the Flow then created a new SharePoint List Item:

How I did it:

The first thing I did was to create the Team that would also host the SharePoint Online Site for my list. The Team was created in my own tenant as a private team, and I elected not to invite any player, or parent, to the Team as guests at this point.

The SharePoint list was created with the following columns to reflect the Form:

  • Renamed Title column to Name
  • Type of Activity (Choice, with the same values as from the Form)
  • Date (Date and Time, Include Time: No)
  • Self Assessment (Number)
  • Type of Fitness/Conditioning Activity (Choice, with same values as from the Form)
  • Technique + Strenght (Multiple Lines of Text)
  • Endurance/Long Actitivty (Multiple Lines of Text)
  • Sent Picture (Yes/No)
  • Number of Km (Number)
  • Number of Mins (Number)
  • Approved (Yes/No)
  • Submitted (Date and Time, Include Time: Yes)

After the list with all the columns was created, the next step was to create the Flow in Power Automate. You can create Flows directly from the SharePoint List, as shown below, and then use one of the provided templates:

Myself I started at https://flow.microsoft.com and selected to create a new Flow as Automated – from blank, and the selecting the trigger for “When a new response is submitted”:

After I give a name to the Flow and select which Form I want to trigger on responses from, I add an Action to get the response details, as shown below:

The next part is a little more complex. When a Form response is submitted, all response details will be provided as Strings. And if I try to update those values directly into the SharePoint list, it will fail because the Column require a number format. So I need to convert those string values to either Integer or Float respectively.

Power Automate have some Data Operation Actions I can use in Flows, and I have used the following three Compose actions, where I get the Self Assessment, Number of Km and Number of Min response details:

The Compose action will create objects I can refer back to later in the Flow. The next complex thing is that I need to check if there are any values for number of kilometers and minutes, and these Form fields are not required and can be empty. There could be several ways to do this, I did it this way:

As shown above, I first add an Action for Initialize Variable, and give it the name for AntallKm (Number of Km), and set the initial value to 0. I specify that the type is Float (as I want to have decimal values). Next I add a Condition action, where I check if the ComposeAntallKm is empty, meaning it is equal to false. I do this as an expression, where the expression is using the empty function and checking against the output from the ComposeAntallKM earlier: empty(outputs(‘ComposeAntallKm’))

If this condition evaluates to false, meaning that there has been provided a value in the Form, then the Flow will go to the If Yes action, and I convert the string to a Float value with the expression: float(outputs(‘ComposeAntallKm’)), I’ve tried to illustrate that with the green arrow below. This expression would have failed the Flow if I didn’t check if it was empty or not. If the value is empty, it would go to the If No action, and this is just empty because I then just let the value be the default 0 i provided when I initialized the variable (illustrated with the red arrow below).

Next in the Flow, I do the exact same logic for the number of minutes:

I’m now ready to update the SharePoint List with a new item. I select the SharePoint Online Create item action, and after specifying the Site and List name, I’ll choose most of the item values from the dynamic content picker. The Km and Minutes values are picked from the respective variable. And for Self Assessment, I do this with an expression consisting of the int function that converts the string to an integer from the Compose action further up.

For reference, my entire Flow at this point is shown below (collapsed without action details):

Take it to the Next Level – Teams Bot with Adaptive Card!

At this point I have a working solution where I get all new responses put in to the SharePoint List. I can now look through, edit and approve the registered trainings.

An even cooler solution would be that each registered training will be posted to the Team Channel as an adaptive card, where I can edit and approve and submit that back so that the list would be updated directly. That way I can follow up and check trainings in my Teams client on either my PC or Mobile, without needing to go to the SharePoint list.

So I added a couple of more steps to my Flow after the Create item action. First I added an action for posting an Adaptive Card to a Teams Channel and wait for a response, the next step was to Update the list item with the values.

I will go into more details later under the “How I did it” section, but the end result of this was that every time a player submits a new training activity, I will get this Adaptive Card in my Teams Channel, summarizing the details of the training and providing me with the ability to edit the player name in case they typed it wrong, update any values for km or minutes, and select yes or no for if the player has sent photos or the training is approved:

When I click the Update button, the values are submitted back to the Flow and the List item is updated.

How I did it:

Before I go into the Flow details on how to post adaptive card to Teams channel and process the response back, I want to show how I built the adaptive card.

Adaptive cards are posted as a JSON formatted message. You can read more about it here https://adaptivecards.io/, see samples and usage scenarios and more. There’s also a great resource called the Adaptive Cards Designer, https://adaptivecards.io/designer/, where you can build your own cards from blank or using one of the sample templates.

The designer lets you add controls and configure properties visually, while producing the JSON message you will need later. This is the design format of the card I ended up with, a little bit of Norwegian text here but you get the main idea, note that I have some placeholder values her, from where I will add data from my Flow later:

In the “Card Payload Editor” window you will see the JSON format you will need use in your Flow, and in the following snippet I’ll provide you with my JSON message for this example here for you to reuse or build on as you like:

{
    "type": "AdaptiveCard",
    "version": "1.0",
    "body": [
        {
            "type": "Image",
            "altText": "Borgen IL",
            "url": "https://borgensawebstorage.z6.web.core.windows.net/borgen_logo.png"
        },
        {
            "type": "TextBlock",
            "size": "Large",
            "weight": "Bolder",
            "id": "Title",
            "text": "Ny Egentrening Registrert!",
            "horizontalAlignment": "Left"
        },
        {
            "type": "TextBlock",
            "text": "Ny egentrening er registrert av <NAVN>.",
            "wrap": true
        },
        {
            "type": "FactSet",
            "facts": [
                {
                    "title": "Type Treningsøkt",
                    "value": ""
                },
                {
                    "title": "Dato",
                    "value": ""
                },
                {
                    "title": "Egenvurdering",
                    "value": ""
                },
                {
                    "title": "Økt beskrivelse",
                    "value": ""
                }
            ]
        },
        {
            "type": "TextBlock",
            "text": "Verifisering",
            "size": "Large",
            "weight": "Bolder",
            "color": "Attention"
        },
        {
            "type": "TextBlock",
            "text": "Verifiser treningsdata og eventuelt oppdater:",
            "size": "Small",
            "weight": "Bolder"
        },
        {
            "type": "TextBlock",
            "text": "Spillernavn",
            "size": "Medium",
            "weight": "Bolder",
            "color": "Attention"
        },
        {
            "type": "Input.Text",
            "value": "",
            "style": "text",
            "isMultiline": false,
            "maxLength": 50,
            "id": "Spillernavn_input"
        },
        {
            "type": "TextBlock",
            "text": "Antall Km",
            "size": "Medium",
            "weight": "Bolder",
            "color": "Attention"
        },
        {
            "type": "Input.Number",
            "value": "",
            "style": "text",
            "isMultiline": false,
            "maxLength": 20,
            "id": "AntallKm_input"
        },
        {
            "type": "TextBlock",
            "text": "Antall Minutter",
            "size": "Medium",
            "weight": "Bolder",
            "color": "Attention"
        },
        {
            "type": "Input.Number",
            "value": "",
            "style": "text",
            "isMultiline": false,
            "maxLength": 20,
            "id": "AntallMin_input"
        },
        {
            "type": "TextBlock",
            "size": "Large",
            "weight": "Bolder",
            "color": "Attention",
            "text": "Godkjenn",
            "horizontalAlignment": "Left"
        },
        {
            "type": "TextBlock",
            "size": "Small",
            "weight": "Bolder",
            "text": "Har spilleren sendt skjermbilde, bilde eller video?",
            "horizontalAlignment": "Left",
            "separator": true
        },
        {
            "type": "Input.ChoiceSet",
            "id": "input_media",
            "value": "1",
            "choices": [
                {
                    "title": "Nei",
                    "value": "false"
                },
                {
                    "title": "Ja",
                    "value": "true"
                }
            ],
            "style": "expanded"
        },
        {
            "type": "TextBlock",
            "size": "Small",
            "weight": "Bolder",
            "text": "Er egentreningen godkjent?",
            "horizontalAlignment": "Left",
            "separator": true
        },
        {
            "type": "Input.ChoiceSet",
            "id": "input_godkjent",
            "value": "1",
            "choices": [
                {
                    "title": "Nei",
                    "value": "false"
                },
                {
                    "title": "Ja",
                    "value": "true"
                }
            ],
            "style": "expanded"
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "Oppdater"
        }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}

With that JSON message ready, I can now add the “Post an Adaptive Card to a Teams Channel and wait for a response” action to my Flow. I select my Team, and the Channel I want to post the adaptive card to, and then paste in the JSON message from the Adaptive Card Designer:


As you can see from the images above, I’ve added dynamic data from the Flow where I had placeholders for values. Note also that I use the values “false” and “true” for the Input.ChoiceSet, this will make it correct when I set the values back to the updated List item.

PS! Another important thing to note is the “id” property of the inputs I want to be able to update back to the Flow. This “id” property needs to be specified later.

The Update message is what will be shown back in Teams after the Adaptive Card has been updated and submitted back with the Action.Submit button. It will look like this:

After the card is updated, a response will be sent back to the Flow. So my next step would be to add a Update item action for updating the selected values in the SharePoint List. From here I will select the SharePoint Site and List Name, and then getting the Id for the existing list item I want to update. This Id is from the earlier action in the Flow from where I created the list item:

The values I want to update back in the List from the Adaptive Card Response is shown above with the blue Teams icon. I will have to specify these by adding an expression that looks like the following, as there are currently no dynamic output from the previous action I can select:

outputs('Post_Adaptive_Card_to_Egentrening_Teams_Channel_Wait_Response')?['body/data/Spillernavn_input']

Note the following from above, refers back to the action name (since I had blank spaces in the step name, I will have to refer back to it with underscore), and from the response body and data section I will refer to the “id” property of the adaptive card input.

Create similar expressions for the other inputs, and that should be it! The complete Flow step is now like this:

Summary and Next Steps

In this quite lengthy blog post I have shown how I built myself a great follow up solution for my soccer team self practice trainings. The boys find it easy to use, and I can use the tools and solutions I know and love for following up. I have also learnt quite a bit of new tricks and tips 😉

I also start to have some great statistics, I can summarize and rank the players so that I can create a top 10 list for example. And these ranks can be published to the boys, they do love a competition and this can motivate them to do some extra work. I have created this HTML table, that I update semi-manually now. I’m already working in the next Flow that will publish updates to this table automatically. So there might be a follow up blog post on this.. 😉

I hope this has been helpful or/and inspiring, reach out to me if you have questions, and remember the Power of the Flow 🙂