Adding backends documentation
This commit is contained in:
parent
85c7faf21b
commit
28862d743d
36 changed files with 592 additions and 167 deletions
25
docs/source/backends.rst
Normal file
25
docs/source/backends.rst
Normal file
|
@ -0,0 +1,25 @@
|
|||
Backends
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Backends:
|
||||
|
||||
platypush/backend.rst
|
||||
platypush/backend/assistant.google.rst
|
||||
platypush/backend/assistant.google.pushtotalk.rst
|
||||
platypush/backend/assistant.snowboy.rst
|
||||
platypush/backend/button.flic.rst
|
||||
platypush/backend/camera.pi.rst
|
||||
platypush/backend/http.rst
|
||||
platypush/backend/http.poll.rst
|
||||
platypush/backend/inotify.rst
|
||||
platypush/backend/kafka.rst
|
||||
platypush/backend/midi.rst
|
||||
platypush/backend/mqtt.rst
|
||||
platypush/backend/music.mpd.rst
|
||||
platypush/backend/pushbullet.rst
|
||||
platypush/backend/redis.rst
|
||||
platypush/backend/scard.rst
|
||||
platypush/backend/sensor.rst
|
||||
|
|
@ -5,6 +5,7 @@ Platypush
|
|||
:maxdepth: 3
|
||||
:caption: Contents:
|
||||
|
||||
backends
|
||||
plugins
|
||||
|
||||
Indices and tables
|
||||
|
|
6
docs/source/platypush/backend.rst
Normal file
6
docs/source/platypush/backend.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend``
|
||||
=====================
|
||||
|
||||
.. automodule:: platypush.backend
|
||||
:members:
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.assistant.google.pushtotalk``
|
||||
=================================================
|
||||
|
||||
.. automodule:: platypush.backend.assistant.google.pushtotalk
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/assistant.google.rst
Normal file
6
docs/source/platypush/backend/assistant.google.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.assistant.google``
|
||||
======================================
|
||||
|
||||
.. automodule:: platypush.backend.assistant.google
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/assistant.snowboy.rst
Normal file
6
docs/source/platypush/backend/assistant.snowboy.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.assistant.snowboy``
|
||||
=======================================
|
||||
|
||||
.. automodule:: platypush.backend.assistant.snowboy
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/button.flic.rst
Normal file
6
docs/source/platypush/backend/button.flic.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.button.flic``
|
||||
=================================
|
||||
|
||||
.. automodule:: platypush.backend.button.flic
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/camera.pi.rst
Normal file
6
docs/source/platypush/backend/camera.pi.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.camera.pi``
|
||||
===============================
|
||||
|
||||
.. automodule:: platypush.backend.camera.pi
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/http.poll.rst
Normal file
6
docs/source/platypush/backend/http.poll.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.http.poll``
|
||||
===============================
|
||||
|
||||
.. automodule:: platypush.backend.http.poll
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/http.rst
Normal file
6
docs/source/platypush/backend/http.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.http``
|
||||
==========================
|
||||
|
||||
.. automodule:: platypush.backend.http
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/inotify.rst
Normal file
6
docs/source/platypush/backend/inotify.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.inotify``
|
||||
=============================
|
||||
|
||||
.. automodule:: platypush.backend.inotify
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/kafka.rst
Normal file
6
docs/source/platypush/backend/kafka.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.kafka``
|
||||
===========================
|
||||
|
||||
.. automodule:: platypush.backend.kafka
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/midi.rst
Normal file
6
docs/source/platypush/backend/midi.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.midi``
|
||||
==========================
|
||||
|
||||
.. automodule:: platypush.backend.midi
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/mqtt.rst
Normal file
6
docs/source/platypush/backend/mqtt.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.mqtt``
|
||||
==========================
|
||||
|
||||
.. automodule:: platypush.backend.mqtt
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/music.mpd.rst
Normal file
6
docs/source/platypush/backend/music.mpd.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.music.mpd``
|
||||
===============================
|
||||
|
||||
.. automodule:: platypush.backend.music.mpd
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/pushbullet.rst
Normal file
6
docs/source/platypush/backend/pushbullet.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.pushbullet``
|
||||
================================
|
||||
|
||||
.. automodule:: platypush.backend.pushbullet
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/redis.rst
Normal file
6
docs/source/platypush/backend/redis.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.redis``
|
||||
===========================
|
||||
|
||||
.. automodule:: platypush.backend.redis
|
||||
:members:
|
||||
|
6
docs/source/platypush/backend/scard.rst
Normal file
6
docs/source/platypush/backend/scard.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
``platypush.backend.scard``
|
||||
===========================
|
||||
|
||||
.. automodule:: platypush.backend.scard
|
||||
:members:
|
||||
|
7
docs/source/platypush/backend/sensor.rst
Normal file
7
docs/source/platypush/backend/sensor.rst
Normal file
|
@ -0,0 +1,7 @@
|
|||
``platypush.backend.sensor``
|
||||
============================
|
||||
|
||||
.. automodule:: platypush.backend.sensor
|
||||
:members:
|
||||
|
||||
|
|
@ -16,16 +16,26 @@ from platypush.message.response import Response
|
|||
|
||||
|
||||
class Backend(Thread):
|
||||
""" Parent class for backends """
|
||||
"""
|
||||
Parent class for backends.
|
||||
|
||||
A backend is basically a thread that checks for new events on some channel
|
||||
(e.g. a network socket, a queue, some new entries on an API endpoint or an
|
||||
RSS feed, a voice command through an assistant, a new measure from a sensor
|
||||
etc.) and propagates event messages to the main application bus whenever a
|
||||
new event happens. You can then build whichever type of custom logic you
|
||||
want on such events.
|
||||
"""
|
||||
|
||||
_default_response_timeout = 5
|
||||
|
||||
def __init__(self, bus=None, **kwargs):
|
||||
"""
|
||||
Params:
|
||||
bus -- Reference to the Platypush bus where the requests and the
|
||||
responses will be posted [Bus]
|
||||
kwargs -- key-value configuration for this backend [Dict]
|
||||
:param bus: Reference to the bus object to be used in the backend
|
||||
:type bus: platypush.bus.Bus
|
||||
|
||||
:param kwargs: Key-value configuration for the backend
|
||||
:type kwargs: dict
|
||||
"""
|
||||
|
||||
# If no bus is specified, create an internal queue where
|
||||
|
@ -55,11 +65,9 @@ class Backend(Thread):
|
|||
It should be called by the derived classes whenever
|
||||
a new message should be processed.
|
||||
|
||||
Params:
|
||||
msg -- The message. It can be either a key-value
|
||||
dictionary, a platypush.message.Message
|
||||
object, or a string/byte UTF-8 encoded string
|
||||
:param msg: Received message. It can be either a key-value dictionary, a platypush.message.Message object, or a string/byte UTF-8 encoded string
|
||||
"""
|
||||
|
||||
msg = Message.build(msg)
|
||||
|
||||
if not getattr(msg, 'target') or msg.target != self.device_id:
|
||||
|
@ -120,10 +128,9 @@ class Backend(Thread):
|
|||
|
||||
def send_event(self, event, **kwargs):
|
||||
"""
|
||||
Send an event message on the backend
|
||||
Params:
|
||||
event -- The request, either a dict, a string/bytes UTF-8 JSON,
|
||||
or a platypush.message.event.Event object.
|
||||
Send an event message on the backend.
|
||||
|
||||
:param event: Event to send. It can be a dict, a string/bytes UTF-8 JSON, or a platypush.message.event.Event object.
|
||||
"""
|
||||
|
||||
event = Event.build(event)
|
||||
|
@ -139,17 +146,15 @@ class Backend(Thread):
|
|||
def send_request(self, request, on_response=None,
|
||||
response_timeout=_default_response_timeout, **kwargs):
|
||||
"""
|
||||
Send a request message on the backend
|
||||
Params:
|
||||
request -- The request, either a dict, a string/bytes UTF-8 JSON,
|
||||
or a platypush.message.request.Request object.
|
||||
Send a request message on the backend.
|
||||
|
||||
on_response -- Response handler, takes a platypush.message.response.Response
|
||||
as argument. If set, the method will wait for a
|
||||
response before exiting (default: None)
|
||||
response_timeout -- If on_response is set, the backend will raise
|
||||
an exception if the response isn't received
|
||||
within this number of seconds (default: 5)
|
||||
:param request: The request, either a dict, a string/bytes UTF-8 JSON, or a platypush.message.request.Request object.
|
||||
|
||||
:param on_response: Optional callback that will be called when a response is received. If set, this method will synchronously wait for a response before exiting.
|
||||
:type on_response: function
|
||||
|
||||
:param response_timeout: If on_response is set, the backend will raise an exception if the response isn't received within this number of seconds (default: None)
|
||||
:type response_timeout: float
|
||||
"""
|
||||
|
||||
request = Request.build(request)
|
||||
|
@ -165,12 +170,10 @@ class Backend(Thread):
|
|||
|
||||
def send_response(self, response, request, **kwargs):
|
||||
"""
|
||||
Send a response message on the backend
|
||||
Params:
|
||||
response -- The response, either a dict, a string/bytes UTF-8 JSON,
|
||||
or a platypush.message.response.Response object
|
||||
request -- Associated request, used to set the response parameters
|
||||
that will link them
|
||||
Send a response message on the backend.
|
||||
|
||||
:param response: The response, either a dict, a string/bytes UTF-8 JSON, or a platypush.message.response.Response object
|
||||
:param request: Associated request, used to set the response parameters that will link them
|
||||
"""
|
||||
|
||||
response = Response.build(response)
|
||||
|
@ -191,8 +194,7 @@ class Backend(Thread):
|
|||
backend is configured then it will try to deliver the message to
|
||||
other consumers through the configured Redis main queue.
|
||||
|
||||
Params:
|
||||
msg -- The message
|
||||
:param msg: The message to send
|
||||
"""
|
||||
try:
|
||||
redis = get_backend('redis')
|
||||
|
|
|
@ -16,18 +16,35 @@ from platypush.message.event.assistant import \
|
|||
|
||||
|
||||
class AssistantGoogleBackend(Backend):
|
||||
""" Class for the Google Assistant backend. It creates and event source
|
||||
that posts recognized phrases on the main bus """
|
||||
"""
|
||||
Google Assistant backend.
|
||||
|
||||
It listens for voice commands and post conversation events on the bus.
|
||||
|
||||
Triggers:
|
||||
|
||||
* :class:`platypush.message.event.assistant.ConversationStartEvent` when a new conversation starts
|
||||
* :class:`platypush.message.event.assistant.SpeechRecognizedEvent` when a new voice command is recognized
|
||||
* :class:`platypush.message.event.assistant.NoResponse` when a conversation returned no response
|
||||
* :class:`platypush.message.event.assistant.ResponseEvent` when the assistant is speaking a response
|
||||
* :class:`platypush.message.event.assistant.ConversationTimeoutEvent` when a conversation times out
|
||||
* :class:`platypush.message.event.assistant.ConversationEndEvent` when a new conversation ends
|
||||
|
||||
Requires:
|
||||
|
||||
* **google-assistant-sdk** (``pip install google-assistant-sdk``)
|
||||
"""
|
||||
|
||||
def __init__(self, credentials_file=os.path.join(
|
||||
os.path.expanduser('~/.config'),
|
||||
'google-oauthlib-tool', 'credentials.json'),
|
||||
device_model_id='Platypush', **kwargs):
|
||||
""" Params:
|
||||
credentials_file -- Path to the Google OAuth credentials file
|
||||
(default: ~/.config/google-oauthlib-tool/credentials.json)
|
||||
device_model_id -- Device model ID to use for the assistant
|
||||
(default: Platypush)
|
||||
"""
|
||||
:param credentials_file: Path to the Google OAuth credentials file (default: ~/.config/google-oauthlib-tool/credentials.json). See https://developers.google.com/assistant/sdk/guides/library/python/embed/install-sample#generate_credentials for how to get your own credentials file.
|
||||
:type credentials_file: str
|
||||
|
||||
:param device_model_id: Device model ID to use for the assistant (default: Platypush)
|
||||
:type device_model_id: str
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
@ -61,20 +78,15 @@ class AssistantGoogleBackend(Backend):
|
|||
|
||||
|
||||
def start_conversation(self):
|
||||
""" Starts an assistant conversation """
|
||||
if self.assistant: self.assistant.start_conversation()
|
||||
|
||||
|
||||
def stop_conversation(self):
|
||||
""" Stops an assistant conversation """
|
||||
if self.assistant: self.assistant.stop_conversation()
|
||||
|
||||
|
||||
def send_message(self, msg):
|
||||
# Can't send a message on an event source, ignoring
|
||||
# TODO Make a class for event sources like these. Event sources
|
||||
# would be a subset of the backends which can fire events on the bus
|
||||
# but not receive requests or process responses.
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
super().run()
|
||||
|
||||
|
|
|
@ -25,12 +25,25 @@ from platypush.message.event.assistant import \
|
|||
|
||||
|
||||
class AssistantGooglePushtotalkBackend(Backend):
|
||||
""" Google Assistant pushtotalk backend. Instead of listening for
|
||||
the "OK Google" hotword like the assistant.google backend,
|
||||
this implementation programmatically starts a conversation
|
||||
upon start_conversation() method call. Use this backend on
|
||||
devices that don't have an Assistant SDK package (e.g. arm6 devices
|
||||
like the RaspberryPi Zero or the RaspberryPi 1) """
|
||||
"""
|
||||
Google Assistant pushtotalk backend. Instead of listening for the "OK
|
||||
Google" hotword like the assistant.google backend, this implementation
|
||||
programmatically starts a conversation upon start_conversation() method
|
||||
call. Use this backend on devices that don't have an Assistant SDK package
|
||||
(e.g. arm6 devices like the RaspberryPi Zero or the RaspberryPi 1).
|
||||
|
||||
Triggers:
|
||||
|
||||
* :class:`platypush.message.event.assistant.ConversationStartEvent` when a new conversation starts
|
||||
* :class:`platypush.message.event.assistant.SpeechRecognizedEvent` when a new voice command is recognized
|
||||
* :class:`platypush.message.event.assistant.ConversationEndEvent` when a new conversation ends
|
||||
|
||||
Requires:
|
||||
|
||||
* **tenacity** (``pip install tenacity``)
|
||||
* **grpc** (``pip install grpc``)
|
||||
* **google-assistant-grpc** (``pip install google-assistant-grpc``)
|
||||
"""
|
||||
|
||||
api_endpoint = 'embeddedassistant.googleapis.com'
|
||||
audio_sample_rate = audio_helpers.DEFAULT_AUDIO_SAMPLE_RATE
|
||||
|
@ -49,13 +62,15 @@ class AssistantGooglePushtotalkBackend(Backend):
|
|||
lang='en-US',
|
||||
conversation_start_fifo = os.path.join(os.path.sep, 'tmp', 'pushtotalk.fifo'),
|
||||
*args, **kwargs):
|
||||
""" Params:
|
||||
credentials_file -- Path to the Google OAuth credentials file
|
||||
(default: ~/.config/google-oauthlib-tool/credentials.json)
|
||||
device_config -- Path to device_config.json. Register your
|
||||
device and create a project, then run the pushtotalk.py
|
||||
script from googlesamples to create your device_config.json
|
||||
lang -- Assistant language (default: en-US)
|
||||
"""
|
||||
:param credentials_file: Path to the Google OAuth credentials file (default: ~/.config/google-oauthlib-tool/credentials.json). See https://developers.google.com/assistant/sdk/guides/library/python/embed/install-sample#generate_credentials for how to get your own credentials file.
|
||||
:type credentials_file: str
|
||||
|
||||
:param device_config: Path to device_config.json. Register your device (see https://developers.google.com/assistant/sdk/guides/library/python/embed/register-device) and create a project, then run the pushtotalk.py script from googlesamples to create your device_config.json
|
||||
:type device_config: str
|
||||
|
||||
:param lang: Assistant language (default: en-US)
|
||||
:type lang: str
|
||||
"""
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -125,11 +140,13 @@ class AssistantGooglePushtotalkBackend(Backend):
|
|||
self.device_handler = device_helpers.DeviceRequestHandler(self.device_id)
|
||||
|
||||
def start_conversation(self):
|
||||
""" Start a conversation """
|
||||
if self.assistant:
|
||||
with open(self.conversation_start_fifo, 'w') as f:
|
||||
f.write('1')
|
||||
|
||||
def stop_conversation(self):
|
||||
""" Stop a conversation """
|
||||
if self.assistant:
|
||||
self.conversation_stream.stop_playback()
|
||||
self.bus.post(ConversationEndEvent())
|
||||
|
@ -345,5 +362,6 @@ class SampleAssistant(object):
|
|||
# Subsequent requests need audio data, but not config.
|
||||
yield embedded_assistant_pb2.AssistRequest(audio_in=data)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
||||
|
|
|
@ -3,28 +3,46 @@ import os
|
|||
import subprocess
|
||||
import time
|
||||
|
||||
from snowboy import snowboydecoder
|
||||
|
||||
from platypush.backend import Backend
|
||||
from platypush.message.event.assistant import \
|
||||
ConversationStartEvent, ConversationEndEvent, \
|
||||
SpeechRecognizedEvent, HotwordDetectedEvent
|
||||
|
||||
class AssistantSnowboyBackend(Backend):
|
||||
""" Backend for detecting custom voice hotwords through Snowboy.
|
||||
The purpose of this component is only to detect the hotword
|
||||
specified in your Snowboy voice model. If you want to trigger
|
||||
proper assistant conversations or custom speech recognition,
|
||||
you should create a hook in your configuration on HotwordDetectedEvent
|
||||
to trigger the conversation on whichever assistant plugin you're using
|
||||
(Google, Alexa...) """
|
||||
"""
|
||||
Backend for detecting custom voice hotwords through Snowboy. The purpose of
|
||||
this component is only to detect the hotword specified in your Snowboy voice
|
||||
model. If you want to trigger proper assistant conversations or custom
|
||||
speech recognition, you should create a hook in your configuration on
|
||||
HotwordDetectedEvent to trigger the conversation on whichever assistant
|
||||
plugin you're using (Google, Alexa...)
|
||||
|
||||
Triggers:
|
||||
|
||||
* :class:`platypush.message.event.assistant.HotwordDetectedEvent` whenever the hotword has been detected
|
||||
|
||||
Requires:
|
||||
|
||||
* **snowboy** (``pip install snowboy``)
|
||||
"""
|
||||
|
||||
def __init__(self, voice_model_file, hotword=None, sensitivity=0.5,
|
||||
audio_gain=1.0, **kwargs):
|
||||
""" Params:
|
||||
voice_model_file -- Snowboy voice model file
|
||||
hotword -- Name of the hotword
|
||||
"""
|
||||
:param voice_model_file: Snowboy voice model file - see https://snowboy.kitt.ai/
|
||||
:type voice_model_file: str
|
||||
|
||||
:param hotword: Name of the hotword
|
||||
:type hotword: str
|
||||
|
||||
:param sensitivity: Hotword recognition sensitivity, between 0 and 1
|
||||
:type sensitivity: float
|
||||
|
||||
:param audio_gain: Audio gain, between 0 and 1
|
||||
:type audio_gain: float
|
||||
"""
|
||||
|
||||
from snowboy import snowboydecoder
|
||||
|
||||
super().__init__(**kwargs)
|
||||
self.voice_model_file = voice_model_file
|
||||
|
@ -38,9 +56,6 @@ class AssistantSnowboyBackend(Backend):
|
|||
|
||||
self.logger.info('Initialized Snowboy hotword detection')
|
||||
|
||||
def send_message(self, msg):
|
||||
pass
|
||||
|
||||
def hotword_detected(self):
|
||||
def callback():
|
||||
self.bus.post(HotwordDetectedEvent(hotword=self.hotword))
|
||||
|
|
|
@ -11,13 +11,37 @@ from .fliclib.fliclib import FlicClient, ButtonConnectionChannel, ClickType
|
|||
|
||||
|
||||
class ButtonFlicBackend(Backend):
|
||||
"""
|
||||
Backend that listen for events from the Flic (https://flic.io/) bluetooth
|
||||
smart buttons.
|
||||
|
||||
Triggers:
|
||||
|
||||
* :class:`platypush.message.event.button.flic.FlicButtonEvent` when a button is pressed. The event will also contain the press sequence (e.g. ``["ShortPressEvent", "LongPressEvent", "ShortPressEvent"]``)
|
||||
|
||||
Requires:
|
||||
|
||||
* **fliclib** (https://github.com/50ButtonsEach/fliclib-linux-hci). For the backend to work properly you need to have the ``flicd`` daemon from the fliclib running, and you have to first pair the buttons with your device using any of the scanners provided by the library.
|
||||
"""
|
||||
|
||||
_long_press_timeout = 0.3
|
||||
_btn_timeout = 0.5
|
||||
ShortPressEvent = "ShortPressEvent"
|
||||
LongPressEvent = "LongPressEvent"
|
||||
|
||||
def __init__(self, server, long_press_timeout=_long_press_timeout,
|
||||
def __init__(self, server='localhost', long_press_timeout=_long_press_timeout,
|
||||
btn_timeout=_btn_timeout, **kwargs):
|
||||
"""
|
||||
:param server: flicd server host (default: localhost)
|
||||
:type server: str
|
||||
|
||||
:param long_press_timeout: How long you should press a button for a press action to be considered "long press" (default: 0.3 secohds)
|
||||
:type long_press_timeout: float
|
||||
|
||||
:param btn_timeout: How long since the last button release before considering the user interaction completed (default: 0.5 seconds)
|
||||
:type btn_timeout: float
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.server = server
|
||||
|
@ -88,9 +112,6 @@ class ButtonFlicBackend(Backend):
|
|||
|
||||
return _f
|
||||
|
||||
def send_message(self, msg):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
super().run()
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import json
|
||||
import socket
|
||||
import time
|
||||
import picamera
|
||||
|
||||
from enum import Enum
|
||||
from redis import Redis
|
||||
|
@ -10,6 +9,17 @@ from threading import Thread
|
|||
from platypush.backend import Backend
|
||||
|
||||
class CameraPiBackend(Backend):
|
||||
"""
|
||||
Backend to interact with a Raspberry Pi camera. It can start and stop
|
||||
recordings and take pictures. It can be programmatically controlled through
|
||||
the :class:`platypush.plugins.camera.pi` plugin.
|
||||
|
||||
Requires:
|
||||
|
||||
* **picamera** (``pip install picamera``)
|
||||
* **redis** (``pip install redis``) for inter-process communication with the camera process
|
||||
"""
|
||||
|
||||
class CameraAction(Enum):
|
||||
START_RECORDING = 'START_RECORDING'
|
||||
STOP_RECORDING = 'STOP_RECORDING'
|
||||
|
@ -27,9 +37,15 @@ class CameraPiBackend(Backend):
|
|||
exposure_mode='auto', meter_mode='average', awb_mode='auto',
|
||||
image_effect='none', color_effects=None, rotation=0,
|
||||
crop=(0.0, 0.0, 1.0, 1.0), **kwargs):
|
||||
""" See https://www.raspberrypi.org/documentation/usage/camera/python/README.md
|
||||
for a detailed reference about the Pi camera options """
|
||||
"""
|
||||
See https://www.raspberrypi.org/documentation/usage/camera/python/README.md
|
||||
for a detailed reference about the Pi camera options.
|
||||
|
||||
:param listen_port: Port where the camera process will provide the video output while recording
|
||||
:type listen_port: int
|
||||
"""
|
||||
|
||||
import picamera
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.listen_port = listen_port
|
||||
|
@ -74,14 +90,30 @@ class CameraPiBackend(Backend):
|
|||
self.redis.rpush(self.redis_queue, json.dumps(action))
|
||||
|
||||
def take_picture(self, image_file):
|
||||
"""
|
||||
Take a picture.
|
||||
|
||||
:param image_file: Output image file
|
||||
:type image_file: str
|
||||
"""
|
||||
self.logger.info('Capturing camera snapshot to {}'.format(image_file))
|
||||
self.camera.capture(image_file)
|
||||
self.logger.info('Captured camera snapshot to {}'.format(image_file))
|
||||
|
||||
def start_recording(self, video_file=None, format='h264'):
|
||||
"""
|
||||
Start a recording.
|
||||
|
||||
:param video_file: Output video file. If specified, the video will be recorded to file, otherwise it will be served via TCP/IP on the listen_port. Use ``stop_recording`` to stop the recording.
|
||||
:type video_file: str
|
||||
|
||||
:param format: Video format (default: h264)
|
||||
:type format: str
|
||||
"""
|
||||
|
||||
def recording_thread():
|
||||
if video_file:
|
||||
self.camera.start_recording(videofile, format=format)
|
||||
self.camera.start_recording(video_file, format=format)
|
||||
while True:
|
||||
self.camera.wait_recording(60)
|
||||
else:
|
||||
|
@ -114,6 +146,8 @@ class CameraPiBackend(Backend):
|
|||
|
||||
|
||||
def stop_recording(self):
|
||||
""" Stops recording """
|
||||
|
||||
self.logger.info('Stopping camera recording')
|
||||
|
||||
try:
|
||||
|
|
|
@ -22,10 +22,30 @@ from .. import Backend
|
|||
|
||||
|
||||
class HttpBackend(Backend):
|
||||
""" Example interaction with the HTTP backend to make requests:
|
||||
$ curl -XPOST -H 'Content-Type: application/json' -H "X-Token: your_token" \
|
||||
-d '{"type":"request","target":"nodename","action":"tts.say","args": {"phrase":"This is a test"}}' \
|
||||
http://localhost:8008/execute """
|
||||
"""
|
||||
The HTTP backend is a general-purpose web server that you can leverage:
|
||||
|
||||
* To execute Platypush commands via HTTP calls. Example::
|
||||
|
||||
curl -XPOST -H 'Content-Type: application/json' -H "X-Token: your_token" \\
|
||||
-d '{
|
||||
"type":"request",
|
||||
"target":"nodename",
|
||||
"action":"tts.say",
|
||||
"args": {"phrase":"This is a test"}
|
||||
}' \\
|
||||
http://localhost:8008/execute
|
||||
|
||||
* To interact with your system (and control plugins and backends) through the Platypush web panel, by default available on your web root document. Any plugin that you have configured and available as a panel plugin will appear on the web panel as well as a tab.
|
||||
|
||||
* To display a fullscreen dashboard with your configured widgets, by default available under ``/dashboard``
|
||||
|
||||
Requires:
|
||||
|
||||
* **flask** (``pip install flask``)
|
||||
* **redis** (``pip install redis``)
|
||||
* **websockets** (``pip install websockets``)
|
||||
"""
|
||||
|
||||
hidden_plugins = {
|
||||
'assistant.google'
|
||||
|
@ -34,6 +54,48 @@ class HttpBackend(Backend):
|
|||
def __init__(self, port=8008, websocket_port=8009, disable_websocket=False,
|
||||
redis_queue='platypush_flask_mq', token=None, dashboard={},
|
||||
maps={}, **kwargs):
|
||||
"""
|
||||
:param port: Listen port for the web server (default: 8008)
|
||||
:type port: int
|
||||
|
||||
:param websocket_port: Listen port for the websocket server (default: 8009)
|
||||
:type websocket_port: int
|
||||
|
||||
:param disable_websocket: Disable the websocket interface (default: False)
|
||||
:type disable_websocket: bool
|
||||
|
||||
:param redis_queue: Name of the Redis queue used to synchronize messages with the web server process (default: ``platypush_flask_mq``)
|
||||
:type redis_queue: str
|
||||
|
||||
:param token: If set (recommended) any interaction with the web server needs to bear an ``X-Token: <token>`` header, or it will fail with a 403: Forbidden
|
||||
:type token: str
|
||||
|
||||
:param dashboard: Set it if you want to use the dashboard service. It will contain the configuration for the widgets to be used (look under ``platypush/backend/http/templates/widgets/`` for the available widgets).
|
||||
|
||||
Example configuration::
|
||||
|
||||
dashboard:
|
||||
background_image: https://site/image.png
|
||||
widgets: # Each row of the dashboard will have 6 columns
|
||||
calendar: # Calendar widget
|
||||
columns: 6
|
||||
music: # Music widget
|
||||
columns: 3
|
||||
date-time-weather: # Date, time and weather widget
|
||||
columns: 3
|
||||
image-carousel: # Image carousel
|
||||
columns: 6
|
||||
images_path: /static/resources/Dropbox/Photos/carousel # Path (relative to ``platypush/backend/http``) containing the carousel pictures
|
||||
refresh_seconds: 15
|
||||
rss-news: # RSS feeds widget
|
||||
# Requires backend.http.poll to be enabled with some RSS sources and write them to sqlite db
|
||||
columns: 6
|
||||
limit: 25
|
||||
db: "sqlite:////home/blacklight/.local/share/platypush/feeds/rss.db"
|
||||
|
||||
:type dashboard: dict
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.port = port
|
||||
|
@ -54,6 +116,7 @@ class HttpBackend(Backend):
|
|||
|
||||
|
||||
def stop(self):
|
||||
""" Stop the web server """
|
||||
self.logger.info('Received STOP event on HttpBackend')
|
||||
|
||||
if self.server_proc:
|
||||
|
@ -62,6 +125,7 @@ class HttpBackend(Backend):
|
|||
|
||||
|
||||
def notify_web_clients(self, event):
|
||||
""" Notify all the connected web clients (over websocket) of a new event """
|
||||
import websockets
|
||||
|
||||
async def send_event(websocket):
|
||||
|
@ -77,6 +141,7 @@ class HttpBackend(Backend):
|
|||
|
||||
|
||||
def redis_poll(self):
|
||||
""" Polls for new messages on the internal Redis queue """
|
||||
while not self.should_stop():
|
||||
msg = self.redis.blpop(self.redis_queue)
|
||||
msg = Message.build(json.loads(msg[1].decode('utf-8')))
|
||||
|
@ -84,6 +149,7 @@ class HttpBackend(Backend):
|
|||
|
||||
|
||||
def webserver(self):
|
||||
""" Web server main process """
|
||||
basedir = os.path.dirname(inspect.getfile(self.__class__))
|
||||
template_dir = os.path.join(basedir, 'templates')
|
||||
static_dir = os.path.join(basedir, 'static')
|
||||
|
@ -92,6 +158,7 @@ class HttpBackend(Backend):
|
|||
|
||||
@app.route('/execute', methods=['POST'])
|
||||
def execute():
|
||||
""" Endpoint to execute commands """
|
||||
args = json.loads(http_request.data.decode('utf-8'))
|
||||
token = http_request.headers['X-Token'] if 'X-Token' in http_request.headers else None
|
||||
if token != self.token: abort(401)
|
||||
|
@ -111,6 +178,7 @@ class HttpBackend(Backend):
|
|||
|
||||
@app.route('/')
|
||||
def index():
|
||||
""" Route to the main web panel """
|
||||
configured_plugins = Config.get_plugins()
|
||||
enabled_plugins = {}
|
||||
hidden_plugins = {}
|
||||
|
@ -129,6 +197,7 @@ class HttpBackend(Backend):
|
|||
|
||||
@app.route('/widget/<widget>', methods=['POST'])
|
||||
def widget_update(widget):
|
||||
""" ``POST /widget/<widget_id>`` will update the specified widget_id on the dashboard with the specified key-values """
|
||||
event = WidgetUpdateEvent(
|
||||
widget=widget, **(json.loads(http_request.data.decode('utf-8'))))
|
||||
|
||||
|
@ -137,10 +206,12 @@ class HttpBackend(Backend):
|
|||
|
||||
@app.route('/static/<path>', methods=['GET'])
|
||||
def static_path(path):
|
||||
""" Static resources """
|
||||
return send_from_directory(static_dir, filename)
|
||||
|
||||
@app.route('/dashboard', methods=['GET'])
|
||||
def dashboard():
|
||||
""" Route for the fullscreen dashboard """
|
||||
return render_template('dashboard.html', config=self.dashboard, utils=HttpUtils,
|
||||
token=self.token, websocket_port=self.websocket_port)
|
||||
|
||||
|
@ -219,6 +290,7 @@ class HttpBackend(Backend):
|
|||
|
||||
|
||||
def websocket(self):
|
||||
""" Websocket main server """
|
||||
import websockets
|
||||
|
||||
async def register_websocket(websocket, path):
|
||||
|
|
|
@ -12,31 +12,47 @@ from platypush.message.request import Request
|
|||
class HttpPollBackend(Backend):
|
||||
"""
|
||||
This backend will poll multiple HTTP endpoints/services and return events
|
||||
the bus whenever something new happened. Example configuration:
|
||||
the bus whenever something new happened. Supported types:
|
||||
:class:`platypush.backend.http.request.JsonHttpRequest` (for polling updates on
|
||||
a JSON endpoint), :class:`platypush.backend.http.request.rss.RssUpdates`
|
||||
(for polling updates on an RSS feed). Example configuration::
|
||||
|
||||
backend.http.poll:
|
||||
requests:
|
||||
-
|
||||
method: GET
|
||||
type: platypush.backend.http.request.JsonHttpRequest
|
||||
args:
|
||||
url: https://host.com/api/v1/endpoint
|
||||
headers:
|
||||
Token: TOKEN
|
||||
params:
|
||||
updatedSince: 1m
|
||||
timeout: 5 # Times out after 5 seconds (default)
|
||||
poll_seconds: 60 # Check for updates on this endpoint every 60 seconds (default)
|
||||
path: ${response['items']} # Path in the JSON to check for new items.
|
||||
# Python expressions are supported.
|
||||
# Note that 'response' identifies the JSON root.
|
||||
# Default value: JSON root.
|
||||
backend.http.poll:
|
||||
requests:
|
||||
-
|
||||
# Poll for updates on a JSON endpoint
|
||||
method: GET
|
||||
type: platypush.backend.http.request.JsonHttpRequest
|
||||
args:
|
||||
url: https://host.com/api/v1/endpoint
|
||||
headers:
|
||||
Token: TOKEN
|
||||
params:
|
||||
updatedSince: 1m
|
||||
timeout: 5 # Times out after 5 seconds (default)
|
||||
poll_seconds: 60 # Check for updates on this endpoint every 60 seconds (default)
|
||||
path: ${response['items']} # Path in the JSON to check for new items.
|
||||
# Python expressions are supported.
|
||||
# Note that 'response' identifies the JSON root.
|
||||
# Default value: JSON root.
|
||||
-
|
||||
# Poll for updates on an RSS feed
|
||||
type: platypush.backend.http.request.rss.RssUpdates
|
||||
url: http://www.theguardian.com/rss/world
|
||||
title: The Guardian - World News
|
||||
poll_seconds: 120
|
||||
max_entries: 10
|
||||
|
||||
Triggers: an update event for the relevant HTTP source if it contains new items. For example:
|
||||
|
||||
* :class:`platypush.message.event.http.rss.NewFeedEvent` if a feed contains new items
|
||||
* :class:`platypush.message.event.http.HttpEvent` if a JSON endpoint contains new items
|
||||
"""
|
||||
|
||||
def __init__(self, requests, *args, **kwargs):
|
||||
"""
|
||||
Params:
|
||||
requests -- List/iterable of HttpRequest objects
|
||||
:param requests: Configuration of the requests to make (see class description for examples)
|
||||
:type requests: dict
|
||||
"""
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -67,9 +83,5 @@ class HttpPollBackend(Backend):
|
|||
time.sleep(0.1) # Prevent a tight loop
|
||||
|
||||
|
||||
def send_message(self, msg):
|
||||
pass
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
||||
|
|
|
@ -7,17 +7,38 @@ from platypush.message.event.path import PathCreateEvent, PathDeleteEvent, \
|
|||
|
||||
|
||||
class InotifyBackend(Backend):
|
||||
"""
|
||||
(Linux only) This backend will listen for events on the filesystem (whether
|
||||
a file/directory on a watch list is opened, modified, created, deleted,
|
||||
closed or had its permissions changed) and will trigger a relevant event.
|
||||
|
||||
Triggers:
|
||||
|
||||
* :class:`platypush.message.event.path.PathCreateEvent` if a resource is created
|
||||
* :class:`platypush.message.event.path.PathOpenEvent` if a resource is opened
|
||||
* :class:`platypush.message.event.path.PathModifyEvent` if a resource is modified
|
||||
* :class:`platypush.message.event.path.PathPermissionsChangeEvent` if the permissions of a resource are changed
|
||||
* :class:`platypush.message.event.path.PathCloseEvent` if a resource is closed
|
||||
* :class:`platypush.message.event.path.PathDeleteEvent` if a resource is removed
|
||||
|
||||
Requires:
|
||||
|
||||
* **inotify** (``pip install inotify``)
|
||||
"""
|
||||
|
||||
inotify_watch = None
|
||||
|
||||
def __init__(self, watch_paths=[], **kwargs):
|
||||
"""
|
||||
:param watch_paths: Filesystem resources to watch for events
|
||||
:type watch_paths: str
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
self.watch_paths = set(map(
|
||||
lambda path: os.path.abspath(os.path.expanduser(path)),
|
||||
watch_paths))
|
||||
|
||||
def send_message(self, msg):
|
||||
pass
|
||||
|
||||
def _cleanup(self):
|
||||
if not self.inotify_watch:
|
||||
return
|
||||
|
|
|
@ -8,9 +8,26 @@ from .. import Backend
|
|||
|
||||
|
||||
class KafkaBackend(Backend):
|
||||
"""
|
||||
Backend to interact with an Apache Kafka (https://kafka.apache.org/)
|
||||
streaming platform, send and receive messages.
|
||||
|
||||
Requires:
|
||||
|
||||
* **kafka** (``pip install kafka-python``)
|
||||
"""
|
||||
|
||||
_conn_retry_secs = 5
|
||||
|
||||
def __init__(self, server, topic, **kwargs):
|
||||
def __init__(self, server, topic='platypush', **kwargs):
|
||||
"""
|
||||
:param server: Kafka server
|
||||
:type server: str
|
||||
|
||||
:param topic: (Prefix) topic to listen to (default: platypush). The Platypush device_id (by default the hostname) will be appended to the topic (the real topic name will e.g. be "platypush.my_rpi")
|
||||
:type topic: str
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.server = server
|
||||
|
|
|
@ -14,22 +14,28 @@ class MidiBackend(Backend):
|
|||
This backend will listen for events from a MIDI device and post a
|
||||
MidiMessageEvent whenever a new MIDI event happens.
|
||||
|
||||
It requires `rtmidi`, `pip install rtmidi`
|
||||
Triggers:
|
||||
|
||||
* :class:`platypush.message.event.midi.MidiMessageEvent` when a new MIDI event is received
|
||||
|
||||
Requires:
|
||||
|
||||
* **rtmidi** (``pip install rtmidi``)
|
||||
"""
|
||||
|
||||
def __init__(self, device_name=None, port_number=None,
|
||||
midi_throttle_time=None, *args, **kwargs):
|
||||
"""
|
||||
Params:
|
||||
device_name -- Name of the MIDI device.
|
||||
*N.B.* either `device_name` or `port_number` must be set
|
||||
port_number -- MIDI port number
|
||||
*N.B.* either `device_name` or `port_number` must be set
|
||||
midi_throttle_time -- If set, the MIDI events will be throttled -
|
||||
max one per selected time frame (in seconds). Set this parameter
|
||||
if you want to synchronize MIDI events with plugins that
|
||||
normally operate with a lower throughput.
|
||||
:param device_name: Name of the MIDI device. *N.B.* either `device_name` or `port_number` must be set
|
||||
:type device_name: str
|
||||
|
||||
:param port_number: MIDI port number
|
||||
:type port_number: int
|
||||
|
||||
:param midi_throttle_time: If set, the MIDI events will be throttled - max one per selected time frame (in seconds). Set this parameter if you want to synchronize MIDI events with plugins that normally operate with a lower throughput.
|
||||
:type midi_throttle_time: int
|
||||
"""
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if (device_name and port_number is not None) or \
|
||||
|
|
|
@ -9,17 +9,26 @@ from platypush.message import Message
|
|||
|
||||
class MqttBackend(Backend):
|
||||
"""
|
||||
Backend that reads messages from a configured MQTT topic
|
||||
(default: `platypush_bus_mq/<device_id>`) and posts them to the application bus.
|
||||
Backend that reads messages from a configured MQTT topic (default:
|
||||
``platypush_bus_mq/<device_id>``) and posts them to the application bus.
|
||||
|
||||
Requires:
|
||||
|
||||
* **paho-mqtt** (``pip install paho-mqtt``)
|
||||
"""
|
||||
|
||||
def __init__(self, host, port=1883, topic='platypush_bus_mq', *args, **kwargs):
|
||||
"""
|
||||
Params:
|
||||
host -- MQTT broker host
|
||||
port -- MQTT broker port (default: 1883)
|
||||
topic -- Topic to read messages from (default: platypush_bus_mq/<device_id>)
|
||||
:param host: MQTT broker host
|
||||
:type host: str
|
||||
|
||||
:param port: MQTT broker port (default: 1883)
|
||||
:type port: int
|
||||
|
||||
:param topic: Topic to read messages from (default: ``platypush_bus_mq/<device_id>``)
|
||||
:type topic: str
|
||||
"""
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.host = host
|
||||
|
|
|
@ -8,7 +8,28 @@ from platypush.message.event.music import MusicPlayEvent, MusicPauseEvent, \
|
|||
|
||||
|
||||
class MusicMpdBackend(Backend):
|
||||
"""
|
||||
This backend listens for events on a MPD/Mopidy music server.
|
||||
|
||||
Triggers:
|
||||
|
||||
* :class:`platypush.message.event.music.MusicPlayEvent` if the playback state changed to play
|
||||
* :class:`platypush.message.event.music.MusicPauseEvent` if the playback state changed to pause
|
||||
* :class:`platypush.message.event.music.MusicStopEvent` if the playback state changed to stop
|
||||
* :class:`platypush.message.event.music.NewPlayingTrackEvent` if a new track is being played
|
||||
* :class:`platypush.message.event.music.PlaylistChangeEvent` if the main playlist has changed
|
||||
|
||||
Requires:
|
||||
* **python-mpd2** (``pip install python-mpd2``)
|
||||
* The :mod:`platypush.plugins.music.mpd` plugin to be configured
|
||||
"""
|
||||
|
||||
def __init__(self, server='localhost', port=6600, poll_seconds=3, **kwargs):
|
||||
"""
|
||||
:param poll_seconds: Interval between queries to the server (default: 3 seconds)
|
||||
:type poll_seconds: float
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.server = server
|
||||
|
@ -16,10 +37,6 @@ class MusicMpdBackend(Backend):
|
|||
self.poll_seconds = poll_seconds
|
||||
|
||||
|
||||
def send_message(self, msg):
|
||||
pass
|
||||
|
||||
|
||||
def run(self):
|
||||
super().run()
|
||||
|
||||
|
|
|
@ -11,7 +11,33 @@ from .. import Backend
|
|||
|
||||
|
||||
class PushbulletBackend(Backend):
|
||||
def __init__(self, token, device, **kwargs):
|
||||
"""
|
||||
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:
|
||||
|
||||
* :class:`platypush.message.event.pushbullet` if a new push is received
|
||||
|
||||
Requires:
|
||||
|
||||
* **requests** (``pip install requests``)
|
||||
* **websocket-client** (``pip install websocket-client``)
|
||||
"""
|
||||
|
||||
def __init__(self, token, device='Platypush', **kwargs):
|
||||
"""
|
||||
:param token: Your Pushbullet API token, see https://docs.pushbullet.com/#authentication
|
||||
:type token: str
|
||||
|
||||
:param device: Name of the virtual device for Platypush (default: Platypush)
|
||||
:type device: str
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.token = token
|
||||
|
|
|
@ -8,20 +8,25 @@ from platypush.message import Message
|
|||
|
||||
class RedisBackend(Backend):
|
||||
"""
|
||||
Backend that reads messages from a configured Redis queue
|
||||
(default: `platypush_bus_mq`) and posts them to the application bus.
|
||||
Very useful when you have plugin whose code is executed in another process
|
||||
Backend that reads messages from a configured Redis queue (default:
|
||||
``platypush_bus_mq``) and posts them to the application bus. Very
|
||||
useful when you have plugin whose code is executed in another process
|
||||
and can't post events or requests to the application bus.
|
||||
|
||||
Requires:
|
||||
|
||||
* **redis** (``pip install redis``)
|
||||
"""
|
||||
|
||||
def __init__(self, queue='platypush_bus_mq', redis_args={}, *args, **kwargs):
|
||||
"""
|
||||
Params:
|
||||
queue -- Queue to poll for new messages
|
||||
redis_args -- Arguments that will be passed to the redis-py
|
||||
constructor (e.g. host, port, password),
|
||||
see http://redis-py.readthedocs.io/en/latest/
|
||||
:param queue: Queue name to listen on (default: ``platypush_bus_mq``)
|
||||
:type queue: str
|
||||
|
||||
:param redis_args: Arguments that will be passed to the redis-py constructor (e.g. host, port, password), see http://redis-py.readthedocs.io/en/latest/
|
||||
:type redis_args: dict
|
||||
"""
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.queue = queue
|
||||
|
|
|
@ -11,22 +11,27 @@ from platypush.message.event.scard import SmartCardDetectedEvent, SmartCardRemov
|
|||
|
||||
class ScardBackend(Backend):
|
||||
"""
|
||||
Generic backend to read smart cards and trigger SmartCardDetectedEvent
|
||||
messages with the card ATR whenever a card is detected. It requires
|
||||
pyscard https://pypi.org/project/pyscard/
|
||||
Generic backend to read smart cards and NFC tags and trigger an event
|
||||
whenever a device is detected.
|
||||
|
||||
Extend this backend to implement more advanced communication with
|
||||
custom smart cards.
|
||||
Extend this backend to implement more advanced communication with custom
|
||||
smart cards.
|
||||
|
||||
Triggers:
|
||||
|
||||
* :class:`platypush.message.event.scard.SmartCardDetectedEvent` when a smart card is detected
|
||||
* :class:`platypush.message.event.scard.SmartCardRemovedEvent` when a smart card is removed
|
||||
|
||||
Requires:
|
||||
|
||||
* **pyscard** (``pip install pyscard``)
|
||||
"""
|
||||
|
||||
def __init__(self, atr=None, *args, **kwargs):
|
||||
"""
|
||||
Params:
|
||||
atr -- If set, the backend will trigger events only for card(s)
|
||||
with the specified ATR(s). It can be either an ATR string
|
||||
(space-separated hex octects) or a list of ATR strings.
|
||||
Default: none (any card will be detected)
|
||||
:param atr: If set, the backend will trigger events only for card(s) with the specified ATR(s). It can be either an ATR string (space-separated hex octects) or a list of ATR strings. Default: none (any card will be detected)
|
||||
"""
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
self.ATRs = []
|
||||
|
||||
|
|
|
@ -6,25 +6,28 @@ from platypush.message.event.sensor import SensorDataChangeEvent, \
|
|||
|
||||
|
||||
class SensorBackend(Backend):
|
||||
"""
|
||||
Abstract backend for polling sensors.
|
||||
|
||||
Triggers:
|
||||
|
||||
* :class:`platypush.message.event.sensor.SensorDataChangeEvent` if the measurements of a sensor have changed
|
||||
* :class:`platypush.message.event.sensor.SensorDataAboveThresholdEvent` if the measurements of a sensor have gone above a configured threshold
|
||||
* :class:`platypush.message.event.sensor.SensorDataBelowThresholdEvent` if the measurements of a sensor have gone below a configured threshold
|
||||
"""
|
||||
|
||||
def __init__(self, thresholds=None, poll_seconds=None, *args, **kwargs):
|
||||
"""
|
||||
Params:
|
||||
-- thresholds: Thresholds can be either a scalr value or a dictionary.
|
||||
:param thresholds: Thresholds can be either a scalar value or a dictionary (e.g. ``{"temperature": 20.0}``). Sensor threshold events will be fired when measurements get above or below these values. Set it as a scalar if your get_measurement() code returns a scalar, as a dictionary if it returns a dictionary of values.
|
||||
|
||||
If set, SensorDataAboveThresholdEvent and SensorDataBelowThresholdEvent
|
||||
events will be triggered whenever the measurement goes above or
|
||||
below that value.
|
||||
For instance, if your sensor code returns both humidity and
|
||||
temperature in a format like ``{'humidity':60.0, 'temperature': 25.0}``,
|
||||
you'll want to set up a threshold on temperature with a syntax like
|
||||
``{'temperature':20.0}`` to trigger events when the temperature goes
|
||||
above/below 20 degrees.
|
||||
|
||||
Set it as a scalar if your get_measurement() code returns a scalar,
|
||||
as a dictionary if it returns a dictionary of values.
|
||||
|
||||
For instance, if your sensor code returns both humidity and
|
||||
temperature in a format like {'humidity':60.0, 'temperature': 25.0},
|
||||
you'll want to set up a threshold on temperature with a syntax like
|
||||
{'temperature':20.0}
|
||||
|
||||
-- poll_seconds: If set, the thread will wait for the specificed
|
||||
number of seconds between a read and the next one.
|
||||
:param poll_seconds: If set, the thread will wait for the specificed number of seconds between a read and the next one.
|
||||
:type poll_seconds: float
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
@ -34,6 +37,7 @@ class SensorBackend(Backend):
|
|||
self.poll_seconds = poll_seconds
|
||||
|
||||
def get_measurement(self):
|
||||
""" To be implemented in the derived classes """
|
||||
raise NotImplementedError('To be implemented in a derived class')
|
||||
|
||||
def run(self):
|
||||
|
|
Loading…
Reference in a new issue