* More consistent management of responses
* Better per-plugin/per-backend logging management
This commit is contained in:
parent
91cf4478d0
commit
491c2cd571
9 changed files with 118 additions and 92 deletions
13
README.md
13
README.md
|
@ -112,6 +112,8 @@ The `__init__.py` will look like this:
|
||||||
```python
|
```python
|
||||||
import batman
|
import batman
|
||||||
|
|
||||||
|
from platypush.response import Response
|
||||||
|
|
||||||
from .. import LightPlugin
|
from .. import LightPlugin
|
||||||
|
|
||||||
class LightBatsignalPlugin(LightPlugin):
|
class LightBatsignalPlugin(LightPlugin):
|
||||||
|
@ -123,22 +125,21 @@ class LightBatsignalPlugin(LightPlugin):
|
||||||
self.batsignal.notify_robin()
|
self.batsignal.notify_robin()
|
||||||
|
|
||||||
self.batsignal.on()
|
self.batsignal.on()
|
||||||
|
return Response('ok')
|
||||||
|
|
||||||
def off(self):
|
def off(self):
|
||||||
self.batsignal.off()
|
self.batsignal.off()
|
||||||
|
return Response('ok')
|
||||||
|
|
||||||
def toggle(self):
|
def toggle(self):
|
||||||
self.batsignal.toggle()
|
self.batsignal.toggle()
|
||||||
|
return Response('ok')
|
||||||
|
|
||||||
def status(self):
|
|
||||||
return [self.batsignal.status().stdout, self.batsignal.status().stderr]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
6. It's a good practice to define a `status` method in your plugin, which returns a 2-items list like `[output, error]`.
|
6. Rebuild and reinstall `platypush` if required and relaunch it.
|
||||||
|
|
||||||
7. Rebuild and reinstall `platypush` if required and relaunch it.
|
7. Test your new plugin by sending some bullets to it:
|
||||||
|
|
||||||
8. Test your new plugin by sending some bullets to it:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pusher --target your_pc --action light.batsignal.on --urgent 1
|
pusher --target your_pc --action light.batsignal.on --urgent 1
|
||||||
|
|
|
@ -66,21 +66,11 @@ def _exec_func(args, retry=True):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ret = plugin.run(method=method_name, **args)
|
response = plugin.run(method=method_name, **args)
|
||||||
out = None
|
if response and response.is_error():
|
||||||
err = None
|
logging.warn('Response processed with errors: {}'.format(response))
|
||||||
|
else:
|
||||||
if isinstance(ret, list):
|
logging.info('Processed response: {}'.format(response))
|
||||||
out = ret[0]
|
|
||||||
err = ret[1] if len(ret) > 1 else None
|
|
||||||
elif ret is not None:
|
|
||||||
out = ret
|
|
||||||
|
|
||||||
if out:
|
|
||||||
logging.info('Command output: {}'.format(out))
|
|
||||||
|
|
||||||
if err:
|
|
||||||
logging.warn('Command error: {}'.format(err))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
if retry:
|
if retry:
|
||||||
|
@ -122,6 +112,14 @@ def parse_config_file(config_file=None):
|
||||||
if 'disabled' in config[section] and config[section]['disabled']:
|
if 'disabled' in config[section] and config[section]['disabled']:
|
||||||
del config[section]
|
del config[section]
|
||||||
|
|
||||||
|
if 'logging' not in config:
|
||||||
|
config['logging'] = logging.INFO
|
||||||
|
else:
|
||||||
|
config['logging'] = getattr(logging, config['logging'].upper())
|
||||||
|
|
||||||
|
if 'device_id' not in config:
|
||||||
|
config['device_id'] = socket.gethostname()
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,13 +159,18 @@ def get_default_pusher_backend(config):
|
||||||
return backends[0] if backends else None
|
return backends[0] if backends else None
|
||||||
|
|
||||||
|
|
||||||
|
def get_logging_level():
|
||||||
|
global config
|
||||||
|
return config['logging']
|
||||||
|
|
||||||
|
|
||||||
def get_device_id():
|
def get_device_id():
|
||||||
global config
|
global config
|
||||||
return config['device_id'] if 'device_id' in config else None
|
return config['device_id'] if 'device_id' in config else None
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
DEBUG = False
|
debug = False
|
||||||
config_file = None
|
config_file = None
|
||||||
|
|
||||||
plugins_dir = os.path.join(wrkdir, 'plugins')
|
plugins_dir = os.path.join(wrkdir, 'plugins')
|
||||||
|
@ -178,7 +181,7 @@ def main():
|
||||||
if opt == '-c':
|
if opt == '-c':
|
||||||
config_file = arg
|
config_file = arg
|
||||||
if opt == '-v':
|
if opt == '-v':
|
||||||
DEBUG = True
|
debug = True
|
||||||
elif opt == '-h':
|
elif opt == '-h':
|
||||||
print('''
|
print('''
|
||||||
Usage: {} [-v] [-h] [-c <config_file>]
|
Usage: {} [-v] [-h] [-c <config_file>]
|
||||||
|
@ -189,19 +192,10 @@ Usage: {} [-v] [-h] [-c <config_file>]
|
||||||
return
|
return
|
||||||
|
|
||||||
config = parse_config_file(config_file)
|
config = parse_config_file(config_file)
|
||||||
|
if debug: config['logging'] = logging.DEBUG
|
||||||
logging.info('Configuration dump: {}'.format(config))
|
logging.info('Configuration dump: {}'.format(config))
|
||||||
|
|
||||||
if 'device_id' not in config:
|
logging.basicConfig(level=get_logging_level())
|
||||||
config['device_id'] = socket.gethostname()
|
|
||||||
|
|
||||||
if 'debug' in config:
|
|
||||||
DEBUG = config['debug']
|
|
||||||
|
|
||||||
if DEBUG:
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
websocket.enableTrace(True)
|
|
||||||
else:
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
|
|
||||||
mq = Queue()
|
mq = Queue()
|
||||||
backends = get_backends(config)
|
backends = get_backends(config)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import logging
|
import logging
|
||||||
import socket
|
|
||||||
import platypush
|
import platypush
|
||||||
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
@ -30,7 +29,7 @@ class Backend(Thread):
|
||||||
self.on_init = on_init
|
self.on_init = on_init
|
||||||
self.on_close = on_close
|
self.on_close = on_close
|
||||||
self.on_error = on_error
|
self.on_error = on_error
|
||||||
self.device_id = platypush.get_device_id() or socket.gethostname()
|
self.device_id = platypush.get_device_id()
|
||||||
|
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
logging.basicConfig(level=logging.INFO
|
logging.basicConfig(level=logging.INFO
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from platypush.response import Response
|
||||||
|
|
||||||
class Plugin(object):
|
class Plugin(object):
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
@ -16,12 +19,18 @@ class Plugin(object):
|
||||||
|
|
||||||
def _set_logging(self):
|
def _set_logging(self):
|
||||||
if 'logging' in self.config:
|
if 'logging' in self.config:
|
||||||
logging.basicConfig(level=getattr(logging, self.config['logging']))
|
self._logging = self.config.pop('logging')
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(level=logging.INFO)
|
self._logging = logging.INFO
|
||||||
|
|
||||||
|
logging.basicConfig(level=self._logging)
|
||||||
|
|
||||||
def run(self, method, *args, **kwargs):
|
def run(self, method, *args, **kwargs):
|
||||||
res = getattr(self, method)(*args, **kwargs)
|
try:
|
||||||
|
res = getattr(self, method)(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
res = Response(output=None, errors=[e, traceback.format_exc()])
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -2,6 +2,7 @@ import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from phue import Bridge
|
from phue import Bridge
|
||||||
|
from platypush.response import Response
|
||||||
|
|
||||||
from .. import LightPlugin
|
from .. import LightPlugin
|
||||||
|
|
||||||
|
@ -57,19 +58,13 @@ class LightHuePlugin(LightPlugin):
|
||||||
scenes = [s.name for s in self.bridge.scenes]
|
scenes = [s.name for s in self.bridge.scenes]
|
||||||
# TODO Expand it with custom scenes specified in config.yaml as in #14
|
# TODO Expand it with custom scenes specified in config.yaml as in #14
|
||||||
|
|
||||||
def _execute(self, attr, *args, **kwargs):
|
def _exec(self, attr, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
self.connect()
|
self.connect()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
|
||||||
# Reset bridge connection
|
# Reset bridge connection
|
||||||
self.bridge = None
|
self.bridge = None
|
||||||
|
raise e
|
||||||
if 'is_retry' not in kwargs:
|
|
||||||
time.sleep(1)
|
|
||||||
self._execute(attr, is_retry=True, *args, **kwargs)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
lights = []; groups = []
|
lights = []; groups = []
|
||||||
if 'lights' in kwargs and kwargs['lights']:
|
if 'lights' in kwargs and kwargs['lights']:
|
||||||
|
@ -93,37 +88,33 @@ class LightHuePlugin(LightPlugin):
|
||||||
elif lights:
|
elif lights:
|
||||||
self.bridge.set_light(lights, attr, *args)
|
self.bridge.set_light(lights, attr, *args)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.exception(e)
|
|
||||||
# Reset bridge connection
|
# Reset bridge connection
|
||||||
self.bridge = None
|
self.bridge = None
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return Response(output='ok')
|
||||||
|
|
||||||
def on(self, lights=[], groups=[]):
|
def on(self, lights=[], groups=[]):
|
||||||
self._execute('on', True, lights=lights, groups=groups)
|
return self._exec('on', True, lights=lights, groups=groups)
|
||||||
|
|
||||||
def off(self, lights=[], groups=[]):
|
def off(self, lights=[], groups=[]):
|
||||||
self._execute('on', False, lights=lights, groups=groups)
|
return self._exec('on', False, lights=lights, groups=groups)
|
||||||
|
|
||||||
def bri(self, value, lights=[], groups=[]):
|
def bri(self, value, lights=[], groups=[]):
|
||||||
self._execute('bri', int(value) % (self.MAX_BRI+1),
|
return self._exec('bri', int(value) % (self.MAX_BRI+1),
|
||||||
lights=lights, groups=groups)
|
lights=lights, groups=groups)
|
||||||
|
|
||||||
def sat(self, value, lights=[], groups=[]):
|
def sat(self, value, lights=[], groups=[]):
|
||||||
self._execute('sat', int(value) % (self.MAX_SAT+1),
|
return self._exec('sat', int(value) % (self.MAX_SAT+1),
|
||||||
lights=lights, groups=groups)
|
lights=lights, groups=groups)
|
||||||
|
|
||||||
def hue(self, value, lights=[], groups=[]):
|
def hue(self, value, lights=[], groups=[]):
|
||||||
self._execute('hue', int(value) % (self.MAX_HUE+1),
|
return self._exec('hue', int(value) % (self.MAX_HUE+1),
|
||||||
lights=lights, groups=groups)
|
|
||||||
|
|
||||||
def hue(self, value, lights=[], groups=[]):
|
|
||||||
self._execute('hue', int(value) % (self.MAX_HUE+1),
|
|
||||||
lights=lights, groups=groups)
|
lights=lights, groups=groups)
|
||||||
|
|
||||||
def scene(self, name, lights=[], groups=[]):
|
def scene(self, name, lights=[], groups=[]):
|
||||||
self._execute('scene', name=name, lights=lights, groups=groups)
|
return self._exec('scene', name=name, lights=lights, groups=groups)
|
||||||
|
|
||||||
def status(self):
|
|
||||||
return ['']
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -1,45 +1,47 @@
|
||||||
import mpd
|
import mpd
|
||||||
|
|
||||||
|
from platypush.response import Response
|
||||||
|
|
||||||
from .. import MusicPlugin
|
from .. import MusicPlugin
|
||||||
|
|
||||||
class MusicMpdPlugin(MusicPlugin):
|
class MusicMpdPlugin(MusicPlugin):
|
||||||
_requires = [
|
|
||||||
'mpd'
|
|
||||||
]
|
|
||||||
|
|
||||||
def _init(self):
|
def _init(self):
|
||||||
self.client = mpd.MPDClient(use_unicode=True)
|
self.client = mpd.MPDClient(use_unicode=True)
|
||||||
self.client.connect(self.config['host'], self.config['port'])
|
self.client.connect(self.config['host'], self.config['port'])
|
||||||
|
|
||||||
|
def _exec(self, method, *args, **kwargs):
|
||||||
|
getattr(self.client, method)(*args, **kwargs)
|
||||||
|
return self.status()
|
||||||
|
|
||||||
def play(self):
|
def play(self):
|
||||||
self.client.play()
|
return self._exec('play')
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
self.client.pause()
|
return self._exec('pause')
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.client.stop()
|
return self._exec('stop')
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
self.client.next()
|
return self._exec('next')
|
||||||
|
|
||||||
def previous(self):
|
def previous(self):
|
||||||
self.client.previous()
|
return self._exec('previous')
|
||||||
|
|
||||||
def setvol(self, vol):
|
def setvol(self, vol):
|
||||||
self.client.setvol(vol)
|
return self._exec('setvol', vol)
|
||||||
|
|
||||||
def add(self, content):
|
def add(self, content):
|
||||||
self.client.add(content)
|
return self._exec('add', content)
|
||||||
|
|
||||||
def playlistadd(self, playlist):
|
def playlistadd(self, playlist):
|
||||||
self.client.playlistadd(playlist)
|
return self._exec('playlistadd', playlist)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.client.clear()
|
return self._exec('clear')
|
||||||
|
|
||||||
def status(self):
|
def status(self):
|
||||||
return self.client.status()
|
return Response(output=self.client.status())
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from platypush.response import Response
|
||||||
|
|
||||||
from .. import Plugin
|
from .. import Plugin
|
||||||
|
|
||||||
class ShellPlugin(Plugin):
|
class ShellPlugin(Plugin):
|
||||||
|
@ -8,11 +10,12 @@ class ShellPlugin(Plugin):
|
||||||
error = None
|
error = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
|
output = subprocess.check_output(
|
||||||
|
cmd, stderr=subprocess.STDOUT, shell=True).decode('utf-8')
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
error = e.output
|
error = e.output.decode('utf-8')
|
||||||
|
|
||||||
return [output, error]
|
return Response(output=output, errors=[error])
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,46 @@
|
||||||
import logging
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
from ouimeaux.environment import Environment, UnknownDevice
|
from ouimeaux.environment import Environment, UnknownDevice
|
||||||
|
from platypush.response import Response
|
||||||
|
|
||||||
from .. import SwitchPlugin
|
from .. import SwitchPlugin
|
||||||
|
|
||||||
class SwitchWemoPlugin(SwitchPlugin):
|
class SwitchWemoPlugin(SwitchPlugin):
|
||||||
def _init(self):
|
def _init(self, discovery_seconds=3):
|
||||||
logging.basicConfig(level=logging.INFO)
|
self.discovery_seconds=discovery_seconds
|
||||||
|
|
||||||
self.env = Environment()
|
self.env = Environment()
|
||||||
self.env.start()
|
self.env.start()
|
||||||
|
self.refresh_devices()
|
||||||
|
|
||||||
|
def refresh_devices(self):
|
||||||
logging.info('Starting WeMo discovery')
|
logging.info('Starting WeMo discovery')
|
||||||
self.env.discover(seconds=3)
|
self.env.discover(seconds=self.discovery_seconds)
|
||||||
|
self.devices = self.env.devices
|
||||||
|
|
||||||
|
def _exec(self, method, device, *args, **kwargs):
|
||||||
|
if device not in self.devices:
|
||||||
|
self.refresh_devices()
|
||||||
|
|
||||||
|
if device not in self.devices:
|
||||||
|
raise RuntimeError('Device {} not found'.format(device))
|
||||||
|
|
||||||
|
logging.info('{} -> {}'.format(device, method))
|
||||||
|
dev = self.devices[device]
|
||||||
|
getattr(dev, method)(*args, **kwargs)
|
||||||
|
|
||||||
|
resp = {'device': device, 'state': dev.get_state()}
|
||||||
|
return Response(output=json.dumps(resp))
|
||||||
|
|
||||||
def on(self, device):
|
def on(self, device):
|
||||||
switch = self.env.get_switch(device)
|
return self._exec('on', device)
|
||||||
logging.info('Turning {} on'.format(device))
|
|
||||||
switch.on()
|
|
||||||
|
|
||||||
def off(self, device):
|
def off(self, device):
|
||||||
switch = self.env.get_switch(device)
|
return self._exec('off', device)
|
||||||
logging.info('Turning {} off'.format(device))
|
|
||||||
switch.off()
|
|
||||||
|
|
||||||
def toggle(self, device):
|
def toggle(self, device):
|
||||||
switch = self.env.get_switch(device)
|
return self._exec('toggle', device)
|
||||||
logging.info('Toggling {}'.format(device))
|
|
||||||
switch.toggle()
|
|
||||||
|
|
||||||
def status(self):
|
|
||||||
return ['']
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
16
platypush/response/__init__.py
Normal file
16
platypush/response/__init__.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
class Response(object):
|
||||||
|
def __init__(self, output=None, errors=[]):
|
||||||
|
self.output = output
|
||||||
|
self.errors = errors
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return json.dumps({ 'output': self.output, 'error': self.errors })
|
||||||
|
|
||||||
|
def is_error(self):
|
||||||
|
return len(self.errors) != 0
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
Loading…
Reference in a new issue