forked from platypush/platypush
Added QR code/barcode integration [closes #119]
This commit is contained in:
parent
c26d456109
commit
c9db887505
10 changed files with 195 additions and 0 deletions
|
@ -246,6 +246,7 @@ autodoc_mock_imports = ['googlesamples.assistant.grpc.audio_helpers',
|
|||
'pvcheetah',
|
||||
'pyotp',
|
||||
'linode_api4',
|
||||
'pyzbar',
|
||||
]
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
|
|
5
docs/source/platypush/plugins/qrcode.rst
Normal file
5
docs/source/platypush/plugins/qrcode.rst
Normal file
|
@ -0,0 +1,5 @@
|
|||
``platypush.plugins.qrcode``
|
||||
============================
|
||||
|
||||
.. automodule:: platypush.plugins.qrcode
|
||||
:members:
|
5
docs/source/platypush/responses/qrcode.rst
Normal file
5
docs/source/platypush/responses/qrcode.rst
Normal file
|
@ -0,0 +1,5 @@
|
|||
``platypush.message.response.qrcode``
|
||||
=====================================
|
||||
|
||||
.. automodule:: platypush.message.response.qrcode
|
||||
:members:
|
|
@ -87,6 +87,7 @@ Plugins
|
|||
platypush/plugins/ping.rst
|
||||
platypush/plugins/printer.cups.rst
|
||||
platypush/plugins/pushbullet.rst
|
||||
platypush/plugins/qrcode.rst
|
||||
platypush/plugins/redis.rst
|
||||
platypush/plugins/sensor.rst
|
||||
platypush/plugins/serial.rst
|
||||
|
|
|
@ -15,6 +15,7 @@ Responses
|
|||
platypush/responses/pihole.rst
|
||||
platypush/responses/ping.rst
|
||||
platypush/responses/printer.cups.rst
|
||||
platypush/responses/qrcode.rst
|
||||
platypush/responses/stt.rst
|
||||
platypush/responses/system.rst
|
||||
platypush/responses/todoist.rst
|
||||
|
|
33
platypush/backend/http/app/routes/plugins/qrcode/__init__.py
Normal file
33
platypush/backend/http/app/routes/plugins/qrcode/__init__.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import base64
|
||||
|
||||
from flask import abort, request, Blueprint, Response
|
||||
|
||||
from platypush.backend.http.app import template_folder
|
||||
from platypush.context import get_plugin
|
||||
from platypush.plugins.qrcode import QrcodePlugin
|
||||
|
||||
qrcode = Blueprint('qrcode', __name__, template_folder=template_folder)
|
||||
|
||||
# Declare routes list
|
||||
__routes__ = [
|
||||
qrcode,
|
||||
]
|
||||
|
||||
|
||||
@qrcode.route('/qrcode', methods=['GET'])
|
||||
def generate_code():
|
||||
"""
|
||||
This route can be used to generate a QR code given a ``content`` parameter.
|
||||
"""
|
||||
|
||||
content = request.args.get('content')
|
||||
if not content:
|
||||
abort(400, 'Expected content parmeter')
|
||||
|
||||
plugin: QrcodePlugin = get_plugin('qrcode')
|
||||
response = plugin.generate(content, format='png').output
|
||||
data = base64.decodebytes(response['data'].encode())
|
||||
return Response(data, mimetype='image/png')
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
62
platypush/message/response/qrcode.py
Normal file
62
platypush/message/response/qrcode.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
import base64
|
||||
from typing import Optional, List
|
||||
|
||||
from pyzbar.pyzbar import Decoded
|
||||
from pyzbar.locations import Rect
|
||||
|
||||
from platypush.message import Mapping
|
||||
from platypush.message.response import Response
|
||||
|
||||
|
||||
class QrcodeResponse(Response):
|
||||
pass
|
||||
|
||||
|
||||
class QrcodeGeneratedResponse(QrcodeResponse):
|
||||
# noinspection PyShadowingBuiltins
|
||||
def __init__(self,
|
||||
content: str,
|
||||
format: str,
|
||||
data: Optional[str] = None,
|
||||
image_file: Optional[str] = None,
|
||||
*args, **kwargs):
|
||||
super().__init__(*args, output={
|
||||
'text': content,
|
||||
'data': data,
|
||||
'format': format,
|
||||
'image_file': image_file,
|
||||
}, **kwargs)
|
||||
|
||||
|
||||
class RectModel(Mapping):
|
||||
def __init__(self, rect: Rect):
|
||||
super().__init__()
|
||||
self.left = rect.left
|
||||
self.top = rect.top
|
||||
self.width = rect.width
|
||||
self.height = rect.height
|
||||
|
||||
|
||||
class ResultModel(Mapping):
|
||||
def __init__(self, result: Decoded, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
data = result.data.decode()
|
||||
except:
|
||||
data = base64.encodebytes(result.data).decode()
|
||||
|
||||
self.data = data
|
||||
self.type = result.type
|
||||
self.rect = dict(RectModel(result.rect)) if result.rect else {}
|
||||
|
||||
|
||||
class QrcodeDecodedResponse(QrcodeResponse):
|
||||
def __init__(self, results: List[Decoded], image_file: Optional[str] = None, *args, **kwargs):
|
||||
super().__init__(*args, output={
|
||||
'image_file': image_file,
|
||||
'results': [dict(ResultModel(result)) for result in results],
|
||||
}, **kwargs)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
80
platypush/plugins/qrcode.py
Normal file
80
platypush/plugins/qrcode.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
import base64
|
||||
import io
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from platypush.message.response.qrcode import QrcodeGeneratedResponse, QrcodeDecodedResponse
|
||||
from platypush.plugins import Plugin, action
|
||||
|
||||
|
||||
class QrcodePlugin(Plugin):
|
||||
"""
|
||||
Plugin to generate and scan QR and bar codes.
|
||||
|
||||
Requires:
|
||||
|
||||
* **qrcode** (``pip install 'qrcode[pil]'``) for QR generation.
|
||||
* **pyzbar** (``pip install pyzbar``) for decoding code from images.
|
||||
* **Pillow** (``pip install Pillow``) for image management.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
# noinspection PyShadowingBuiltins
|
||||
@action
|
||||
def generate(self, content: str, output_file: Optional[str] = None, show: bool = False,
|
||||
format: str = 'png') -> QrcodeGeneratedResponse:
|
||||
"""
|
||||
Generate a QR code.
|
||||
If you configured the :class`:platypush.backend.http.HttpBackend` then you can also generate
|
||||
codes directly from the browser through ``http://<host>:<port>/qrcode?content=...``.
|
||||
|
||||
:param content: Text, URL or content of the QR code.
|
||||
:param output_file: If set then the QR code will be exported in the specified image file.
|
||||
Otherwise, a base64-encoded representation of its binary content will be returned in
|
||||
the response as ``data``.
|
||||
:param show: If True, and if the device where the application runs has an active display,
|
||||
then the generated QR code will be shown on display.
|
||||
:param format: Output image format (default: ``png``).
|
||||
:return: :class:`platypush.message.response.qrcode.QrcodeGeneratedResponse`.
|
||||
"""
|
||||
import qrcode
|
||||
qr = qrcode.make(content)
|
||||
img = qr.get_image()
|
||||
ret = {
|
||||
'content': content,
|
||||
'format': format,
|
||||
}
|
||||
|
||||
if show:
|
||||
img.show()
|
||||
if output_file:
|
||||
output_file = os.path.abspath(os.path.expanduser(output_file))
|
||||
img.save(output_file, format=format)
|
||||
ret['image_file'] = output_file
|
||||
else:
|
||||
f = io.BytesIO()
|
||||
img.save(f, format=format)
|
||||
ret['data'] = base64.encodebytes(f.getvalue()).decode()
|
||||
|
||||
return QrcodeGeneratedResponse(**ret)
|
||||
|
||||
@action
|
||||
def decode(self, image_file: str) -> QrcodeDecodedResponse:
|
||||
"""
|
||||
Decode a QR code from an image file.
|
||||
|
||||
:param image_file: Path of the image file.
|
||||
"""
|
||||
from pyzbar import pyzbar
|
||||
from PIL import Image
|
||||
|
||||
image_file = os.path.abspath(os.path.expanduser(image_file))
|
||||
img = Image.open(image_file)
|
||||
results = pyzbar.decode(img)
|
||||
return QrcodeDecodedResponse(results)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
|
@ -246,3 +246,8 @@ croniter
|
|||
|
||||
# Support for Linode integration
|
||||
# linode_api4
|
||||
|
||||
# Support for QR codes
|
||||
# qrcode
|
||||
# Pillow
|
||||
# pyzbar
|
||||
|
|
2
setup.py
2
setup.py
|
@ -293,5 +293,7 @@ setup(
|
|||
'otp': ['pyotp'],
|
||||
# Support for Linode integration
|
||||
'linode': ['linode_api4'],
|
||||
# Support for QR codes
|
||||
'qrcode': ['qrcode[pil]', 'Pillow', 'pyzbar'],
|
||||
},
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue