{"id":2170,"date":"2015-09-15T23:00:00","date_gmt":"2015-09-16T06:00:00","guid":{"rendered":"https:\/\/www.microsoft.com\/reallifecode\/index.php\/2015\/09\/15\/talking-alljoyn-with-universal-windows-platform-uwp\/"},"modified":"2020-03-18T15:46:07","modified_gmt":"2020-03-18T22:46:07","slug":"talking-alljoyn-with-universal-windows-platform-uwp","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/talking-alljoyn-with-universal-windows-platform-uwp\/","title":{"rendered":"Talking AllJoyn with Universal Windows Platform (UWP)"},"content":{"rendered":"<p>Windows 10 introduced support for AllJoyn giving any Windows 10 device \u2013 phone, desktop, tablet \u2013 the ability to connect and control multiple Internet of Things (IoT) devices like <a href=\"https:\/\/twitter.com\/heavenfresh\">@HeavenFresh<\/a> air purifiers and humidifiers. In this case study, we will delve into the process of how we added AllJoyn support to the HeavenFresh Universal Windows Platform (UWP) app, enabling the app to discover and control AllJoyn-compliant HeavenFresh air purifiers and humidifers.<\/p>\n<h2 id=\"alljoyn-a-brief-introduction\">AllJoyn: A Brief Introduction<\/h2>\n<p>Driven by the <a href=\"https:\/\/allseenalliance.org\/\">AllSeen Alliance<\/a>, <a href=\"https:\/\/allseenalliance.org\/developers\/learn\">AllJoyn<\/a> is an open-source framework designed for interoperablity between Internet of Things (IoT) devices. The framework provides a common language for devices to discover, communicate, and interact directly with other nearby devices. This enables scenarios like an AllJoyn-compliant air quality monitor directly controlling the settings of an AllJoyn-compliant purifier and, at the same time, sending notifications to an AllJoyn-compliant television to display the current air quality on-screen.<\/p>\n<h2 id=\"customer-problem\">Customer Problem<\/h2>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2015-09-15-Talking-AllJoyn-with-Universal-Windows-Platform-heavenfresh-logo.jpg\" alt=\"HeavenFresh Logo\" \/><\/p>\n<p><a href=\"http:\/\/www.heavenfresh.com\/\">HeavenFresh<\/a> makes a number of products for the home including AllJoyn connected air purifiers and humidifers. In preparation for <a href=\"http:\/\/b2b.ifa-berlin.com\/\">IFA Berlin 2015<\/a>, HeavenFresh needed a Windows 10 app able to:<\/p>\n<ul>\n<li>discover the AllJoyn-compliant air purifiers and humidiers on the local network<\/li>\n<li>view and monitor current settings<\/li>\n<li>adjust configurations to desired levels<\/li>\n<\/ul>\n<p>For example, in the case of the humidifer, through the application, a user should be able to view the current state of the humidifer (Is it on? What is the current humidity level?) and adjust settings such as changing the mist level to high or turning on\/off the humidifer after 2 hours.<\/p>\n<h2 id=\"overview-of-the-solution\">Overview of the Solution<\/h2>\n<p>With Windows 10, the <a href=\"https:\/\/allseenalliance.org\/developers\/learn\/architecture\">AllJoyn Router<\/a> runs as a Windows Service; as a result, Windows 10 devices have native support for discovering and communicating with other AllJoyn devices\/apps including the HeavenFresh air purifier and humidifer. We held a hackfest to build the HeavenFresh <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/apps\/Dn958439.aspx\">Universal Windows Platform (UWP)<\/a> app which would act as a controller able to onboard HeavenFresh devices to the wireless network and view and adjust the current settings of the air purifiers\/humidifers.<\/p>\n<p>AllJoyn devices\/apps generally fall into two categories: producer or consumer. A producer produces data\/notifications and listens for instructions. A consumer, on the other hand, connects to one or more producers and consumes and controls producers. The UWP application is a consumer that connects to two producers: the HeavenFresh air purifier and the HeavenFresh humidifer.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2015-09-15-Talking-AllJoyn-with-Universal-Windows-Platform-alljoyn-architecture.png\" alt=\"HeavenFresh UWP Network Architecture\" \/><\/p>\n<h2 id=\"implementation\">Implementation<\/h2>\n<p>The following will outline the steps taken to create the UWP application with native AllJoyn support.<\/p>\n<h3 id=\"setup\">Setup<\/h3>\n<p>Before we begin writing code, let\u2019s ensure our developer machine is:<\/p>\n<ol>\n<li>Running Windows 10<\/li>\n<li><a href=\"https:\/\/www.visualstudio.com\/en-us\/downloads\/download-visual-studio-vs.aspx\">Visual Studio 2015<\/a> with <a href=\"https:\/\/dev.windows.com\/en-us\/downloads\">Windows developer tools<\/a> installed<\/li>\n<li>Producer AllJoyn devices are turned on and connected to the same network and subnet as the developer machine<\/li>\n<\/ol>\n<h3 id=\"about-service\">About Service<\/h3>\n<p>In order to be discovered by Windows 10 UWP applications, the AllJoyn producer device needs to implement <a href=\"https:\/\/allseenalliance.org\/framework\/documentation\/learn\/core\/system-description\/advertisement-discovery\">About-based discovery<\/a>. The <a href=\"https:\/\/allseenalliance.org\/framework\/documentation\/learn\/core\/about-announcement\">About Service<\/a> serves as a way for a device to advertise itself and the various service interfaces it implements. Given the interface definitions which are defined in <a href=\"http:\/\/go.microsoft.com\/fwlink\/p\/?LinkId=616545\">D-Bus introspection<\/a> format, a client application (consumer) can build the necessary method calls to communicate to said AllJoyn device.<\/p>\n<p>Both HeavenFresh devices, the air purifier and humidifer, have implemented the About Service. Shown below is the introspection XML for the HeavenFresh humidifer illustrating the methods, properties, and signals that the device supports.<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code>&lt;node name=\"org\/alljoyn\/Bus\/ControlPanel\"&gt;\r\n    &lt;interface name=\"org.alljoyn.ControlPanel.Humidifier\"&gt;\r\n\r\n        &lt;method name=\"PowerON\"\/&gt;\r\n        &lt;method name=\"PowerOFF\"\/&gt;\r\n        &lt;method name=\"SendSoftwareUpgradeFile\"&gt;\r\n            &lt;arg name=\"currentIndex\" type=\"u\" direction=\"in\"&gt;\r\n            &lt;\/arg&gt;\r\n            &lt;arg name=\"fileData\" type=\"ay\" direction=\"in\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/method&gt;\r\n\r\n        &lt;property name=\"powerStatus\" type=\"i\" access=\"readwrite\"\/&gt;\r\n        &lt;property name=\"humidityValue\" type=\"i\" access=\"readwrite\"\/&gt;\r\n        &lt;property name=\"ionStatus\" type=\"i\" access=\"readwrite\"\/&gt;\r\n        &lt;property name=\"warmMistStatus\" type=\"i\" access=\"readwrite\"\/&gt;\r\n        &lt;property name=\"mistVolumeValue\" type=\"i\" access=\"readwrite\"\/&gt;\r\n        &lt;property name=\"timerValue\" type=\"i\" access=\"readwrite\"\/&gt;\r\n        &lt;property name=\"currentRoomTempValue\" type=\"i\" access=\"read\"\/&gt;\r\n        &lt;property name=\"currentRoomHumidityValue\" type=\"i\" access=\"read\"\/&gt;\r\n        &lt;property name=\"waterTankStatus\" type=\"i\" access=\"read\"\/&gt;\r\n\r\n        &lt;signal name=\"currentRoomTempValueChanged\"&gt;\r\n            &lt;arg name=\"newCurrentRoomTempValue\" type=\"i\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/signal&gt;\r\n        &lt;signal name=\"currentRoomHumidityValueChanged\"&gt;\r\n            &lt;arg name=\"newCurrentRoomHumidityValue\" type=\"i\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/signal&gt;\r\n        &lt;signal name=\"waterTankStatusChanged\"&gt;\r\n            &lt;arg name=\"newWaterTankStatus\" type=\"i\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/signal&gt;\r\n        &lt;signal name=\"powerStatusChanged\"&gt;\r\n            &lt;arg name=\"newPowerStatus\" type=\"i\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/signal&gt;\r\n        &lt;signal name=\"humidityValueChanged\"&gt;\r\n            &lt;arg name=\"newHumidityValue\" type=\"i\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/signal&gt;\r\n        &lt;signal name=\"ionStatusChanged\"&gt;\r\n            &lt;arg name=\"newIonStatus\" type=\"i\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/signal&gt;\r\n        &lt;signal name=\"warmMistStatusChanged\"&gt;\r\n            &lt;arg name=\"newWarmMistStatus\" type=\"i\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/signal&gt;\r\n        &lt;signal name=\"mistVolumeValueChanged\"&gt;\r\n            &lt;arg name=\"newMistVolumeValue\" type=\"i\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/signal&gt;\r\n        &lt;signal name=\"timerValueChanged\"&gt;\r\n            &lt;arg name=\"newTimerValue\" type=\"i\"&gt;\r\n            &lt;\/arg&gt;\r\n        &lt;\/signal&gt;\r\n    &lt;\/interface&gt;\r\n&lt;\/node&gt;\r\n<\/code><\/pre>\n<\/div>\n<h3 id=\"alljoyn-studio\">AllJoyn Studio<\/h3>\n<p>The <a href=\"https:\/\/visualstudiogallery.msdn.microsoft.com\/064e58a7-fb56-464b-bed5-f85914c89286\">AllJoyn Studio Visual Studio Extension<\/a> will query the local network for AllJoyn producers, extract their introspection XML, and based on the introspection XML, auto-generate C++ <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/apps\/hh441572.aspx\">Windows Runtime (WinRT) Component(s)<\/a> that can be directly consumed by our UWP application.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2015-09-15-Talking-AllJoyn-with-Universal-Windows-Platform-codegen.png\" alt=\"AllJoyn Studio\" \/><\/p>\n<h4 id=\"creating-an-alljoyn-app\">Creating an AllJoyn App<\/h4>\n<p>Installing the AllJoyn Studio extension will add a new project template: AllJoyn App.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2015-09-15-Talking-AllJoyn-with-Universal-Windows-Platform-alljoyn-studio-new-alljoyn-app.png\" alt=\"Visual Studio - AllJoyn App\" \/><\/p>\n<p>The next step of the wizard will query the network for AllJoyn devices that are advertising itself. As a result, ensure that your AllJoyn producer devices are turned on and are connected to the same network as your current Windows 10 developer machine. All discovered interfaces are then displayed allowing us to choose which interfaces to implement.<\/p>\n<p>With the HeavenFresh humidifer and air purifier in our local network, we see the following interfaces:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2015-09-15-Talking-AllJoyn-with-Universal-Windows-Platform-alljoyn-studio-discovered-interfaces.png\" alt=\"Visual Studio - Discovery\" \/><\/p>\n<p>After selecting the interfaces, Visual Studio and the AllJoyn Studio extension will use the introspection XMLs and <a href=\"https:\/\/developer.microsoft.com\/en-us\/windows\/iot\/docs\/alljoyncodegen\">AllJoyn Code Generator<\/a> to generate the WinRT components. The resulting WinRT component can then be referenced in any UWP-supported language (C#, C++, JavaScript, Visual Basic).<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2015-09-15-Talking-AllJoyn-with-Universal-Windows-Platform-alljoyn-studio-solution.png\" alt=\"Visual Studio - Solution\" \/><\/p>\n<p>At this point, we can start implementing AllJoyn functionality into our application.<\/p>\n<h3 id=\"get-on-the-alljoyn-bus\">Get on the (AllJoyn) Bus<\/h3>\n<p>The generated code includes two classes: watcher and a consumer.<\/p>\n<h4 id=\"watcher\">Watcher<\/h4>\n<p>The watcher monitors the local network for the given AllJoyn producer and fires an event when the producer is found or stopped. For instance, the <a href=\"https:\/\/github.com\/jpoon\/HeavenFresh-AllJoyn-Example\/blob\/bc4931fe17a59168673effef233228d7d6646104\/org.alljoyn.ControlPanel.AirPurifier\/AirPurifierWatcher.cpp\">AirPurifierWatcher<\/a> will fire every time an air purifier is discovered.<\/p>\n<p>In order to start the watcher, we first create an AllJoyn connection using the <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/apps\/windows.devices.alljoyn.alljoynbusattachment\">AllJoyn Bus Attachment<\/a>.<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code>_busAttachment = new AllJoynBusAttachment();\r\n\r\nvar airPurifierWatcher = new AirPurifierWatcher(_busAttachment);\r\nairPurifierWatcher.Added += AirPurifierWatcher_Added;\r\nairPurifierWatcher.Start();\r\n<\/code><\/pre>\n<\/div>\n<p>The <code class=\"highlighter-rouge\">Added<\/code> event fires every time an air purifier is discovered. If multiple purifiers are on the same local network, an instance of <code class=\"highlighter-rouge\">AirPurifierConsumer<\/code> is instantiated per purifier found. We track the instances in the <code class=\"highlighter-rouge\">NavMenuItem<\/code> which will eventually be passed along to the <code class=\"highlighter-rouge\">AirPurifierPage<\/code> when a user chooses a new <code class=\"highlighter-rouge\">NavMenuItem<\/code> in the navigation pane.<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code>private async void AirPurifierWatcher_Added(AirPurifierWatcher sender, AllJoynServiceInfo args)\r\n{\r\n    AirPurifierJoinSessionResult joinResult = await AirPurifierConsumer.JoinSessionAsync(args, sender);\r\n\r\n    if (joinResult.Status == AllJoynStatus.Ok)\r\n    {\r\n        var airPurifierConsumer = joinResult.Consumer;\r\n\r\n        \/\/ Add a new navigation menu item\r\n        var newMenuItem = new NavMenuItem\r\n        {\r\n            Label = \"Air Purifier\",\r\n            DestPage = typeof(AirPurifierPage),\r\n            Arguments = airPurifierConsumer\r\n        };\r\n\r\n        \/\/ Update the UI\r\n        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate { _deviceList.Add(newMenuItem); })\r\n    }\r\n    else\r\n    {\r\n        \/\/ error\r\n    }\r\n}\r\n<\/code><\/pre>\n<\/div>\n<h4 id=\"consumer\">Consumer<\/h4>\n<p>The consumer class exposes the functionality to control the AllJoyn producer. The auto-generated <a href=\"https:\/\/github.com\/jpoon\/HeavenFresh-AllJoyn-Example\/blob\/bc4931fe17a59168673effef233228d7d6646104\/org.alljoyn.ControlPanel.AirPurifier\/AirPurifierConsumer.cpp\">AirPurifierConsumer<\/a> contains the following implementations:<\/p>\n<div class=\"highlighter-rouge\">\n<pre class=\"highlight\"><code>\/\/ AirPurifierConsumer\r\npublic IAsyncOperation&lt;AirPurifierGetFlowValueResult&gt; GetFlowValueAsync();\r\npublic IAsyncOperation&lt;AirPurifierGetPowerStatusResult&gt; GetPowerStatusAsync();\r\npublic IAsyncOperation&lt;AirPurifierGetSensorAllergenValueResult&gt; GetSensorAllergenValueAsync();\r\npublic IAsyncOperation&lt;AirPurifierGetSensorCleanMetalGridValueResult&gt; GetSensorCleanMetalGridValueAsync();\r\npublic IAsyncOperation&lt;AirPurifierGetSensorCleanMonitorValueResult&gt; GetSensorCleanMonitorValueAsync();\r\npublic IAsyncOperation&lt;AirPurifierGetSensorDustValueResult&gt; GetSensorDustValueAsync();\r\npublic IAsyncOperation&lt;AirPurifierGetSensorOdorValueResult&gt; GetSensorOdorValueAsync();\r\npublic IAsyncOperation&lt;AirPurifierGetSensorReplaceFilterValueResult&gt; GetSensorReplaceFilterValueAsync();\r\npublic IAsyncOperation&lt;AirPurifierGetTimerValueResult&gt; GetTimerValueAsync();\r\npublic IAsyncOperation&lt;AirPurifierPowerOFFResult&gt; PowerOFFAsync();\r\npublic IAsyncOperation&lt;AirPurifierPowerONResult&gt; PowerONAsync();\r\npublic IAsyncOperation&lt;AirPurifierResetResult&gt; ResetAsync();\r\npublic IAsyncOperation&lt;AirPurifierSendSoftwareUpgradeFileResult&gt; SendSoftwareUpgradeFileAsync(uint interfaceMemberCurrentIndex, IReadOnlyList&lt;byte&gt; interfaceMemberFileData);\r\npublic IAsyncOperation&lt;AirPurifierSetFlowValueResult&gt; SetFlowValueAsync(int value);\r\npublic IAsyncOperation&lt;AirPurifierSetPowerStatusResult&gt; SetPowerStatusAsync(int value);\r\npublic IAsyncOperation&lt;AirPurifierSetTimerValueResult&gt; SetTimerValueAsync(int value);\r\n<\/code><\/pre>\n<\/div>\n<p>Note that each method is directly related to the introspection XML that was shown earlier for the air purifier.<\/p>\n<h2 id=\"opportunities-for-reuse\">Opportunities for Reuse<\/h2>\n<p>The approach described in this case study can be leveraged to add AllJoyn support to any UWP application. In addition, the WinRT components generated against the HeavenFresh air purifier and humidifers are available on <a href=\"https:\/\/github.com\/jpoon\/HeavenFresh-AllJoyn-Example\">GitHub<\/a> if you would like to write your own HeavenFresh controller.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How we created a UWP app with native AllJoyn support.<\/p>\n","protected":false},"author":21365,"featured_media":12774,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[16],"tags":[35,36,199,216,370],"class_list":["post-2170","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","tag-alljoyn","tag-allseen","tag-heavenfresh","tag-iot","tag-universal-windows-platform-uwp"],"acf":[],"blog_post_summary":"<p>How we created a UWP app with native AllJoyn support.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2170","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21365"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=2170"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2170\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/12774"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=2170"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=2170"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=2170"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}