add an OnoffCommand device
authorHelmut Grohne <helmut@subdivi.de>
Mon, 17 Jun 2013 10:28:21 +0000 (12:28 +0200)
committerHelmut Grohne <helmut@subdivi.de>
Mon, 17 Jun 2013 10:28:21 +0000 (12:28 +0200)
onoff/command.py [new file with mode: 0644]

diff --git a/onoff/command.py b/onoff/command.py
new file mode 100644 (file)
index 0000000..c3dd4a6
--- /dev/null
@@ -0,0 +1,60 @@
+import logging
+
+from gi.repository import GObject
+
+from .common import OnoffDevice, ST_ACTIVE, ST_TRANSITION
+
+logger = logging.getLogger("onoff.command")
+
+class OnoffCommand(OnoffDevice):
+    def __init__(self, oncommand, offcommand):
+        OnoffDevice.__init__(self)
+        self.oncommand = oncommand
+        self.offcommand = offcommand
+        self.desired_state = 0 # bit mask of just ST_ACTIVE
+        self.pid = None
+        self.watch = None
+        self.target_state = 0 # state the last command targeted
+
+    @property
+    def state(self):
+        if self.pid is not None:
+            return self.desired_state | ST_TRANSITION
+        return self.desired_state
+
+    def transition(self, state):
+        command, name = [(self.offcommand, "offcommand"),
+                         (self.oncommand, "oncommand")][state]
+        self.target_state = state
+        logger.info("invoking %s %s", name, " ".join(command))
+        ret = GObject.spawn_async(command, flags=GObject.SPAWN_SEARCH_PATH | GObject.SPAWN_DO_NOT_REAP_CHILD)
+        self.pid = ret[0]
+        assert self.pid
+        logger.debug("started %s as pid %d", name, self.pid)
+        self.watch = GObject.child_watch_add(self.pid, self.process_died)
+        self.changestate(self.state)
+
+    def process_died(self, pid, condition):
+        assert self.pid == pid
+        assert self.watch is not None
+        self.watch = None
+        self.pid = None
+        logger.info("command %d targeting %d completed", pid, self.target_state)
+        if self.desired_state == self.target_state:
+            self.changestate(self.state)
+        else:
+            logger.info("desired state changed to %d during invocation " +
+                        "targeting %d", self.desired_state, self.target_state)
+            self.transition(self.desired_state)
+
+    def activate(self):
+        if self.desired_state != ST_ACTIVE:
+            self.desired_state = ST_ACTIVE
+            if self.pid is None:
+                self.transition(self.desired_state)
+
+    def deactivate(self):
+        if self.desired_state != 0:
+            self.desired_state = 0
+            if self.pid is None:
+                self.transition(self.desired_state)