A state machine for lighting control
When I first started automating my home the software I used offered a very limited rules system, essentially, if this trigger happened then that light goes on. Later I upgraded to a slightly better system that provided some simple conditions allowing lights to come on only at certain times of day.
When I wrote my own system I quickly graduated to a what would now be called reactive-programming (in the style of Rx) but back then I called them sequential logic blocks because Rx hadn't been invented yet. These were great because they provided a fluent english-like syntax for defining complex logic expressions with a strong temporal component. They were also were able to explain 'why' they had turned a light on because they could chain back up the logic expression that had caused them to come on and log the reason.
Over time my lighting rules became more and more complex. Lights would examine the occupancy of the room they were in, the time of day, the amount of sun entering the house and their recent on/off history to look for manual changes at the light switch that should act as overrides for some period of time.
That's when I realized that nearly all of the lighting scenarios I had programmed could be condensed into a single state machine that captured the essence of all the rules I had found through trial and error and a very patient family testing it over the years. And so, I built a state-machine which is a compact and more easily understood way of representing the states and the transitions between them. But first, before I could do that I had to find an existing state machine in .NET or write my own. It turned out that none of the existing C# state machines offered a full UML Hierarchical State Machine so I wrote my own which is available now on GitHub and Nuget. A further enhancement over other state machines is that my state machine has the concept of a scheduler listing future events which makes it easier to code time-based state transitions.
Currently the state machine looks like the image shown above.
Of note is the separation of manually controlled states from automatically controlled states. When a user presses a light switch they don't expect it to change again immediately. The worst case scenario here was the birthday cake scenario where someone turns the lights off and on return with the lit cake all the lights come back on. In my state machine when a manual state is activated it stays in manual mode for several minutes before reverting to automatic control.
Another key improvement in my light control algorithm is the Dimming
state which a light enters once a room is
no longer occupied. In this state the light gradually dims, almost inperceptibly. As soon as you move in that room
the light returns to its desired brightness, but if you don't it carries on dimming down to zero, or some preset
limit for that light which prevents flicker (e.g. some LEDs cannot go below 20%, say, without flickering).
The addition of an imperceptible dim solves many of the problems I had earlier when the home automation system would turn lights off directly. For example, someone stays motionless in a room for a long time, the system decides nobody is there and it shuts off the light. Another example would be when you are heading into a room and it just reached the timeout time and just before you step into what was a brightly lit room it goes off. With the slow dim in place neither 'bug' happens. Someone notices it's getting a bit dim, they move slightly and the light comes smoothly back to full brightness. When you enter a room that's dimming it becomes bright as you step in.
The light state machine also understands a LightProfile
which encapsulates the different brightness profiles
that a light may have. For example:
Bedroom Light This does not come on during the night, comes on dimly around bedtime or wake up time but comes on full brightness during the day if it's dark outside.
Bathroom light This comes on dimly if you enter late at night or on full brightness the rest of the time.
Driveway light This doesn't respond to activity, it comes on at sunset and stays on until a fixed time of night.
It also has the concept of a task light and a path light which have their own behaviors.