Dynamics CRM and the Internet of Things - part 4

This is the fourth post in a five-part series on how I integrated a Raspberry Pi with Microsoft Dynamics CRM to recognize contacts using automobile license plates. Although the code samples are focused on license plate recognition, the solution architecture I used is applicable to any Dynamics CRM + Internet of Things (IoT) integration. In my previous post, I showed how to trigger the license plate recognition process and then use the extracted license plate number to find a contact in my Dynamics CRM organization with a custom workflow activity. Today I'll show how to execute the license plate recognition and contact search with JavaScript directly in the web resource.

The approach

As I described in part 1 of this series, this approach uses JavaScript to call the Raspberry Pi to take a picture and return the parsed license plate number. Once a license plate number is retrieved, the JavaScript code then queries CRM for a contact with the returned license plate number and displays its details to the user. As long as the CRM end user's computer can access the Raspberry Pi, it doesn't matter if the Pi is visible to the CRM server, so this easily works with CRM Online.

The web resource

A web resource is used so the user can interactively trigger the license plate recognition and open the contact record if a match is found. The web resource also displays the image that is captured by the Raspberry Pi so the user can validate that the license plate number extracted by OpenALPR matches the actual license plate number.

Executing the license plate recognition on the Raspberry Pi just requires making a GET request to the Node.js web page described in part 2 and then parsing the JSON response.

//command to start checklplate action call when button is pushed  
function executeCheckplate() { 
	$("#outputdiv").text("");
	$("#checkButton").prop('disabled', true);
	var checkplateURI = piRootPath + "/check_plate";
    var req = new XMLHttpRequest();
    req.open("GET", encodeURI(checkplateURI), true);
    req.setRequestHeader("Accept", "application/json");
    //req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.onreadystatechange = function () {
        //debugger;
        if (this.readyState == 4 /* complete */) {
            req.onreadystatechange = null; //avoids memory leaks
            if (this.status == 200) {
                successPlateCallback(JSON.parse(this.responseText));
            }
            else {
                errorCallback();
            }
        }
    };
    req.send();
}

Once the response is returned from the Raspberry Pi, the web resource then queries CRM to find a contact, unless no plate was detected, in which case it displays a failure message.

function successPlateCallback(resultObj) {
	if(resultObj.results.length > 0) {
		var plateNum = resultObj.results[0].plate;
		var imgUrl = resultObj.image;
		$("#outputdiv").append("Detected plate number: " + plateNum + "

"); //show the captured plate image $("#outputdiv").append("<img src='" + piRootPath + imgUrl+ "' width='400' />"); var oDataURI = Xrm.Page.context.getClientUrl() + "/XRMServices/2011/OrganizationData.svc/" + "ContactSet?$select=ContactId,FullName&$filter=lpa_Platenumber eq '" + plateNum +"'"; var req = new XMLHttpRequest(); req.open("GET", encodeURI(oDataURI), true); req.setRequestHeader("Accept", "application/json"); //req.setRequestHeader("Content-Type", "application/json; charset=utf-8"); req.onreadystatechange = function () { if (this.readyState == 4 /* complete */) { req.onreadystatechange = null; //avoids memory leaks if (this.status == 200) { successCrmCallback(JSON.parse(this.responseText).d.results); } else { errorCallback(); } } }; req.send(); } else { $("#outputdiv").append("No plate detected

"); $("#checkButton").prop('disabled', false); } }

If a matching contact is found, it's displayed. Otherwise a failure message is displayed instead.

function successCrmCallback(contacts) {
	if(contacts.length > 0) {
		var contactUrl = Xrm.Page.context.getClientUrl() + "/main.aspx?etc=2&extraqs=&pagetype=entityrecord&id=%7b" + contacts[0].ContactId + "%7d";
		$("#outputdiv").prepend("Contact: " + contacts[0].FullName + "

"); } else { //otherwise display a message that no contact could be found $("#outputdiv").prepend("No contact found

"); } $("#checkButton").prop('disabled', false); }

The web resource (lpa_checkplatejs.htm) is included in my sample CRM solution along with the contact entity configured to store the license plate number. The sample CRM solution is available in my GitHub repository here.

Demo

Here's the web resource open a new tab.

Here's the result after I click the "check plate" button. The contact name is a hyperlink that will open the contact record in a new window.

That's it for today. In my next and final post in this series, I'll show how to set up a streaming interface so the Raspberry Pi can take a picture and parse the plate number, which will then trigger the web resource to search for and display a contact without any input from the end user.