liamw9534/mopidy — pull request #2 by liamw9534 (2014-09-12 10:16:37+00:00)
Title: Service framework

@adamcik @jodal - thanks for agreeing to review this framework for services. Please refer to https://github.com/mopidy/mopidy/issues/778 for details/background and bear in mind the following points:

* A service is just an interface which can be inherited by either a backend or frontend so it can be retrospectively applied to existing extensions or used as part of new extensions.
* All extensions that inherit from ``mopidy.service.Service`` will be registered into a service registry which is maintained by ``Core``. ``Core`` also provides a ``core.service.ServiceController``. The ``core.service.ServiceController`` is the principal means by which remote configuration can be achieved via HTTP/JSON RPC.
* The class ``mopidy.service.Service`` defines the required methods, which includes the ability to get and set properties dynamically. The underlying implementation is not exposed in the API so it doesn't matter if it's a file, dbus or whatever.
* The ``name`` string attribute defines a unique name for the service. It must be set during ``__init__()`` of the service object. Any name clashes will be picked-up during the registration process.
* The ``public`` boolean attribute allows a service's API to be fully exported through HTTP JSON/RPC. This is handled automatically as part of the service registration process.
* Note: As new services are started the HTTP frontend is notified and re-syncs its API factory via Tornado - this is important since extensions can be started in any order and you can't assume HTTP will be started last.
* If ``public`` is ``True``, then the classes' API calls are visible via HTTP/JSON RPC using the ``mopidy.servicename.method(args)`` calling convention.
* If ``public`` is ``False`` then only the methods from ``Service`` shall be callable through the ``mopidy.service.method(service_name, args)`` methods. In this case the service name is supplied to ``ServiceController`` to resolve.
* The ``mopidy.service.ServiceListener`` class provides a set of generic events to indicate when a service has started, is stopped or a property is changed. These are expected to be notified where appropriate in the underlying implementation of the service.
* If implementing your own discovery service (e.g., bluetooth) it is likely you will need to add your own implementation-specific events. These can be sent over ``mopidy.service.ServiceListener`` and will be relayed through the websocket. I would suggest we adopt some event naming conventions to avoid confusion.

You can find some example frontend services I have written here:

* https://github.com/liamw9534/mopidy-btmanager
* https://github.com/liamw9534/mopidy-pulseaudio
* https://github.com/liamw9534/mopidy-connman

They all implement dynamic configuration property setting in a very simplistic manner which does not allow for persistent storage of values. This is the major missing part which I didn't wish to embark upon since it probably needs a bit more thought to make it backwardly compatible with the existing configuration mechanism.

@jodal I noticed you had written https://github.com/mopidy/mopidy-alsamixer extension recently. I think this would be a good candidate for trying to turn into a frontend service to get a feel for it.