use the enum module to represent states
[~helmut/onoff.git] / README.md
index 86d9356..cd41bc6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,15 +1,19 @@
-onoff
-=====
-
-Activate devices on demand and turn them off after usage automatically. This is
-what onoff is for. From the perspective of onoff a device has four states. It
-may be active, inactive of transitioning to one of those two states. Devices
-are exported via a dbus interface (currently versioned 0 to declare API
-instability). A client can activate a device for a particular duration or
-activate it until a filedescriptor is closed. The latter allows inheriting
-filedescriptors to other processes and getting a signal once they terminate.
-Consider the following use cases:
+About onoff
+===========
 
+Onoff is a dbus service to manage physical or virtual devices that have two
+distinct states. A managed device can be inactive or active or transitioning
+from one of those two states to the other. A dbus client can request a device
+to be activated for a specific amount of time or until a process dies. The
+service is to ensure that the requested device resides in the active state as
+long as at least one client requests the activation.
+
+Currently onoff is mostly a library of common functionality for implementing
+such a service or client. Code for configuring or starting the service is
+notably absent.
+
+Use cases
+=========
  * When printing a page, first switch on the printer and after printing, switch
    off the printer. When printing two jobs, do not turn it of between the jobs.
    This can be achieved with [sispmctl][] or [slugpower][] for example.
@@ -26,19 +30,102 @@ Consider the following use cases:
    services. This is another inhibiting strategy. It is already being solved by
    [systemd-inhibit][] though.
 
+Dependencies
+============
+
+ * At least Python version 3.5 (e.g. Debian stretch).
+ * python3-dbus
+ * python3-gi
+ * python3-mpd if you intend to use `mpd_watcher.py`
+ * sispmctl if you intend to control a power socket with this tool
+
+API
+===
+
+On the python API side there is the base class OnoffDevice. A device must
+implement `activate` and `deactivate` methods to initiate state transitions. It
+also must provide a `state` attribute to query the current device state. When
+the devices changes its state, it must invoke the `changestate` method to
+signal interested parties.
+
+There currently are four implementations.
+
+ *  An `OnoffCommand` can be used if the state of a device can be changed by
+    invoking a command for either direction.  The transition is considered
+    complete when the respective command terminates.  This device is used to
+    implement a power socket with [sispmctl][] in
+    `onoff.tools.sispmctl_device`.
+
+ *  An `OnoffProcess` can be used if the device is considered active as long as
+    the process runs. Deactivation is achieved by killing the process. For
+    example [redshift][] can be started in this way.
+
+        dev = onoff.processs.OnoffProcess(["redshift"])
+
+    To account for the time it takes the process to start up, it can be wrapped
+    in a `ThrottledDevice` (see below).
+
+ *  An `InvertedDevice` can be used to swap the activation states. It can be
+    used to activate a device all the time and inhibit activation whenever the
+    inverted device is activated. So an `InvertedDevice` wraps an existing
+    `OnoffDevice`.
+
+ *  A `ThrottledDevice` can be used to add artificial delays to the transition
+    periods of the activation and deactivation. If a device takes noticeable
+    time to settle even after it has been signaled as active, the activation
+    signal can be delayed. Similarly if a device should not be switched too
+    often, deactivation can be delayed. If the device is reactivated shortly
+    after it has been released, it simply keeps being active.
+
+Once you have an `OnoffDevice` you can pass a dbus bus connection, a name and
+the device to the `OnoffControl` constructor. The created object exports the
+device via under the given name on the given bus.
+
+On the dbus side the API is versioned. The current API is version as
+`de.subdivi.onoff0` with a number 0 to declare an unstable API. Each device is
+represented as an object. For each object there is a `changestate` signal. A
+client can connect to this signal to learn when a device becomes usable. Each
+object also provides two methods for requesting activation. The `activatetime`
+method takes a number of seconds and activates the device for the specified
+number of seconds. It returns the state after activation, which most likely is
+the transition state to active. If the interval is too short a device may never
+reach the active state. The other method is `activatefd`. It takes no
+parameters and returns a file descriptor in addition to the current state. The
+device is considered activated until the client closes the returned file
+descriptor. The intended use here is to duplicate the file descriptor to a high
+number and inherit it to an external process such as a video player. When that
+process terminates, the file descriptor is automatically closed and the device
+released.
+
 Configuration
 =============
-Activation with sispmctl using the first socket of the first usb device
-delaying of deactivation by 3 seconds:
 
-    dev = onoff.command.OnoffCommand(["sispmctl", "-o", "1"],
-                                     ["sispmctl", "-f", "1"])
-    dev = onoff.common.ThrottledDevice(dev, 3)
+To use onoff you need two components. First of all a dbus service is needed.
+Since there is no configuration language for onoff yet, you are supposed to
+write a python script and plug the pieces together yourself. An example service
+can be found in `dbus_service.py`. In such a script you configure all available
+devices. It is up to you to decide whether to use the system bus or the session
+bus. For a power socket the system bus is suggested and for tools like redshift
+the session bus is suggested. If you intend to use the system bus, you will
+need to configure dbus to allow interaction. An example dbus policy can be
+found in `dbus_policy.conf`. It is also up to you to start the service script.
+In theory it should be possible to use dbus activation to activate it on demand
+(unless you use an `InvertedDevice`). If you have multiple devices, that
+require different permission, you can spread them to multiple services. In this
+case you can no longer use the common busname `de.subdivi.onoff0` for all
+services. You need to override it.
+
+Once you have a service running, you can use the `dbus_client.py` to invoke the
+dbus methods. Of course you can also use the dbus API directly. For instance to
+inhibit the example `redshift` device exported from the `dbus_service.py`
+example as long as you watch a video, you can use the following invocation.
 
-When inhibiting redshift, run redshift from the dbus service:
+    ./dbus_client.py --device redshift mplayer -fs cool_video.webm
 
-    dev = onoff.processs.OnoffProcess(["redshift"], 3)
-    dev = onoff.common.InvertedDevice(dev)
+Another example client can be found in `mpd_watcher.py`. It monitors the local
+[mpd][] for the playing state and activates the device given on command line
+whenever mpd plays music. As such it also is a background process and it is
+your task to start it.
 
 [sispmctl]: http://sispmctl.sf.net
 [slugpower]: http://chezphil.org/slugpower/