Simplified backend interface, fixed some bugs with response management

This commit is contained in:
Fabio Manganiello 2017-12-16 04:56:43 +01:00
parent 97f2733308
commit 8c89a10710
5 changed files with 41 additions and 44 deletions

View File

@ -58,6 +58,7 @@ def _init_plugin(plugin_name, reload=False):
def _exec_func(args, backend=None, retry=True): def _exec_func(args, backend=None, retry=True):
origin = args.pop('origin') if 'origin' in args else None
action = args.pop('action') action = args.pop('action')
tokens = action.split('.') tokens = action.split('.')
module_name = str.join('.', tokens[:-1]) module_name = str.join('.', tokens[:-1])
@ -79,14 +80,14 @@ def _exec_func(args, backend=None, retry=True):
response = Response(output=None, errors=[e, traceback.format_exc()]) response = Response(output=None, errors=[e, traceback.format_exc()])
logging.exception(e) logging.exception(e)
if retry: if retry:
# Put the action back where it was before retrying # Put the popped args back where they were before retrying
args['action'] = action args['action'] = action; args['origin'] = origin
logging.info('Reloading plugin {} and retrying'.format(module_name)) logging.info('Reloading plugin {} and retrying'.format(module_name))
_init_plugin(module_name, reload=True) _init_plugin(module_name, reload=True)
_exec_func(args, backend, retry=False) _exec_func(args, backend, retry=False)
finally: finally:
if backend: backend.send_msg({ 'response':str(response) }) if backend: backend.send_response(origin, response)
def on_msg(msg, backend=None): def on_msg(msg, backend=None):

View File

@ -1,3 +1,4 @@
import json
import logging import logging
import sys import sys
import platypush import platypush
@ -53,6 +54,10 @@ class Backend(Thread):
if target != self.device_id and not self.is_local(): if target != self.device_id and not self.is_local():
return # Not for me return # Not for me
if 'response' in msg:
logging.info('Received response: {}'.format(msg))
return
if 'action' not in msg: if 'action' not in msg:
self.on_error('No action specified: {}'.format(msg)) self.on_error('No action specified: {}'.format(msg))
return return
@ -60,7 +65,24 @@ class Backend(Thread):
self.mq.put(msg) self.mq.put(msg)
def send_msg(self, msg): def send_msg(self, msg):
raise NotImplementedError() if isinstance(msg, str):
msg = json.loads(msg)
if not isinstance(msg, dict):
raise RuntimeError('send_msg expects either a JSON string or ' +
'a dictionary but received {}'.format(type(msg)))
msg['origin'] = self.device_id # To get the response
self._send_msg(msg)
def send_response(self, target, response):
self.send_msg({
'target' : target,
'response' : {
'output' : response.output,
'errors' : response.errors,
}
})
def run(self): def run(self):
raise NotImplementedError() raise NotImplementedError()

View File

@ -9,7 +9,7 @@ class KafkaBackend(Backend):
def _init(self, server, topic): def _init(self, server, topic):
self.server = server self.server = server
self.topic_prefix = topic self.topic_prefix = topic
self.topic = topic + '.' + self.device_id self.topic = self._topic_by_device_id(self.device_id)
self.producer = None self.producer = None
def _on_record(self, record): def _on_record(self, record):
@ -27,18 +27,15 @@ class KafkaBackend(Backend):
if not self.producer: if not self.producer:
self.producer = KafkaProducer(bootstrap_servers=self.server) self.producer = KafkaProducer(bootstrap_servers=self.server)
def send_msg(self, msg): def _topic_by_device_id(self, device_id):
if isinstance(msg, str): return '{}.{}'.format(self.topic_prefix, device_id)
msg = json.loads(msg)
if isinstance(msg, dict): def _send_msg(self, msg):
target = msg['target'] target = msg['target']
msg = json.dumps(msg).encode('utf-8') msg = json.dumps(msg).encode('utf-8')
if not isinstance(msg, bytes):
msg = json.dumps(msg)
raise RuntimeError('Invalid non-JSON message')
self._init_producer() self._init_producer()
self.producer.send(self.topic_prefix + '.' + target, msg) self.producer.send(self._topic_by_device_id(target), msg)
self.producer.flush() self.producer.flush()
def run(self): def run(self):

View File

@ -8,12 +8,9 @@ from .. import Backend
class LocalBackend(Backend): class LocalBackend(Backend):
def _init(self, fifo): def _init(self, fifo):
self.fifo = fifo self.fifo = fifo
def send_msg(self, msg):
if isinstance(msg, dict):
msg = json.dumps(msg)
if not isinstance(msg, str):
raise RuntimeError('Invalid non-JSON message')
def _send_msg(self, msg):
msg = json.dumps(msg)
msglen = len(msg)+1 # Include \n msglen = len(msg)+1 # Include \n
msg = bytearray((str(msglen) + '\n' + msg + '\n').encode('utf-8')) msg = bytearray((str(msglen) + '\n' + msg + '\n').encode('utf-8'))
with open(self.fifo, 'wb') as f: with open(self.fifo, 'wb') as f:

View File

@ -12,14 +12,6 @@ class PushbulletBackend(Backend):
self.device_name = device self.device_name = device
self.pb_device_id = self.get_device_id() self.pb_device_id = self.get_device_id()
@staticmethod
def _on_init(ws):
logging.info('Connection opened')
@staticmethod
def _on_close(ws):
logging.info('Connection closed')
@staticmethod @staticmethod
def _on_msg(ws, msg): def _on_msg(ws, msg):
ws.backend._on_push(msg) ws.backend._on_push(msg)
@ -54,7 +46,6 @@ class PushbulletBackend(Backend):
else: else:
return {} return {}
def _on_push(self, data): def _on_push(self, data):
try: try:
data = json.loads(data) if isinstance(data, str) else push data = json.loads(data) if isinstance(data, str) else push
@ -82,10 +73,8 @@ class PushbulletBackend(Backend):
def _init_socket(self): def _init_socket(self):
self.ws = websocket.WebSocketApp( self.ws = websocket.WebSocketApp(
'wss://stream.pushbullet.com/websocket/' + self.token, 'wss://stream.pushbullet.com/websocket/' + self.token,
on_open = self._on_init,
on_message = self._on_msg, on_message = self._on_msg,
on_error = self._on_error, on_error = self._on_error)
on_close = self._on_close)
self.ws.backend = self self.ws.backend = self
@ -104,26 +93,17 @@ class PushbulletBackend(Backend):
return devices[0]['iden'] return devices[0]['iden']
def send_msg(self, msg): def _send_msg(self, msg):
if isinstance(msg, dict): requests.post(
msg = json.dumps(msg)
if not isinstance(msg, str):
raise RuntimeError('Invalid non-JSON message')
response = requests.post(
u'https://api.pushbullet.com/v2/pushes', u'https://api.pushbullet.com/v2/pushes',
headers = { 'Access-Token': self.token }, headers = { 'Access-Token': self.token },
json = { json = {
'type': 'note', 'type': 'note',
'device_iden': self.pb_device_id, 'device_iden': self.pb_device_id,
'body': msg, 'body': json.dumps(msg),
} }
).json() ).json()
if 'dismissed' not in response or response['dismissed'] is True:
raise RuntimeError('Error while pushing the message: {}'.
format(response))
def run(self): def run(self):
self._init_socket() self._init_socket()
logging.info('Initialized Pushbullet backend - device_id: {}' logging.info('Initialized Pushbullet backend - device_id: {}'