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:
```shell
echo '{"cmd":"scp /home/user/photos/*.jpg backup_host:/mnt/hd/photos"}' | pusher --target laptop --plugin shell
echo '{"play":true}' | pusher --target raspberrypi --plugin music.mpd
echo '{"cmd":"scp /home/user/photos/*.jpg backup_host:/mnt/hd/photos"}' | pusher --target laptop --action shell.exec
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
--------------------
@ -52,11 +54,13 @@ Writing your own `runbullet` plugin, that would execute your own custom logic wh
The `__init__.py` will look like this:
```python
import batman
from .. import LightPlugin
class LightBatsignalPlugin(LightPlugin):
def _init(self):
self.batsignal = batsignal.Batsignal(self.config['intensity'])
self.batsignal = batman.Batsignal(self.config['intensity'])
def on(self):
self.batsignal.on()
@ -78,6 +82,5 @@ class LightBatsignalPlugin(LightPlugin):
8. Test your new plugin by sending some bullets to it:
```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):
args = {}
plugin_name = body['plugin']
if 'args' in body:
args = json.loads(body['args']) \
if isinstance(body['args'], str) \
else body['args']
if 'action' not in body:
logging.warn('No action specified')
return
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:
plugin = _init_plugin(plugin_name)
plugin = _init_plugin(module_name)
except RuntimeError as e: # Module/class not found
return
try:
ret = plugin.run(args)
ret = plugin.run(method=method_name, **args)
out = None
err = None
@ -102,8 +109,8 @@ def _exec_func(body, retry=True):
except Exception as e:
logging.exception(e)
if retry:
logging.info('Reloading plugin {} and retrying'.format(plugin_name))
_init_plugin(plugin_name, reload=True)
logging.info('Reloading plugin {} and retrying'.format(module_name))
_init_plugin(module_name, reload=True)
_exec_func(body, retry=False)
@ -135,10 +142,6 @@ def _on_push(ws, data):
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.start()
@ -222,3 +225,6 @@ Usage: {} [-v] [-h] [-c <config_file>]
if __name__ == '__main__':
main()
# vim:sw=4:ts=4:et:

View file

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

View file

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

View file

@ -1,34 +1,6 @@
from .. import 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):
raise NotImplementedError()

View file

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

View file

@ -1,16 +1,6 @@
from .. import 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):
raise NotImplementedError()

View file

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