Blackened the qrcode and pushbullet plugins

This commit is contained in:
Fabio Manganiello 2023-05-22 02:33:54 +02:00
parent aaac6488d6
commit 7eca1c27c9
Signed by: blacklight
GPG key ID: D90FBA7F76362774
2 changed files with 144 additions and 80 deletions

View file

@ -16,7 +16,8 @@ class PushbulletPlugin(Plugin):
Requires: Requires:
* The :class:`platypush.backend.pushbullet.Pushbullet` backend enabled * The :class:`platypush.backend.pushbullet.PushbulletBackend` backend enabled
""" """
def __init__(self, token: Optional[str] = None, **kwargs): def __init__(self, token: Optional[str] = None, **kwargs):
@ -44,23 +45,23 @@ class PushbulletPlugin(Plugin):
""" """
Get the list of available devices Get the list of available devices
""" """
resp = requests.get('https://api.pushbullet.com/v2/devices', resp = requests.get(
headers={'Authorization': 'Bearer ' + self.token, 'https://api.pushbullet.com/v2/devices',
'Content-Type': 'application/json'}) headers={
'Authorization': 'Bearer ' + self.token,
'Content-Type': 'application/json',
},
)
self._devices = resp.json().get('devices', []) self._devices = resp.json().get('devices', [])
self._devices_by_id = { self._devices_by_id = {dev['iden']: dev for dev in self._devices}
dev['iden']: dev
for dev in self._devices
}
self._devices_by_name = { self._devices_by_name = {
dev['nickname']: dev dev['nickname']: dev for dev in self._devices if 'nickname' in dev
for dev in self._devices if 'nickname' in dev
} }
@action @action
def get_device(self, device): def get_device(self, device) -> Optional[dict]:
""" """
:param device: Device ID or name :param device: Device ID or name
""" """
@ -79,7 +80,14 @@ class PushbulletPlugin(Plugin):
refreshed = True refreshed = True
@action @action
def send_note(self, device: str = None, body: str = None, title: str = None, url: str = None, **kwargs): def send_note(
self,
device: Optional[str] = None,
body: Optional[str] = None,
title: Optional[str] = None,
url: Optional[str] = None,
**kwargs,
):
""" """
Send a note push. Send a note push.
@ -90,10 +98,11 @@ class PushbulletPlugin(Plugin):
:param kwargs: Push arguments, see https://docs.pushbullet.com/#create-push :param kwargs: Push arguments, see https://docs.pushbullet.com/#create-push
""" """
dev = None
if device: if device:
device = self.get_device(device).output dev = self.get_device(device).output
if not device: if not dev:
raise RuntimeError('No such device') raise RuntimeError(f'No such device: {device}')
kwargs['body'] = body kwargs['body'] = body
kwargs['title'] = title kwargs['title'] = title
@ -102,21 +111,25 @@ class PushbulletPlugin(Plugin):
kwargs['type'] = 'link' kwargs['type'] = 'link'
kwargs['url'] = url kwargs['url'] = url
if device: if dev:
# noinspection PyTypeChecker kwargs['device_iden'] = dev['iden']
kwargs['device_iden'] = device['iden']
resp = requests.post('https://api.pushbullet.com/v2/pushes', resp = requests.post(
'https://api.pushbullet.com/v2/pushes',
data=json.dumps(kwargs), data=json.dumps(kwargs),
headers={'Authorization': 'Bearer ' + self.token, headers={
'Content-Type': 'application/json'}) 'Authorization': 'Bearer ' + self.token,
'Content-Type': 'application/json',
},
)
if resp.status_code >= 400: if resp.status_code >= 400:
raise Exception('Pushbullet push failed with status {}: {}'. raise Exception(
format(resp.status_code, resp.json())) f'Pushbullet push failed with status {resp.status_code}: {resp.json()}'
)
@action @action
def send_file(self, filename: str, device: str = None): def send_file(self, filename: str, device: Optional[str] = None):
""" """
Send a file. Send a file.
@ -124,48 +137,61 @@ class PushbulletPlugin(Plugin):
:param filename: Path to the local file :param filename: Path to the local file
""" """
dev = None
if device: if device:
device = self.get_device(device).output dev = self.get_device(device).output
if not device: if not dev:
raise RuntimeError('No such device') raise RuntimeError(f'No such device: {device}')
resp = requests.post('https://api.pushbullet.com/v2/upload-request', resp = requests.post(
'https://api.pushbullet.com/v2/upload-request',
data=json.dumps({'file_name': os.path.basename(filename)}), data=json.dumps({'file_name': os.path.basename(filename)}),
headers={'Authorization': 'Bearer ' + self.token, headers={
'Content-Type': 'application/json'}) 'Authorization': 'Bearer ' + self.token,
'Content-Type': 'application/json',
},
)
if resp.status_code != 200: if resp.status_code != 200:
raise Exception('Pushbullet file upload request failed with status {}'. raise Exception(
format(resp.status_code)) f'Pushbullet file upload request failed with status {resp.status_code}'
)
r = resp.json() r = resp.json()
resp = requests.post(r['upload_url'], data=r['data'], with open(filename, 'rb') as f:
files={'file': open(filename, 'rb')}) resp = requests.post(r['upload_url'], data=r['data'], files={'file': f})
if resp.status_code != 204: if resp.status_code != 204:
raise Exception('Pushbullet file upload failed with status {}'. raise Exception(
format(resp.status_code)) f'Pushbullet file upload failed with status {resp.status_code}'
)
# noinspection PyTypeChecker resp = requests.post(
resp = requests.post('https://api.pushbullet.com/v2/pushes', 'https://api.pushbullet.com/v2/pushes',
headers={'Authorization': 'Bearer ' + self.token, headers={
'Content-Type': 'application/json'}, 'Authorization': 'Bearer ' + self.token,
'Content-Type': 'application/json',
data=json.dumps({ },
data=json.dumps(
{
'type': 'file', 'type': 'file',
'device_iden': device['iden'] if device else None, 'device_iden': dev['iden'] if dev else None,
'file_name': r['file_name'], 'file_name': r['file_name'],
'file_type': r['file_type'], 'file_type': r['file_type'],
'file_url': r['file_url']})) 'file_url': r['file_url'],
}
),
)
if resp.status_code >= 400: if resp.status_code >= 400:
raise Exception('Pushbullet file push failed with status {}'. raise Exception(
format(resp.status_code)) f'Pushbullet file push failed with status {resp.status_code}'
)
return { return {
'filename': r['file_name'], 'filename': r['file_name'],
'type': r['file_type'], 'type': r['file_type'],
'url': r['file_url'] 'url': r['file_url'],
} }
@action @action
@ -178,17 +204,23 @@ class PushbulletPlugin(Plugin):
backend = get_backend('pushbullet') backend = get_backend('pushbullet')
device_id = backend.get_device_id() if backend else None device_id = backend.get_device_id() if backend else None
resp = requests.post('https://api.pushbullet.com/v2/ephemerals', resp = requests.post(
data=json.dumps({ 'https://api.pushbullet.com/v2/ephemerals',
data=json.dumps(
{
'type': 'push', 'type': 'push',
'push': { 'push': {
'body': text, 'body': text,
'type': 'clip', 'type': 'clip',
'source_device_iden': device_id, 'source_device_iden': device_id,
}, },
}), }
headers={'Authorization': 'Bearer ' + self.token, ),
'Content-Type': 'application/json'}) headers={
'Authorization': 'Bearer ' + self.token,
'Content-Type': 'application/json',
},
)
resp.raise_for_status() resp.raise_for_status()

View file

@ -8,7 +8,11 @@ from typing import Optional, List
from platypush import Config from platypush import Config
from platypush.context import get_bus from platypush.context import get_bus
from platypush.message.event.qrcode import QrcodeScannedEvent from platypush.message.event.qrcode import QrcodeScannedEvent
from platypush.message.response.qrcode import QrcodeGeneratedResponse, QrcodeDecodedResponse, ResultModel from platypush.message.response.qrcode import (
QrcodeGeneratedResponse,
QrcodeDecodedResponse,
ResultModel,
)
from platypush.plugins import Plugin, action from platypush.plugins import Plugin, action
from platypush.plugins.camera import CameraPlugin from platypush.plugins.camera import CameraPlugin
from platypush.utils import get_plugin_class_by_name from platypush.utils import get_plugin_class_by_name
@ -36,20 +40,28 @@ class QrcodePlugin(Plugin):
self.camera_plugin = camera_plugin self.camera_plugin = camera_plugin
self._capturing = threading.Event() self._capturing = threading.Event()
def _get_camera(self, camera_plugin: Optional[str] = None, **config) -> CameraPlugin: def _get_camera(
self, camera_plugin: Optional[str] = None, **config
) -> CameraPlugin:
camera_plugin = camera_plugin or self.camera_plugin camera_plugin = camera_plugin or self.camera_plugin
if not config: if not config:
config = Config.get(camera_plugin) or {} config = Config.get(camera_plugin) or {}
config['stream_raw_frames'] = True config['stream_raw_frames'] = True
cls = get_plugin_class_by_name(camera_plugin) cls = get_plugin_class_by_name(camera_plugin)
assert cls and issubclass(cls, CameraPlugin), '{} is not a valid camera plugin'.format(camera_plugin) assert cls and issubclass(
cls, CameraPlugin
), '{} is not a valid camera plugin'.format(camera_plugin)
return cls(**config) return cls(**config)
# noinspection PyShadowingBuiltins
@action @action
def generate(self, content: str, output_file: Optional[str] = None, show: bool = False, def generate(
format: str = 'png') -> QrcodeGeneratedResponse: self,
content: str,
output_file: Optional[str] = None,
show: bool = False,
format: str = 'png',
) -> QrcodeGeneratedResponse:
""" """
Generate a QR code. Generate a QR code.
If you configured the :class:`platypush.backend.http.HttpBackend` then you can also generate If you configured the :class:`platypush.backend.http.HttpBackend` then you can also generate
@ -65,6 +77,7 @@ class QrcodePlugin(Plugin):
:return: :class:`platypush.message.response.qrcode.QrcodeGeneratedResponse`. :return: :class:`platypush.message.response.qrcode.QrcodeGeneratedResponse`.
""" """
import qrcode import qrcode
qr = qrcode.make(content) qr = qrcode.make(content)
img = qr.get_image() img = qr.get_image()
ret = { ret = {
@ -105,17 +118,26 @@ class QrcodePlugin(Plugin):
import numpy as np import numpy as np
from PIL import Image from PIL import Image
assert isinstance(frame, np.ndarray), \ assert isinstance(
'Image conversion only works with numpy arrays for now (got {})'.format(type(frame)) frame, np.ndarray
), 'Image conversion only works with numpy arrays for now (got {})'.format(
type(frame)
)
mode = 'RGB' mode = 'RGB'
if len(frame.shape) > 2 and frame.shape[2] == 4: if len(frame.shape) > 2 and frame.shape[2] == 4:
mode = 'RGBA' mode = 'RGBA'
return Image.frombuffer(mode, (frame.shape[1], frame.shape[0]), frame, 'raw', mode, 0, 1) return Image.frombuffer(
mode, (frame.shape[1], frame.shape[0]), frame, 'raw', mode, 0, 1
)
@action @action
def start_scanning(self, camera_plugin: Optional[str] = None, duration: Optional[float] = None, def start_scanning(
n_codes: Optional[int] = None) -> Optional[List[ResultModel]]: self,
camera_plugin: Optional[str] = None,
duration: Optional[float] = None,
n_codes: Optional[int] = None,
) -> Optional[List[ResultModel]]:
""" """
Decode QR-codes and bar codes using a camera. Decode QR-codes and bar codes using a camera.
@ -130,6 +152,7 @@ class QrcodePlugin(Plugin):
:class:`platypush.message.response.qrcode.ResultModel` instances with the scanned results, :class:`platypush.message.response.qrcode.ResultModel` instances with the scanned results,
""" """
from pyzbar import pyzbar from pyzbar import pyzbar
assert not self._capturing.is_set(), 'A capturing process is already running' assert not self._capturing.is_set(), 'A capturing process is already running'
camera = self._get_camera(camera_plugin) camera = self._get_camera(camera_plugin)
@ -143,9 +166,11 @@ class QrcodePlugin(Plugin):
with camera: with camera:
start_time = time.time() start_time = time.time()
while self._capturing.is_set() \ while (
and (not duration or time.time() < start_time + duration) \ self._capturing.is_set()
and (not n_codes or len(codes) < n_codes): and (not duration or time.time() < start_time + duration)
and (not n_codes or len(codes) < n_codes)
):
output = camera.get_stream() output = camera.get_stream()
with output.ready: with output.ready:
output.ready.wait() output.ready.wait()
@ -153,15 +178,21 @@ class QrcodePlugin(Plugin):
results = pyzbar.decode(img) results = pyzbar.decode(img)
if results: if results:
results = [ results = [
result for result in QrcodeDecodedResponse(results).output['results'] result
for result in QrcodeDecodedResponse(results).output[
'results'
]
if result['data'] not in last_results if result['data'] not in last_results
or time.time() >= last_results_time + last_results_timeout or time.time()
>= last_results_time + last_results_timeout
] ]
if results: if results:
codes.extend(results) codes.extend(results)
get_bus().post(QrcodeScannedEvent(results=results)) get_bus().post(QrcodeScannedEvent(results=results))
last_results = {result['data']: result for result in results} last_results = {
result['data']: result for result in results
}
last_results_time = time.time() last_results_time = time.time()
finally: finally:
self._capturing.clear() self._capturing.clear()
@ -172,4 +203,5 @@ class QrcodePlugin(Plugin):
def stop_scanning(self): def stop_scanning(self):
self._capturing.clear() self._capturing.clear()
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et: