Implemented local backend

This commit is contained in:
Fabio Manganiello 2017-12-11 16:48:28 +01:00
parent 3b74ed2bb7
commit e9e0512a52
5 changed files with 91 additions and 6 deletions

View file

@ -17,9 +17,22 @@ Copy /etc/runbullet/config.example.yaml to /etc/runbullet/config.yaml (system-wi
Edit the file to include: Edit the file to include:
### For the PushBullet backend
* Your PushBullet access token (create one [here](https://www.pushbullet.com/#settings/account)); * 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)). * The name of the (virtual) PushBullet device used to listen for events (create one [here](https://www.pushbullet.com/#devices)).
### For the Apache Kafka backend
* The host and port of the Kafka installation
* The topic that will be used to deliver and process messages
### For the local socket backend
* The name of the local FIFO that will be used to deliver and process messages
### device_id
Each target device is identified by a unique device_id in the messages sent over your account. The device_id is the hostname by default, unless changed in config.yaml. Each target device is identified by a unique device_id in the messages sent over your account. The device_id is the hostname by default, unless changed in config.yaml.
Shell interface Shell interface
@ -65,10 +78,10 @@ music.mpd:
pusher --target raspberry --action switch.wemo.on pusher --target raspberry --action switch.wemo.on
``` ```
* *TODO* `runbullet.plugins.light.hue`: Controls a Philips Hue smart lights system. Requires the package `phue` on the target machine. Example: * `runbullet.plugins.light.hue`: Controls a Philips Hue smart lights system. Requires the package `phue` on the target machine. Example:
```shell ```shell
pusher --target raspberry --action light.hue.set_scene --scene "Sunset" --group "Living Room" pusher --target raspberry --action light.hue.scene --name "Sunset" --group "Living Room"
``` ```
Writing your plugins Writing your plugins

View file

@ -39,12 +39,16 @@ class Backend(Thread):
if cls is not object and hasattr(cls, '_init'): if cls is not object and hasattr(cls, '_init'):
cls._init(self, **config) cls._init(self, **config)
def is_local(self):
from runbullet.backend.local import LocalBackend
return isinstance(self, LocalBackend)
def on_msg(self, msg): def on_msg(self, msg):
if 'target' not in msg: if 'target' not in msg:
return # No target return # No target
target = msg.pop('target') target = msg.pop('target')
if target != runbullet.get_device_id(): if target != runbullet.get_device_id() and not self.is_local():
return # Not for me return # Not for me
if 'action' not in msg: if 'action' not in msg:

View file

@ -0,0 +1,49 @@
import logging
import json
import os
import time
from .. import Backend
class LocalBackend(Backend):
def _init(self, fifo):
self.fifo = fifo
try: os.mkfifo(self.fifo)
except FileExistsError as e: pass
logging.info('Initialized local backend on fifo {}'.format(self.fifo))
def send_msg(self, msg):
if isinstance(msg, dict):
msg = json.dumps(msg)
if not isinstance(msg, str):
msg = json.dumps(msg)
raise RuntimeError('Invalid non-JSON message')
msglen = len(msg)+1 # Include \n
msg = bytearray((str(msglen) + '\n' + msg + '\n').encode('utf-8'))
with open(self.fifo, 'wb') as f:
f.write(msg)
def run(self):
with open(self.fifo, 'rb', 0) as f:
while True:
try:
msglen = int(f.readline())
except ValueError as e:
time.sleep(0.1)
continue
msg = f.read(msglen-1)
if not msg: continue
try:
msg = json.loads(msg.decode('utf-8'))
except Exception as e:
logging.exception(e)
continue
logging.debug('Received message: {}'.format(msg))
self.on_msg(msg)
# vim:sw=4:ts=4:et:

View file

@ -10,6 +10,7 @@ import yaml
from pushbullet import Pushbullet from pushbullet import Pushbullet
from runbullet import parse_config_file from runbullet import parse_config_file
from runbullet.backend.kafka import KafkaBackend from runbullet.backend.kafka import KafkaBackend
from runbullet.backend.local import LocalBackend
def print_usage(): def print_usage():
@ -40,10 +41,18 @@ def send_pb_message(pb, device_name, msg):
def send_kafka_message(backend, msg): def send_kafka_message(backend, msg):
backend.send_msg(msg) backend.send_msg(msg)
def send_local_message(backend, msg):
backend.send_msg(msg)
def get_backend(config): def get_backend(config):
# TODO Refactor this as something better and reuse the same # TODO Refactor this as something better and reuse the same
# backend classes from the runbullet consumer module # backend classes from the runbullet consumer module
if 'backend.pushbullet' in config \ if 'backend.local' in config \
and 'pusher' in config['backend.local'] \
and config['backend.local']['pusher']:
c = config['backend.local']
return LocalBackend({'fifo': c['fifo']})
elif 'backend.pushbullet' in config \
and 'pusher' in config['backend.pushbullet'] \ and 'pusher' in config['backend.pushbullet'] \
and config['backend.pushbullet']['pusher']: and config['backend.pushbullet']['pusher']:
API_KEY = config['backend.pushbullet']['token'] API_KEY = config['backend.pushbullet']['token']
@ -66,8 +75,8 @@ def main():
parser.add_argument('--backend', '-b', dest='backend', required=False, parser.add_argument('--backend', '-b', dest='backend', required=False,
help="Backend to deliver the message " + help="Backend to deliver the message " +
"[pushbullet|kafka] (default: whatever specified " + "[pushbullet|kafka|local] (default: whatever " +
"in your config with pusher=True)") "specified in your config with pusher=True)")
opts, args = parser.parse_known_args(sys.argv[1:]) opts, args = parser.parse_known_args(sys.argv[1:])
payload = {} payload = {}
@ -76,6 +85,11 @@ def main():
raise RuntimeError('Odd number of key-value options passed: {}'. raise RuntimeError('Odd number of key-value options passed: {}'.
format(args)) format(args))
if opts.target == 'localhost' and 'backend.local' in config:
cfg = config['backend.local']
cfg['pusher'] = True
config = { 'backend.local': cfg }
if opts.backend: if opts.backend:
backend_cfg_name = 'backend.' + opts.backend backend_cfg_name = 'backend.' + opts.backend
if backend_cfg_name not in config: if backend_cfg_name not in config:
@ -103,6 +117,8 @@ def main():
send_pb_message(backend, config['backend.pushbullet']['device'], msg) send_pb_message(backend, config['backend.pushbullet']['device'], msg)
elif isinstance(backend, KafkaBackend): elif isinstance(backend, KafkaBackend):
send_kafka_message(backend, msg) send_kafka_message(backend, msg)
elif isinstance(backend, LocalBackend):
send_local_message(backend, msg)
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -11,6 +11,9 @@ backend.pushbullet:
token: your_pushbullet_token_here token: your_pushbullet_token_here
device: your_pushbullet_virtual_device_name device: your_pushbullet_virtual_device_name
backend.local:
fifo: /tmp/runbullet.fifo
# device_id: <your_device_id> (default: current hostname) # device_id: <your_device_id> (default: current hostname)
# debug: True (default: False) # debug: True (default: False)