Posted on May 12, 2023

Announcing the release of automation-service

I’m announcing the release of automation-service today. I’ve spent the last three months and change working on this, more or less full time.

I got the basic idea at the end of last year when I started messing around with home automation, in particular Home Assistant. I checked out other tools too like Node-RED and AppDaemon–these are all mature, full-featured pieces of software that collectively give you many different ways to write automations. But, being the FP nerd I am, I wanted to see if I could do this with Haskell in particular.

At the point I wrote my first toy Haskell automation over winter vacation last year, I was only interested in getting some FP into my home automation stack. I didn’t intend to make anything more than a fun proof of concept. Then I got laid off at the end of January, and suddenly wasn’t sure what to do with myself. I was lucky enough that I had some severance, allowing me to take a break from work. I needed to burn off some energy while also getting over some burnout, as counterintuitive as that may sound.

Taking some loose inspiration from Erlang/OTP’s design, and similarly considering (stealing ideas from) the constrained environments presented by Processing and Arduino, I used the amazing HsLua to add a scripting layer to the system, with an ad-hoc, simple central messaging protocol that supports a small set of operations available over MQTT (starting and stopping automations, scheduling and unscheduling jobs), and an expanded set of operations internally (registering devices and more).

I have been using automation-service continuously in my own house for about a month and a half, through different incarnations. Right now it leans heavily on Zigbee2MQTT for device information. Longer term I’d like to support other platforms that communicate over MQTT, like ESPHome, as much as possible too.

Here is an example of a very simple sunset-calculating script that is run daily at the same time, which schedules a light to go on at a new time every day depending on the calculated sunset:

-- sunsetLighting.lua
-- This is run every day early morning and schedules
-- another job for half an hour before sunset, as
-- calculated for that day.

function setup()
   local latitude = 41.5020948
   local longitude = -73.982543
   local sunEvents = getSunEvents(latitude, longitude)

   local thirtyBeforeSunset = addMinutes(-30, sunEvents.sunset)
   local thirtyBeforeSunsetCron = timestampToCron(thirtyBeforeSunset)

      jobId = "sunsetLightsOn",
      schedule = thirtyBeforeSunsetCron,
      -- Jobs are just the name of the lua script,
      -- and this job message can be any message
      -- accepted by the system.
      job = { start = "sunsetLightsOn" }

This is the script it runs:

-- sunsetLightsOn.lua
local standingLamp = nil

function setup()
   -- This records this group as being in use by this automation,
   -- and returns a data structure with information including the
   -- topics needed to subscribe and publish information to the
   -- group via MQTT.
   standingLamp = registerGroup(5)

   -- Lua data can be used directly as a JSON-analog, and is
   -- converted to JSON by HsLua.
   publish(standingLamp.topicSet, { state = "ON" })

Hopefully that gives one a sense of how the system works.

In terms of specific features, depending on interest I’ll be expending more energy on broadening the types of devices automation-service supports, and adding more UI and other features to eliminate pain and labor as much as possible. For example, I’m happy enough for now to tail -f the debug logs to test my Lua scripts, but I think it’d be way nicer for everyone if error messages were exposed in the Home Assistant UI for ease of debugging. Etc.

Which is to say, the quality of this is very alpha right now (I should probably figure out what versioning system I want to use 😅), but I’ve already dragged my heels on sharing this more publicly for some time–I don’t think I’ll stop being embarrassed by the state of it any time soon, and I know there’s a lot of it that is messy still. Along those lines, I’d appreciate any feedback anyone wants to give me, especially if you decide to test this out in your own setup.

I’m not sure where this project will go, but I have had a blast in building this over the past three months. Building something on my own, in Haskell, that is useful to me daily is something I’ve wanted to do for a long time, so this adventure has given me a tremendous sense of satisfaction, joy, and gratitude in even getting to this point.

I hope others can find some utility and maybe even joy as well in this project. In any case, thank you for checking it out and reading this far!