New format for messges, using actions instead of plugin names + custom args for each plugin
This commit is contained in:
parent
6a8a17eabd
commit
c689b9bbf1
8 changed files with 54 additions and 87 deletions
13
README.md
13
README.md
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue