2017-11-09 05:04:48 +01:00
|
|
|
import json
|
2017-12-13 23:55:38 +01:00
|
|
|
import time
|
2020-02-17 00:46:33 +01:00
|
|
|
from typing import Optional
|
2017-11-09 05:04:48 +01:00
|
|
|
|
2018-12-27 02:29:44 +01:00
|
|
|
from platypush.backend import Backend
|
2018-01-07 00:58:03 +01:00
|
|
|
from platypush.message.event.pushbullet import PushbulletEvent
|
2017-12-17 16:15:44 +01:00
|
|
|
|
2018-06-06 20:09:18 +02:00
|
|
|
|
2017-11-09 05:04:48 +01:00
|
|
|
class PushbulletBackend(Backend):
|
2018-06-26 00:16:39 +02:00
|
|
|
"""
|
|
|
|
This backend will listen for events on a Pushbullet (https://pushbullet.com)
|
|
|
|
channel and propagate them to the bus. This backend is quite useful if you
|
|
|
|
want to synchronize events and actions with your mobile phone (through the
|
|
|
|
Pushbullet app and/or through Tasker), synchronize clipboards, send pictures
|
|
|
|
and files to other devices etc. You can also wrap Platypush messages as JSON
|
|
|
|
into a push body to execute them.
|
|
|
|
|
|
|
|
Triggers:
|
|
|
|
|
2018-07-23 02:11:16 +02:00
|
|
|
* :class:`platypush.message.event.pushbullet.PushbulletEvent` if a new push is received
|
2018-06-26 00:16:39 +02:00
|
|
|
|
|
|
|
Requires:
|
|
|
|
|
2021-09-16 17:53:40 +02:00
|
|
|
* **pushbullet.py** (``pip install git+https://github.com/pushbullet.py/pushbullet.py``)
|
|
|
|
|
2018-06-26 00:16:39 +02:00
|
|
|
"""
|
|
|
|
|
2020-02-17 00:46:33 +01:00
|
|
|
def __init__(self, token: str, device: str = 'Platypush', proxy_host: Optional[str] = None,
|
|
|
|
proxy_port: Optional[int] = None, **kwargs):
|
2018-06-26 00:16:39 +02:00
|
|
|
"""
|
2020-02-17 00:46:33 +01:00
|
|
|
:param token: Your Pushbullet API token, see https://docs.pushbullet.com/#authentication
|
2018-06-26 00:16:39 +02:00
|
|
|
:param device: Name of the virtual device for Platypush (default: Platypush)
|
2018-12-27 02:29:44 +01:00
|
|
|
:param proxy_host: HTTP proxy host (default: None)
|
|
|
|
:param proxy_port: HTTP proxy port (default: None)
|
2018-06-26 00:16:39 +02:00
|
|
|
"""
|
2017-12-17 16:15:44 +01:00
|
|
|
super().__init__(**kwargs)
|
|
|
|
|
2017-11-09 05:04:48 +01:00
|
|
|
self.token = token
|
2017-12-12 20:14:02 +01:00
|
|
|
self.device_name = device
|
2018-12-27 02:29:44 +01:00
|
|
|
self.proxy_host = proxy_host
|
|
|
|
self.proxy_port = proxy_port
|
2020-02-17 00:46:33 +01:00
|
|
|
self.device = None
|
|
|
|
self.pb_device_id = None
|
|
|
|
self.pb = None
|
2018-12-27 02:29:44 +01:00
|
|
|
self.listener = None
|
2019-03-07 20:05:40 +01:00
|
|
|
|
2020-02-17 00:46:33 +01:00
|
|
|
def _initialize(self):
|
|
|
|
# noinspection PyPackageRequirements
|
|
|
|
from pushbullet import Pushbullet
|
|
|
|
self.pb = Pushbullet(self.token)
|
|
|
|
|
2019-03-07 20:05:40 +01:00
|
|
|
try:
|
|
|
|
self.device = self.pb.get_device(self.device_name)
|
2021-04-05 00:58:44 +02:00
|
|
|
except Exception as e:
|
2021-09-16 17:53:40 +02:00
|
|
|
self.logger.info(f'Device {self.device_name} does not exist: {e}. Creating it')
|
2019-03-07 20:05:40 +01:00
|
|
|
self.device = self.pb.new_device(self.device_name)
|
2017-11-09 05:04:48 +01:00
|
|
|
|
2019-03-07 20:11:32 +01:00
|
|
|
self.pb_device_id = self.get_device_id()
|
2017-11-09 05:04:48 +01:00
|
|
|
|
2017-12-13 23:55:38 +01:00
|
|
|
def _get_latest_push(self):
|
2018-01-07 13:18:01 +01:00
|
|
|
t = int(time.time()) - 5
|
2018-12-27 02:29:44 +01:00
|
|
|
pushes = self.pb.get_pushes(modified_after=str(t), limit=1)
|
2019-06-08 17:16:47 +02:00
|
|
|
if pushes:
|
|
|
|
return pushes[0]
|
2017-11-09 05:04:48 +01:00
|
|
|
|
2018-12-27 02:29:44 +01:00
|
|
|
def on_push(self):
|
|
|
|
def callback(data):
|
2017-12-17 16:15:44 +01:00
|
|
|
try:
|
2018-12-27 02:29:44 +01:00
|
|
|
# Parse the push
|
2018-12-24 12:31:38 +01:00
|
|
|
try:
|
2018-12-27 02:29:44 +01:00
|
|
|
data = json.loads(data) if isinstance(data, str) else data
|
2018-12-24 12:31:38 +01:00
|
|
|
except Exception as e:
|
|
|
|
self.logger.exception(e)
|
2018-12-27 02:29:44 +01:00
|
|
|
return
|
2018-12-18 19:01:51 +01:00
|
|
|
|
2018-12-27 02:29:44 +01:00
|
|
|
# If it's a push, get it
|
|
|
|
if data['type'] == 'tickle' and data['subtype'] == 'push':
|
|
|
|
push = self._get_latest_push()
|
|
|
|
elif data['type'] == 'push':
|
|
|
|
push = data['push']
|
2020-01-11 12:31:32 +01:00
|
|
|
else:
|
|
|
|
return # Not a push notification
|
2018-12-18 19:01:51 +01:00
|
|
|
|
2019-06-08 17:16:47 +02:00
|
|
|
if not push:
|
|
|
|
return
|
|
|
|
|
2018-12-27 02:29:44 +01:00
|
|
|
# Post an event, useful to react on mobile notifications if
|
|
|
|
# you enabled notification mirroring on your PushBullet app
|
|
|
|
event = PushbulletEvent(**push)
|
|
|
|
self.on_message(event)
|
2018-12-24 12:31:38 +01:00
|
|
|
|
2020-01-11 12:31:32 +01:00
|
|
|
if 'body' not in push:
|
|
|
|
return
|
2021-09-16 17:53:40 +02:00
|
|
|
self.logger.debug(f'Received push: {push}')
|
2018-12-18 19:01:51 +01:00
|
|
|
|
2018-12-27 02:29:44 +01:00
|
|
|
body = push['body']
|
2018-12-27 22:45:31 +01:00
|
|
|
try:
|
|
|
|
body = json.loads(body)
|
|
|
|
self.on_message(body)
|
|
|
|
except Exception as e:
|
2021-09-16 17:53:40 +02:00
|
|
|
self.logger.debug('Unexpected message received on the ' +
|
|
|
|
f'Pushbullet backend: {e}. Message: {body}')
|
2018-12-27 02:29:44 +01:00
|
|
|
except Exception as e:
|
|
|
|
self.logger.exception(e)
|
|
|
|
return
|
2017-12-12 20:14:02 +01:00
|
|
|
|
2018-12-27 02:29:44 +01:00
|
|
|
return callback
|
2018-12-18 19:01:51 +01:00
|
|
|
|
2018-12-27 02:29:44 +01:00
|
|
|
def get_device_id(self):
|
2020-01-11 12:31:32 +01:00
|
|
|
# noinspection PyBroadException
|
2018-12-18 19:01:51 +01:00
|
|
|
try:
|
2018-12-27 02:29:44 +01:00
|
|
|
return self.pb.get_device(self.device_name).device_iden
|
2020-01-11 12:31:32 +01:00
|
|
|
except Exception:
|
2019-06-08 22:37:19 +02:00
|
|
|
device = self.pb.new_device(self.device_name, model='Platypush virtual device',
|
|
|
|
manufacturer='platypush', icon='system')
|
2018-12-18 19:01:51 +01:00
|
|
|
|
2021-09-16 17:53:40 +02:00
|
|
|
self.logger.info(f'Created Pushbullet device {self.device_name}')
|
2018-12-27 02:29:44 +01:00
|
|
|
return device.device_iden
|
2017-12-12 20:14:02 +01:00
|
|
|
|
2018-12-18 19:01:51 +01:00
|
|
|
def close(self):
|
2018-12-27 02:29:44 +01:00
|
|
|
if self.listener:
|
|
|
|
self.listener.close()
|
2020-12-30 19:44:30 +01:00
|
|
|
self.listener = None
|
2018-12-18 19:01:51 +01:00
|
|
|
|
2017-12-20 20:25:08 +01:00
|
|
|
def on_stop(self):
|
2021-02-23 23:07:35 +01:00
|
|
|
self.logger.info('Received STOP event on the Pushbullet backend')
|
2020-08-14 00:34:13 +02:00
|
|
|
super().on_stop()
|
2021-02-23 23:07:35 +01:00
|
|
|
self.close()
|
|
|
|
self.logger.info('Pushbullet backend terminated')
|
2017-12-20 20:25:08 +01:00
|
|
|
|
2021-05-20 02:06:43 +02:00
|
|
|
def on_close(self, err=None):
|
2021-09-16 17:53:40 +02:00
|
|
|
def callback(*_):
|
|
|
|
self.listener = None
|
|
|
|
raise RuntimeError(err or 'Connection closed')
|
|
|
|
|
|
|
|
return callback
|
2021-05-19 18:43:44 +02:00
|
|
|
|
2022-01-08 19:52:41 +01:00
|
|
|
def on_error(self, *_):
|
|
|
|
def callback(*args):
|
|
|
|
self.logger.error(f'Pushbullet error: {args}')
|
|
|
|
try:
|
|
|
|
if self.listener:
|
|
|
|
self.listener.close()
|
|
|
|
except Exception as e:
|
|
|
|
self.logger.error('Error on Pushbullet connection close upon error')
|
|
|
|
self.logger.exception(e)
|
|
|
|
finally:
|
|
|
|
self.listener = None
|
|
|
|
|
|
|
|
return callback
|
|
|
|
|
2021-05-19 18:43:44 +02:00
|
|
|
def on_open(self):
|
2021-09-16 17:53:40 +02:00
|
|
|
def callback(*_):
|
|
|
|
self.logger.info('Pushbullet service connected')
|
|
|
|
|
|
|
|
return callback
|
2020-12-30 19:44:30 +01:00
|
|
|
|
|
|
|
def run_listener(self):
|
2021-05-19 18:43:44 +02:00
|
|
|
from .listener import Listener
|
2020-12-30 19:44:30 +01:00
|
|
|
|
2021-09-16 17:53:40 +02:00
|
|
|
self.logger.info(f'Initializing Pushbullet backend - device_id: {self.device_name}')
|
|
|
|
self.listener = Listener(account=self.pb,
|
|
|
|
on_push=self.on_push(),
|
|
|
|
on_open=self.on_open(),
|
|
|
|
on_close=self.on_close(),
|
2022-01-08 19:52:41 +01:00
|
|
|
on_error=self.on_error(),
|
2020-12-30 19:44:30 +01:00
|
|
|
http_proxy_host=self.proxy_host,
|
|
|
|
http_proxy_port=self.proxy_port)
|
|
|
|
|
|
|
|
self.listener.run_forever()
|
|
|
|
|
2018-10-25 20:45:58 +02:00
|
|
|
def run(self):
|
|
|
|
super().run()
|
2020-02-17 00:46:33 +01:00
|
|
|
initialized = False
|
|
|
|
|
|
|
|
while not initialized:
|
|
|
|
try:
|
|
|
|
self._initialize()
|
|
|
|
initialized = True
|
|
|
|
except Exception as e:
|
|
|
|
self.logger.exception(e)
|
2021-09-16 17:53:40 +02:00
|
|
|
self.logger.error(f'Pushbullet initialization error: {e}')
|
2021-05-20 02:06:43 +02:00
|
|
|
time.sleep(10)
|
|
|
|
|
|
|
|
while not self.should_stop():
|
|
|
|
try:
|
|
|
|
self.run_listener()
|
|
|
|
except Exception as e:
|
|
|
|
self.logger.exception(e)
|
|
|
|
time.sleep(10)
|
|
|
|
self.logger.info('Retrying connection')
|
2017-12-20 20:25:08 +01:00
|
|
|
|
2018-12-27 02:29:44 +01:00
|
|
|
|
2017-11-09 05:04:48 +01:00
|
|
|
# vim:sw=4:ts=4:et:
|