* More consistent management of responses

* Better per-plugin/per-backend logging management
This commit is contained in:
Fabio Manganiello 2017-12-13 03:37:28 +01:00
parent 91cf4478d0
commit 491c2cd571
9 changed files with 118 additions and 92 deletions

View file

@ -112,6 +112,8 @@ The `__init__.py` will look like this:
```python
import batman
from platypush.response import Response
from .. import LightPlugin
class LightBatsignalPlugin(LightPlugin):
@ -123,22 +125,21 @@ class LightBatsignalPlugin(LightPlugin):
self.batsignal.notify_robin()
self.batsignal.on()
return Response('ok')
def off(self):
self.batsignal.off()
return Response('ok')
def toggle(self):
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.
8. Test your new plugin by sending some bullets to it:
7. Test your new plugin by sending some bullets to it:
```shell
pusher --target your_pc --action light.batsignal.on --urgent 1

View file

@ -66,21 +66,11 @@ def _exec_func(args, retry=True):
return
try:
ret = plugin.run(method=method_name, **args)
out = None
err = None
if isinstance(ret, list):
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))
response = plugin.run(method=method_name, **args)
if response and response.is_error():
logging.warn('Response processed with errors: {}'.format(response))
else:
logging.info('Processed response: {}'.format(response))
except Exception as e:
logging.exception(e)
if retry:
@ -122,6 +112,14 @@ def parse_config_file(config_file=None):
if 'disabled' in config[section] and config[section]['disabled']:
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
@ -161,13 +159,18 @@ def get_default_pusher_backend(config):
return backends[0] if backends else None
def get_logging_level():
global config
return config['logging']
def get_device_id():
global config
return config['device_id'] if 'device_id' in config else None
def main():
DEBUG = False
debug = False
config_file = None
plugins_dir = os.path.join(wrkdir, 'plugins')
@ -178,7 +181,7 @@ def main():
if opt == '-c':
config_file = arg
if opt == '-v':
DEBUG = True
debug = True
elif opt == '-h':
print('''
Usage: {} [-v] [-h] [-c <config_file>]
@ -189,19 +192,10 @@ Usage: {} [-v] [-h] [-c <config_file>]
return
config = parse_config_file(config_file)
if debug: config['logging'] = logging.DEBUG
logging.info('Configuration dump: {}'.format(config))
if 'device_id' not in config:
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)
logging.basicConfig(level=get_logging_level())
mq = Queue()
backends = get_backends(config)

View file

@ -1,5 +1,4 @@
import logging
import socket
import platypush
from threading import Thread
@ -30,7 +29,7 @@ class Backend(Thread):
self.on_init = on_init
self.on_close = on_close
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)
logging.basicConfig(level=logging.INFO

View file

@ -1,6 +1,9 @@
import os
import sys
import logging
import traceback
from platypush.response import Response
class Plugin(object):
def __init__(self, config):
@ -16,12 +19,18 @@ class Plugin(object):
def _set_logging(self):
if 'logging' in self.config:
logging.basicConfig(level=getattr(logging, self.config['logging']))
self._logging = self.config.pop('logging')
else:
logging.basicConfig(level=logging.INFO)
self._logging = logging.INFO
logging.basicConfig(level=self._logging)
def run(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
# vim:sw=4:ts=4:et:

View file

@ -2,6 +2,7 @@ import logging
import time
from phue import Bridge
from platypush.response import Response
from .. import LightPlugin
@ -57,19 +58,13 @@ class LightHuePlugin(LightPlugin):
scenes = [s.name for s in self.bridge.scenes]
# 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:
self.connect()
except Exception as e:
logging.exception(e)
# Reset bridge connection
self.bridge = None
if 'is_retry' not in kwargs:
time.sleep(1)
self._execute(attr, is_retry=True, *args, **kwargs)
return
raise e
lights = []; groups = []
if 'lights' in kwargs and kwargs['lights']:
@ -93,37 +88,33 @@ class LightHuePlugin(LightPlugin):
elif lights:
self.bridge.set_light(lights, attr, *args)
except Exception as e:
logging.exception(e)
# Reset bridge connection
self.bridge = None
raise e
return Response(output='ok')
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=[]):
self._execute('on', False, lights=lights, groups=groups)
return self._exec('on', False, lights=lights, groups=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)
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)
def hue(self, value, lights=[], groups=[]):
self._execute('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),
return self._exec('hue', int(value) % (self.MAX_HUE+1),
lights=lights, groups=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:

View file

@ -1,45 +1,47 @@
import mpd
from platypush.response import Response
from .. import MusicPlugin
class MusicMpdPlugin(MusicPlugin):
_requires = [
'mpd'
]
def _init(self):
self.client = mpd.MPDClient(use_unicode=True)
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):
self.client.play()
return self._exec('play')
def pause(self):
self.client.pause()
return self._exec('pause')
def stop(self):
self.client.stop()
return self._exec('stop')
def next(self):
self.client.next()
return self._exec('next')
def previous(self):
self.client.previous()
return self._exec('previous')
def setvol(self, vol):
self.client.setvol(vol)
return self._exec('setvol', vol)
def add(self, content):
self.client.add(content)
return self._exec('add', content)
def playlistadd(self, playlist):
self.client.playlistadd(playlist)
return self._exec('playlistadd', playlist)
def clear(self):
self.client.clear()
return self._exec('clear')
def status(self):
return self.client.status()
return Response(output=self.client.status())
# vim:sw=4:ts=4:et:

View file

@ -1,5 +1,7 @@
import subprocess
from platypush.response import Response
from .. import Plugin
class ShellPlugin(Plugin):
@ -8,11 +10,12 @@ class ShellPlugin(Plugin):
error = None
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:
error = e.output
error = e.output.decode('utf-8')
return [output, error]
return Response(output=output, errors=[error])
# vim:sw=4:ts=4:et:

View file

@ -1,35 +1,46 @@
import logging
import json
from ouimeaux.environment import Environment, UnknownDevice
from platypush.response import Response
from .. import SwitchPlugin
class SwitchWemoPlugin(SwitchPlugin):
def _init(self):
logging.basicConfig(level=logging.INFO)
def _init(self, discovery_seconds=3):
self.discovery_seconds=discovery_seconds
self.env = Environment()
self.env.start()
self.refresh_devices()
def refresh_devices(self):
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):
switch = self.env.get_switch(device)
logging.info('Turning {} on'.format(device))
switch.on()
return self._exec('on', device)
def off(self, device):
switch = self.env.get_switch(device)
logging.info('Turning {} off'.format(device))
switch.off()
return self._exec('off', device)
def toggle(self, device):
switch = self.env.get_switch(device)
logging.info('Toggling {}'.format(device))
switch.toggle()
return self._exec('toggle', device)
def status(self):
return ['']
# vim:sw=4:ts=4:et:

View 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: