drop support for Python 2.x
[~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  * At least Python version 3.5 (e.g. Debian stretch).
37  * python3-dbus
38  * python3-gi
39  * python3-mpd if you intend to use `mpd_watcher.py`
40  * sispmctl if you intend to control a power socket with this tool
41
42 API
43 ===
44
45 On the python API side there is the base class OnoffDevice. A device must
46 implement `activate` and `deactivate` methods to initiate state transitions. It
47 also must provide a `state` attribute to query the current device state. When
48 the devices changes its state, it must invoke the `changestate` method to
49 signal interested parties.
50
51 There currently are four implementations.
52
53  *  An `OnoffCommand` can be used if the state of a device can be changed by
54     invoking a command for either direction.  The transition is considered
55     complete when the respective command terminates.  This device is used to
56     implement a power socket with [sispmctl][] in
57     `onoff.tools.sispmctl_device`.
58
59  *  An `OnoffProcess` can be used if the device is considered active as long as
60     the process runs. Deactivation is achieved by killing the process. For
61     example [redshift][] can be started in this way.
62
63         dev = onoff.processs.OnoffProcess(["redshift"])
64
65     To account for the time it takes the process to start up, it can be wrapped
66     in a `ThrottledDevice` (see below).
67
68  *  An `InvertedDevice` can be used to swap the activation states. It can be
69     used to activate a device all the time and inhibit activation whenever the
70     inverted device is activated. So an `InvertedDevice` wraps an existing
71     `OnoffDevice`.
72
73  *  A `ThrottledDevice` can be used to add artificial delays to the transition
74     periods of the activation and deactivation. If a device takes noticeable
75     time to settle even after it has been signaled as active, the activation
76     signal can be delayed. Similarly if a device should not be switched too
77     often, deactivation can be delayed. If the device is reactivated shortly
78     after it has been released, it simply keeps being active.
79
80 Once you have an `OnoffDevice` you can pass a dbus bus connection, a name and
81 the device to the `OnoffControl` constructor. The created object exports the
82 device via under the given name on the given bus.
83
84 On the dbus side the API is versioned. The current API is version as
85 `de.subdivi.onoff0` with a number 0 to declare an unstable API. Each device is
86 represented as an object. For each object there is a `changestate` signal. A
87 client can connect to this signal to learn when a device becomes usable. Each
88 object also provides two methods for requesting activation. The `activatetime`
89 method takes a number of seconds and activates the device for the specified
90 number of seconds. It returns the state after activation, which most likely is
91 the transition state to active. If the interval is too short a device may never
92 reach the active state. The other method is `activatefd`. It takes no
93 parameters and returns a file descriptor in addition to the current state. The
94 device is considered activated until the client closes the returned file
95 descriptor. The intended use here is to duplicate the file descriptor to a high
96 number and inherit it to an external process such as a video player. When that
97 process terminates, the file descriptor is automatically closed and the device
98 released.
99
100 Configuration
101 =============
102
103 To use onoff you need two components. First of all a dbus service is needed.
104 Since there is no configuration language for onoff yet, you are supposed to
105 write a python script and plug the pieces together yourself. An example service
106 can be found in `dbus_service.py`. In such a script you configure all available
107 devices. It is up to you to decide whether to use the system bus or the session
108 bus. For a power socket the system bus is suggested and for tools like redshift
109 the session bus is suggested. If you intend to use the system bus, you will
110 need to configure dbus to allow interaction. An example dbus policy can be
111 found in `dbus_policy.conf`. It is also up to you to start the service script.
112 In theory it should be possible to use dbus activation to activate it on demand
113 (unless you use an `InvertedDevice`). If you have multiple devices, that
114 require different permission, you can spread them to multiple services. In this
115 case you can no longer use the common busname `de.subdivi.onoff0` for all
116 services. You need to override it.
117
118 Once you have a service running, you can use the `dbus_client.py` to invoke the
119 dbus methods. Of course you can also use the dbus API directly. For instance to
120 inhibit the example `redshift` device exported from the `dbus_service.py`
121 example as long as you watch a video, you can use the following invocation.
122
123     ./dbus_client.py --device redshift mplayer -fs cool_video.webm
124
125 Another example client can be found in `mpd_watcher.py`. It monitors the local
126 [mpd][] for the playing state and activates the device given on command line
127 whenever mpd plays music. As such it also is a background process and it is
128 your task to start it.
129
130 [sispmctl]: http://sispmctl.sf.net
131 [slugpower]: http://chezphil.org/slugpower/
132 [redshift]: http://jonls.dk/redshift/
133 [mpd]: http://www.musicpd.org
134 [systemd-inhibit]: http://www.freedesktop.org/software/systemd/man/systemd-inhibit.html