forked from platypush/platypush
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:
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in a new issue