ThrottledDevice: speed up quick reactivation
[~helmut/onoff.git] / README.md
1 About onoff
2 ===========
3
4 Onoff is a dbus service to manage physical or virtual devices that have two
5 distinct states. A managed device can be inactive or active or transitioning
6 from one of those two states to the other. A dbus client can request a device
7 to be activated for a specific amount of time or until a process dies. The
8 service is to ensure that the requested device resides in the active state as
9 long as at least one client requests the activation.
10
11 Currently onoff is mostly a library of common functionality for implementing
12 such a service or client. Code for configuring or starting the service is
13 notably absent.
14
15 Use cases
16 =========
17  * When printing a page, first switch on the printer and after printing, switch
18    off the printer. When printing two jobs, do not turn it of between the jobs.
19    This can be achieved with [sispmctl][] or [slugpower][] for example.
20  * Similarly when scanning, turn on the scanner.
21  * Using [redshift][] is a nice thing in the evening. Occasionally you may need
22    clear colours though. Videos tend to look bad with redshift activated. So
23    consider automatically inhibiting redshift during video playback.
24  * Monitor the local [mpd][]. Whenever it plays music switch on the sound
25    device and turn it of when music stops. Also turn on sound when watching a
26    video of course.
27  * The inhibitor concept can also be applied to [mpd][] itself. When your phone
28    rings, stop mpd.
29  * When you suspend the whole machine, clients should disconnect from remote
30    services. This is another inhibiting strategy. It is already being solved by
31    [systemd-inhibit][] though.
32
33 Dependencies
34 ============
35
36  * python version 2.6 or 2.7. In 3.x `GObject.spawn_async` is
37    [broken](http://bugs.debian.org/712537).
38  * python-dbus
39  * python-gi
40  * python-argparse if your python version is less than 2.7
41  * python-mpd if you intend to use `mpd_watcher.py`
42  * sispmctl if you intend to control a power socket with this tool
43
44 API
45 ===
46
47 On the python API side there is the base class OnoffDevice. A device must
48 implement `activate` and `deactivate` methods to initiate state transitions. It
49 also must provide a `state` attribute to query the current device state. When
50 the devices changes its state, it must invoke the `changestate` method to
51 signal interested parties.
52
53 There currently are four implementations.
54
55  *  An `OnoffCommand` can be used if the state of a device can be changed by
56     invoking a command for either direction.  The transition is considered
57     complete when the respective command terminates.  This device is used to
58     implement a power socket with [sispmctl][] in
59     `onoff.tools.sispmctl_device`.
60
61  *  An `OnoffProcess` can be used if the device is considered active as long as
62     the process runs. Deactivation is achieved by killing the process. For
63     example the [redshift][] can be started in this way.
64
65         dev = onoff.processs.OnoffProcess(["redshift"], 3)
66
67  *  An `InvertedDevice` can be used to swap the activation states. It can be
68     used to activate a device all the time and inhibit activation whenever the
69     inverted device is activated. So an `InvertedDevice` wraps an existing
70     `OnoffDevice`.
71
72  *  A `ThrottledDevice` can be used to add artificial delays to the transition
73     periods of the activation and deactivation. If a device takes noticeable
74     time to settle even after it has been signaled as active, the activation
75     signal can be delayed. Similarly if a device should not be switched too
76     often, deactivation can be delayed. If the device is reactivated shortly
77     after it has been released, it simply keeps being active.
78
79 Once you have an `OnoffDevice` you can pass a dbus bus connection, a name and
80 the device to the `OnoffControl` constructor. The created object exports the
81 device via under the given name on the given bus.
82
83 On the dbus side the API is versioned. The current API is version as
84 `de.subdivi.onoff0` with a number 0 to declare an unstable API. Each device is
85 represented as an object. For each object there is a `changestate` signal. A
86 client can connect to this signal to learn when a device becomes usable. Each
87 object also provides two methods for requesting activation. The `activatetime`
88 method takes a number of seconds and activates the device for the specified
89 number of seconds. It returns the state after activation, which most likely is
90 the transition state to active. If the interval is too short a device may never
91 reach the active state. The other method is `activatefd`. It takes no
92 parameters and returns a file descriptor in addition to the current state. The
93 device is considered activated until the client closes the returned file
94 descriptor. The intended use here is to duplicate the file descriptor to a high
95 number and inherit it to an external process such as a video player. When that
96 process terminates, the file descriptor is automatically closed and the device
97 released.
98
99 Configuration
100 =============
101
102 To use onoff you need two components. First of all a dbus service is needed.
103 Since there is no configuration language for onoff yet, you are supposed to
104 write a python script and plug the pieces together yourself. An example service
105 can be found in `dbus_service.py`. In such a script you configure all available
106 devices. It is up to you to decide whether to use the system bus or the session
107 bus. For a power socket the system bus is suggested and for tools like redshift
108 the session bus is suggested. If you intend to use the system bus, you will
109 need to configure dbus to allow interaction. An example dbus policy can be
110 found in `dbus_policy.conf`. It is also up to you to start the service script.
111 In theory it should be possible to use dbus activation to activate it on demand
112 (unless you use an `InvertedDevice`). If you have multiple devices, that
113 require different permission, you can spread them to multiple services. In this
114 case you can no longer use the common busname `de.subdivi.onoff0` for all
115 services. You need to override it.
116
117 Once you have a service running, you can use the `dbus_client.py` to invoke the
118 dbus methods. Of course you can also use the dbus API directly. For instance to
119 inhibit the example `redshift` device exported from the `dbus_service.py`
120 example as long as you watch a video, you can use the following invocation.
121
122     ./dbus_client.py --device redshift mplayer -fs cool_video.webm
123
124 Another example client can be found in `mpd_watcher.py`. It monitors the local
125 [mpd][] for the playing state and activates the device given on command line
126 whenever mpd plays music. As such it also is a background process and it is
127 your task to start it.
128
129 [sispmctl]: http://sispmctl.sf.net
130 [slugpower]: http://chezphil.org/slugpower/
131 [redshift]: http://jonls.dk/redshift/
132 [mpd]: http://www.musicpd.org
133 [systemd-inhibit]: http://www.freedesktop.org/software/systemd/man/systemd-inhibit.html