add an OnoffCommand device
[~helmut/onoff.git] / onoff / command.py
1 import logging
2
3 from gi.repository import GObject
4
5 from .common import OnoffDevice, ST_ACTIVE, ST_TRANSITION
6
7 logger = logging.getLogger("onoff.command")
8
9 class OnoffCommand(OnoffDevice):
10     def __init__(self, oncommand, offcommand):
11         OnoffDevice.__init__(self)
12         self.oncommand = oncommand
13         self.offcommand = offcommand
14         self.desired_state = 0 # bit mask of just ST_ACTIVE
15         self.pid = None
16         self.watch = None
17         self.target_state = 0 # state the last command targeted
18
19     @property
20     def state(self):
21         if self.pid is not None:
22             return self.desired_state | ST_TRANSITION
23         return self.desired_state
24
25     def transition(self, state):
26         command, name = [(self.offcommand, "offcommand"),
27                          (self.oncommand, "oncommand")][state]
28         self.target_state = state
29         logger.info("invoking %s %s", name, " ".join(command))
30         ret = GObject.spawn_async(command, flags=GObject.SPAWN_SEARCH_PATH | GObject.SPAWN_DO_NOT_REAP_CHILD)
31         self.pid = ret[0]
32         assert self.pid
33         logger.debug("started %s as pid %d", name, self.pid)
34         self.watch = GObject.child_watch_add(self.pid, self.process_died)
35         self.changestate(self.state)
36
37     def process_died(self, pid, condition):
38         assert self.pid == pid
39         assert self.watch is not None
40         self.watch = None
41         self.pid = None
42         logger.info("command %d targeting %d completed", pid, self.target_state)
43         if self.desired_state == self.target_state:
44             self.changestate(self.state)
45         else:
46             logger.info("desired state changed to %d during invocation " +
47                         "targeting %d", self.desired_state, self.target_state)
48             self.transition(self.desired_state)
49
50     def activate(self):
51         if self.desired_state != ST_ACTIVE:
52             self.desired_state = ST_ACTIVE
53             if self.pid is None:
54                 self.transition(self.desired_state)
55
56     def deactivate(self):
57         if self.desired_state != 0:
58             self.desired_state = 0
59             if self.pid is None:
60                 self.transition(self.desired_state)