Azure, Cognitive services, Connected Field Service

Invalid subscription key error when connecting Azure cognitive services Face API

Recently during an integration scenario with D365 Field services in order to identify if the person making a change to the Customer asset is an employee or not we spinned up an Azure Face API service and on trying to connect it from an Azure function we were getting Invalid key exception.

We were using the Face API client library which defaults to west US endpoint of the service but our service was in EastUS.

Default endpoint: https://westus.api.cognitive.microsoft.com/face/v1.0/

The solution is to override the endpoint to https://eastus.api.cognitive.microsoft.com/face/v1.0/ which can be done by using another constructor of the FaceServiceClient class

FaceServiceAPIClient = new FaceServiceClient("Face API subscription key", "https://eastus.api.cognitive.microsoft.com/face/v1.0");

 

 

Advertisements
Standard
Dynamics 365 Field Services, Microsoft Dynamics 365 Field Service

How to sync Resource Bookings in Field service with MS Outlook

A very basic Field service use case is that bookings should show as appointments in MS outlook so that users can keep track of bookings and other office tasks in one single view.

Fortunately this is configurable in D365 FS, however there are few prerequisites:

  • Dynamics 365 Field Service licenses
  • Office 365 Enterprise E3 licenses

Here are the steps to enable synchronization of bookings with User’s calendar:

1.1.1.1       To be done by Dynamics 365 Administrator

  • From the main menu, click Settings -> Administration.
  • Click System Settings -> Synchronization.
  • Under Select whether to enable syncing of resource booking with, check the Synchronize resource booking with Outlook.

 

1.png

 

1.1.1.2       To be done by each User(Bookable Resource)

  • Click the Settings button in the upper-right corner of the screen.
  • Click Options and then Synchronization tab.

 

2.png

  • Under Resource booking sync with Outlook, check the Synchronization resource booking with Outlook.

 

Standard
CRM, Dynamics 365 Field Services, Microsoft CRM Dynamics, Microsoft Dynamics 365, Microsoft Dynamics 365 Field Service

Creating Booking Rules in Microsoft Dynamics 365 Field Services

Booking Rules are Javascript validations that you can attach to the Booking creation event which is triggered when a booking is created on the schedule board or the schedule assistant.

To create a Booking Rule,  Field Service > Administration, and then choose Booking Rules.

Capture.JPG

What we need to do is attach a JS web resource with the Booking rule. The JS web resource should have a function which takes in the context and is called when a Resource requirement is dragged and dropped on to the Schedule board or the assistant.

Let’s see some code, in this example we are restricting creating of a Booking on the basis of how many bookings are already being created for a Resource.

function validateRules(sbContext) {
debugger;
var ruleResult = {};
var resourceReqId = '';
if (sbContext.newValues.ResourceRequirementId != undefined) {
resourceReqId = sbContext.newValues.ResourceRequirementId;
}
else if (sbContext.newValues.ResourceScheduleSource != undefined && sbContext.newValues.ResourceScheduleSource == 690970004) {//SA
var rr = Xrm.Page.getAttribute('msdyn_resourcerequirement');
if (rr != null && rr.getValue() != null && rr.getValue().length > 0) {
resourceReqId = rr.getValue()[0].id;
}
}
if (resourceReqId == '') {
console.log('RR is null. Exiting..');

ruleResult.IsValid = true;
ruleResult.Message = “No RR”;
ruleResult.Type = ‘success’;
return ruleResult;
}

resourceReqId = resourceReqId.replace(‘{‘, ”).replace(‘}’, ”);
var resourceId = sbContext.newValues.ResourceId;
var startTime = sbContext.newValues.StartTime;
var endTime = sbContext.newValues.EndTime;
var hearingType = ”;

var req = new XMLHttpRequest();
req.open(“GET”, encodeURI(Xrm.Page.context.getClientUrl() + ‘/api/data/v8.2/msdyn_resourcerequirements?$filter=msdyn_resourcerequirementid eq ‘ + resourceReqId + ‘&$expand=new_wa_case($select=wa_hearingtype)’), false);
req.setRequestHeader(“Accept”, “application/json”);
req.setRequestHeader(“Content-Type”, “application/json;charset=utf-8”);
req.setRequestHeader(“OData-MaxVersion”, “4.0”);
req.setRequestHeader(“OData-Version”, “4.0”);
req.setRequestHeader(“Prefer”, “odata.include-annotations=OData.Community.Display.V1.FormattedValue”);
req.send();
if (req.readyState === 4) {
req.onreadystatechange = null;
if (req.status === 200) {
var result = JSON.parse(req.response);
hearingType = result.value[0].new_wa_case[‘wa_hearingtype’];

if (hearingType == null) {
hearingType = 953860001; // Motion as default
console.log(‘motion set as default’);
}
console.log(hearingType);

//————————————————
var dt = new Date(startTime).toISOString();
var st = dt.substr(0, dt.indexOf(‘T’)) + ‘T00:00:00Z’;
var et = dt.substr(0, dt.indexOf(‘T’)) + ‘T23:59:59Z’; //2018-07-17T11:59:59Z
console.log(dt); // today’s date
var req = new XMLHttpRequest();
req.open(“GET”,
encodeURI(Xrm.Page.context.getClientUrl() +
‘/api/data/v8.2/bookableresourcebookings?$select=bookableresourcebookingid,starttime,endtime&$expand=Resource($select=wa_jurytrialhearinglimit,wa_motion)&$filter=_resource_value eq ‘ +
resourceId +
‘ and wa_hearingtype eq ‘ +
hearingType +
‘ and starttime gt (‘ + st + ‘) and endtime lt (‘ + et + ‘)’),
false);
req.setRequestHeader(“Accept”, “application/json”);
req.setRequestHeader(“Content-Type”, “application/json;charset=utf-8”);
req.setRequestHeader(“OData-MaxVersion”, “4.0”);
req.setRequestHeader(“OData-Version”, “4.0”);
req.setRequestHeader(“Prefer”, “odata.include-annotations=OData.Community.Display.V1.FormattedValue”);
req.send();
if (req.readyState === 4) {
req.onreadystatechange = null;
if (req.status === 200) {
var res = JSON.parse(req.response);
var data = res.value;

ruleResult.IsValid = true;
ruleResult.Message = “Validation succeeded”;
ruleResult.Type = ‘success’;

if (data.length > 0 && data[0].Resource != null) { // Court Room
var juryLimit = data[0].Resource.wa_jurytrialhearinglimit;
var motionLimit = data[0].Resource.wa_motion;
console.log(data, data.length, juryLimit, motionLimit);
var isDone = false;
for (var i = 0; i = new Date(data[i].starttime) && startTime = juryLimit) {
ruleResult.IsValid = false;
ruleResult.Message = ‘Jury Hearing capacity reached. Bookings not allowed.’;
ruleResult.Type = ‘error’;
} else if (hearingType == 953860001 && data.length >= motionLimit) {
ruleResult.IsValid = false;
ruleResult.Message = ‘Motion Hearing capacity reached. Bookings not allowed.’;
ruleResult.Type = ‘error’;
}
}
}
} else {
console.log(req.statusText);
}
}
}
else {
console.log(req.statusText);
}
}
return ruleResult;
}

Standard
CRM, Dynamics 365 Field Services, Microsoft Dynamics 365, Microsoft Dynamics 365 Field Service

How to hide Initial public view from schedule board in D365 Field services

As you would know in D365 field services schedule board, we can configure multiple boards and configure different filters or one scenario which is very common is having a board for each Organization unit/Territory.

We have a request from customer that they want to hide the Default Initial Public view from the board. There’s an easy way to configure this in D365.

Go to Advanced find and search for “Schedule board Settings entity”:

Find the record with Tab Name: “Initial public view” and change the Share Type to System instead of Everyone.

This is going to hide the tab from the schedule board.

Capture6.JPG

 

 

Standard
Azure, Azure IOTHub, Connected Field Service

Direct methods vs Cloud to Device messaging in Azure IOT Hub – When to use what?

Let’s see types of messages supported in Azure IOT Hub:

Cloud to Device messaging

Direct method

There are use cases for using each of these message types and one or the other isn’t  the solution for all problems.

Let’s see few of the differences between how these 2 methods work:

  • CDM uses One way messaging where as Direct method uses 2 way connection
  • Because of the above reason there is no easy feedback loop, there is a way to      leverage feedback endpoint in IOTHub but there is no way to relate feedback with message
  • CDM uses Hub to store the message and Hub is responsible of re-trying logic to send the message until expiration (configured at Hub level or can be configured for each device)
  • CDM Supports AMQP/ MQTT and HTTPS but Direct Methods don’t support HTTPS
Standard
Azure, Azure IOTHub, C#

Leveraging feedback endpoint in Azure IOT Hub

If you have played with Azure IOT Hub you might already know that there is an Azure Event hub endpoint behind the scene which takes care of message ingestion.

So your devices and backend applications use this endpoint for Cloud to device and Device to cloud communication.

In a scenario where the backend application wants to send a message to a Device and doesn’t care if the message was accepted/rejected or abandoned by the device then subscribing to the Event hub end point alone may do the trick.

But if you want feedback from IOT Hub that the message was indeed delivered and was it accepted or rejected you can subscribe to another already configured end point in Azure IOT hub which is the Feedback End point.

Let me show you the default endpoints configured in Azure IOT hub I just created:

6

If you see the above screenshot, there are 2 end points listed in IOT Hub:

  • Feedback
  • Events (Event Hub endpoint)

Now the next question is how to leverage this endpoint?

Fortunately Azure IOT Hub sdks have classes that take care of this, the class is called the FeedbackReceiver class and you can instantiate it using the ServiceClient object.

var feedbackReceiver = serviceClient.GetFeedbackReceiver();

Once you have the instance of this class, you can call the ReceiveAsyn() method to loop through the feedbacks to obtain the MessageId and the Status which could be one of the following:

  • Success
  • DeliveryCountExceeded
  • Expired
  • Rejected
  • Purged

Finally we can let the Hub know we are done processing feedbacks by calling the CompleteAsync() method.

 

As you see it’s not that hard to plug in the feedback loop for cloud to device messages 🙂

 

 

Standard
Azure, Azure IOTHub, Connected Field Service, Dynamics 365 Field Services, Logic Apps, Microsoft Dynamics 365

Get Device ConnectionString from IOTHub using LogicApp

As of today, there are 3 connectors to Azure IOT Hub:

5.JPG

I recently had a requirement to get the Device Connectionstring from IOTHub using DeviceId and IOT Hub connectionstring.

The solution comprised of following 2 items:

  1.  Custom connector to get Device Connectionstring
    1. This is a Web api deployed on Azure which has a Post method, takes in DeviceId and IOTHub connectionstring and returns Device Connectionstring using Azure IOTHub sdk.
    2.  HTTP + Swagger action was used in Logic app to consume this.
  2.  Logic app wrapper which was consumed by other apps.

If you want to use the Connector, consume the following swagger file:

https://getdeviceidconnector.azurewebsites.net/swagger/docs/v1

 

Here’s the logic app definition:

https://www.dropbox.com/s/cc86kk3co6wdmuv/GetIOTHubDeviceConnectionString.txt?dl=0

 

 

 

 

 

 

 

Standard