use the enum module to represent states
[~helmut/onoff.git] / onoff / command.py
1 import logging
2
3 from .common import OnoffDevice, OnoffState
4 from .gobject import spawn_child
5
6 logger = logging.getLogger("onoff.command")
7
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
13             it lives.
14     @type desired_state: bool
15     @ivar desired_state: is the state that should be transitioned to
16     @type target_state: bool
17     @ivar target_state: is the state that we are currently transitioning to
18     """
19     def __init__(self, oncommand, offcommand):
20         """
21
22         @type oncommand: [str]
23         @param oncommand: an argument vector to be executed for activation.
24         @type offcommand: [str]
25         @param offcommand: an argument vector to be executed for deactivation.
26         @note: For both commands the first element is used as executable and
27                 looked up in $PATH.
28         """
29         OnoffDevice.__init__(self)
30         self.oncommand = oncommand
31         self.offcommand = offcommand
32         self.desired_state = OnoffState.inactive
33         self.pid = None
34         self.target_state = OnoffState.inactive  # last command target state
35
36     @property
37     def state(self):
38         if self.pid is not None:
39             return self.desired_state | OnoffState.transition
40         return self.desired_state
41
42     def transition(self, state):
43         command, name = [(self.offcommand, "offcommand"),
44                          (self.oncommand, "oncommand")][state]
45         self.target_state = state
46         logger.info("invoking %s %s", name, " ".join(command))
47         self.pid = spawn_child(command, self.process_died)
48         logger.debug("started %s as pid %d", name, self.pid)
49         self.changestate(self.state)
50
51     def process_died(self, pid, condition):
52         assert self.pid == pid
53         self.pid = None
54         logger.info("command %d targeting %s completed", pid,
55                     self.target_state.name)
56         if self.desired_state == self.target_state:
57             self.changestate(self.state)
58         else:
59             logger.info("desired state changed to %s during invocation " +
60                         "targeting %s", self.desired_state.name,
61                         self.target_state.name)
62             self.transition(self.desired_state)
63
64     def activate(self):
65         if self.desired_state != OnoffState.active:
66             self.desired_state = OnoffState.active
67             if self.pid is None:
68                 self.transition(self.desired_state)
69
70     def deactivate(self):
71         if self.desired_state != OnoffState.inactive:
72             self.desired_state = OnoffState.inactive
73             if self.pid is None:
74                 self.transition(self.desired_state)