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