Duo Desire (a small IoT project)

My wife was watching Shark Tank a few weeks ago and we were both chatting about a thing we saw (which wasn’t funded). It was kind of a clever idea, and I’ll sidestep the entire discussion of whether or not it is a “good” product or not, I just wanted to see what it would take to build something kind of like it (this is loosely inspired by LoveSync, but the LoveSync does have some interesting features around time and also their hardware is much prettier than what I’m using).
The Rebutton (ignore the dirty valet box)
As a quick aside: this method of asynchronous communication in which parties are only notified after reaching consensus seems like an interesting UX pattern. I was very interested to see almost the same pattern pop up (in almost the exact opposite context of LoveSync) in the form of the Bail App, which I believe is still in development (if it’s even feasible).

Quick Overview

So, to the meat of the project: this project is 2 IoT buttons hooked up to an Azure IoT Hub which trigger Azure functions and make use of Azure Table Storage for data persistence. When consensus is reached, a notification is sent via Twilio to all interested parties. Why Azure you ask? Easy: the buttons that were in stock only had the Azure model available, so that made the decision pretty easy. Amazon’s infamous IoT button is apparently no longer for sale to individuals (or if it is it is so well hidden I gave up on looking). Developing this on Azure worked pretty well, although there were some weird issues when I attempted to interact with the IoT Hub service using C#, so I ended up doing everything in Javascript (I know, IoT in JS, I never thought I’d see the day).

Bill of Materials

Building It

To start, configure the Rebutton to communicate with your IoT Hub. The docs do a pretty good job of describing this. Ensure that the buttons are communicating with the hub. This command will let you see events as they come from the event hub: az iot hub monitor-events -n <yourapp> --properties anno sys --timeout 0 In the Azure Function Apps blade, you’ll need to create the following functions

Event Hub Function

The first of the three functions needed is the function which will process event hub messages. At a high level all this is doing is being invoked any time an event occurs and then just writing the event data to the Azure Table Storage table defined in the configuration. You’ll need to configure it something like:
The Azure IoT Event Hub function
The code for the function is here. As I alluded to above, I was having some weird issues with the C# bindings in Visual Studio, so I ended up just writing everything in Javascript directly in browser. Not the greatest development environment…

Timer Function

The timer is the next function. It runs on whatever cron schedule is defined (I currently have it running every 10 minutes), and it just looks for consensus in the table for both configured devices. Consensus in this case is defined as one or more single-click events from each device. If any non-single-click events are discovered, then it is assumed that consensus is impossible, and the messages will not go out. It would be an interesting exercise to extend the current logic and use the other button events (double-click, long-press, super-long-press) for other functions. Upon discovering consensus, the timer function writes to an Azure Queue and writes a “FINISH” record to the table storage to prevent the next run of the function from also triggering a message.
The timer configuration
The code for it is here.

Queue Trigger Function

The final function needed is the queue trigger which is responsible for actually sending the messages to Twilio. The code is incredibly simple as Azure has a pre-built integration with Twilio which abstracts away the vast majority of the code that would otherwise need to be written.
The Queue Trigger configuration
At this point, everything should be wired up, so pressing both buttons should trigger the Twilio message.

Worth It?

Assuming usage remains fairly constant, Azure forecasts that I’ll spend ~$0.30 per month, plus ~$50 for the buttons. I think it’s worth it for the fun I had building this, plus if the current application of the buttons gets stale, I’m sure I can repurpose them for something else. Interestingly, these buttons are built on the Arduino platform, so conceivably I could do a whole lot of customization by changing the firmware around (which might happen eventually).

In Closing

This was a fun project which didn’t take very long to put together and entertained me. It was a timely project for Valentine’s Day. If you end up building something like it or have any feedback on this article, please reach out, I’d love to hear!

Reducing Bandwidth Costs by Moving S3 Video to BackBlaze B2 with CloudFlare

I tweeted about the cost savings that I was able to achieve by moving some videos to S3. Unfortunately in my tweet I was off on the number’s a bit. Basically by switching costs went from $163.45 in September to $2.91 on Backblaze B2 in October (not quite as nice as I said in my tweet, only a 56x reduction in cost instead of a 100x reduction #oops).

Regardless of my inability to recall the amounts of bills, I figured it would be good to quickly document how I have everything setup. The setup instructions I followed are basically the same as what’s documented by Backblaze here. For the purposes of this guide, I’ll assume you already have a CloudFlare account (they have free plans available if you don’t).

  1. Create the bucket in B2 ensuring that the bucket is marked as “Public”
  2. Upload the video to the B2 bucket
  3. Take note of the URL where the video (i.e. f002.backblazeb2.com)
  4. In CloudFlare create a CNAME (with the “Orange Cloud”) which points to the host name found in the URL of your uploaded video
  5. Access the video using your-new-cname.yourdomain.extension/file//

As far as I can tell all files in your bucket will always resolve to the same hostname (the fxxx.backblazeb2.com identifier), so this is only necessary to configure once. Going forward just copy the public link from your Backblaze bucket and replace the backblazeb2.com part with your CNAME, and everything should be happy.

Quick disclaimer: I wasn’t compensated by CloudFlare or Backblaze for this, I’m just sharing my experience. I make no guarantees about any results you’ll see.

Making EF work with DotNet SPA 2.0.0 prerelease

The Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0-rc2-final SPA template currently has a little issue with making EF work properly. If you encounter this issue you’ll notice that running Update-Database in the Package Manager Console results in ng serve being run, which prevents the migrations from being applied.

To fix the issue, we need to slightly change Program.cs and Startup.cs as follows:

    public class Program
    {
        public static void Main(string[] args)
        {
            // This needs to have all of the code originally in BuildWebHost
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>().Build().Run();
        }

        // This is only invoked by EF
        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((ctx, cfg) =>
            {
                cfg.SetBasePath(Directory.GetCurrentDirectory())
                  .AddJsonFile("appSettings.json", true) // require the appsettings file!
                  .AddEnvironmentVariables();
            })
                .UseStartup<Startup>().UseSetting("DesignTime", "true").Build();
    }

In Startup.cs look for app.UseSpa inside of Configure:

            app.UseSpa(spa =>
            {
                // To learn more about options for serving an Angular SPA from ASP.NET Core,
                // see https://go.microsoft.com/fwlink/?linkid=864501

                spa.Options.SourcePath = "ClientApp";
                // Get the "DesignTime" configuration param set above
                bool.TryParse(Configuration["DesignTime"], out var designTime);
                // Only launch the server if we are in development mode AND we are not in design time (which is EF)
                if (env.IsDevelopment() && !designTime)
                {
                    spa.UseAngularCliServer(npmScript: "start");
                }
            });

With those changes Entity Framework starts working perfectly again. Hope this saves people a few hours.