3 from .common import OnoffDevice, ST_ACTIVE, ST_TRANSITION
4 from .gobject import spawn_child
6 logger = logging.getLogger("onoff.command")
8 class OnoffCommand(OnoffDevice):
9 """A device that is enabled and disabled by executing separate commands.
10 The transition period is the duration of the commands.
11 @type pid: int or None
12 @ivar pid: is either None or the pid of a transition command as long as
14 @ivar watch: is either None or a GObject event source id of the
15 callback waiting for the termination of the spawned process.
16 @type desired_state: bool
17 @ivar desired_state: is the state that should be transitioned to
18 @type target_state: bool
19 @ivar target_state: is the state that we are currently transitioning to
21 def __init__(self, oncommand, offcommand):
24 @type oncommand: [str]
25 @param command: an argument vector to be executed for activation.
26 @type offcommand: [str]
27 @param command: an argument vector to be executed for deactivation.
28 @note: For both commands the first element is used as executable and
31 OnoffDevice.__init__(self)
32 self.oncommand = oncommand
33 self.offcommand = offcommand
34 self.desired_state = 0 # bit mask of just ST_ACTIVE
37 self.target_state = 0 # state the last command targeted
41 if self.pid is not None:
42 return self.desired_state | ST_TRANSITION
43 return self.desired_state
45 def transition(self, state):
46 command, name = [(self.offcommand, "offcommand"),
47 (self.oncommand, "oncommand")][state]
48 self.target_state = state
49 logger.info("invoking %s %s", name, " ".join(command))
50 self.pid, self.watch = spawn_child(command, self.process_died)
51 logger.debug("started %s as pid %d", name, self.pid)
52 self.changestate(self.state)
54 def process_died(self, pid, condition):
55 assert self.pid == pid
56 assert self.watch is not None
59 logger.info("command %d targeting %d completed", pid, self.target_state)
60 if self.desired_state == self.target_state:
61 self.changestate(self.state)
63 logger.info("desired state changed to %d during invocation " +
64 "targeting %d", self.desired_state, self.target_state)
65 self.transition(self.desired_state)
68 if self.desired_state != ST_ACTIVE:
69 self.desired_state = ST_ACTIVE
71 self.transition(self.desired_state)
74 if self.desired_state != 0:
75 self.desired_state = 0
77 self.transition(self.desired_state)