blink1-0.4: blink1.hs
{-# LANGUAGE CPP, RankNTypes #-}
import Control.Applicative ((<$>), (<|>))
import Control.Monad (foldM)
import Data.Maybe (fromMaybe)
import System.Environment (getArgs)
import System.Exit (exitFailure)
import qualified System.Console.GetOpt as Opt
import System.Hardware.Blink1
import System.Hardware.Blink1.Class (Blink1)
import System.Hardware.Blink1.Dummy (Blink1Dummy, openDummy)
#ifdef USE_LINUX
import System.Hardware.Blink1.Linux (Blink1Raw, openRawHID, openRawDev)
#endif
#ifdef USE_USB
import System.Hardware.Blink1.USB (Blink1USB, openUSB)
#endif
data Dev
= NoDev
| DummyDev Blink1Dummy
#ifdef USE_LINUX
| LinuxRawDev Blink1Raw
#endif
#ifdef USE_USB
| USBDev Blink1USB
#endif
withDev :: Dev -> (forall b . Blink1 b => b -> a) -> a
withDev NoDev _ = error "No device opened"
withDev (DummyDev b) f = f b
#ifdef USE_LINUX
withDev (LinuxRawDev b) f = f b
#endif
#ifdef USE_USB
withDev (USBDev b) f = f b
#endif
withDev_ :: Dev -> (forall b . Blink1 b => b -> IO ()) -> IO ()
withDev_ NoDev _ = return ()
withDev_ d f = withDev d f
data State = State
{ dev :: !Dev
, led :: Maybe LED
, time :: Delay
}
opening :: IO Dev -> State -> IO State
opening o s = do
withDev_ (dev s) closeBlink1
d <- o
return s{ dev = d }
close :: State -> IO State
close = opening (return NoDev)
run :: (forall b . Blink1 b => b -> IO a) -> (a -> IO ()) -> State -> IO State
run f g s = withDev (dev s) f >>= g >> return s
options :: [Opt.OptDescr (State -> IO State)]
options =
[ Opt.Option "D" ["dummy"]
(Opt.NoArg $ opening (return $ DummyDev $ openDummy True))
"open a dummy blink(1) device"
#ifdef USE_LINUX
, Opt.Option "L" ["hidraw"]
(Opt.OptArg (\f -> opening (LinuxRawDev <$> maybe openRawHID openRawDev f)) "DEVICE")
"open a linux hidraw device"
#endif
#ifdef USE_USB
, Opt.Option "U" ["usb"]
(Opt.NoArg $ opening (USBDev <$> openUSB))
"open a libusb device"
#endif
, Opt.Option "V" ["version"]
(Opt.NoArg $ run getVersion (\(a, b) -> putStrLn [a,'.',b]))
"get the version number"
, Opt.Option "T" ["test"]
(Opt.NoArg $ run testBlink1 (putStrLn . either (\r -> "unexpected: " ++ show r) (\r -> if r then "pass" else "fail")))
"issue the test command"
, Opt.Option "S" ["serial"]
(Opt.NoArg $ run getSerialNum print)
"get the serial number (only works on mk1)"
, Opt.Option "l" ["led"]
(Opt.OptArg (\l s -> return s{ led = fmap (toEnum . read) l }) "INDEX")
"apply the next actions only to LED INDEX [all]"
, Opt.Option "g" ["get"]
(Opt.OptArg (\l s -> do
withDev (dev s) getColor2 (fromMaybe 0 (fmap (toEnum . read) l <|> led s)) >>= print
return s) "LED")
"get the current color of the LED"
, Opt.Option "s" ["set"]
(Opt.ReqArg (\c s -> do
withDev (dev s) setColor2 (led s) (read c)
return s) "RGB")
"set the color immediately"
, Opt.Option "t" ["time"]
(Opt.ReqArg (\t s -> return s{ time = read t }) "DELAY")
"set the delay/fade time for the next actions"
, Opt.Option "f" ["fade"]
(Opt.ReqArg (\c s -> do
withDev (dev s) fadeToColor2 (led s) (time s) (read c)
return s) "RGB")
"fade to color over DELAY"
]
usage :: String
usage = Opt.usageInfo "Usage: blink1 [OPTIONS]" options
main :: IO State
main = do
args <- getArgs
r <- case Opt.getOpt Opt.RequireOrder options args of
(r, [], []) -> return r
(_, _, err) -> do
mapM_ putStrLn err
putStrLn usage
exitFailure
foldM (flip ($)) (State NoDev Nothing 0) r >>= close