Home Automation with Windows Workflow
| | There are some pretty good Home Automation packages out there on the market. Some of these are made for installers – and are thus closed to easy customization by the end user. Then there are packages that are made for hobbyists. These have good core automation systems, and provide some add-in points for customization. There are very few good “toolkits” for home automation enthusiasts who like to Code4Fun, so I decided to build one. |
| Charles Stacy Harris Difficulty: Intermediate Time Required: 3-6 hours Cost: $50-$100 Software: Windows Workflow Foundation Hardware: Z-Wave devices |
For me, home automation ranks near the top of the list of ultimate computer geek hobbies. You get to channel your inner “Scotty/Geordi” by playing with cool hardware, and you can channel your inner “Spock/Data” by playing with cool software. It doesn’t take much hardware to get started. A simple kit such as the ControlThink Z-Wave PC SDK or one of the myriad others will get you going. And you can add more bits and pieces such as motion detectors, alarm controllers, etc. - as you go.
Enter Workflow
Many people think of Windows Workflow Foundation as being useful only for rule-driven, line of business applications, systems management, or one of several other “enterprise business application” uses. These are all good things, but what about using workflow for something a little more fun? After all, Windows Workflow Foundation has features like a cool graphical designer, an awesome rules engine, a robust persistence model, and an extensible service model just to name a few. Somehow, my thoughts about workflow and my thoughts about home automation converged. I wondered: “Can I use workflow to turn on a light?” Yeah, you know like that switch in the refrigerator that costs a few pennies. I wanted to use workflow for turning the light in my pantry on or off. The pantry door has a hardwired contact switch that connects to my Automation/Alarm system – an Elk M1G. My lights are controlled via several Z-Wave controllers including the USB stick that ships with the ControlThink SDK mentioned above. The problem was to get these two pieces of hardware talking so that I could have a workflow that looks like this:
The idea is that the PantryDoorChange activity and the PantryLightOn/Off shapes would be instances of custom workflow activities. PantryDoorChange would monitor the Elk M1G waiting for the door’s contact switch to open or close. PantryLightOn/Off would send the appropriate Z-Wave command for turning the light on/off.
First Order of Business
Before we can get to the custom activities, we have to think about the services behind them, and how we’re going to communicate with the various devices. Communicating with the Z-Wave devices is made extremely easy with the rockin’ ControlThink Z-Wave SDK, so let’s leave that one aside for a moment.
Elk Products, Inc. publishes a protocol that allows application developers to write code to interact with the Elk-M1G. This protocol is accessible via serial port or TCP/IP. I opted for TCP/IP which requires the optional Elk Ethernet module. In either case, the code is fairly straightforward. You can send the Elk-M1G an ASCII request message and it will either carry out an action, reply asynchronously with a response message, or both. You can also set some “global settings” on the Elk-M1G to instruct it to send unsolicited messages whenever events occur.
I started by creating the Elk class. This is a component that implements a .NET interface into the Elk-M1G protocol. The class is a derived from the Component class which makes it easy to use in a Windows Forms application, but it can also be used in any other .NET application. The current implementation only supports a fraction of the request and response messages available within the Elk-M1G. The message that we care about for the above workflow is the ZC – zone status change. This message will tell us when the zone changes status and that that status is. The various status values are given by:
1: [Serializable]
2: public enum ZoneStatus : byte
3: {
4: NormalUnconfigured,
5: NormalOpen,
6: NormalEOL,
7: NormalShort,
8: notused1,
9: TroubleOpen,
10: TroubleEOL,
11: TroubleShort,
12: notused2,
13: ViolatedOpen,
14: ViolatedEOL,
15: ViolatedShort,
16: notused3,
17: BypassedOpen,
18: BypassedEOL,
19: BypassedShort
20: }
The documentation for the Elk-M1G serial port protocol describes these values in detail. For us, the key is to find out whether a zone – e.g., the contact switch on my pantry door, is open or closed. The Elk component will listen on the socket for an incoming zone change message – ASCII string ZC followed by zone information – and raise a .NET event when the zone change is received. The .NET event contains the following event arguments:
1: [Serializable]
2: public class ZoneStatusChangeEventArgs : EventArgs
3: {
4: public ZoneStatusChangeEventArgs(byte zone, ZoneStatus status)
5: {
6: this.zone = zone;
7: this.status = status;
8: }
9:
10: private byte zone;
11:
12: public byte Zone
13: {
14: get { return zone; }
15: }
16:
17: private ZoneStatus status;
18:
19: public ZoneStatus Status
20: {
21: get { return status; }
22: }
23: }
I’ve implemented only a few of the many functions available on the Elk-M1G. Following the patterns in the code, it should be fairly easy to implement the remaining features thus opening up the possibility for much more advanced applications than why I have here.
Workflow Services
The next step in this process is to create the actual services and the Workflow Activities which they serve. I designed two services for my application. The ElkService will funnel Elk-M1G events into a workflow activity and allow a workflow activity to send command messages to the Elk-M1G. The ZWaveService allows activities to send ZWave commands and monitor devices for status changes. As with the Elk component class, these have the basic features implemented, but can easily be extended to support the full range of features offered by their respective SDKs.
Both the ElkService and the ZWaveService serve events to EventActivity derived classes. In order to make life a tad simpler, I wrote the EventActivity base class. The class definition looks like this:
1: public abstract class EventActivity:
2: Activity,
3: IEventActivity,
4: IActivityEventListener<QueueEventArgs> {…}
This class is similar to the InputActivity class found in the custom activity framework sample code at http://wf.netfx3.com/. I needed to make a few tweaks because my services support multiple different event activity types, but the concepts are the same.
There are three custom event driven activities in the current ElkWorkflow library. They are ZoneChange, TaskChange, and OutputChange. These activities will listen for zone, task, and output changes and will wake up when their respective events occur. If you look at the properties of these activities in the Workflow designer, you’ll see that you can set a Filter property to limit which zone, task or output to monitor. If the filter value is non-zero, the event will only be triggered when the corresponding zone, task, or output is triggered, otherwise all zone, task, or output changes will trigger the event.
There is also an activity called ActivateTask that will activate one of the “automation” tasks on the Elk-M1G. These are essentially flags that can trigger events that are programmed into the Elk-M1G directly using its scripting capabilities. ActivateTask demonstrates how to write a shape that will allow you to send commands to the Elk-M1G.
The ZWaveWorkflow library implements a basic ZWave service that currently supports basic dimmer switch capabilities. The Dimmer activity can set the dim/on/off level of a Z-Wave light. There are lots of additional capabilities that can be added to the ZWaveService including capturing device level changes, scene activations, and so on. The ControlThink Z-Wave SDK makes adding new capabilities extremely simple.
Not Done Yet
Here’s a laundry list of some of the things that are left to do. I’m sure there are many things that are not on the list!
1. Better design time experience. This includes the mundane things like better graphics for the custom shapes, and more important things like having the designer show a list of device names rather than having to use device I.D. numbers.
2. More features in the services. Both the ElkService and the ZWaveService can be expanded quite a bit. Features like retrieving the state of devices on startup would make state machine workflows easier to program.
3. A real workflow host. Hosting the workflow in a console application is fine for experimentation, but a windows service might be a better choice for a real home automation system.
4. Dynamic update of in-progress workflows. You don’t want to have to shut down your home automation system every time you need to make a change to a workflow. Windows Workflow Foundation makes it easy to do this in several different ways. Probably the most effective way in the home automation scenario is to create a mechanism for the host to suspend the workflow of interest, and allow the user to edit the workflow design and restart it.
I hope that I’ve shown the fun side of Windows Workflow Foundation. I also hope this is a good start on a home automation toolkit for people who like Coding4Fun.