Heyu/X10 state machine and controller written in Coffeescript
The X10 protocol can control electrical appliances around the house. In our house X10 is used to (remotely) control lights, heating, a water bed and it reboots the ADSL router. For years the heyu tool did the job, loading the scheduler on a device named a CM11A, the schedule created by a Ruby script. The nice thing about the CM11A scheduler is that it runs independently even when computers fail. The not so nice thing is that the CM11A 'programming language' is archaic and limited. I wanted more control and move both the scheduler and state machine to the 'server'.
heyu-run --id light1 --switch off heyu-run --time 2013-04-12 10:45 --id light1 --switch on heyu-run --time 10:46 --id light1 --switch off heyu-run --exec heyu-run --replay
Advantages of heyu-run
The state-machine and event timer is handled by the heyu-run script independently. heyu-run only uses the heyu software to send signals to the CM11A or equivalent controller. In other words, both the heyu software and the X10 devices are considered dumb switches. When any of these gets reset, software or hardware, simply run replay mode to get to the current state. heyu-run works perfectly with CRON.
The heyu-run program gets invoked by the user, and also regularly by a CRON job. In CRON exec mode it reads the timed command queue, and updates the electrical appliances by writing a shell script, which in turn invokes heyu. The queue and state machine are maintained in JSON files on disk. The state machine is simple - appliances are 'on' or 'off'. Later we may add dimming states. Appliances become known to the state machine when they are used the first time.
Because of the state machine, the correct state will be updated even after a computer fails and comes back online.
Setting up the environment
Coffeescript needs to be installed on a workstation (it is not on OpenWRT just yet). On Debian
apt-get install coffeescript coffee --version CoffeeScript version 1.1.2 js --version v0.4.12
Note: older versions of Debian may require building node.js.
make -f Makefile.ref JS_HAS_FILE_OBJECT=1
Compile and run
coffee -c bin/heyu-run.coffee lib/*.coffee
set the js alias and run a test
alias js-1.6=/opt/js-1.6.20070208/bin/js js-1.6 bin/heyu-run.js --test (...) Tests passed
To use heyu-run on OpenWRT is similar. Install the 'js' package and check the scripts in ./script.
js bin/heyu-run.js --id light1 --switch on | sh
and to switch it off
js bin/heyu-run.js --id light1 --switch off | sh
You can see that in both cases heyu gets invoked. To query the current state
js bin/heyu-run.js --id light1 --state on
to program a timer
js bin/heyu-run.js --time yyyy-mm-dd hh:mm --id light1 --switch on | sh
which adds the timed command to the event queue.
Run the script and execute programmed state changes
js bin/heyu-run.js --exec | sh
Exec can be run from a cron job - say every few minutes. We make sure no two jobs can write to the same file at the same time (through a write lock).
To synchronize all appliances to the state in the local state machine, call replay
js bin/heyu-run.js --replay | sh
To switch off all known appliances and remove the queue, simply remove the database files and run --replay. It is safe to run --replay in a CRON job. I run --replay every few hours.
To catch heyu errors you can send the STDERR output to a file and test the error return code with bash. We can also use 'tee' to send output to a log file. E.g.
set -e set -o pipefail js bin/heyu-run.js --exec | tee -a heyu.log | sh 2>> heyu.err echo $? # 0 on success, 1 on heyu error
A cron job could be to exec every minute and to replay every hour
* * * * * js heyu-run.js --exec | tee -a heyu.log | sh 2>> heyu.err 0 * * * * js heyu-run.js --replay |sh
or, perhaps, more advanced using a script and killing the relay (which sometimes stops communicating when the CM11A resets)
* * * * * cd ~/opt/heyu-run && ./scripts/run.sh --exec | tee -a heyu.log | sh 2>> heyu.err 0 * * * * killall heyu_relay ; cd ~/opt/heyu-run && ./scripts/run.sh --replay |sh
on openwrt make sure to enable the cron daemon
/etc/init.d/cron enable /etc/init.d/cron start ```sh Set regular job through CRON (here I reset the ADSL modem at 3AM every morning) ```cron 21 3 * * * js heyu-run.js --switch off --id adsl |sh 22 3 * * * js heyu-run.js --switch on --id adsl |sh
Which just touches on the power of running a heyu state machine using CRON!!
Bugs / features
It is possible to add multiple events when the state of an appliance gets changed to the timed events queue, also without using the --time switch. The time granularity is one minute or second, so it can be the user adds two or more conflicting events at the same time. Luck will chose which one is the final state(!). To be on the safe side, remove the ambiguous event from the database file or add a timed event one minute or second later.
Planned for / wished for
- Introduce nicer JS state machine
- Send update messages to a service such as jabber
- Write a schedule for Heyu to upload to the CM11A
- Maybe mix and match with the Heyu state engine
This software is published under the liberal BSD license. See LICENSE.TXT. Copyright(c) 2013 Pjotr Prins