forked from platypush/platypush
Updated email addresses and black'd some old source files.
This commit is contained in:
parent
cf8ecf349b
commit
66981bd00b
18 changed files with 358 additions and 256 deletions
|
@ -25,7 +25,7 @@ from .message.request import Request
|
|||
from .message.response import Response
|
||||
from .utils import set_thread_name, get_enabled_plugins
|
||||
|
||||
__author__ = 'Fabio Manganiello <info@fabiomanganiello.com>'
|
||||
__author__ = 'Fabio Manganiello <fabio@manganiello.tech>'
|
||||
__version__ = '0.50.2'
|
||||
|
||||
log = logging.getLogger('platypush')
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
.. license: MIT
|
||||
"""
|
||||
|
||||
import logging
|
||||
import re
|
||||
import socket
|
||||
|
@ -15,9 +10,17 @@ from platypush.bus import Bus
|
|||
from platypush.common import ExtensionWithManifest
|
||||
from platypush.config import Config
|
||||
from platypush.context import get_backend
|
||||
from platypush.message.event.zeroconf import ZeroconfServiceAddedEvent, ZeroconfServiceRemovedEvent
|
||||
from platypush.utils import set_timeout, clear_timeout, \
|
||||
get_redis_queue_name_by_message, set_thread_name, get_backend_name_by_class
|
||||
from platypush.message.event.zeroconf import (
|
||||
ZeroconfServiceAddedEvent,
|
||||
ZeroconfServiceRemovedEvent,
|
||||
)
|
||||
from platypush.utils import (
|
||||
set_timeout,
|
||||
clear_timeout,
|
||||
get_redis_queue_name_by_message,
|
||||
set_thread_name,
|
||||
get_backend_name_by_class,
|
||||
)
|
||||
|
||||
from platypush import __version__
|
||||
from platypush.event import EventGenerator
|
||||
|
@ -44,7 +47,9 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
# Loop function, can be implemented by derived classes
|
||||
loop = None
|
||||
|
||||
def __init__(self, bus: Optional[Bus] = None, poll_seconds: Optional[float] = None, **kwargs):
|
||||
def __init__(
|
||||
self, bus: Optional[Bus] = None, poll_seconds: Optional[float] = None, **kwargs
|
||||
):
|
||||
"""
|
||||
:param bus: Reference to the bus object to be used in the backend
|
||||
:param poll_seconds: If the backend implements a ``loop`` method, this parameter expresses how often the
|
||||
|
@ -65,14 +70,15 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
self.thread_id = None
|
||||
self._stop_event = ThreadEvent()
|
||||
self._kwargs = kwargs
|
||||
self.logger = logging.getLogger('platypush:backend:' + get_backend_name_by_class(self.__class__))
|
||||
self.logger = logging.getLogger(
|
||||
'platypush:backend:' + get_backend_name_by_class(self.__class__)
|
||||
)
|
||||
self.zeroconf = None
|
||||
self.zeroconf_info = None
|
||||
|
||||
# Internal-only, we set the request context on a backend if that
|
||||
# backend is intended to react for a response to a specific request
|
||||
self._request_context = kwargs['_req_ctx'] if '_req_ctx' in kwargs \
|
||||
else None
|
||||
self._request_context = kwargs['_req_ctx'] if '_req_ctx' in kwargs else None
|
||||
|
||||
if 'logging' in kwargs:
|
||||
self.logger.setLevel(getattr(logging, kwargs.get('logging').upper()))
|
||||
|
@ -90,11 +96,14 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
|
||||
msg = Message.build(msg)
|
||||
|
||||
if not getattr(msg, 'target') or msg.target != self.device_id:
|
||||
if not getattr(msg, 'target', None) or msg.target != self.device_id:
|
||||
return # Not for me
|
||||
|
||||
self.logger.debug('Message received on the {} backend: {}'.format(
|
||||
self.__class__.__name__, msg))
|
||||
self.logger.debug(
|
||||
'Message received on the {} backend: {}'.format(
|
||||
self.__class__.__name__, msg
|
||||
)
|
||||
)
|
||||
|
||||
if self._is_expected_response(msg):
|
||||
# Expected response, trigger the response handler
|
||||
|
@ -108,22 +117,27 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
self.bus.post(msg)
|
||||
|
||||
def _is_expected_response(self, msg):
|
||||
""" Internal only - returns true if we are expecting for a response
|
||||
and msg is that response """
|
||||
"""Internal only - returns true if we are expecting for a response
|
||||
and msg is that response"""
|
||||
|
||||
# pylint: disable=unsubscriptable-object
|
||||
return self._request_context \
|
||||
and isinstance(msg, Response) \
|
||||
return (
|
||||
self._request_context
|
||||
and isinstance(msg, Response)
|
||||
and msg.id == self._request_context['request'].id
|
||||
)
|
||||
|
||||
def _get_backend_config(self):
|
||||
config_name = 'backend.' + self.__class__.__name__.split('Backend', maxsplit=1)[0].lower()
|
||||
config_name = (
|
||||
'backend.' + self.__class__.__name__.split('Backend', maxsplit=1)[0].lower()
|
||||
)
|
||||
return Config.get(config_name)
|
||||
|
||||
def _setup_response_handler(self, request, on_response, response_timeout):
|
||||
def _timeout_hndl():
|
||||
raise RuntimeError('Timed out while waiting for a response from {}'.
|
||||
format(request.target))
|
||||
raise RuntimeError(
|
||||
'Timed out while waiting for a response from {}'.format(request.target)
|
||||
)
|
||||
|
||||
req_ctx = {
|
||||
'request': request,
|
||||
|
@ -131,8 +145,9 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
'response_timeout': response_timeout,
|
||||
}
|
||||
|
||||
resp_backend = self.__class__(bus=self.bus, _req_ctx=req_ctx,
|
||||
**self._get_backend_config(), **self._kwargs)
|
||||
resp_backend = self.__class__(
|
||||
bus=self.bus, _req_ctx=req_ctx, **self._get_backend_config(), **self._kwargs
|
||||
)
|
||||
|
||||
# Set the response timeout
|
||||
if response_timeout:
|
||||
|
@ -157,8 +172,13 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
|
||||
self.send_message(event, **kwargs)
|
||||
|
||||
def send_request(self, request, on_response=None,
|
||||
response_timeout=_default_response_timeout, **kwargs):
|
||||
def send_request(
|
||||
self,
|
||||
request,
|
||||
on_response=None,
|
||||
response_timeout=_default_response_timeout,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
Send a request message on the backend.
|
||||
|
||||
|
@ -215,16 +235,18 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
if not redis:
|
||||
raise KeyError()
|
||||
except KeyError:
|
||||
self.logger.warning((
|
||||
self.logger.warning(
|
||||
(
|
||||
"Backend {} does not implement send_message "
|
||||
"and the fallback Redis backend isn't configured"
|
||||
).format(self.__class__.__name__))
|
||||
).format(self.__class__.__name__)
|
||||
)
|
||||
return
|
||||
|
||||
redis.send_message(msg, queue_name=queue_name)
|
||||
|
||||
def run(self):
|
||||
""" Starts the backend thread. To be implemented in the derived classes if the loop method isn't defined. """
|
||||
"""Starts the backend thread. To be implemented in the derived classes if the loop method isn't defined."""
|
||||
self.thread_id = get_ident()
|
||||
set_thread_name(self._thread_name)
|
||||
if not callable(self.loop):
|
||||
|
@ -249,24 +271,29 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
elif has_error:
|
||||
time.sleep(5)
|
||||
except Exception as e:
|
||||
self.logger.error('{} initialization error: {}'.format(self.__class__.__name__, str(e)))
|
||||
self.logger.error(
|
||||
'{} initialization error: {}'.format(
|
||||
self.__class__.__name__, str(e)
|
||||
)
|
||||
)
|
||||
self.logger.exception(e)
|
||||
time.sleep(self.poll_seconds or 5)
|
||||
|
||||
def __enter__(self):
|
||||
""" Invoked when the backend is initialized, if the main logic is within a ``loop()`` function """
|
||||
"""Invoked when the backend is initialized, if the main logic is within a ``loop()`` function"""
|
||||
self.logger.info('Initialized backend {}'.format(self.__class__.__name__))
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
""" Invoked when the backend is terminated, if the main logic is within a ``loop()`` function """
|
||||
"""Invoked when the backend is terminated, if the main logic is within a ``loop()`` function"""
|
||||
self.on_stop()
|
||||
self.logger.info('Terminated backend {}'.format(self.__class__.__name__))
|
||||
|
||||
def on_stop(self):
|
||||
""" Callback invoked when the process stops """
|
||||
"""Callback invoked when the process stops"""
|
||||
|
||||
def stop(self):
|
||||
""" Stops the backend thread by sending a STOP event on its bus """
|
||||
"""Stops the backend thread by sending a STOP event on its bus"""
|
||||
|
||||
def _async_stop():
|
||||
self._stop_event.set()
|
||||
self.unregister_service()
|
||||
|
@ -285,8 +312,10 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
|
||||
redis_backend = get_backend('redis')
|
||||
if not redis_backend:
|
||||
self.logger.warning('Redis backend not configured - some '
|
||||
'web server features may not be working properly')
|
||||
self.logger.warning(
|
||||
'Redis backend not configured - some '
|
||||
'web server features may not be working properly'
|
||||
)
|
||||
redis_args = {}
|
||||
else:
|
||||
redis_args = redis_backend.redis_args
|
||||
|
@ -305,7 +334,9 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
|
||||
return response
|
||||
except Exception as e:
|
||||
self.logger.error('Error while processing response to {}: {}'.format(msg, str(e)))
|
||||
self.logger.error(
|
||||
'Error while processing response to {}: {}'.format(msg, str(e))
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_ip() -> str:
|
||||
|
@ -318,13 +349,15 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
s.close()
|
||||
return addr
|
||||
|
||||
def register_service(self,
|
||||
def register_service(
|
||||
self,
|
||||
port: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
srv_type: Optional[str] = None,
|
||||
srv_name: Optional[str] = None,
|
||||
udp: bool = False,
|
||||
properties: Optional[Dict] = None):
|
||||
properties: Optional[Dict] = None,
|
||||
):
|
||||
"""
|
||||
Initialize the Zeroconf service configuration for this backend.
|
||||
|
||||
|
@ -348,7 +381,9 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
from zeroconf import ServiceInfo, Zeroconf
|
||||
from platypush.plugins.zeroconf import ZeroconfListener
|
||||
except ImportError:
|
||||
self.logger.warning('zeroconf package not available, service discovery will be disabled.')
|
||||
self.logger.warning(
|
||||
'zeroconf package not available, service discovery will be disabled.'
|
||||
)
|
||||
return
|
||||
|
||||
self.zeroconf = Zeroconf()
|
||||
|
@ -360,28 +395,40 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
}
|
||||
|
||||
name = name or re.sub(r'Backend$', '', self.__class__.__name__).lower()
|
||||
srv_type = srv_type or '_platypush-{name}._{proto}.local.'.format(name=name, proto='udp' if udp else 'tcp')
|
||||
srv_name = srv_name or '{host}.{type}'.format(host=self.device_id, type=srv_type)
|
||||
srv_type = srv_type or '_platypush-{name}._{proto}.local.'.format(
|
||||
name=name, proto='udp' if udp else 'tcp'
|
||||
)
|
||||
srv_name = srv_name or '{host}.{type}'.format(
|
||||
host=self.device_id, type=srv_type
|
||||
)
|
||||
|
||||
if port:
|
||||
srv_port = port
|
||||
else:
|
||||
srv_port = self.port if hasattr(self, 'port') else None
|
||||
|
||||
self.zeroconf_info = ServiceInfo(srv_type, srv_name,
|
||||
self.zeroconf_info = ServiceInfo(
|
||||
srv_type,
|
||||
srv_name,
|
||||
addresses=[socket.inet_aton(self._get_ip())],
|
||||
port=srv_port,
|
||||
weight=0,
|
||||
priority=0,
|
||||
properties=srv_desc)
|
||||
properties=srv_desc,
|
||||
)
|
||||
|
||||
if not self.zeroconf_info:
|
||||
self.logger.warning('Could not register Zeroconf service')
|
||||
return
|
||||
|
||||
self.zeroconf.register_service(self.zeroconf_info)
|
||||
self.bus.post(ZeroconfServiceAddedEvent(service_type=srv_type, service_name=srv_name,
|
||||
service_info=ZeroconfListener.parse_service_info(self.zeroconf_info)))
|
||||
self.bus.post(
|
||||
ZeroconfServiceAddedEvent(
|
||||
service_type=srv_type,
|
||||
service_name=srv_name,
|
||||
service_info=ZeroconfListener.parse_service_info(self.zeroconf_info),
|
||||
)
|
||||
)
|
||||
|
||||
def unregister_service(self):
|
||||
"""
|
||||
|
@ -391,17 +438,26 @@ class Backend(Thread, EventGenerator, ExtensionWithManifest):
|
|||
try:
|
||||
self.zeroconf.unregister_service(self.zeroconf_info)
|
||||
except Exception as e:
|
||||
self.logger.warning('Could not register Zeroconf service {}: {}: {}'.format(
|
||||
self.zeroconf_info.name, type(e).__name__, str(e)))
|
||||
self.logger.warning(
|
||||
'Could not register Zeroconf service {}: {}: {}'.format(
|
||||
self.zeroconf_info.name, type(e).__name__, str(e)
|
||||
)
|
||||
)
|
||||
|
||||
if self.zeroconf:
|
||||
self.zeroconf.close()
|
||||
|
||||
if self.zeroconf_info:
|
||||
self.bus.post(ZeroconfServiceRemovedEvent(service_type=self.zeroconf_info.type,
|
||||
service_name=self.zeroconf_info.name))
|
||||
self.bus.post(
|
||||
ZeroconfServiceRemovedEvent(
|
||||
service_type=self.zeroconf_info.type,
|
||||
service_name=self.zeroconf_info.name,
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.bus.post(ZeroconfServiceRemovedEvent(service_type=None, service_name=None))
|
||||
self.bus.post(
|
||||
ZeroconfServiceRemovedEvent(service_type=None, service_name=None)
|
||||
)
|
||||
|
||||
self.zeroconf_info = None
|
||||
self.zeroconf = None
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
.. license: MIT
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
.. license: MIT
|
||||
"""
|
||||
|
||||
import os
|
||||
import threading
|
||||
|
||||
|
@ -89,10 +84,14 @@ class AssistantSnowboyBackend(AssistantBackend):
|
|||
self.detector = snowboydecoder.HotwordDetector(
|
||||
[model['voice_model_file'] for model in self.models.values()],
|
||||
sensitivity=[model['sensitivity'] for model in self.models.values()],
|
||||
audio_gain=self.audio_gain)
|
||||
audio_gain=self.audio_gain,
|
||||
)
|
||||
|
||||
self.logger.info('Initialized Snowboy hotword detection with {} voice model configurations'.
|
||||
format(len(self.models)))
|
||||
self.logger.info(
|
||||
'Initialized Snowboy hotword detection with {} voice model configurations'.format(
|
||||
len(self.models)
|
||||
)
|
||||
)
|
||||
|
||||
def _init_models(self, models):
|
||||
if not models:
|
||||
|
@ -107,7 +106,9 @@ class AssistantSnowboyBackend(AssistantBackend):
|
|||
detect_sound = conf.get('detect_sound')
|
||||
|
||||
if not model_file:
|
||||
raise AttributeError('No voice_model_file specified for model {}'.format(name))
|
||||
raise AttributeError(
|
||||
'No voice_model_file specified for model {}'.format(name)
|
||||
)
|
||||
|
||||
model_file = os.path.abspath(os.path.expanduser(model_file))
|
||||
assistant_plugin_name = conf.get('assistant_plugin')
|
||||
|
@ -116,14 +117,19 @@ class AssistantSnowboyBackend(AssistantBackend):
|
|||
detect_sound = os.path.abspath(os.path.expanduser(detect_sound))
|
||||
|
||||
if not os.path.isfile(model_file):
|
||||
raise FileNotFoundError('Voice model file {} does not exist or it not a regular file'.
|
||||
format(model_file))
|
||||
raise FileNotFoundError(
|
||||
'Voice model file {} does not exist or it not a regular file'.format(
|
||||
model_file
|
||||
)
|
||||
)
|
||||
|
||||
self.models[name] = {
|
||||
'voice_model_file': model_file,
|
||||
'sensitivity': conf.get('sensitivity', 0.5),
|
||||
'detect_sound': detect_sound,
|
||||
'assistant_plugin': get_plugin(assistant_plugin_name) if assistant_plugin_name else None,
|
||||
'assistant_plugin': get_plugin(assistant_plugin_name)
|
||||
if assistant_plugin_name
|
||||
else None,
|
||||
'assistant_language': conf.get('assistant_language'),
|
||||
'tts_plugin': conf.get('tts_plugin'),
|
||||
'tts_args': conf.get('tts_args', {}),
|
||||
|
@ -143,7 +149,9 @@ class AssistantSnowboyBackend(AssistantBackend):
|
|||
|
||||
def callback():
|
||||
if not self.is_detecting():
|
||||
self.logger.info('Hotword detected but assistant response currently paused')
|
||||
self.logger.info(
|
||||
'Hotword detected but assistant response currently paused'
|
||||
)
|
||||
return
|
||||
|
||||
self.bus.post(HotwordDetectedEvent(hotword=hotword))
|
||||
|
@ -159,8 +167,11 @@ class AssistantSnowboyBackend(AssistantBackend):
|
|||
threading.Thread(target=sound_thread, args=(detect_sound,)).start()
|
||||
|
||||
if assistant_plugin:
|
||||
assistant_plugin.start_conversation(language=assistant_language, tts_plugin=tts_plugin,
|
||||
tts_args=tts_args)
|
||||
assistant_plugin.start_conversation(
|
||||
language=assistant_language,
|
||||
tts_plugin=tts_plugin,
|
||||
tts_args=tts_args,
|
||||
)
|
||||
|
||||
return callback
|
||||
|
||||
|
@ -172,10 +183,11 @@ class AssistantSnowboyBackend(AssistantBackend):
|
|||
|
||||
def run(self):
|
||||
super().run()
|
||||
self.detector.start(detected_callback=[
|
||||
self.hotword_detected(hotword)
|
||||
for hotword in self.models.keys()
|
||||
])
|
||||
self.detector.start(
|
||||
detected_callback=[
|
||||
self.hotword_detected(hotword) for hotword in self.models.keys()
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
import re
|
||||
import time
|
||||
|
||||
from platypush.backend import Backend
|
||||
from platypush.message.event.wiimote import WiimoteEvent, \
|
||||
WiimoteConnectionEvent, WiimoteDisconnectionEvent
|
||||
from platypush.message.event.wiimote import (
|
||||
WiimoteEvent,
|
||||
WiimoteConnectionEvent,
|
||||
WiimoteDisconnectionEvent,
|
||||
)
|
||||
|
||||
|
||||
class WiimoteBackend(Backend):
|
||||
|
@ -30,8 +29,9 @@ class WiimoteBackend(Backend):
|
|||
_last_btn_event_time = 0
|
||||
_bdaddr = None
|
||||
|
||||
def __init__(self, bdaddr=_bdaddr, inactivity_timeout=_inactivity_timeout,
|
||||
*args, **kwargs):
|
||||
def __init__(
|
||||
self, bdaddr=_bdaddr, inactivity_timeout=_inactivity_timeout, *args, **kwargs
|
||||
):
|
||||
"""
|
||||
:param bdaddr: If set, connect to this specific Wiimote physical address (example: 00:11:22:33:44:55)
|
||||
:type bdaddr: str
|
||||
|
@ -55,7 +55,9 @@ class WiimoteBackend(Backend):
|
|||
self._wiimote = cwiid.Wiimote()
|
||||
|
||||
self._wiimote.enable(cwiid.FLAG_MOTIONPLUS)
|
||||
self._wiimote.rpt_mode = cwiid.RPT_ACC | cwiid.RPT_BTN | cwiid.RPT_MOTIONPLUS
|
||||
self._wiimote.rpt_mode = (
|
||||
cwiid.RPT_ACC | cwiid.RPT_BTN | cwiid.RPT_MOTIONPLUS
|
||||
)
|
||||
|
||||
self.logger.info('WiiMote connected')
|
||||
self._last_btn_event_time = time.time()
|
||||
|
@ -65,24 +67,34 @@ class WiimoteBackend(Backend):
|
|||
|
||||
def get_state(self):
|
||||
import cwiid
|
||||
|
||||
wm = self.get_wiimote()
|
||||
state = wm.state
|
||||
parsed_state = {}
|
||||
|
||||
# Get buttons
|
||||
all_btns = [attr for attr in dir(cwiid) if attr.startswith('BTN_')]
|
||||
parsed_state['buttons'] = {btn: True for btn in all_btns
|
||||
if state.get('buttons', 0) & getattr(cwiid, btn) != 0}
|
||||
parsed_state['buttons'] = {
|
||||
btn: True
|
||||
for btn in all_btns
|
||||
if state.get('buttons', 0) & getattr(cwiid, btn) != 0
|
||||
}
|
||||
|
||||
# Get LEDs
|
||||
all_leds = [attr for attr in dir(cwiid) if re.match('LED\d_ON', attr)]
|
||||
parsed_state['led'] = {led[:4]: True for led in all_leds
|
||||
if state.get('leds', 0) & getattr(cwiid, led) != 0}
|
||||
all_leds = [attr for attr in dir(cwiid) if re.match(r'LED\d_ON', attr)]
|
||||
parsed_state['led'] = {
|
||||
led[:4]: True
|
||||
for led in all_leds
|
||||
if state.get('leds', 0) & getattr(cwiid, led) != 0
|
||||
}
|
||||
|
||||
# Get errors
|
||||
all_errs = [attr for attr in dir(cwiid) if attr.startswith('ERROR_')]
|
||||
parsed_state['error'] = {err: True for err in all_errs
|
||||
if state.get('errs', 0) & getattr(cwiid, err) != 0}
|
||||
parsed_state['error'] = {
|
||||
err: True
|
||||
for err in all_errs
|
||||
if state.get('errs', 0) & getattr(cwiid, err) != 0
|
||||
}
|
||||
|
||||
parsed_state['battery'] = round(state.get('battery', 0) / cwiid.BATTERY_MAX, 3)
|
||||
parsed_state['rumble'] = bool(state.get('rumble', 0))
|
||||
|
@ -92,8 +104,9 @@ class WiimoteBackend(Backend):
|
|||
|
||||
if 'motionplus' in state:
|
||||
parsed_state['motionplus'] = {
|
||||
'angle_rate': tuple(int(angle / 100) for angle
|
||||
in state['motionplus']['angle_rate']),
|
||||
'angle_rate': tuple(
|
||||
int(angle / 100) for angle in state['motionplus']['angle_rate']
|
||||
),
|
||||
'low_speed': state['motionplus']['low_speed'],
|
||||
}
|
||||
|
||||
|
@ -121,23 +134,30 @@ class WiimoteBackend(Backend):
|
|||
while not self.should_stop():
|
||||
try:
|
||||
state = self.get_state()
|
||||
changed_state = {k: state[k] for k in state.keys()
|
||||
if state[k] != last_state.get(k)}
|
||||
changed_state = {
|
||||
k: state[k] for k in state.keys() if state[k] != last_state.get(k)
|
||||
}
|
||||
|
||||
if changed_state:
|
||||
self.bus.post(WiimoteEvent(**changed_state))
|
||||
|
||||
if 'buttons' in changed_state:
|
||||
self._last_btn_event_time = time.time()
|
||||
elif last_state and time.time() - \
|
||||
self._last_btn_event_time >= self._inactivity_timeout:
|
||||
elif (
|
||||
last_state
|
||||
and time.time() - self._last_btn_event_time
|
||||
>= self._inactivity_timeout
|
||||
):
|
||||
self.logger.info('Wiimote disconnected upon timeout')
|
||||
self.close()
|
||||
|
||||
last_state = state
|
||||
time.sleep(0.1)
|
||||
except RuntimeError as e:
|
||||
if type(e) == RuntimeError and str(e) == 'Error opening wiimote connection':
|
||||
if (
|
||||
type(e) == RuntimeError
|
||||
and str(e) == 'Error opening wiimote connection'
|
||||
):
|
||||
if self._connection_attempts == 0:
|
||||
self.logger.info('Press 1+2 to pair your WiiMote controller')
|
||||
else:
|
||||
|
@ -146,4 +166,5 @@ class WiimoteBackend(Backend):
|
|||
self.close()
|
||||
self._connection_attempts += 1
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
|
@ -3,9 +3,6 @@ Platydock
|
|||
|
||||
Platydock is a helper that allows you to easily manage (create, destroy, start,
|
||||
stop and list) Platypush instances as Docker images.
|
||||
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
.. license: MIT
|
||||
"""
|
||||
|
||||
import argparse
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from platypush.context import get_bus
|
||||
from platypush.plugins import action
|
||||
from platypush.plugins.assistant import AssistantPlugin
|
||||
|
||||
from platypush.message.event.assistant import ConversationStartEvent, \
|
||||
ConversationEndEvent, SpeechRecognizedEvent, ResponseEvent
|
||||
from platypush.message.event.assistant import (
|
||||
ConversationStartEvent,
|
||||
ConversationEndEvent,
|
||||
SpeechRecognizedEvent,
|
||||
ResponseEvent,
|
||||
)
|
||||
|
||||
|
||||
class AssistantEchoPlugin(AssistantPlugin):
|
||||
|
@ -38,8 +39,13 @@ class AssistantEchoPlugin(AssistantPlugin):
|
|||
* **avs** (``pip install avs``)
|
||||
"""
|
||||
|
||||
def __init__(self, avs_config_file: str = None, audio_device: str = 'default',
|
||||
audio_player: str = 'default', **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
avs_config_file: Optional[str] = None,
|
||||
audio_device: str = 'default',
|
||||
audio_player: str = 'default',
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
:param avs_config_file: AVS credentials file - default: ~/.avs.json. If the file doesn't exist then
|
||||
an instance of the AVS authentication service will be spawned. You can login through an Amazon
|
||||
|
@ -61,9 +67,12 @@ class AssistantEchoPlugin(AssistantPlugin):
|
|||
|
||||
if not avs_config_file or not os.path.isfile(avs_config_file):
|
||||
from avs.auth import auth
|
||||
|
||||
auth(None, avs_config_file)
|
||||
self.logger.warning('Amazon Echo assistant credentials not configured. Open http://localhost:3000 ' +
|
||||
'to authenticate this client')
|
||||
self.logger.warning(
|
||||
'Amazon Echo assistant credentials not configured. Open http://localhost:3000 '
|
||||
+ 'to authenticate this client'
|
||||
)
|
||||
|
||||
self.audio_device = audio_device
|
||||
self.audio_player = audio_player
|
||||
|
@ -84,37 +93,43 @@ class AssistantEchoPlugin(AssistantPlugin):
|
|||
def _on_ready(self):
|
||||
def _callback():
|
||||
self._ready = True
|
||||
|
||||
return _callback
|
||||
|
||||
def _on_listening(self):
|
||||
def _callback():
|
||||
get_bus().post(ConversationStartEvent(assistant=self))
|
||||
|
||||
return _callback
|
||||
|
||||
def _on_speaking(self):
|
||||
def _callback():
|
||||
# AVS doesn't provide a way to access the response text
|
||||
get_bus().post(ResponseEvent(assistant=self, response_text=''))
|
||||
|
||||
return _callback
|
||||
|
||||
def _on_finished(self):
|
||||
def _callback():
|
||||
get_bus().post(ConversationEndEvent(assistant=self))
|
||||
|
||||
return _callback
|
||||
|
||||
def _on_disconnected(self):
|
||||
def _callback():
|
||||
self._ready = False
|
||||
|
||||
return _callback
|
||||
|
||||
def _on_thinking(self):
|
||||
def _callback():
|
||||
# AVS doesn't provide a way to access the detected text
|
||||
get_bus().post(SpeechRecognizedEvent(assistant=self, phrase=''))
|
||||
|
||||
return _callback
|
||||
|
||||
@action
|
||||
def start_conversation(self, **kwargs):
|
||||
def start_conversation(self, **_):
|
||||
if not self._ready:
|
||||
raise RuntimeError('Echo assistant not ready')
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
from platypush.backend.assistant.google import AssistantGoogleBackend
|
||||
from platypush.context import get_backend
|
||||
from platypush.plugins import action
|
||||
|
@ -19,10 +15,12 @@ class AssistantGooglePlugin(AssistantPlugin):
|
|||
super().__init__(**kwargs)
|
||||
|
||||
def _get_assistant(self) -> AssistantGoogleBackend:
|
||||
return get_backend('assistant.google')
|
||||
backend = get_backend('assistant.google')
|
||||
assert backend, 'The assistant.google backend is not configured.'
|
||||
return backend
|
||||
|
||||
@action
|
||||
def start_conversation(self, **kwargs):
|
||||
def start_conversation(self):
|
||||
"""
|
||||
Programmatically start a conversation with the assistant
|
||||
"""
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Optional, Dict, Any
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
import dateutil.parser
|
||||
import importlib
|
||||
|
||||
|
@ -53,7 +49,9 @@ class CalendarPlugin(Plugin, CalendarInterface):
|
|||
|
||||
for calendar in calendars:
|
||||
if 'type' not in calendar:
|
||||
self.logger.warning("Invalid calendar with no type specified: {}".format(calendar))
|
||||
self.logger.warning(
|
||||
"Invalid calendar with no type specified: {}".format(calendar)
|
||||
)
|
||||
continue
|
||||
|
||||
cal_type = calendar.pop('type')
|
||||
|
@ -62,7 +60,6 @@ class CalendarPlugin(Plugin, CalendarInterface):
|
|||
module = importlib.import_module(module_name)
|
||||
self.calendars.append(getattr(module, class_name)(**calendar))
|
||||
|
||||
|
||||
@action
|
||||
def get_upcoming_events(self, max_results=10):
|
||||
"""
|
||||
|
@ -71,7 +68,8 @@ class CalendarPlugin(Plugin, CalendarInterface):
|
|||
:param max_results: Maximum number of results to be returned (default: 10)
|
||||
:type max_results: int
|
||||
|
||||
:returns: platypush.message.Response -- Response object with the list of events in the Google calendar API format.
|
||||
:returns: platypush.message.Response -- Response object with the list of
|
||||
events in the Google calendar API format.
|
||||
|
||||
Example::
|
||||
|
||||
|
@ -113,15 +111,16 @@ class CalendarPlugin(Plugin, CalendarInterface):
|
|||
except Exception as e:
|
||||
self.logger.warning('Could not retrieve events: {}'.format(str(e)))
|
||||
|
||||
events = sorted(events, key=lambda event:
|
||||
dateutil.parser.parse(
|
||||
events = sorted(
|
||||
events,
|
||||
key=lambda event: dateutil.parser.parse(
|
||||
event['start']['dateTime']
|
||||
if 'dateTime' in event['start']
|
||||
else event['start']['date'] + 'T00:00:00+00:00'
|
||||
))[:max_results]
|
||||
),
|
||||
)[:max_results]
|
||||
|
||||
return events
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import requests
|
||||
from typing import Optional
|
||||
|
@ -35,15 +31,14 @@ class CalendarIcalPlugin(Plugin, CalendarInterface):
|
|||
return
|
||||
|
||||
if type(t.dt) == datetime.date:
|
||||
return (
|
||||
datetime.datetime(
|
||||
return datetime.datetime(
|
||||
t.dt.year, t.dt.month, t.dt.day, tzinfo=datetime.timezone.utc
|
||||
).isoformat()
|
||||
)
|
||||
|
||||
return (
|
||||
datetime.datetime.utcfromtimestamp(t.dt.timestamp())
|
||||
.replace(tzinfo=datetime.timezone.utc).isoformat()
|
||||
.replace(tzinfo=datetime.timezone.utc)
|
||||
.isoformat()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
@ -52,23 +47,27 @@ class CalendarIcalPlugin(Plugin, CalendarInterface):
|
|||
'id': str(event.get('uid')) if event.get('uid') else None,
|
||||
'kind': 'calendar#event',
|
||||
'summary': str(event.get('summary')) if event.get('summary') else None,
|
||||
'description': str(event.get('description')) if event.get('description') else None,
|
||||
'description': str(event.get('description'))
|
||||
if event.get('description')
|
||||
else None,
|
||||
'status': str(event.get('status')).lower() if event.get('status') else None,
|
||||
'responseStatus': str(event.get('partstat')).lower() if event.get('partstat') else None,
|
||||
'responseStatus': str(event.get('partstat')).lower()
|
||||
if event.get('partstat')
|
||||
else None,
|
||||
'location': str(event.get('location')) if event.get('location') else None,
|
||||
'htmlLink': str(event.get('url')) if event.get('url') else None,
|
||||
'organizer': {
|
||||
'email': str(event.get('organizer')).replace('MAILTO:', ''),
|
||||
'displayName': event.get('organizer').params.get('cn')
|
||||
} if event.get('organizer') else None,
|
||||
|
||||
'displayName': event.get('organizer').params.get('cn'),
|
||||
}
|
||||
if event.get('organizer')
|
||||
else None,
|
||||
'created': cls._convert_timestamp(event, 'created'),
|
||||
'updated': cls._convert_timestamp(event, 'last-modified'),
|
||||
'start': {
|
||||
'dateTime': cls._convert_timestamp(event, 'dtstart'),
|
||||
'timeZone': 'UTC',
|
||||
},
|
||||
|
||||
'end': {
|
||||
'dateTime': cls._convert_timestamp(event, 'dtend'),
|
||||
'timeZone': 'UTC',
|
||||
|
@ -76,7 +75,7 @@ class CalendarIcalPlugin(Plugin, CalendarInterface):
|
|||
}
|
||||
|
||||
@action
|
||||
def get_upcoming_events(self, max_results=10, only_participating=True):
|
||||
def get_upcoming_events(self, *_, only_participating=True, **__):
|
||||
"""
|
||||
Get the upcoming events. See
|
||||
:func:`~platypush.plugins.calendar.CalendarPlugin.get_upcoming_events`.
|
||||
|
@ -86,8 +85,9 @@ class CalendarIcalPlugin(Plugin, CalendarInterface):
|
|||
|
||||
events = []
|
||||
response = requests.get(self.url)
|
||||
assert response.ok, \
|
||||
"HTTP error while getting events from {}: {}".format(self.url, response.text)
|
||||
assert response.ok, "HTTP error while getting events from {}: {}".format(
|
||||
self.url, response.text
|
||||
)
|
||||
|
||||
calendar = Calendar.from_ical(response.text)
|
||||
for event in calendar.walk():
|
||||
|
@ -99,14 +99,22 @@ class CalendarIcalPlugin(Plugin, CalendarInterface):
|
|||
if (
|
||||
event['status'] != 'cancelled'
|
||||
and event['end'].get('dateTime')
|
||||
and event['end']['dateTime'] >= datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat()
|
||||
and event['end']['dateTime']
|
||||
>= datetime.datetime.utcnow()
|
||||
.replace(tzinfo=datetime.timezone.utc)
|
||||
.isoformat()
|
||||
and (
|
||||
(only_participating
|
||||
and event.get('responseStatus') in [None, 'accepted', 'tentative'])
|
||||
or not only_participating)
|
||||
(
|
||||
only_participating
|
||||
and event.get('responseStatus')
|
||||
in [None, 'accepted', 'tentative']
|
||||
)
|
||||
or not only_participating
|
||||
)
|
||||
):
|
||||
events.append(event)
|
||||
|
||||
return events
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
from platypush.plugins import Plugin
|
||||
|
||||
|
||||
|
@ -23,7 +19,8 @@ class GooglePlugin(Plugin):
|
|||
|
||||
5. Generate a credentials file for the needed scope::
|
||||
|
||||
python -m platypush.plugins.google.credentials 'https://www.googleapis.com/auth/gmail.compose' ~/client_secret.json
|
||||
python -m platypush.plugins.google.credentials \
|
||||
'https://www.googleapis.com/auth/gmail.compose' ~/client_secret.json
|
||||
|
||||
Requires:
|
||||
|
||||
|
@ -32,7 +29,7 @@ class GooglePlugin(Plugin):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, scopes=None, *args, **kwargs):
|
||||
def __init__(self, scopes=None, **kwargs):
|
||||
"""
|
||||
Initialized the Google plugin with the required scopes.
|
||||
|
||||
|
@ -41,14 +38,13 @@ class GooglePlugin(Plugin):
|
|||
"""
|
||||
|
||||
from platypush.plugins.google.credentials import get_credentials
|
||||
|
||||
super().__init__(**kwargs)
|
||||
self._scopes = scopes or []
|
||||
|
||||
if self._scopes:
|
||||
scopes = ' '.join(sorted(self._scopes))
|
||||
self.credentials = {
|
||||
scopes: get_credentials(scopes)
|
||||
}
|
||||
self.credentials = {scopes: get_credentials(scopes)}
|
||||
else:
|
||||
self.credentials = {}
|
||||
|
||||
|
@ -57,7 +53,7 @@ class GooglePlugin(Plugin):
|
|||
from apiclient import discovery
|
||||
|
||||
if scopes is None:
|
||||
scopes = getattr(self, 'scopes') if hasattr(self, 'scopes') else []
|
||||
scopes = getattr(self, 'scopes', [])
|
||||
|
||||
scopes = ' '.join(sorted(scopes))
|
||||
credentials = self.credentials[scopes]
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
import datetime
|
||||
|
||||
from platypush.plugins import action
|
||||
|
@ -34,9 +30,17 @@ class GoogleCalendarPlugin(GooglePlugin, CalendarInterface):
|
|||
|
||||
now = datetime.datetime.utcnow().isoformat() + 'Z'
|
||||
service = self.get_service('calendar', 'v3')
|
||||
result = service.events().list(calendarId='primary', timeMin=now,
|
||||
maxResults=max_results, singleEvents=True,
|
||||
orderBy='startTime').execute()
|
||||
result = (
|
||||
service.events()
|
||||
.list(
|
||||
calendarId='primary',
|
||||
timeMin=now,
|
||||
maxResults=max_results,
|
||||
singleEvents=True,
|
||||
orderBy='startTime',
|
||||
)
|
||||
.execute()
|
||||
)
|
||||
|
||||
events = result.get('items', [])
|
||||
return events
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
import base64
|
||||
import mimetypes
|
||||
import os
|
||||
|
@ -81,8 +77,9 @@ class GoogleMailPlugin(GooglePlugin):
|
|||
elif main_type == 'audio':
|
||||
msg = MIMEAudio(content, _subtype=sub_type)
|
||||
elif main_type == 'application':
|
||||
msg = MIMEApplication(content, _subtype=sub_type,
|
||||
_encoder=encode_base64)
|
||||
msg = MIMEApplication(
|
||||
content, _subtype=sub_type, _encoder=encode_base64
|
||||
)
|
||||
else:
|
||||
msg = MIMEBase(main_type, sub_type)
|
||||
msg.set_payload(content)
|
||||
|
@ -93,8 +90,7 @@ class GoogleMailPlugin(GooglePlugin):
|
|||
|
||||
service = self.get_service('gmail', 'v1')
|
||||
body = {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}
|
||||
message = (service.users().messages().send(
|
||||
userId='me', body=body).execute())
|
||||
message = service.users().messages().send(userId='me', body=body).execute()
|
||||
|
||||
return message
|
||||
|
||||
|
@ -108,4 +104,5 @@ class GoogleMailPlugin(GooglePlugin):
|
|||
labels = results.get('labels', [])
|
||||
return labels
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Union, Optional
|
||||
|
||||
|
@ -50,23 +46,29 @@ class GoogleMapsPlugin(GooglePlugin):
|
|||
:type longitude: float
|
||||
"""
|
||||
|
||||
response = requests.get('https://maps.googleapis.com/maps/api/geocode/json',
|
||||
response = requests.get(
|
||||
'https://maps.googleapis.com/maps/api/geocode/json',
|
||||
params={
|
||||
'latlng': '{},{}'.format(latitude, longitude),
|
||||
'key': self.api_key,
|
||||
}).json()
|
||||
},
|
||||
).json()
|
||||
|
||||
address = dict(
|
||||
(t, None) for t in ['street_number', 'street', 'locality', 'country', 'postal_code']
|
||||
)
|
||||
address = {
|
||||
t: None
|
||||
for t in ['street_number', 'street', 'locality', 'country', 'postal_code']
|
||||
}
|
||||
|
||||
address['latitude'] = latitude
|
||||
address['longitude'] = longitude
|
||||
|
||||
if 'results' in response and response['results']:
|
||||
result = response['results'][0]
|
||||
self.logger.info('Google Maps geocode response for latlng ({},{}): {}'.
|
||||
format(latitude, longitude, result))
|
||||
self.logger.info(
|
||||
'Google Maps geocode response for latlng ({},{}): {}'.format(
|
||||
latitude, longitude, result
|
||||
)
|
||||
)
|
||||
|
||||
address['address'] = result['formatted_address'].split(',')[0]
|
||||
for addr_component in result['address_components']:
|
||||
|
@ -92,11 +94,13 @@ class GoogleMapsPlugin(GooglePlugin):
|
|||
:type longitude: float
|
||||
"""
|
||||
|
||||
response = requests.get('https://maps.googleapis.com/maps/api/elevation/json',
|
||||
response = requests.get(
|
||||
'https://maps.googleapis.com/maps/api/elevation/json',
|
||||
params={
|
||||
'locations': '{},{}'.format(latitude, longitude),
|
||||
'key': self.api_key,
|
||||
}).json()
|
||||
},
|
||||
).json()
|
||||
|
||||
elevation = None
|
||||
|
||||
|
@ -106,7 +110,10 @@ class GoogleMapsPlugin(GooglePlugin):
|
|||
return {'elevation': elevation}
|
||||
|
||||
@action
|
||||
def get_travel_time(self, origins: List[str], destinations: List[str],
|
||||
def get_travel_time(
|
||||
self,
|
||||
origins: List[str],
|
||||
destinations: List[str],
|
||||
departure_time: Optional[datetime_types] = None,
|
||||
arrival_time: Optional[datetime_types] = None,
|
||||
units: str = 'metric',
|
||||
|
@ -115,7 +122,8 @@ class GoogleMapsPlugin(GooglePlugin):
|
|||
mode: Optional[str] = None,
|
||||
traffic_model: Optional[str] = None,
|
||||
transit_mode: Optional[List[str]] = None,
|
||||
transit_route_preference: Optional[str] = None):
|
||||
transit_route_preference: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Get the estimated travel time between a set of departure points and a set of destinations.
|
||||
|
||||
|
@ -194,17 +202,25 @@ class GoogleMapsPlugin(GooglePlugin):
|
|||
'origins': '|'.join(origins),
|
||||
'destinations': '|'.join(destinations),
|
||||
'units': units,
|
||||
**({'departure_time': to_datetime(departure_time)} if departure_time else {}),
|
||||
**(
|
||||
{'departure_time': to_datetime(departure_time)}
|
||||
if departure_time
|
||||
else {}
|
||||
),
|
||||
**({'arrival_time': to_datetime(arrival_time)} if arrival_time else {}),
|
||||
**({'avoid': '|'.join(avoid)} if avoid else {}),
|
||||
**({'language': language} if language else {}),
|
||||
**({'mode': mode} if mode else {}),
|
||||
**({'traffic_model': traffic_model} if traffic_model else {}),
|
||||
**({'transit_mode': transit_mode} if transit_mode else {}),
|
||||
**({'transit_route_preference': transit_route_preference}
|
||||
if transit_route_preference else {}),
|
||||
**(
|
||||
{'transit_route_preference': transit_route_preference}
|
||||
if transit_route_preference
|
||||
else {}
|
||||
),
|
||||
'key': self.api_key,
|
||||
}).json()
|
||||
},
|
||||
).json()
|
||||
|
||||
assert not rs.get('error_message'), f'{rs["status"]}: {rs["error_message"]}'
|
||||
rows = rs.get('rows', [])
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
from platypush.plugins import action
|
||||
from platypush.plugins.google import GooglePlugin
|
||||
|
||||
|
@ -48,10 +44,12 @@ class GoogleYoutubePlugin(GooglePlugin):
|
|||
:type max_results: int
|
||||
|
||||
:param kwargs: Any extra arguments that will be transparently passed to the YouTube API.
|
||||
See the `Getting started - parameters <https://developers.google.com/youtube/v3/docs/search/list#parameters>`_.
|
||||
See the `Getting started - parameters
|
||||
<https://developers.google.com/youtube/v3/docs/search/list#parameters>`_.
|
||||
|
||||
:return: A list of YouTube resources.
|
||||
See the `Getting started - Resource <https://developers.google.com/youtube/v3/docs/search#resource>`_.
|
||||
See the `Getting started - Resource
|
||||
<https://developers.google.com/youtube/v3/docs/search#resource>`_.
|
||||
"""
|
||||
|
||||
parts = parts or self._default_parts[:]
|
||||
|
@ -63,9 +61,11 @@ class GoogleYoutubePlugin(GooglePlugin):
|
|||
types = ','.join(types)
|
||||
|
||||
service = self.get_service('youtube', 'v3')
|
||||
result = service.search().list(part=parts, q=query, type=types,
|
||||
maxResults=max_results,
|
||||
**kwargs).execute()
|
||||
result = (
|
||||
service.search()
|
||||
.list(part=parts, q=query, type=types, maxResults=max_results, **kwargs)
|
||||
.execute()
|
||||
)
|
||||
|
||||
events = result.get('items', [])
|
||||
return events
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
"""
|
||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from platypush.context import get_backend
|
||||
from platypush.plugins import Plugin, action
|
||||
|
||||
|
||||
class WiimotePlugin(Plugin):
|
||||
"""
|
||||
WiiMote plugin.
|
||||
|
@ -20,7 +17,6 @@ class WiimotePlugin(Plugin):
|
|||
def _get_wiimote(cls):
|
||||
return get_backend('wiimote').get_wiimote()
|
||||
|
||||
|
||||
@action
|
||||
def connect(self):
|
||||
"""
|
||||
|
@ -28,7 +24,6 @@ class WiimotePlugin(Plugin):
|
|||
"""
|
||||
self._get_wiimote()
|
||||
|
||||
|
||||
@action
|
||||
def close(self):
|
||||
"""
|
||||
|
@ -36,7 +31,6 @@ class WiimotePlugin(Plugin):
|
|||
"""
|
||||
get_backend('wiimote').close()
|
||||
|
||||
|
||||
@action
|
||||
def rumble(self, secs):
|
||||
"""
|
||||
|
@ -47,7 +41,6 @@ class WiimotePlugin(Plugin):
|
|||
time.sleep(secs)
|
||||
wm.rumble = False
|
||||
|
||||
|
||||
@action
|
||||
def state(self):
|
||||
"""
|
||||
|
@ -55,23 +48,22 @@ class WiimotePlugin(Plugin):
|
|||
"""
|
||||
return get_backend('wiimote').get_state()
|
||||
|
||||
|
||||
@action
|
||||
def set_leds(self, leds):
|
||||
"""
|
||||
Set the LEDs state on the controller
|
||||
|
||||
:param leds: Iterable with the new states to be applied to the LEDs. Example: [1, 0, 0, 0] or (False, True, False, False)
|
||||
:param leds: Iterable with the new states to be applied to the LEDs.
|
||||
Example: [1, 0, 0, 0] or (False, True, False, False)
|
||||
:type leds: list
|
||||
"""
|
||||
|
||||
new_led = 0
|
||||
for i, led in enumerate(leds):
|
||||
if led:
|
||||
new_led |= (1 << i)
|
||||
new_led |= 1 << i
|
||||
|
||||
self._get_wiimote().led = new_led
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -30,7 +30,7 @@ setup(
|
|||
name="platypush",
|
||||
version="0.50.2",
|
||||
author="Fabio Manganiello",
|
||||
author_email="info@fabiomanganiello.com",
|
||||
author_email="fabio@manganiello.tech",
|
||||
description="Platypush service",
|
||||
license="MIT",
|
||||
python_requires='>= 3.6',
|
||||
|
|
Loading…
Reference in a new issue