New format for messges, using actions instead of plugin names + custom args for each plugin

This commit is contained in:
Fabio Manganiello 2017-11-04 12:28:15 +01:00
parent 6a8a17eabd
commit c689b9bbf1
8 changed files with 54 additions and 87 deletions

View file

@ -30,10 +30,12 @@ Testing
Some examples: Some examples:
```shell ```shell
echo '{"cmd":"scp /home/user/photos/*.jpg backup_host:/mnt/hd/photos"}' | pusher --target laptop --plugin shell echo '{"cmd":"scp /home/user/photos/*.jpg backup_host:/mnt/hd/photos"}' | pusher --target laptop --action shell.exec
echo '{"play":true}' | pusher --target raspberrypi --plugin music.mpd echo '{}' | pusher --target raspberrypi --action music.mpd.play
``` ```
The logic to execute is specified by the `--action` option, whose format is `package_name.method_name` (with method_name part of the package main class).
Writing your plugins Writing your plugins
-------------------- --------------------
@ -52,11 +54,13 @@ Writing your own `runbullet` plugin, that would execute your own custom logic wh
The `__init__.py` will look like this: The `__init__.py` will look like this:
```python ```python
import batman
from .. import LightPlugin from .. import LightPlugin
class LightBatsignalPlugin(LightPlugin): class LightBatsignalPlugin(LightPlugin):
def _init(self): def _init(self):
self.batsignal = batsignal.Batsignal(self.config['intensity']) self.batsignal = batman.Batsignal(self.config['intensity'])
def on(self): def on(self):
self.batsignal.on() self.batsignal.on()
@ -78,6 +82,5 @@ class LightBatsignalPlugin(LightPlugin):
8. Test your new plugin by sending some bullets to it: 8. Test your new plugin by sending some bullets to it:
```shell ```shell
echo '{"on":true}' | pusher --target your_pc --plugin light.batsignal echo '{}' | pusher --target your_pc --action light.batsignal.on
``` ```

View file

@ -71,20 +71,27 @@ def _init_plugin(plugin_name, reload=False):
def _exec_func(body, retry=True): def _exec_func(body, retry=True):
args = {} args = {}
plugin_name = body['plugin']
if 'args' in body: if 'action' not in body:
args = json.loads(body['args']) \ logging.warn('No action specified')
if isinstance(body['args'], str) \ return
else body['args']
target = body.pop('target')
tokens = body.pop('action').split('.')
module_name = str.join('.', tokens[:-1])
method_name = tokens[-1:][0]
args = json.loads(body) \
if isinstance(body, str) \
else body
try: try:
plugin = _init_plugin(plugin_name) plugin = _init_plugin(module_name)
except RuntimeError as e: # Module/class not found except RuntimeError as e: # Module/class not found
return return
try: try:
ret = plugin.run(args) ret = plugin.run(method=method_name, **args)
out = None out = None
err = None err = None
@ -102,8 +109,8 @@ def _exec_func(body, retry=True):
except Exception as e: except Exception as e:
logging.exception(e) logging.exception(e)
if retry: if retry:
logging.info('Reloading plugin {} and retrying'.format(plugin_name)) logging.info('Reloading plugin {} and retrying'.format(module_name))
_init_plugin(plugin_name, reload=True) _init_plugin(module_name, reload=True)
_exec_func(body, retry=False) _exec_func(body, retry=False)
@ -135,10 +142,6 @@ def _on_push(ws, data):
logging.info('Received push addressed to me: {}'.format(body)) logging.info('Received push addressed to me: {}'.format(body))
if 'plugin' not in body:
logging.warn('No plugin specified')
return
thread = Thread(target=_exec_func, args=(body,)) thread = Thread(target=_exec_func, args=(body,))
thread.start() thread.start()
@ -222,3 +225,6 @@ Usage: {} [-v] [-h] [-c <config_file>]
if __name__ == '__main__': if __name__ == '__main__':
main() main()
# vim:sw=4:ts=4:et:

View file

@ -11,11 +11,11 @@ from runbullet import parse_config_file
def print_usage(): def print_usage():
print ('''Usage: {} [-h|--help] <-t|--target <target name>> <-p|--plugin <plugin name>> payload print ('''Usage: {} [-h|--help] <-t|--target <target name>> <-a|--action <action name>> payload
-h, --help:\t\tShow this help and exit -h, --help:\t\tShow this help and exit
-t, --target:\tName of the target device/host -t, --target:\tName of the target device/host
-p, --plugin:\tPlugin to use (e.g. shell or music.mpd) -a, --action\tAction to run, it includes both the package name and the method (e.g. shell.exec or music.mpd.play)
payload:\t\tArguments to the plugin payload:\t\tArguments to the action
'''.format(sys.argv[0])) '''.format(sys.argv[0]))
def main(): def main():
@ -34,11 +34,11 @@ def main():
'your PushBullet account'.format(config['pushbullet']['device'])) 'your PushBullet account'.format(config['pushbullet']['device']))
return return
optlist, args = getopt(sys.argv[1:], 'ht:p:', optlist, args = getopt(sys.argv[1:], 'ht:a:',
['help', 'target=', 'plugin=']) ['help', 'target=', 'action='])
target = None target = None
plugin = None action = None
payload = {} payload = {}
for opt, arg in optlist: for opt, arg in optlist:
@ -47,24 +47,25 @@ def main():
return return
elif opt == 't' or opt == '--target': elif opt == 't' or opt == '--target':
target = arg target = arg
elif opt == 'p' or opt == '--plugin': elif opt == 'a' or opt == '--action':
plugin = arg action = arg
if len(args): if len(args):
payload = json.loads(args[0]) payload = json.loads(args[0])
else: else:
payload = sys.stdin.read() payload = json.loads(sys.stdin.read())
if not (target and plugin): if not (target and action):
print_usage() print_usage()
return return
msg = { msg = {
'target': target, 'target': target,
'plugin': plugin, 'action': action,
'args': payload, **payload,
} }
print('msg: {}'.format(msg))
pb.push_note('', json.dumps(msg), device) pb.push_note('', json.dumps(msg), device)

View file

@ -13,8 +13,9 @@ class Plugin(object):
pass pass
def run(self, args): def run(self, method, *args, **kwargs):
raise NotImplementedError() res = getattr(self, method)(*args, **kwargs)
return res
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View file

@ -1,34 +1,6 @@
from .. import Plugin from .. import Plugin
class MusicPlugin(Plugin): class MusicPlugin(Plugin):
def run(self, args):
if 'clear' in args and args['clear']:
self.clear()
if 'playlistadd' in args and args['playlistadd']:
self.playlistadd(args['playlistadd'])
if 'add' in args and args['add']:
self.add(args['add'])
if 'next' in args and args['next']:
self.next()
elif 'previous' in args and args['previous']:
self.previous()
if 'setvol' in args and args['setvol']:
self.setvol(args['setvol'])
status = self.status()
if 'play' in args and args['play'] and status['state'] != 'play':
self.play()
elif 'pause' in args and args['pause'] and status['state'] != 'pause':
self.pause()
elif 'stop' in args and args['stop']:
self.stop()
return self.status()
def play(self): def play(self):
raise NotImplementedError() raise NotImplementedError()

View file

@ -3,18 +3,12 @@ import subprocess
from .. import Plugin from .. import Plugin
class ShellPlugin(Plugin): class ShellPlugin(Plugin):
def run(self, args): def exec(self, cmd):
if 'cmd' not in args:
raise RuntimeError('No cmd parameter specified')
cmd = args['cmd']
output = None output = None
error = None error = None
try: try:
output = subprocess.check_output(cmd, output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
stderr=subprocess.STDOUT,
shell=True)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
error = e.output error = e.output

View file

@ -1,16 +1,6 @@
from .. import Plugin from .. import Plugin
class SwitchPlugin(Plugin): class SwitchPlugin(Plugin):
def run(self, args):
if 'on' in args and args['on']:
self.on(args)
elif 'off' in args and args['off']:
self.off(args)
elif 'toggle' in args and args['toggle']:
self.toggle(args)
return self.status()
def on(self, args): def on(self, args):
raise NotImplementedError() raise NotImplementedError()

View file

@ -17,19 +17,19 @@ class SwitchWemoPlugin(SwitchPlugin):
logging.info('Starting WeMo discovery') logging.info('Starting WeMo discovery')
self.env.discover(seconds=3) self.env.discover(seconds=3)
def on(self, args): def on(self, device):
switch = self.env.get_switch(args['device']) switch = self.env.get_switch(device)
logging.info('Turning {} on'.format(args['device'])) logging.info('Turning {} on'.format(device))
switch.on() switch.on()
def off(self, args): def off(self, device):
switch = self.env.get_switch(args['device']) switch = self.env.get_switch(device)
logging.info('Turning {} off'.format(args['device'])) logging.info('Turning {} off'.format(device))
switch.off() switch.off()
def toggle(self, args): def toggle(self, device):
switch = self.env.get_switch(args['device']) switch = self.env.get_switch(device)
logging.info('Toggling {}'.format(args['device'])) logging.info('Toggling {}'.format(device))
switch.toggle() switch.toggle()
def status(self): def status(self):