Proper release with setuptools support
This commit is contained in:
parent
173ce6782f
commit
99a93012ce
8 changed files with 159 additions and 47 deletions
17
README.md
17
README.md
|
@ -3,3 +3,20 @@ Runbullet
|
|||
|
||||
Execute any command or custom complex logic on your devices, wherever they are, using your PushBullet account.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
```shell
|
||||
pip install runbullet
|
||||
```
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Copy /etc/runbullet/config.example.yaml to /etc/runbullet/config.yaml (system-wise settings) or ~/.config/runbullet/config.yaml (user-wise settings).
|
||||
|
||||
Edit the file to include:
|
||||
|
||||
* Your PushBullet access token (create one [here](https://www.pushbullet.com/#settings/account));
|
||||
* The name of the (virtual) PushBullet device used to listen for events (create one [here](https://www.pushbullet.com/#devices)).
|
||||
|
||||
|
|
|
@ -23,9 +23,9 @@ curdir = os.path.dirname(os.path.realpath(__file__))
|
|||
lib_dir = curdir + os.sep + 'lib'
|
||||
sys.path.insert(0, lib_dir)
|
||||
|
||||
DEVICE_ID = None
|
||||
modules = {}
|
||||
plugins = {}
|
||||
config = {}
|
||||
|
||||
|
||||
def on_open(ws):
|
||||
|
@ -56,8 +56,10 @@ def _init_plugin(plugin, reload=False):
|
|||
) + 'Plugin'
|
||||
|
||||
if cls_name not in plugins or reload:
|
||||
plugin_conf = config[plugin] if plugin in config else {}
|
||||
|
||||
try:
|
||||
plugins[cls_name] = getattr(modules[module_name], cls_name)()
|
||||
plugins[cls_name] = getattr(modules[module_name], cls_name)(plugin_conf)
|
||||
except AttributeError as e:
|
||||
logging.warn('No such class in {}: {}'.format(
|
||||
module_name, cls_name))
|
||||
|
@ -69,7 +71,7 @@ def _init_plugin(plugin, reload=False):
|
|||
def _exec_func(body, retry=True):
|
||||
try:
|
||||
logging.info('Received push addressed to me: {}'.format(body))
|
||||
args = body['args'] if 'args' in body else {}
|
||||
args = json.loads(body['args']) if 'args' in body else {}
|
||||
if 'plugin' not in body:
|
||||
logging.warn('No plugin specified')
|
||||
return
|
||||
|
@ -105,8 +107,6 @@ def _exec_func(body, retry=True):
|
|||
|
||||
|
||||
def _on_push(ws, data):
|
||||
global DEVICE_ID
|
||||
|
||||
data = json.loads(data)
|
||||
if data['type'] == 'tickle' and data['subtype'] == 'push':
|
||||
logging.debug('Received push tickle')
|
||||
|
@ -127,7 +127,7 @@ def _on_push(ws, data):
|
|||
except ValueError as e:
|
||||
return
|
||||
|
||||
if 'target' not in body or body['target'] != DEVICE_ID:
|
||||
if 'target' not in body or body['target'] != config['device_id']:
|
||||
return # Not for me
|
||||
|
||||
if 'plugin' not in body:
|
||||
|
@ -143,11 +143,35 @@ def on_push(ws, data):
|
|||
on_error(ws, e)
|
||||
|
||||
|
||||
def main():
|
||||
global DEVICE_ID
|
||||
def parse_config_file(config_file=None):
|
||||
global config
|
||||
|
||||
if config_file:
|
||||
locations = [config_file]
|
||||
else:
|
||||
locations = [
|
||||
# ./config.yaml
|
||||
os.path.join(curdir, 'config.yaml'),
|
||||
# ~/.config/runbullet/config.yaml
|
||||
os.path.join(os.environ['HOME'], '.config', 'runbullet', 'config.yaml'),
|
||||
# /etc/runbullet/config.yaml
|
||||
os.path.join(os.sep, 'etc', 'runbullet', 'config.yaml'),
|
||||
]
|
||||
|
||||
config = {}
|
||||
for loc in locations:
|
||||
try:
|
||||
with open(loc,'r') as f:
|
||||
config = yaml.load(f)
|
||||
except FileNotFoundError as e:
|
||||
pass
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def main():
|
||||
DEBUG = False
|
||||
config_file = curdir + os.sep + 'config.yaml'
|
||||
config_file = None
|
||||
|
||||
optlist, args = getopt(sys.argv[1:], 'vh')
|
||||
for opt, arg in optlist:
|
||||
|
@ -164,13 +188,10 @@ Usage: {} [-v] [-h] [-c <config_file>]
|
|||
'''.format(sys.argv[0]))
|
||||
return
|
||||
|
||||
config = {}
|
||||
with open(config_file,'r') as f:
|
||||
config = yaml.load(f)
|
||||
config = parse_config_file(config_file)
|
||||
|
||||
API_KEY = config['pushbullet']['token']
|
||||
DEVICE_ID = config['device_id'] \
|
||||
if 'device_id' in config else socket.gethostname()
|
||||
if 'device_id' not in config:
|
||||
config['device_id'] = socket.gethostname()
|
||||
|
||||
if 'debug' in config:
|
||||
DEBUG = config['debug']
|
||||
|
@ -182,7 +203,7 @@ Usage: {} [-v] [-h] [-c <config_file>]
|
|||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
ws = websocket.WebSocketApp('wss://stream.pushbullet.com/websocket/' +
|
||||
API_KEY,
|
||||
config['pushbullet']['token'],
|
||||
on_message = on_push,
|
||||
on_error = on_error,
|
||||
on_close = on_close)
|
||||
|
|
14
runbullet/pusher.py → runbullet/bin/pusher
Normal file → Executable file
14
runbullet/pusher.py → runbullet/bin/pusher
Normal file → Executable file
|
@ -1,3 +1,5 @@
|
|||
#!python
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
@ -5,16 +7,8 @@ import yaml
|
|||
|
||||
from getopt import getopt
|
||||
from pushbullet import Pushbullet
|
||||
from runbullet import parse_config_file
|
||||
|
||||
def get_config():
|
||||
curdir = os.path.dirname(os.path.realpath(__file__))
|
||||
config_file = curdir + os.sep + 'config.yaml'
|
||||
config = {}
|
||||
|
||||
with open(config_file,'r') as f:
|
||||
config = yaml.load(f)
|
||||
|
||||
return config
|
||||
|
||||
def print_usage():
|
||||
print ('''Usage: {} [-h|--help] <-t|--target <target name>> <-p|--plugin <plugin name>> payload
|
||||
|
@ -25,7 +19,7 @@ def print_usage():
|
|||
'''.format(sys.argv[0]))
|
||||
|
||||
def main():
|
||||
config = get_config()
|
||||
config = parse_config_file()
|
||||
API_KEY = config['pushbullet']['token']
|
||||
pb = Pushbullet(API_KEY)
|
||||
|
8
runbullet/bin/runbullet
Executable file
8
runbullet/bin/runbullet
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!python
|
||||
|
||||
import runbullet
|
||||
|
||||
runbullet.main()
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
import os
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
class Plugin(object):
|
||||
def __init__(self):
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
for cls in reversed(self.__class__.mro()):
|
||||
if cls is not object:
|
||||
try:
|
||||
|
@ -12,19 +13,6 @@ class Plugin(object):
|
|||
pass
|
||||
|
||||
|
||||
def _init(self):
|
||||
module_dir = os.path.dirname(sys.modules[self.__module__].__file__)
|
||||
config_file = module_dir + os.sep + 'config.yaml'
|
||||
|
||||
config = {}
|
||||
|
||||
try:
|
||||
with open(config_file, 'r') as f:
|
||||
self.config = yaml.load(f)
|
||||
except FileNotFoundError as e:
|
||||
pass
|
||||
|
||||
|
||||
def run(self, args):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
|
|
@ -2,11 +2,29 @@ from .. import Plugin
|
|||
|
||||
class MusicPlugin(Plugin):
|
||||
def run(self, args):
|
||||
if 'play' in args and self.status()['state'] != 'play':
|
||||
if 'clear' in args and args['clear']:
|
||||
self.add(args['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 self.status()['state'] != 'pause':
|
||||
elif 'pause' in args and args['pause'] and status['state'] != 'pause':
|
||||
self.pause()
|
||||
elif 'stop' in args:
|
||||
elif 'stop' in args and args['stop']:
|
||||
self.stop()
|
||||
|
||||
return self.status()
|
||||
|
@ -20,6 +38,24 @@ class MusicPlugin(Plugin):
|
|||
def stop(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def next(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def previous(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def setvol(self, vol):
|
||||
raise NotImplementedError()
|
||||
|
||||
def add(self, content):
|
||||
raise NotImplementedError()
|
||||
|
||||
def playlistadd(self, playlist):
|
||||
raise NotImplementedError()
|
||||
|
||||
def clear(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def status(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
|
|
@ -16,6 +16,24 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
def stop(self):
|
||||
self.client.stop()
|
||||
|
||||
def next(self):
|
||||
self.client.next()
|
||||
|
||||
def previous(self):
|
||||
self.client.previous()
|
||||
|
||||
def setvol(self, vol):
|
||||
self.client.setvol(vol)
|
||||
|
||||
def add(self, content):
|
||||
self.client.add(content)
|
||||
|
||||
def playlistadd(self, playlist):
|
||||
self.client.playlistadd(playlist)
|
||||
|
||||
def clear(self):
|
||||
self.client.clear()
|
||||
|
||||
def status(self):
|
||||
return self.client.status()
|
||||
|
||||
|
|
30
setup.py
30
setup.py
|
@ -1,11 +1,33 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import errno
|
||||
import os
|
||||
from setuptools import setup
|
||||
|
||||
def read(fname):
|
||||
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||
|
||||
def pkg_files(dir):
|
||||
paths = []
|
||||
for (path, dirs, files) in os.walk(dir):
|
||||
for file in files:
|
||||
paths.append(os.path.join('..', path, file))
|
||||
return paths
|
||||
|
||||
def create_etc_dir():
|
||||
path = '/etc/runbullet'
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as e:
|
||||
if isinstance(e, PermissionError) \
|
||||
or e.errno == errno.EEXIST and os.path.isdir(path):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
plugins = pkg_files('runbullet/lib/plugins')
|
||||
create_etc_dir()
|
||||
|
||||
setup(
|
||||
name = "runbullet",
|
||||
version = "0.1",
|
||||
|
@ -16,10 +38,18 @@ setup(
|
|||
keywords = "pushbullet notifications automation",
|
||||
url = "https://www.fabiomanganiello.com",
|
||||
packages = ['runbullet'],
|
||||
package_data = {'': plugins},
|
||||
scripts = ['runbullet/bin/pusher', 'runbullet/bin/runbullet'],
|
||||
data_files = [
|
||||
('/etc/runbullet', ['runbullet/config.example.yaml'])
|
||||
],
|
||||
long_description = read('README.md'),
|
||||
classifiers = [
|
||||
"Topic :: Utilities",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
],
|
||||
install_requires = [
|
||||
'pyyaml'
|
||||
]
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue