Moved imports for extra dependencies inside the methods where they are actually used
This commit is contained in:
parent
f8d3ea5197
commit
d38746d278
33 changed files with 112 additions and 89 deletions
|
@ -7,11 +7,6 @@ import json
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import google.oauth2.credentials
|
|
||||||
|
|
||||||
from google.assistant.library import Assistant
|
|
||||||
from google.assistant.library.event import EventType, AlertType
|
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.message.event.assistant import \
|
from platypush.message.event.assistant import \
|
||||||
ConversationStartEvent, ConversationEndEvent, ConversationTimeoutEvent, \
|
ConversationStartEvent, ConversationEndEvent, ConversationTimeoutEvent, \
|
||||||
|
@ -83,17 +78,15 @@ class AssistantGoogleBackend(Backend):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.credentials_file = credentials_file
|
self.credentials_file = credentials_file
|
||||||
self.device_model_id = device_model_id
|
self.device_model_id = device_model_id
|
||||||
|
self.credentials = None
|
||||||
self.assistant = None
|
self.assistant = None
|
||||||
self._has_error = False
|
self._has_error = False
|
||||||
|
|
||||||
with open(self.credentials_file, 'r') as f:
|
|
||||||
self.credentials = google.oauth2.credentials.Credentials(
|
|
||||||
token=None,
|
|
||||||
**json.load(f))
|
|
||||||
|
|
||||||
self.logger.info('Initialized Google Assistant backend')
|
self.logger.info('Initialized Google Assistant backend')
|
||||||
|
|
||||||
def _process_event(self, event):
|
def _process_event(self, event):
|
||||||
|
from google.assistant.library.event import EventType, AlertType
|
||||||
|
|
||||||
self.logger.info('Received assistant event: {}'.format(event))
|
self.logger.info('Received assistant event: {}'.format(event))
|
||||||
self._has_error = False
|
self._has_error = False
|
||||||
|
|
||||||
|
@ -149,8 +142,16 @@ class AssistantGoogleBackend(Backend):
|
||||||
self.assistant.stop_conversation()
|
self.assistant.stop_conversation()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
import google.oauth2.credentials
|
||||||
|
from google.assistant.library import Assistant
|
||||||
|
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
|
with open(self.credentials_file, 'r') as f:
|
||||||
|
self.credentials = google.oauth2.credentials.Credentials(
|
||||||
|
token=None,
|
||||||
|
**json.load(f))
|
||||||
|
|
||||||
while not self.should_stop():
|
while not self.should_stop():
|
||||||
self._has_error = False
|
self._has_error = False
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import gps
|
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
@ -49,6 +48,8 @@ class GpsBackend(Backend):
|
||||||
self._devices = {}
|
self._devices = {}
|
||||||
|
|
||||||
def _get_session(self):
|
def _get_session(self):
|
||||||
|
import gps
|
||||||
|
|
||||||
with self._session_lock:
|
with self._session_lock:
|
||||||
if not self._session:
|
if not self._session:
|
||||||
self._session = gps.gps(host=self.gpsd_server, port=self.gpsd_port, reconnect=True)
|
self._session = gps.gps(host=self.gpsd_server, port=self.gpsd_port, reconnect=True)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import datetime
|
import datetime
|
||||||
import enum
|
import enum
|
||||||
import feedparser
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from sqlalchemy import create_engine, Column, Integer, String, DateTime, \
|
from sqlalchemy import create_engine, Column, Integer, String, DateTime, \
|
||||||
|
@ -20,7 +19,13 @@ Session = scoped_session(sessionmaker())
|
||||||
|
|
||||||
|
|
||||||
class RssUpdates(HttpRequest):
|
class RssUpdates(HttpRequest):
|
||||||
""" Gets new items in an RSS feed """
|
"""
|
||||||
|
Gets new items in an RSS feed
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **feedparser** (``pip install feedparser``)
|
||||||
|
"""
|
||||||
|
|
||||||
user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' + \
|
user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' + \
|
||||||
'Chrome/62.0.3202.94 Safari/537.36'
|
'Chrome/62.0.3202.94 Safari/537.36'
|
||||||
|
@ -80,6 +85,7 @@ class RssUpdates(HttpRequest):
|
||||||
return response.get('content')
|
return response.get('content')
|
||||||
|
|
||||||
def get_new_items(self, response):
|
def get_new_items(self, response):
|
||||||
|
import feedparser
|
||||||
engine = create_engine('sqlite:///{}'.format(self.dbfile),
|
engine = create_engine('sqlite:///{}'.format(self.dbfile),
|
||||||
connect_args={'check_same_thread': False})
|
connect_args={'check_same_thread': False})
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import os
|
import os
|
||||||
import inotify.adapters
|
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.message.event.path import PathCreateEvent, PathDeleteEvent, \
|
from platypush.message.event.path import PathCreateEvent, PathDeleteEvent, \
|
||||||
|
@ -49,6 +48,7 @@ class InotifyBackend(Backend):
|
||||||
self.inotify_watch = None
|
self.inotify_watch = None
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
import inotify.adapters
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
self.inotify_watch = inotify.adapters.Inotify()
|
self.inotify_watch = inotify.adapters.Inotify()
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import inputs
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
|
@ -30,6 +29,8 @@ class JoystickBackend(Backend):
|
||||||
self.device = device
|
self.device = device
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
import inputs
|
||||||
|
|
||||||
super().run()
|
super().run()
|
||||||
self.logger.info('Initialized joystick backend on device {}'.format(self.device))
|
self.logger.info('Initialized joystick backend on device {}'.format(self.device))
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from kafka import KafkaConsumer, KafkaProducer
|
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.context import get_plugin
|
from platypush.context import get_plugin
|
||||||
from platypush.message import Message
|
from platypush.message import Message
|
||||||
|
@ -82,6 +80,7 @@ class KafkaBackend(Backend):
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
from kafka import KafkaConsumer
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
self.consumer = KafkaConsumer(self.topic, bootstrap_servers=self.server)
|
self.consumer = KafkaConsumer(self.topic, bootstrap_servers=self.server)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
import nfc
|
|
||||||
import ndef
|
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.message.event.nfc import NFCTagDetectedEvent, NFCTagRemovedEvent, NFCDeviceConnectedEvent, \
|
from platypush.message.event.nfc import NFCTagDetectedEvent, NFCTagRemovedEvent, NFCDeviceConnectedEvent, \
|
||||||
|
@ -21,7 +19,8 @@ class NfcBackend(Backend):
|
||||||
|
|
||||||
Requires:
|
Requires:
|
||||||
|
|
||||||
* **nfcpy** >= 1.0 (``pip install nfcpy``)
|
* **nfcpy** >= 1.0 (``pip install 'nfcpy>=1.0'``)
|
||||||
|
* **ndef** (``pip install ndef``)
|
||||||
|
|
||||||
Run the following to check if your device is compatible with nfcpy and the right permissions are set::
|
Run the following to check if your device is compatible with nfcpy and the right permissions are set::
|
||||||
|
|
||||||
|
@ -44,6 +43,8 @@ class NfcBackend(Backend):
|
||||||
self._clf = None
|
self._clf = None
|
||||||
|
|
||||||
def _get_clf(self):
|
def _get_clf(self):
|
||||||
|
import nfc
|
||||||
|
|
||||||
if not self._clf:
|
if not self._clf:
|
||||||
self._clf = nfc.ContactlessFrontend()
|
self._clf = nfc.ContactlessFrontend()
|
||||||
self._clf.open(self.device_id)
|
self._clf.open(self.device_id)
|
||||||
|
@ -63,6 +64,7 @@ class NfcBackend(Backend):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_records(tag):
|
def _parse_records(tag):
|
||||||
|
import ndef
|
||||||
records = []
|
records = []
|
||||||
|
|
||||||
for record in tag.ndef.records:
|
for record in tag.ndef.records:
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from pushbullet import Pushbullet, Listener
|
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.message.event.pushbullet import PushbulletEvent
|
from platypush.message.event.pushbullet import PushbulletEvent
|
||||||
|
|
||||||
|
@ -43,6 +41,7 @@ class PushbulletBackend(Backend):
|
||||||
:type proxy_port: int
|
:type proxy_port: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from pushbullet import Pushbullet
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.token = token
|
self.token = token
|
||||||
|
@ -133,6 +132,7 @@ class PushbulletBackend(Backend):
|
||||||
return self.close()
|
return self.close()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
from pushbullet import Listener
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
self.logger.info('Initialized Pushbullet backend - device_id: {}'
|
self.logger.info('Initialized Pushbullet backend - device_id: {}'
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from smartcard.CardType import AnyCardType, ATRCardType
|
|
||||||
from smartcard.CardRequest import CardRequest
|
|
||||||
from smartcard.Exceptions import NoCardException, CardConnectionException
|
|
||||||
from smartcard.util import toHexString
|
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.message.event.scard import SmartCardDetectedEvent, SmartCardRemovedEvent
|
from platypush.message.event.scard import SmartCardDetectedEvent, SmartCardRemovedEvent
|
||||||
|
|
||||||
|
@ -32,6 +27,7 @@ class ScardBackend(Backend):
|
||||||
: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)
|
: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)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from smartcard.CardType import AnyCardType, ATRCardType
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.ATRs = []
|
self.ATRs = []
|
||||||
|
|
||||||
|
@ -51,6 +47,10 @@ class ScardBackend(Backend):
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
from smartcard.CardRequest import CardRequest
|
||||||
|
from smartcard.Exceptions import NoCardException, CardConnectionException
|
||||||
|
from smartcard.util import toHexString
|
||||||
|
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
self.logger.info('Initialized smart card reader backend - ATR filter: {}'.
|
self.logger.info('Initialized smart card reader backend - ATR filter: {}'.
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import cwiid
|
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
@ -51,6 +50,8 @@ class WiimoteBackend(Backend):
|
||||||
|
|
||||||
|
|
||||||
def get_wiimote(self):
|
def get_wiimote(self):
|
||||||
|
import cwiid
|
||||||
|
|
||||||
if not self._wiimote:
|
if not self._wiimote:
|
||||||
if self._bdaddr:
|
if self._bdaddr:
|
||||||
self._wiimote = cwiid.Wiimote(bdaddr=self._bdaddr)
|
self._wiimote = cwiid.Wiimote(bdaddr=self._bdaddr)
|
||||||
|
@ -67,6 +68,7 @@ class WiimoteBackend(Backend):
|
||||||
return self._wiimote
|
return self._wiimote
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
|
import cwiid
|
||||||
wm = self.get_wiimote()
|
wm = self.get_wiimote()
|
||||||
state = wm.state
|
state = wm.state
|
||||||
parsed_state = {}
|
parsed_state = {}
|
||||||
|
|
|
@ -4,8 +4,6 @@ import json
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from threading import Thread, Lock
|
from threading import Thread, Lock
|
||||||
from Adafruit_IO import Client
|
|
||||||
from Adafruit_IO.errors import ThrottlingError
|
|
||||||
|
|
||||||
from platypush.context import get_backend
|
from platypush.context import get_backend
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
@ -64,6 +62,7 @@ class AdafruitIoPlugin(Plugin):
|
||||||
:type throttle_seconds: float
|
:type throttle_seconds: float
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from Adafruit_IO import Client
|
||||||
global data_throttler_lock
|
global data_throttler_lock
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,6 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from avs.auth import auth
|
|
||||||
from avs.alexa import Alexa
|
|
||||||
from avs.config import DEFAULT_CONFIG_FILE
|
|
||||||
from avs.mic import Audio
|
|
||||||
|
|
||||||
from platypush.context import get_bus
|
from platypush.context import get_bus
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.assistant import AssistantPlugin
|
from platypush.plugins.assistant import AssistantPlugin
|
||||||
|
@ -43,7 +38,7 @@ class AssistantEchoPlugin(AssistantPlugin):
|
||||||
* **avs** (``pip install avs``)
|
* **avs** (``pip install avs``)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, avs_config_file: str = DEFAULT_CONFIG_FILE, audio_device: str = 'default',
|
def __init__(self, avs_config_file: str = None, audio_device: str = 'default',
|
||||||
audio_player: str = 'default', **kwargs):
|
audio_player: str = 'default', **kwargs):
|
||||||
"""
|
"""
|
||||||
:param avs_config_file: AVS credentials file - default: ~/.avs.json. If the file doesn't exist then
|
:param avs_config_file: AVS credentials file - default: ~/.avs.json. If the file doesn't exist then
|
||||||
|
@ -55,9 +50,17 @@ class AssistantEchoPlugin(AssistantPlugin):
|
||||||
:param audio_player: Player to be used for audio playback (default: 'default').
|
:param audio_player: Player to be used for audio playback (default: 'default').
|
||||||
Supported values: 'mpv', 'mpg123', 'gstreamer'
|
Supported values: 'mpv', 'mpg123', 'gstreamer'
|
||||||
"""
|
"""
|
||||||
|
from avs.alexa import Alexa
|
||||||
|
from avs.config import DEFAULT_CONFIG_FILE
|
||||||
|
from avs.mic import Audio
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
if not avs_config_file:
|
||||||
|
avs_config_file = DEFAULT_CONFIG_FILE
|
||||||
|
|
||||||
if not avs_config_file or not os.path.isfile(avs_config_file):
|
if not avs_config_file or not os.path.isfile(avs_config_file):
|
||||||
|
from avs.auth import auth
|
||||||
auth(None, avs_config_file)
|
auth(None, avs_config_file)
|
||||||
self.logger.warning('Amazon Echo assistant credentials not configured. Open http://localhost:3000 ' +
|
self.logger.warning('Amazon Echo assistant credentials not configured. Open http://localhost:3000 ' +
|
||||||
'to authenticate this client')
|
'to authenticate this client')
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import googlesamples.assistant.grpc.audio_helpers as audio_helpers
|
|
||||||
import googlesamples.assistant.grpc.device_helpers as device_helpers
|
|
||||||
|
|
||||||
from platypush.context import get_bus
|
from platypush.context import get_bus
|
||||||
from platypush.message.event.assistant import ConversationStartEvent, \
|
from platypush.message.event.assistant import ConversationStartEvent, \
|
||||||
ConversationEndEvent, SpeechRecognizedEvent, VolumeChangedEvent, \
|
ConversationEndEvent, SpeechRecognizedEvent, VolumeChangedEvent, \
|
||||||
|
@ -17,7 +14,6 @@ from platypush.message.event.google import GoogleDeviceOnOffEvent
|
||||||
|
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.assistant import AssistantPlugin
|
from platypush.plugins.assistant import AssistantPlugin
|
||||||
from platypush.plugins.assistant.google.lib import SampleAssistant
|
|
||||||
|
|
||||||
|
|
||||||
class AssistantGooglePushtotalkPlugin(AssistantPlugin):
|
class AssistantGooglePushtotalkPlugin(AssistantPlugin):
|
||||||
|
@ -40,11 +36,6 @@ class AssistantGooglePushtotalkPlugin(AssistantPlugin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
api_endpoint = 'embeddedassistant.googleapis.com'
|
api_endpoint = 'embeddedassistant.googleapis.com'
|
||||||
audio_sample_rate = audio_helpers.DEFAULT_AUDIO_SAMPLE_RATE
|
|
||||||
audio_sample_width = audio_helpers.DEFAULT_AUDIO_SAMPLE_WIDTH
|
|
||||||
audio_iter_size = audio_helpers.DEFAULT_AUDIO_ITER_SIZE
|
|
||||||
audio_block_size = audio_helpers.DEFAULT_AUDIO_DEVICE_BLOCK_SIZE
|
|
||||||
audio_flush_size = audio_helpers.DEFAULT_AUDIO_DEVICE_FLUSH_SIZE
|
|
||||||
grpc_deadline = 60 * 3 + 5
|
grpc_deadline = 60 * 3 + 5
|
||||||
device_handler = None
|
device_handler = None
|
||||||
|
|
||||||
|
@ -79,8 +70,15 @@ class AssistantGooglePushtotalkPlugin(AssistantPlugin):
|
||||||
:type play_response: bool
|
:type play_response: bool
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import googlesamples.assistant.grpc.audio_helpers as audio_helpers
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
self.audio_sample_rate = audio_helpers.DEFAULT_AUDIO_SAMPLE_RATE
|
||||||
|
self.audio_sample_width = audio_helpers.DEFAULT_AUDIO_SAMPLE_WIDTH
|
||||||
|
self.audio_iter_size = audio_helpers.DEFAULT_AUDIO_ITER_SIZE
|
||||||
|
self.audio_block_size = audio_helpers.DEFAULT_AUDIO_DEVICE_BLOCK_SIZE
|
||||||
|
self.audio_flush_size = audio_helpers.DEFAULT_AUDIO_DEVICE_FLUSH_SIZE
|
||||||
|
|
||||||
self.language = language
|
self.language = language
|
||||||
self.credentials_file = credentials_file
|
self.credentials_file = credentials_file
|
||||||
self.device_config = device_config
|
self.device_config = device_config
|
||||||
|
@ -112,7 +110,9 @@ class AssistantGooglePushtotalkPlugin(AssistantPlugin):
|
||||||
self.conversation_stream = None
|
self.conversation_stream = None
|
||||||
|
|
||||||
def _init_assistant(self):
|
def _init_assistant(self):
|
||||||
|
import googlesamples.assistant.grpc.audio_helpers as audio_helpers
|
||||||
from google.auth.transport.grpc import secure_authorized_channel
|
from google.auth.transport.grpc import secure_authorized_channel
|
||||||
|
|
||||||
self.interactions = []
|
self.interactions = []
|
||||||
|
|
||||||
# Create an authorized gRPC channel.
|
# Create an authorized gRPC channel.
|
||||||
|
@ -217,6 +217,8 @@ class AssistantGooglePushtotalkPlugin(AssistantPlugin):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from platypush.plugins.assistant.google.lib import SampleAssistant
|
||||||
|
|
||||||
if not language:
|
if not language:
|
||||||
language = self.language
|
language = self.language
|
||||||
|
|
||||||
|
@ -262,6 +264,7 @@ class AssistantGooglePushtotalkPlugin(AssistantPlugin):
|
||||||
get_bus().post(ConversationEndEvent(assistant=self))
|
get_bus().post(ConversationEndEvent(assistant=self))
|
||||||
|
|
||||||
def _install_device_handlers(self):
|
def _install_device_handlers(self):
|
||||||
|
import googlesamples.assistant.grpc.device_helpers as device_helpers
|
||||||
self.device_handler = device_helpers.DeviceRequestHandler(self.device_id)
|
self.device_handler = device_helpers.DeviceRequestHandler(self.device_id)
|
||||||
|
|
||||||
@self.device_handler.command('action.devices.commands.OnOff')
|
@self.device_handler.command('action.devices.commands.OnOff')
|
||||||
|
|
|
@ -6,8 +6,6 @@ import datetime
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from icalendar import Calendar
|
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
from platypush.plugins.calendar import CalendarInterface
|
from platypush.plugins.calendar import CalendarInterface
|
||||||
|
|
||||||
|
@ -68,6 +66,7 @@ class CalendarIcalPlugin(Plugin, CalendarInterface):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
|
from icalendar import Calendar
|
||||||
|
|
||||||
events = []
|
events = []
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -4,9 +4,6 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# noinspection PyPackageRequirements
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,11 +33,7 @@ class CameraIrMlx90640Plugin(Plugin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_img_size = (32, 24)
|
_img_size = (32, 24)
|
||||||
_rotate_values = {
|
_rotate_values = {}
|
||||||
90: Image.ROTATE_90,
|
|
||||||
180: Image.ROTATE_180,
|
|
||||||
270: Image.ROTATE_270,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, fps=16, skip_frames=2, scale_factor=1, rotate=0, rawrgb_path=None, **kwargs):
|
def __init__(self, fps=16, skip_frames=2, scale_factor=1, rotate=0, rawrgb_path=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -53,8 +46,15 @@ class CameraIrMlx90640Plugin(Plugin):
|
||||||
https://github.com/pimoroni/mlx90640-library is in another folder than
|
https://github.com/pimoroni/mlx90640-library is in another folder than
|
||||||
`<directory of this file>/lib/examples`.
|
`<directory of this file>/lib/examples`.
|
||||||
"""
|
"""
|
||||||
|
from PIL import Image
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
self._rotate_values = {
|
||||||
|
90: Image.ROTATE_90,
|
||||||
|
180: Image.ROTATE_180,
|
||||||
|
270: Image.ROTATE_270,
|
||||||
|
}
|
||||||
|
|
||||||
if not rawrgb_path:
|
if not rawrgb_path:
|
||||||
rawrgb_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib', 'examples', 'rawrgb')
|
rawrgb_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib', 'examples', 'rawrgb')
|
||||||
rawrgb_path = os.path.abspath(os.path.expanduser(rawrgb_path))
|
rawrgb_path = os.path.abspath(os.path.expanduser(rawrgb_path))
|
||||||
|
@ -121,6 +121,7 @@ class CameraIrMlx90640Plugin(Plugin):
|
||||||
output_file is not set, otherwise a list with the captured image files will be returned.
|
output_file is not set, otherwise a list with the captured image files will be returned.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
fps = self.fps if fps is None else fps
|
fps = self.fps if fps is None else fps
|
||||||
skip_frames = self.skip_frames if skip_frames is None else skip_frames
|
skip_frames = self.skip_frames if skip_frames is None else skip_frames
|
||||||
scale_factor = self.scale_factor if scale_factor is None else scale_factor
|
scale_factor = self.scale_factor if scale_factor is None else scale_factor
|
||||||
|
@ -170,6 +171,7 @@ class CameraIrMlx90640Plugin(Plugin):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _convert_to_grayscale(image):
|
def _convert_to_grayscale(image):
|
||||||
|
from PIL import Image
|
||||||
new_image = Image.new('L', image.size)
|
new_image = Image.new('L', image.size)
|
||||||
|
|
||||||
for i in range(0, image.size[0]):
|
for i in range(0, image.size[0]):
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import pyperclip
|
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +18,7 @@ class ClipboardPlugin(Plugin):
|
||||||
:param text: Text to copy
|
:param text: Text to copy
|
||||||
:type text: str
|
:type text: str
|
||||||
"""
|
"""
|
||||||
|
import pyperclip
|
||||||
pyperclip.copy(text)
|
pyperclip.copy(text)
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +27,7 @@ class ClipboardPlugin(Plugin):
|
||||||
"""
|
"""
|
||||||
Get the current content of the clipboard
|
Get the current content of the clipboard
|
||||||
"""
|
"""
|
||||||
|
import pyperclip
|
||||||
return pyperclip.paste()
|
return pyperclip.paste()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from platypush.plugins import Plugin
|
from platypush.plugins import Plugin
|
||||||
from platypush.plugins.google.credentials import get_credentials
|
|
||||||
|
|
||||||
|
|
||||||
class GooglePlugin(Plugin):
|
class GooglePlugin(Plugin):
|
||||||
|
@ -41,6 +40,7 @@ class GooglePlugin(Plugin):
|
||||||
:type scopes: list
|
:type scopes: list
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from platypush.plugins.google.credentials import get_credentials
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self._scopes = scopes or []
|
self._scopes = scopes or []
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,6 @@ import httplib2
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from apiclient import discovery
|
|
||||||
|
|
||||||
from email.encoders import encode_base64
|
from email.encoders import encode_base64
|
||||||
from email.mime.application import MIMEApplication
|
from email.mime.application import MIMEApplication
|
||||||
from email.mime.audio import MIMEAudio
|
from email.mime.audio import MIMEAudio
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import envirophat
|
|
||||||
|
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.gpio.sensor import GpioSensorPlugin
|
from platypush.plugins.gpio.sensor import GpioSensorPlugin
|
||||||
|
|
||||||
|
@ -46,6 +44,8 @@ class GpioSensorEnvirophatPlugin(GpioSensorPlugin):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import envirophat
|
||||||
|
|
||||||
ret = {}
|
ret = {}
|
||||||
weather = envirophat.weather
|
weather = envirophat.weather
|
||||||
light = envirophat.light
|
light = envirophat.light
|
||||||
|
|
|
@ -2,9 +2,6 @@ import enum
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# noinspection PyUnresolvedReferences,PyPackageRequirements
|
|
||||||
from pmw3901 import PMW3901, BG_CS_FRONT_BCM, BG_CS_BACK_BCM
|
|
||||||
|
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.gpio.sensor import GpioSensorPlugin
|
from platypush.plugins.gpio.sensor import GpioSensorPlugin
|
||||||
|
|
||||||
|
@ -43,7 +40,9 @@ class GpioSensorMotionPwm3901Plugin(GpioSensorPlugin):
|
||||||
:param spi_port: SPI port (default: 0)
|
:param spi_port: SPI port (default: 0)
|
||||||
:type spi_slot: int
|
:type spi_slot: int
|
||||||
"""
|
"""
|
||||||
|
from pmw3901 import BG_CS_FRONT_BCM, BG_CS_BACK_BCM
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.spi_port = spi_port
|
self.spi_port = spi_port
|
||||||
self._sensor = None
|
self._sensor = None
|
||||||
self._events_per_sec = {}
|
self._events_per_sec = {}
|
||||||
|
@ -70,6 +69,8 @@ class GpioSensorMotionPwm3901Plugin(GpioSensorPlugin):
|
||||||
spi_slot, [s.value for s in SPISlot]))
|
spi_slot, [s.value for s in SPISlot]))
|
||||||
|
|
||||||
def _get_sensor(self):
|
def _get_sensor(self):
|
||||||
|
from pmw3901 import PMW3901
|
||||||
|
|
||||||
if not self._sensor:
|
if not self._sensor:
|
||||||
self._sensor = PMW3901(spi_port=self.spi_port,
|
self._sensor = PMW3901(spi_port=self.spi_port,
|
||||||
spi_cs=1,
|
spi_cs=1,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import feedparser
|
|
||||||
|
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.http.request import HttpRequestPlugin
|
from platypush.plugins.http.request import HttpRequestPlugin
|
||||||
|
|
||||||
|
@ -14,6 +12,7 @@ class HttpRequestRssPlugin(HttpRequestPlugin):
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get(self, url):
|
def get(self, url):
|
||||||
|
import feedparser
|
||||||
response = super().get(url, output='text').output
|
response = super().get(url, output='text').output
|
||||||
feed = feedparser.parse(response)
|
feed = feedparser.parse(response)
|
||||||
return feed.entries
|
return feed.entries
|
||||||
|
|
|
@ -2,8 +2,6 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from kafka import KafkaProducer
|
|
||||||
|
|
||||||
from platypush.context import get_backend
|
from platypush.context import get_backend
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
@ -47,6 +45,8 @@ class KafkaPlugin(Plugin):
|
||||||
:type server: str
|
:type server: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from kafka import KafkaProducer
|
||||||
|
|
||||||
if not server:
|
if not server:
|
||||||
if not self.server:
|
if not self.server:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import pylast
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
@ -28,7 +27,9 @@ class LastfmPlugin(Plugin):
|
||||||
:type api_key: str
|
:type api_key: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import pylast
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.api_key = api_key
|
self.api_key = api_key
|
||||||
self.api_secret = api_secret
|
self.api_secret = api_secret
|
||||||
self.username = username
|
self.username = username
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
import pychromecast
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from pychromecast.controllers.youtube import YouTubeController
|
|
||||||
|
|
||||||
from platypush.context import get_plugin, get_bus
|
from platypush.context import get_plugin, get_bus
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.media import MediaPlugin
|
from platypush.plugins.media import MediaPlugin
|
||||||
|
@ -151,6 +148,7 @@ class MediaChromecastPlugin(MediaPlugin):
|
||||||
:type callback: func
|
:type callback: func
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import pychromecast
|
||||||
self.chromecasts.update({
|
self.chromecasts.update({
|
||||||
cast.device.friendly_name: cast
|
cast.device.friendly_name: cast
|
||||||
for cast in pychromecast.get_chromecasts(tries=tries, retry_wait=retry_wait,
|
for cast in pychromecast.get_chromecasts(tries=tries, retry_wait=retry_wait,
|
||||||
|
@ -193,6 +191,7 @@ class MediaChromecastPlugin(MediaPlugin):
|
||||||
cast.media_controller.register_status_listener(self._media_listeners[name])
|
cast.media_controller.register_status_listener(self._media_listeners[name])
|
||||||
|
|
||||||
def get_chromecast(self, chromecast=None, n_tries=2):
|
def get_chromecast(self, chromecast=None, n_tries=2):
|
||||||
|
import pychromecast
|
||||||
if isinstance(chromecast, pychromecast.Chromecast):
|
if isinstance(chromecast, pychromecast.Chromecast):
|
||||||
return chromecast
|
return chromecast
|
||||||
|
|
||||||
|
@ -268,6 +267,7 @@ class MediaChromecastPlugin(MediaPlugin):
|
||||||
:type subtitle_id: int
|
:type subtitle_id: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from pychromecast.controllers.youtube import YouTubeController
|
||||||
if not chromecast:
|
if not chromecast:
|
||||||
chromecast = self.chromecast
|
chromecast = self.chromecast
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
from plexapi.myplex import MyPlexAccount
|
|
||||||
from plexapi.video import Movie, Show
|
|
||||||
|
|
||||||
from platypush.context import get_plugin
|
from platypush.context import get_plugin
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
@ -26,6 +23,7 @@ class MediaPlexPlugin(Plugin):
|
||||||
:type username: str
|
:type username: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from plexapi.myplex import MyPlexAccount
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.resource = MyPlexAccount(username, password).resource(server)
|
self.resource = MyPlexAccount(username, password).resource(server)
|
||||||
|
@ -378,6 +376,8 @@ class MediaPlexPlugin(Plugin):
|
||||||
|
|
||||||
|
|
||||||
def _flatten_item(self, item):
|
def _flatten_item(self, item):
|
||||||
|
from plexapi.video import Movie, Show
|
||||||
|
|
||||||
_item = {
|
_item = {
|
||||||
'summary': item.summary,
|
'summary': item.summary,
|
||||||
'title': item.title,
|
'title': item.title,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import rtmidi
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
@ -23,6 +22,7 @@ class MidiPlugin(Plugin):
|
||||||
:type device_name: str
|
:type device_name: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import rtmidi
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.device_name = device_name
|
self.device_name = device_name
|
||||||
|
@ -126,6 +126,7 @@ class MidiPlugin(Plugin):
|
||||||
:returns: dict: A list of the available MIDI ports with index and name
|
:returns: dict: A list of the available MIDI ports with index and name
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import rtmidi
|
||||||
in_ports = rtmidi.MidiIn().get_ports()
|
in_ports = rtmidi.MidiIn().get_ports()
|
||||||
out_ports = rtmidi.MidiOut().get_ports()
|
out_ports = rtmidi.MidiOut().get_ports()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import paho.mqtt.publish as publisher
|
|
||||||
|
|
||||||
from platypush.message import Message
|
from platypush.message import Message
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
@ -105,6 +104,8 @@ class MqttPlugin(Plugin):
|
||||||
:type password: str
|
:type password: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import paho.mqtt.publish as publisher
|
||||||
|
|
||||||
if not host and not self.host:
|
if not host and not self.host:
|
||||||
raise RuntimeError('No host specified and no default host configured')
|
raise RuntimeError('No host specified and no default host configured')
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import mpd
|
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
@ -39,6 +38,8 @@ class MusicMpdPlugin(MusicPlugin):
|
||||||
self.client = None
|
self.client = None
|
||||||
|
|
||||||
def _connect(self, n_tries=2):
|
def _connect(self, n_tries=2):
|
||||||
|
import mpd
|
||||||
|
|
||||||
with self._client_lock:
|
with self._client_lock:
|
||||||
if self.client:
|
if self.client:
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,8 +2,6 @@ import struct
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from bluetooth.ble import DiscoveryService, GATTRequester
|
|
||||||
|
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.switch import SwitchPlugin
|
from platypush.plugins.switch import SwitchPlugin
|
||||||
|
|
||||||
|
@ -39,6 +37,7 @@ class Scanner(object):
|
||||||
return uuids
|
return uuids
|
||||||
|
|
||||||
def scan(self):
|
def scan(self):
|
||||||
|
from bluetooth.ble import DiscoveryService
|
||||||
service = DiscoveryService(self.bt_interface) \
|
service = DiscoveryService(self.bt_interface) \
|
||||||
if self.bt_interface else DiscoveryService()
|
if self.bt_interface else DiscoveryService()
|
||||||
|
|
||||||
|
@ -62,6 +61,7 @@ class Driver(object):
|
||||||
self.req = None
|
self.req = None
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
|
from bluetooth.ble import GATTRequester
|
||||||
if self.bt_interface:
|
if self.bt_interface:
|
||||||
self.req = GATTRequester(self.device, False, self.bt_interface)
|
self.req = GATTRequester(self.device, False, self.bt_interface)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from pyHS100 import Discover
|
|
||||||
|
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.switch import SwitchPlugin
|
from platypush.plugins.switch import SwitchPlugin
|
||||||
|
|
||||||
|
@ -21,6 +19,8 @@ class SwitchTplinkPlugin(SwitchPlugin):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def _scan(self):
|
def _scan(self):
|
||||||
|
from pyHS100 import Discover
|
||||||
|
|
||||||
devices = Discover.discover()
|
devices = Discover.discover()
|
||||||
self._ip_to_dev = {}
|
self._ip_to_dev = {}
|
||||||
self._alias_to_dev = {}
|
self._alias_to_dev = {}
|
||||||
|
|
|
@ -2,8 +2,6 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from google.cloud import texttospeech
|
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,7 +33,9 @@ class TtsGooglePlugin(Plugin):
|
||||||
:type credentials_file: str
|
:type credentials_file: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from google.cloud import texttospeech
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.language = language
|
self.language = language
|
||||||
self.voice = voice
|
self.voice = voice
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ class TtsGooglePlugin(Plugin):
|
||||||
:type gender: str
|
:type gender: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from google.cloud import texttospeech
|
||||||
client = texttospeech.TextToSpeechClient()
|
client = texttospeech.TextToSpeechClient()
|
||||||
synthesis_input = texttospeech.types.SynthesisInput(text=text)
|
synthesis_input = texttospeech.types.SynthesisInput(text=text)
|
||||||
|
|
||||||
|
@ -120,4 +121,3 @@ class TtsGooglePlugin(Plugin):
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ pyyaml
|
||||||
# kafka-python
|
# kafka-python
|
||||||
|
|
||||||
# Pushbullet backend support
|
# Pushbullet backend support
|
||||||
git+https://github.com/rbrcsk/pushbullet.py
|
# git+https://github.com/rbrcsk/pushbullet.py
|
||||||
|
|
||||||
# HTTP backend support
|
# HTTP backend support
|
||||||
flask
|
flask
|
||||||
|
@ -181,3 +181,6 @@ croniter
|
||||||
|
|
||||||
# Support for Alexa/Echo voice integrations
|
# Support for Alexa/Echo voice integrations
|
||||||
# git+https://github.com:BlackLight/avs.git
|
# git+https://github.com:BlackLight/avs.git
|
||||||
|
|
||||||
|
# Support for clipboard manipulation
|
||||||
|
# pyperclip
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -145,9 +145,9 @@ setup(
|
||||||
# Support for Pushbullet backend and plugin
|
# Support for Pushbullet backend and plugin
|
||||||
'pushbullet': ['pushbullet.py'],
|
'pushbullet': ['pushbullet.py'],
|
||||||
# Support for HTTP backend
|
# Support for HTTP backend
|
||||||
'http': ['flask','websockets', 'python-dateutil', 'tz', 'frozendict', 'bcrypt', 'sqlalchemy'],
|
'http': ['flask', 'websockets', 'python-dateutil', 'tz', 'frozendict', 'bcrypt', 'sqlalchemy'],
|
||||||
# Support for uWSGI HTTP backend
|
# Support for uWSGI HTTP backend
|
||||||
'uwsgi': ['flask','websockets', 'python-dateutil', 'tz', 'frozendict', 'uwsgi', 'bcrypt', 'sqlalchemy'],
|
'uwsgi': ['flask', 'websockets', 'python-dateutil', 'tz', 'frozendict', 'uwsgi', 'bcrypt', 'sqlalchemy'],
|
||||||
# Support for database
|
# Support for database
|
||||||
'db': ['sqlalchemy'],
|
'db': ['sqlalchemy'],
|
||||||
# Support for MQTT backends
|
# Support for MQTT backends
|
||||||
|
|
Loading…
Reference in a new issue