Xamarin Forms Application For Home Automation

[caption id=”attachment_1943” align=”alignright” width=”168”] Xamarin Forms Home Automation Xamarin Forms application for Home Automation[/caption] On the plane to the Xamarin Evolve conference last month I decided to get up to speed with the new Xamarin Forms technology. We’ve been using Xamarin for iOS and Android for some time now but Xamarin Forms is a relatively new addition to Xamarin’s offerings that allows UI code to be shared between Android, iOS and Windows Phone and not just the data and service layers that we normally share.

Within the course of a single plane ride I was able to get the basics of my home automation application ported to Xamarin Forms, so now I can use it on Android or iOS. (Previously I had only done the Android version).

You can see the application at the right, with each room or device color-coded to show how recently it was triggered. The colors and timestamps update in real time and it polls the home automation API periodically to update the display too. You can also click on any light, sprinkler or other switched device to turn it on or off.

Xamarin Forms was very easy to use and had most of the capabilities I needed for this application. It’s clearly still developing rapidly and the documentation and samples haven’t quite caught up yet. It also supports XAML for UI design, but personally I prefer the code-first approach.

There were a couple of areas of the application that took slightly more effort. One was the real-time update of the ‘time ago’ and color values for each cell in the grid. For that I ended up using Reactive Extensions.

[code language=”csharp”]

// A Subject\ tracks when a view model is no longer needed private Subject\ finished = new Subject\ ();

// A global clock is passed to each view model clock = Observable.Interval (TimeSpan.FromSeconds (1)) .Where(x => this.IsVisible) .Select (x => DateTime.UtcNow);

// The view model refreshes the view on each tick until it is told to finish clock .TakeUntil (finished) .ObserveOn (System.Threading.SynchronizationContext.Current) .Subscribe(v => { Refresh(v); } ); [/code]

As you can see, Reactive Extensions provides a very nice way to express some fairly complex logic with ticks coming from a clock, being gated by whether the application is visible, and then again by whether the form is still in use, flipping back to the UI thread and then refreshing the cell.

The other slightly tricky piece of code was the method below to fetch Json from my home automation API. Every time you click in the UI it initiates a fetch to get the latest information for the object you selected. Clicking rapidly around the screen led to lengthy delays as each earlier click had to be dealt with before the latest one was handled and ultimately led to random crashes with errors relating to “_wapi_connect: error looking up socket handle”.

The code below wraps `HttpClient` and implements a simple timeout, the ability to cancel pending requests and a mechanism to to limit concurrent calls by the mobile application to the web server.

[csharp] /// \ /// Utility to request Json from the web server /// \ public class JsonUtility \ { static MediaTypeWithQualityHeaderValue jsonType = MediaTypeWithQualityHeaderValue.Parse(“application/json”); const string authType = “token”;

private static JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.IsoDateFormat, DateParseHandling = DateParseHandling.DateTimeOffset };

public static async Task\ HttpGet(string url, string auth = null) { return await HttpGet (url, CancellationToken.None, auth); }

// Only allow two simultaneous requests to avoid a “_wapi_connect: error looking up socket handle” problem private static SemaphoreSlim semaphore = new SemaphoreSlim(2);

public static async Task\ HttpGet(string url, CancellationToken cancellationToken, string auth = null) { try { cancellationToken.ThrowIfCancellationRequested (); // Check immediately await semaphore.WaitAsync (cancellationToken); // Make sure \<2 already in progress cancellationToken.ThrowIfCancellationRequested (); // Check after lock has been obtained

System.Diagnostics.Debug.WriteLine (“Fetching “ + url); string resultString; using (var webClient = new HttpClient ()) { webClient.Timeout = TimeSpan.FromSeconds(5); webClient.DefaultRequestHeaders.Accept.Add (jsonType); if (auth != null) webClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue (authType, auth);

using (var result = await webClient.GetAsync (url, HttpCompletionOption.ResponseContentRead, cancellationToken)) { resultString = await result.Content.ReadAsStringAsync (); } var resp = JsonConvert.DeserializeObject\ (resultString);

System.Diagnostics.Debug.WriteLine (“Fetched “ + url);

if (resp == null) return default(TResponseType); else return JsonConvert.DeserializeObject\ (resultString, jsonSerializerSettings); } } finally { semaphore.Release (); } } } [/csharp]

Tue Nov 04 2014 23:57:03 GMT-0800 (Pacific Standard Time)

Disqus goes here