89acc3a1d4ac4676f3246e57bdbc630744b40c68
[~helmut/onoff.git] / dbus_service.py
1 #!/usr/bin/env python
2
3 import logging
4 import socket
5
6 import dbus
7 import dbus.service
8 from dbus.mainloop.glib import DBusGMainLoop
9 from gi.repository import GObject
10
11 import onoff.common
12 import onoff.process
13
14 logger = logging.getLogger("dbus_service")
15
16 def dbus_socket_pair():
17     """Create a socket pair where the latter end is suitable for dbus.
18     @rtype: (socket, dbus.types.UnixFd)
19     """
20     s1, s2 = socket.socketpair()
21     s3 = dbus.types.UnixFd(s2)
22     s2.close()
23     return s1, s3
24
25 class OnoffControl(dbus.service.Object):
26     domain = "de.subdivi.onoff0"
27     path = "/de/subdivi/onoff0"
28
29     def __init__(self, bus, name, device):
30         busname = dbus.service.BusName(self.domain, bus=bus)
31         dbus.service.Object.__init__(self, busname, "%s/%s" % (self.path, name))
32         self.device = device
33         device.notify.add(self.changestate)
34         self.usecount = 0
35
36     @dbus.service.signal(domain, signature="q")
37     def changestate(self, st):
38         logger.debug("emitting state %d", st)
39
40     @dbus.service.method(domain, out_signature="q")
41     def state(self):
42         return self.device.state
43
44     @dbus.service.method(domain, in_signature="q", out_signature="q")
45     def activatetime(self, duration):
46         logger.info("activatetime %d", duration)
47         GObject.timeout_add(duration * 1000, self.unuse)
48         return self.use()
49
50     @dbus.service.method(domain, in_signature="q", out_signature="qh")
51     def activatefd(self, duration):
52         logger.info("activatefd duration %d", duration)
53         notifyfd, retfd = dbus_socket_pair()
54         def callback(fd, _):
55             logger.info("fd %d completed", fd.fileno())
56             fd.close()
57             GObject.timeout_add(duration * 1000, self.unuse)
58             return False
59         GObject.io_add_watch(notifyfd, GObject.IO_HUP|GObject.IO_ERR, callback)
60         return (self.use(), retfd)
61
62     def use(self):
63         self.usecount += 1
64         if self.usecount <= 1:
65             self.device.activate()
66         return self.device.state
67
68     def unuse(self):
69         self.usecount -= 1
70         if not self.usecount:
71             self.device.deactivate()
72         else:
73             logger.debug("%d users left", self.usecount)
74         return False
75
76
77 def main():
78     logging.basicConfig()
79     logging.getLogger().setLevel(logging.DEBUG)
80     DBusGMainLoop(set_as_default=True)
81     bus = dbus.SessionBus()
82     dev = onoff.process.OnoffProcess(["redshift"], 3)
83     dev = onoff.common.InvertedDevice(dev)
84     OnoffControl(bus, "redshift", dev)
85     GObject.MainLoop().run()
86
87 if __name__ == "__main__":
88     main()