forked from platypush/platypush
* 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
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
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