;;; Bottom level abstraction - an X10 controller object.;; These map to an X10 controller attached to the computer. They;; support three operations: open, close and and set-device. Since;; these correspond to hardware ("things you can kick") that usually;; can't be used in parallel, they should always be wrapped by an;; agent.(ns x10.controllers [:use [x10.config :only (*controllers*)] [clojure.walk :only (walk)]] [:import gnu.io.CommPortIdentifier [com.micheldalal.x10 CM17A]]);; The tricky part here is that the open should only happen once. We;; can't open things when we build the *devices* structure, as it will;; then happen at compile time, which causes the compile to break.(defprotocol Controller "X10 Controller Modules" (open [this] "Whatever is needed to open the controller and ports") (close [this] "Close & free the port for possible reuse.") (set-device [this code unit state] "Set the device at address code/unit to state"));;; To add a new controller type;; Write the appropriate defrecored that instantiates the Controller;; protocol, then add an entry to the *controller-type-map* to map the;; name used in the config database to the record.(deftype CM17A-controller [^CM17A controller ^String port delay] Controller (open [this] (when-not (.getSerialPort controller) (.setSerialPort controller (.open (CommPortIdentifier/getPortIdentifier port) "Mike's X10 Controller" 2000)) (Thread/sleep (Integer/parseInt delay))) this) (close [this] (.close (.getSerialPort controller)) this) (set-device [this code unit new-state] (.setState controller (first code) (Integer/parseInt unit) new-state) (Thread/sleep (Integer/parseInt delay)) this));;; Map from controller type names to code.;; Note that the keys here must match the types in;; config/*controllers*. There should be one entry for each unique;; controller type. It maps to a function to produce a new controller;; given a port and delay.(def *controller-type-map* {"CM17A" (fn [port delay] (CM17A-controller. (new CM17A) port delay))});;; Map of all available controllers, built from the above and(def *controller-map* (walk (fn [[name module port delay]] {name (agent ((*controller-type-map* module) port delay))}) #(apply merge %) *controllers*))