Major LINT fixes
This commit is contained in:
parent
86761e7088
commit
2a78f81a7b
101 changed files with 527 additions and 669 deletions
|
@ -3,6 +3,14 @@
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
Given the high speed of development in the first phase, changes are being reported only starting from v0.20.2.
|
Given the high speed of development in the first phase, changes are being reported only starting from v0.20.2.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Major LINT fixes.
|
||||||
|
|
||||||
|
- Removed unmaintained integrations: TorrentCast and Booking.com
|
||||||
|
|
||||||
## [0.20.8] - 2021-04-04
|
## [0.20.8] - 2021-04-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -28,7 +28,6 @@ Events
|
||||||
platypush/events/gps.rst
|
platypush/events/gps.rst
|
||||||
platypush/events/http.rst
|
platypush/events/http.rst
|
||||||
platypush/events/http.hook.rst
|
platypush/events/http.hook.rst
|
||||||
platypush/events/http.ota.booking.rst
|
|
||||||
platypush/events/http.rss.rst
|
platypush/events/http.rss.rst
|
||||||
platypush/events/inotify.rst
|
platypush/events/inotify.rst
|
||||||
platypush/events/joystick.rst
|
platypush/events/joystick.rst
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
``platypush.message.event.http.ota.booking``
|
|
||||||
============================================
|
|
||||||
|
|
||||||
.. automodule:: platypush.message.event.http.ota.booking
|
|
||||||
:members:
|
|
|
@ -1,5 +0,0 @@
|
||||||
``platypush.plugins.http.request.ota.booking``
|
|
||||||
==============================================
|
|
||||||
|
|
||||||
.. automodule:: platypush.plugins.http.request.ota.booking
|
|
||||||
:members:
|
|
|
@ -1,5 +0,0 @@
|
||||||
``platypush.plugins.video.torrentcast``
|
|
||||||
=======================================
|
|
||||||
|
|
||||||
.. automodule:: platypush.plugins.video.torrentcast
|
|
||||||
:members:
|
|
|
@ -61,7 +61,6 @@ Plugins
|
||||||
platypush/plugins/graphite.rst
|
platypush/plugins/graphite.rst
|
||||||
platypush/plugins/homeseer.rst
|
platypush/plugins/homeseer.rst
|
||||||
platypush/plugins/http.request.rst
|
platypush/plugins/http.request.rst
|
||||||
platypush/plugins/http.request.ota.booking.rst
|
|
||||||
platypush/plugins/http.request.rss.rst
|
platypush/plugins/http.request.rss.rst
|
||||||
platypush/plugins/http.webpage.rst
|
platypush/plugins/http.webpage.rst
|
||||||
platypush/plugins/ifttt.rst
|
platypush/plugins/ifttt.rst
|
||||||
|
@ -137,7 +136,6 @@ Plugins
|
||||||
platypush/plugins/user.rst
|
platypush/plugins/user.rst
|
||||||
platypush/plugins/utils.rst
|
platypush/plugins/utils.rst
|
||||||
platypush/plugins/variable.rst
|
platypush/plugins/variable.rst
|
||||||
platypush/plugins/video.torrentcast.rst
|
|
||||||
platypush/plugins/weather.rst
|
platypush/plugins/weather.rst
|
||||||
platypush/plugins/weather.buienradar.rst
|
platypush/plugins/weather.buienradar.rst
|
||||||
platypush/plugins/weather.darksky.rst
|
platypush/plugins/weather.darksky.rst
|
||||||
|
|
|
@ -6,10 +6,9 @@
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from threading import Thread
|
from threading import Thread, Event as ThreadEvent, get_ident
|
||||||
from typing import Optional, Dict
|
from typing import Optional, Dict
|
||||||
|
|
||||||
from platypush.bus import Bus
|
from platypush.bus import Bus
|
||||||
|
@ -62,7 +61,7 @@ class Backend(Thread, EventGenerator):
|
||||||
self.poll_seconds = float(poll_seconds) if poll_seconds else None
|
self.poll_seconds = float(poll_seconds) if poll_seconds else None
|
||||||
self.device_id = Config.get('device_id')
|
self.device_id = Config.get('device_id')
|
||||||
self.thread_id = None
|
self.thread_id = None
|
||||||
self._stop_event = threading.Event()
|
self._stop_event = ThreadEvent()
|
||||||
self._kwargs = kwargs
|
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 = None
|
||||||
|
@ -220,7 +219,7 @@ class Backend(Thread, EventGenerator):
|
||||||
|
|
||||||
def run(self):
|
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 = threading.get_ident()
|
self.thread_id = get_ident()
|
||||||
set_thread_name(self._thread_name)
|
set_thread_name(self._thread_name)
|
||||||
if not callable(self.loop):
|
if not callable(self.loop):
|
||||||
return
|
return
|
||||||
|
|
|
@ -65,11 +65,11 @@ class AdafruitIoBackend(Backend):
|
||||||
def on_message(self, msg):
|
def on_message(self, msg):
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
def _handler(client, feed, data):
|
def _handler(client, feed, data):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
data = float(data)
|
data = float(data)
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug('Not a number: {}: {}'.format(data, e))
|
||||||
|
|
||||||
self.bus.post(FeedUpdateEvent(feed=feed, data=data))
|
self.bus.post(FeedUpdateEvent(feed=feed, data=data))
|
||||||
|
|
||||||
return _handler
|
return _handler
|
||||||
|
|
|
@ -55,15 +55,17 @@ class Alarm:
|
||||||
self._runtime_snooze_interval = snooze_interval
|
self._runtime_snooze_interval = snooze_interval
|
||||||
|
|
||||||
def get_next(self) -> float:
|
def get_next(self) -> float:
|
||||||
now = datetime.datetime.now().replace(tzinfo=gettz())
|
now = datetime.datetime.now().replace(tzinfo=gettz()) # lgtm [py/call-to-non-callable]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cron = croniter.croniter(self.when, now)
|
cron = croniter.croniter(self.when, now)
|
||||||
return cron.get_next()
|
return cron.get_next()
|
||||||
except (AttributeError, croniter.CroniterBadCronError):
|
except (AttributeError, croniter.CroniterBadCronError):
|
||||||
try:
|
try:
|
||||||
|
# lgtm [py/call-to-non-callable]
|
||||||
timestamp = datetime.datetime.fromisoformat(self.when).replace(tzinfo=gettz())
|
timestamp = datetime.datetime.fromisoformat(self.when).replace(tzinfo=gettz())
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
|
# lgtm [py/call-to-non-callable]
|
||||||
timestamp = (datetime.datetime.now().replace(tzinfo=gettz()) +
|
timestamp = (datetime.datetime.now().replace(tzinfo=gettz()) +
|
||||||
datetime.timedelta(seconds=int(self.when)))
|
datetime.timedelta(seconds=int(self.when)))
|
||||||
|
|
||||||
|
|
|
@ -13,37 +13,41 @@ Bd addr are represented as standard python strings, e.g. "aa:bb:cc:dd:ee:ff".
|
||||||
import asyncio
|
import asyncio
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import time
|
|
||||||
import struct
|
import struct
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
|
|
||||||
class CreateConnectionChannelError(Enum):
|
class CreateConnectionChannelError(Enum):
|
||||||
NoError = 0
|
NoError = 0
|
||||||
MaxPendingConnectionsReached = 1
|
MaxPendingConnectionsReached = 1
|
||||||
|
|
||||||
|
|
||||||
class ConnectionStatus(Enum):
|
class ConnectionStatus(Enum):
|
||||||
Disconnected = 0
|
Disconnected = 0
|
||||||
Connected = 1
|
Connected = 1
|
||||||
Ready = 2
|
Ready = 2
|
||||||
|
|
||||||
|
|
||||||
class DisconnectReason(Enum):
|
class DisconnectReason(Enum):
|
||||||
Unspecified = 0
|
Unspecified = 0
|
||||||
ConnectionEstablishmentFailed = 1
|
ConnectionEstablishmentFailed = 1
|
||||||
TimedOut = 2
|
TimedOut = 2
|
||||||
BondingKeysMismatch = 3
|
BondingKeysMismatch = 3
|
||||||
|
|
||||||
|
|
||||||
class RemovedReason(Enum):
|
class RemovedReason(Enum):
|
||||||
RemovedByThisClient = 0
|
RemovedByThisClient = 0
|
||||||
ForceDisconnectedByThisClient = 1
|
ForceDisconnectedByThisClient = 1
|
||||||
ForceDisconnectedByOtherClient = 2
|
ForceDisconnectedByOtherClient = 2
|
||||||
|
|
||||||
ButtonIsPrivate = 3
|
ButtonIsPrivate = 3
|
||||||
VerifyTimeout = 4
|
VerifyTimeout = 4
|
||||||
InternetBackendError = 5
|
InternetBackendError = 5
|
||||||
InvalidData = 6
|
InvalidData = 6
|
||||||
|
|
||||||
CouldntLoadDevice = 7
|
CouldntLoadDevice = 7
|
||||||
|
|
||||||
|
|
||||||
class ClickType(Enum):
|
class ClickType(Enum):
|
||||||
ButtonDown = 0
|
ButtonDown = 0
|
||||||
ButtonUp = 1
|
ButtonUp = 1
|
||||||
|
@ -52,20 +56,24 @@ class ClickType(Enum):
|
||||||
ButtonDoubleClick = 4
|
ButtonDoubleClick = 4
|
||||||
ButtonHold = 5
|
ButtonHold = 5
|
||||||
|
|
||||||
|
|
||||||
class BdAddrType(Enum):
|
class BdAddrType(Enum):
|
||||||
PublicBdAddrType = 0
|
PublicBdAddrType = 0
|
||||||
RandomBdAddrType = 1
|
RandomBdAddrType = 1
|
||||||
|
|
||||||
|
|
||||||
class LatencyMode(Enum):
|
class LatencyMode(Enum):
|
||||||
NormalLatency = 0
|
NormalLatency = 0
|
||||||
LowLatency = 1
|
LowLatency = 1
|
||||||
HighLatency = 2
|
HighLatency = 2
|
||||||
|
|
||||||
|
|
||||||
class BluetoothControllerState(Enum):
|
class BluetoothControllerState(Enum):
|
||||||
Detached = 0
|
Detached = 0
|
||||||
Resetting = 1
|
Resetting = 1
|
||||||
Attached = 2
|
Attached = 2
|
||||||
|
|
||||||
|
|
||||||
class ScanWizardResult(Enum):
|
class ScanWizardResult(Enum):
|
||||||
WizardSuccess = 0
|
WizardSuccess = 0
|
||||||
WizardCancelledByUser = 1
|
WizardCancelledByUser = 1
|
||||||
|
@ -75,24 +83,26 @@ class ScanWizardResult(Enum):
|
||||||
WizardInternetBackendError = 5
|
WizardInternetBackendError = 5
|
||||||
WizardInvalidData = 6
|
WizardInvalidData = 6
|
||||||
|
|
||||||
|
|
||||||
class ButtonScanner:
|
class ButtonScanner:
|
||||||
"""ButtonScanner class.
|
"""ButtonScanner class.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
scanner = ButtonScanner()
|
scanner = ButtonScanner()
|
||||||
scanner.on_advertisement_packet = lambda scanner, bd_addr, name, rssi, is_private, already_verified: ...
|
scanner.on_advertisement_packet = lambda scanner, bd_addr, name, rssi, is_private, already_verified: ...
|
||||||
client.add_scanner(scanner)
|
client.add_scanner(scanner)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_cnt = itertools.count()
|
_cnt = itertools.count()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._scan_id = next(ButtonScanner._cnt)
|
self._scan_id = next(ButtonScanner._cnt)
|
||||||
self.on_advertisement_packet = lambda scanner, bd_addr, name, rssi, is_private, already_verified: None
|
self.on_advertisement_packet = lambda scanner, bd_addr, name, rssi, is_private, already_verified: None
|
||||||
|
|
||||||
|
|
||||||
class ScanWizard:
|
class ScanWizard:
|
||||||
"""ScanWizard class
|
"""ScanWizard class
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
wizard = ScanWizard()
|
wizard = ScanWizard()
|
||||||
wizard.on_found_private_button = lambda scan_wizard: ...
|
wizard.on_found_private_button = lambda scan_wizard: ...
|
||||||
|
@ -101,9 +111,9 @@ class ScanWizard:
|
||||||
wizard.on_completed = lambda scan_wizard, result, bd_addr, name: ...
|
wizard.on_completed = lambda scan_wizard, result, bd_addr, name: ...
|
||||||
client.add_scan_wizard(wizard)
|
client.add_scan_wizard(wizard)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_cnt = itertools.count()
|
_cnt = itertools.count()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._scan_wizard_id = next(ScanWizard._cnt)
|
self._scan_wizard_id = next(ScanWizard._cnt)
|
||||||
self._bd_addr = None
|
self._bd_addr = None
|
||||||
|
@ -113,33 +123,34 @@ class ScanWizard:
|
||||||
self.on_button_connected = lambda scan_wizard, bd_addr, name: None
|
self.on_button_connected = lambda scan_wizard, bd_addr, name: None
|
||||||
self.on_completed = lambda scan_wizard, result, bd_addr, name: None
|
self.on_completed = lambda scan_wizard, result, bd_addr, name: None
|
||||||
|
|
||||||
|
|
||||||
class ButtonConnectionChannel:
|
class ButtonConnectionChannel:
|
||||||
"""ButtonConnectionChannel class.
|
"""ButtonConnectionChannel class.
|
||||||
|
|
||||||
This class represents a connection channel to a Flic button.
|
This class represents a connection channel to a Flic button.
|
||||||
Add this button connection channel to a FlicClient by executing client.add_connection_channel(connection_channel).
|
Add this button connection channel to a FlicClient by executing client.add_connection_channel(connection_channel).
|
||||||
You may only have this connection channel added to one FlicClient at a time.
|
You may only have this connection channel added to one FlicClient at a time.
|
||||||
|
|
||||||
Before you add the connection channel to the client, you should set up your callback functions by assigning
|
Before you add the connection channel to the client, you should set up your callback functions by assigning
|
||||||
the corresponding properties to this object with a function. Each callback function has a channel parameter as the first one,
|
the corresponding properties to this object with a function. Each callback function has a channel parameter as the first one,
|
||||||
referencing this object.
|
referencing this object.
|
||||||
|
|
||||||
Available properties and the function parameters are:
|
Available properties and the function parameters are:
|
||||||
on_create_connection_channel_response: channel, error, connection_status
|
on_create_connection_channel_response: channel, error, connection_status
|
||||||
on_removed: channel, removed_reason
|
on_removed: channel, removed_reason
|
||||||
on_connection_status_changed: channel, connection_status, disconnect_reason
|
on_connection_status_changed: channel, connection_status, disconnect_reason
|
||||||
on_button_up_or_down / on_button_click_or_hold / on_button_single_or_double_click / on_button_single_or_double_click_or_hold: channel, click_type, was_queued, time_diff
|
on_button_up_or_down / on_button_click_or_hold / on_button_single_or_double_click / on_button_single_or_double_click_or_hold: channel, click_type, was_queued, time_diff
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_cnt = itertools.count()
|
_cnt = itertools.count()
|
||||||
|
|
||||||
def __init__(self, bd_addr, latency_mode = LatencyMode.NormalLatency, auto_disconnect_time = 511):
|
def __init__(self, bd_addr, latency_mode=LatencyMode.NormalLatency, auto_disconnect_time=511):
|
||||||
self._conn_id = next(ButtonConnectionChannel._cnt)
|
self._conn_id = next(ButtonConnectionChannel._cnt)
|
||||||
self._bd_addr = bd_addr
|
self._bd_addr = bd_addr
|
||||||
self._latency_mode = latency_mode
|
self._latency_mode = latency_mode
|
||||||
self._auto_disconnect_time = auto_disconnect_time
|
self._auto_disconnect_time = auto_disconnect_time
|
||||||
self._client = None
|
self._client = None
|
||||||
|
|
||||||
self.on_create_connection_channel_response = lambda channel, error, connection_status: None
|
self.on_create_connection_channel_response = lambda channel, error, connection_status: None
|
||||||
self.on_removed = lambda channel, removed_reason: None
|
self.on_removed = lambda channel, removed_reason: None
|
||||||
self.on_connection_status_changed = lambda channel, connection_status, disconnect_reason: None
|
self.on_connection_status_changed = lambda channel, connection_status, disconnect_reason: None
|
||||||
|
@ -147,61 +158,66 @@ class ButtonConnectionChannel:
|
||||||
self.on_button_click_or_hold = lambda channel, click_type, was_queued, time_diff: None
|
self.on_button_click_or_hold = lambda channel, click_type, was_queued, time_diff: None
|
||||||
self.on_button_single_or_double_click = lambda channel, click_type, was_queued, time_diff: None
|
self.on_button_single_or_double_click = lambda channel, click_type, was_queued, time_diff: None
|
||||||
self.on_button_single_or_double_click_or_hold = lambda channel, click_type, was_queued, time_diff: None
|
self.on_button_single_or_double_click_or_hold = lambda channel, click_type, was_queued, time_diff: None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bd_addr(self):
|
def bd_addr(self):
|
||||||
return self._bd_addr
|
return self._bd_addr
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def latency_mode(self):
|
def latency_mode(self):
|
||||||
return self._latency_mode
|
return self._latency_mode
|
||||||
|
|
||||||
@latency_mode.setter
|
@latency_mode.setter
|
||||||
def latency_mode(self, latency_mode):
|
def latency_mode(self, latency_mode):
|
||||||
if self._client is None:
|
if self._client is None:
|
||||||
self._latency_mode = latency_mode
|
self._latency_mode = latency_mode
|
||||||
return
|
return
|
||||||
|
|
||||||
self._latency_mode = latency_mode
|
self._latency_mode = latency_mode
|
||||||
if not self._client._closed:
|
if not self._client._closed:
|
||||||
self._client._send_command("CmdChangeModeParameters", {"conn_id": self._conn_id, "latency_mode": self._latency_mode, "auto_disconnect_time": self._auto_disconnect_time})
|
self._client._send_command("CmdChangeModeParameters",
|
||||||
|
{"conn_id": self._conn_id, "latency_mode": self._latency_mode,
|
||||||
|
"auto_disconnect_time": self._auto_disconnect_time})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def auto_disconnect_time(self):
|
def auto_disconnect_time(self):
|
||||||
return self._auto_disconnect_time
|
return self._auto_disconnect_time
|
||||||
|
|
||||||
@auto_disconnect_time.setter
|
@auto_disconnect_time.setter
|
||||||
def auto_disconnect_time(self, auto_disconnect_time):
|
def auto_disconnect_time(self, auto_disconnect_time):
|
||||||
if self._client is None:
|
if self._client is None:
|
||||||
self._auto_disconnect_time = auto_disconnect_time
|
self._auto_disconnect_time = auto_disconnect_time
|
||||||
return
|
return
|
||||||
|
|
||||||
self._auto_disconnect_time = auto_disconnect_time
|
self._auto_disconnect_time = auto_disconnect_time
|
||||||
if not self._client._closed:
|
if not self._client._closed:
|
||||||
self._client._send_command("CmdChangeModeParameters", {"conn_id": self._conn_id, "latency_mode": self._latency_mode, "auto_disconnect_time": self._auto_disconnect_time})
|
self._client._send_command("CmdChangeModeParameters",
|
||||||
|
{"conn_id": self._conn_id, "latency_mode": self._latency_mode,
|
||||||
|
"auto_disconnect_time": self._auto_disconnect_time})
|
||||||
|
|
||||||
|
|
||||||
class FlicClient(asyncio.Protocol):
|
class FlicClient(asyncio.Protocol):
|
||||||
"""FlicClient class.
|
"""FlicClient class.
|
||||||
|
|
||||||
When this class is constructed, a socket connection is established.
|
When this class is constructed, a socket connection is established.
|
||||||
You may then send commands to the server and set timers.
|
You may then send commands to the server and set timers.
|
||||||
Once you are ready with the initialization you must call the handle_events() method which is a main loop that never exits, unless the socket is closed.
|
Once you are ready with the initialization you must call the handle_events() method which is a main loop that never exits, unless the socket is closed.
|
||||||
For a more detailed description of all commands, events and enums, check the protocol specification.
|
For a more detailed description of all commands, events and enums, check the protocol specification.
|
||||||
|
|
||||||
All commands are wrapped in more high level functions and events are reported using callback functions.
|
All commands are wrapped in more high level functions and events are reported using callback functions.
|
||||||
|
|
||||||
All methods called on this class will take effect only if you eventually call the handle_events() method.
|
All methods called on this class will take effect only if you eventually call the handle_events() method.
|
||||||
|
|
||||||
The ButtonScanner is used to set up a handler for advertisement packets.
|
The ButtonScanner is used to set up a handler for advertisement packets.
|
||||||
The ButtonConnectionChannel is used to interact with connections to flic buttons and receive their events.
|
The ButtonConnectionChannel is used to interact with connections to flic buttons and receive their events.
|
||||||
|
|
||||||
Other events are handled by the following callback functions that can be assigned to this object (and a list of the callback function parameters):
|
Other events are handled by the following callback functions that can be assigned to this object (and a list of the callback function parameters):
|
||||||
on_new_verified_button: bd_addr
|
on_new_verified_button: bd_addr
|
||||||
on_no_space_for_new_connection: max_concurrently_connected_buttons
|
on_no_space_for_new_connection: max_concurrently_connected_buttons
|
||||||
on_got_space_for_new_connection: max_concurrently_connected_buttons
|
on_got_space_for_new_connection: max_concurrently_connected_buttons
|
||||||
on_bluetooth_controller_state_change: state
|
on_bluetooth_controller_state_change: state
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_EVENTS = [
|
_EVENTS = [
|
||||||
("EvtAdvertisementPacket", "<I6s17pb??", "scan_id bd_addr name rssi is_private already_verified"),
|
("EvtAdvertisementPacket", "<I6s17pb??", "scan_id bd_addr name rssi is_private already_verified"),
|
||||||
("EvtCreateConnectionChannelResponse", "<IBB", "conn_id error connection_status"),
|
("EvtCreateConnectionChannelResponse", "<IBB", "conn_id error connection_status"),
|
||||||
|
@ -212,7 +228,8 @@ class FlicClient(asyncio.Protocol):
|
||||||
("EvtButtonSingleOrDoubleClick", "<IBBI", "conn_id click_type was_queued time_diff"),
|
("EvtButtonSingleOrDoubleClick", "<IBBI", "conn_id click_type was_queued time_diff"),
|
||||||
("EvtButtonSingleOrDoubleClickOrHold", "<IBBI", "conn_id click_type was_queued time_diff"),
|
("EvtButtonSingleOrDoubleClickOrHold", "<IBBI", "conn_id click_type was_queued time_diff"),
|
||||||
("EvtNewVerifiedButton", "<6s", "bd_addr"),
|
("EvtNewVerifiedButton", "<6s", "bd_addr"),
|
||||||
("EvtGetInfoResponse", "<B6sBBhBBH", "bluetooth_controller_state my_bd_addr my_bd_addr_type max_pending_connections max_concurrently_connected_buttons current_pending_connections currently_no_space_for_new_connection nb_verified_buttons"),
|
("EvtGetInfoResponse", "<B6sBBhBBH",
|
||||||
|
"bluetooth_controller_state my_bd_addr my_bd_addr_type max_pending_connections max_concurrently_connected_buttons current_pending_connections currently_no_space_for_new_connection nb_verified_buttons"),
|
||||||
("EvtNoSpaceForNewConnection", "<B", "max_concurrently_connected_buttons"),
|
("EvtNoSpaceForNewConnection", "<B", "max_concurrently_connected_buttons"),
|
||||||
("EvtGotSpaceForNewConnection", "<B", "max_concurrently_connected_buttons"),
|
("EvtGotSpaceForNewConnection", "<B", "max_concurrently_connected_buttons"),
|
||||||
("EvtBluetoothControllerStateChange", "<B", "state"),
|
("EvtBluetoothControllerStateChange", "<B", "state"),
|
||||||
|
@ -223,9 +240,9 @@ class FlicClient(asyncio.Protocol):
|
||||||
("EvtScanWizardButtonConnected", "<I", "scan_wizard_id"),
|
("EvtScanWizardButtonConnected", "<I", "scan_wizard_id"),
|
||||||
("EvtScanWizardCompleted", "<IB", "scan_wizard_id result")
|
("EvtScanWizardCompleted", "<IB", "scan_wizard_id result")
|
||||||
]
|
]
|
||||||
_EVENT_STRUCTS = list(map(lambda x: None if x == None else struct.Struct(x[1]), _EVENTS))
|
_EVENT_STRUCTS = list(map(lambda x: None if x is None else struct.Struct(x[1]), _EVENTS))
|
||||||
_EVENT_NAMED_TUPLES = list(map(lambda x: None if x == None else namedtuple(x[0], x[2]), _EVENTS))
|
_EVENT_NAMED_TUPLES = list(map(lambda x: None if x is None else namedtuple(x[0], x[2]), _EVENTS))
|
||||||
|
|
||||||
_COMMANDS = [
|
_COMMANDS = [
|
||||||
("CmdGetInfo", "", ""),
|
("CmdGetInfo", "", ""),
|
||||||
("CmdCreateScanner", "<I", "scan_id"),
|
("CmdCreateScanner", "<I", "scan_id"),
|
||||||
|
@ -239,168 +256,169 @@ class FlicClient(asyncio.Protocol):
|
||||||
("CmdCreateScanWizard", "<I", "scan_wizard_id"),
|
("CmdCreateScanWizard", "<I", "scan_wizard_id"),
|
||||||
("CmdCancelScanWizard", "<I", "scan_wizard_id")
|
("CmdCancelScanWizard", "<I", "scan_wizard_id")
|
||||||
]
|
]
|
||||||
|
|
||||||
_COMMAND_STRUCTS = list(map(lambda x: struct.Struct(x[1]), _COMMANDS))
|
_COMMAND_STRUCTS = list(map(lambda x: struct.Struct(x[1]), _COMMANDS))
|
||||||
_COMMAND_NAMED_TUPLES = list(map(lambda x: namedtuple(x[0], x[2]), _COMMANDS))
|
_COMMAND_NAMED_TUPLES = list(map(lambda x: namedtuple(x[0], x[2]), _COMMANDS))
|
||||||
_COMMAND_NAME_TO_OPCODE = dict((x[0], i) for i, x in enumerate(_COMMANDS))
|
_COMMAND_NAME_TO_OPCODE = dict((x[0], i) for i, x in enumerate(_COMMANDS))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def _bdaddr_bytes_to_string(bdaddr_bytes):
|
def _bdaddr_bytes_to_string(bdaddr_bytes):
|
||||||
return ":".join(map(lambda x: "%02x" % x, reversed(bdaddr_bytes)))
|
return ":".join(map(lambda x: "%02x" % x, reversed(bdaddr_bytes)))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def _bdaddr_string_to_bytes(bdaddr_string):
|
def _bdaddr_string_to_bytes(bdaddr_string):
|
||||||
return bytearray.fromhex("".join(reversed(bdaddr_string.split(":"))))
|
return bytearray.fromhex("".join(reversed(bdaddr_string.split(":"))))
|
||||||
|
|
||||||
def __init__(self, loop,parent=None):
|
def __init__(self, loop, parent=None):
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.buffer=b""
|
self.buffer = b""
|
||||||
self.transport=None
|
self.transport = None
|
||||||
self.parent=parent
|
self.parent = parent
|
||||||
self._scanners = {}
|
self._scanners = {}
|
||||||
self._scan_wizards = {}
|
self._scan_wizards = {}
|
||||||
self._connection_channels = {}
|
self._connection_channels = {}
|
||||||
self._closed = False
|
self._closed = False
|
||||||
|
|
||||||
self.on_new_verified_button = lambda bd_addr: None
|
self.on_new_verified_button = lambda bd_addr: None
|
||||||
self.on_no_space_for_new_connection = lambda max_concurrently_connected_buttons: None
|
self.on_no_space_for_new_connection = lambda max_concurrently_connected_buttons: None
|
||||||
self.on_got_space_for_new_connection = lambda max_concurrently_connected_buttons: None
|
self.on_got_space_for_new_connection = lambda max_concurrently_connected_buttons: None
|
||||||
self.on_bluetooth_controller_state_change = lambda state: None
|
self.on_bluetooth_controller_state_change = lambda state: None
|
||||||
self.on_get_info = lambda items: None
|
self.on_get_info = lambda items: None
|
||||||
self.on_get_button_uuid = lambda addr, uuid: None
|
self.on_get_button_uuid = lambda addr, uuid: None
|
||||||
|
|
||||||
def connection_made(self, transport):
|
def connection_made(self, transport):
|
||||||
self.transport=transport
|
self.transport = transport
|
||||||
if self.parent:
|
if self.parent:
|
||||||
self.parent.register_protocol(self)
|
self.parent.register_protocol(self)
|
||||||
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Closes the client. The handle_events() method will return."""
|
"""Closes the client. The handle_events() method will return."""
|
||||||
if self._closed:
|
if self._closed:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._closed = True
|
self._closed = True
|
||||||
|
|
||||||
def add_scanner(self, scanner):
|
def add_scanner(self, scanner):
|
||||||
"""Add a ButtonScanner object.
|
"""Add a ButtonScanner object.
|
||||||
|
|
||||||
The scan will start directly once the scanner is added.
|
The scan will start directly once the scanner is added.
|
||||||
"""
|
"""
|
||||||
if scanner._scan_id in self._scanners:
|
if scanner._scan_id in self._scanners:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._scanners[scanner._scan_id] = scanner
|
self._scanners[scanner._scan_id] = scanner
|
||||||
self._send_command("CmdCreateScanner", {"scan_id": scanner._scan_id})
|
self._send_command("CmdCreateScanner", {"scan_id": scanner._scan_id})
|
||||||
|
|
||||||
def remove_scanner(self, scanner):
|
def remove_scanner(self, scanner):
|
||||||
"""Remove a ButtonScanner object.
|
"""Remove a ButtonScanner object.
|
||||||
|
|
||||||
You will no longer receive advertisement packets.
|
You will no longer receive advertisement packets.
|
||||||
"""
|
"""
|
||||||
if scanner._scan_id not in self._scanners:
|
if scanner._scan_id not in self._scanners:
|
||||||
return
|
return
|
||||||
|
|
||||||
del self._scanners[scanner._scan_id]
|
del self._scanners[scanner._scan_id]
|
||||||
self._send_command("CmdRemoveScanner", {"scan_id": scanner._scan_id})
|
self._send_command("CmdRemoveScanner", {"scan_id": scanner._scan_id})
|
||||||
|
|
||||||
def add_scan_wizard(self, scan_wizard):
|
def add_scan_wizard(self, scan_wizard):
|
||||||
"""Add a ScanWizard object.
|
"""Add a ScanWizard object.
|
||||||
|
|
||||||
The scan wizard will start directly once the scan wizard is added.
|
The scan wizard will start directly once the scan wizard is added.
|
||||||
"""
|
"""
|
||||||
if scan_wizard._scan_wizard_id in self._scan_wizards:
|
if scan_wizard._scan_wizard_id in self._scan_wizards:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._scan_wizards[scan_wizard._scan_wizard_id] = scan_wizard
|
self._scan_wizards[scan_wizard._scan_wizard_id] = scan_wizard
|
||||||
self._send_command("CmdCreateScanWizard", {"scan_wizard_id": scan_wizard._scan_wizard_id})
|
self._send_command("CmdCreateScanWizard", {"scan_wizard_id": scan_wizard._scan_wizard_id})
|
||||||
|
|
||||||
def cancel_scan_wizard(self, scan_wizard):
|
def cancel_scan_wizard(self, scan_wizard):
|
||||||
"""Cancel a ScanWizard.
|
"""Cancel a ScanWizard.
|
||||||
|
|
||||||
Note: The effect of this command will take place at the time the on_completed event arrives on the scan wizard object.
|
Note: The effect of this command will take place at the time the on_completed event arrives on the scan wizard object.
|
||||||
If cancelled due to this command, "result" in the on_completed event will be "WizardCancelledByUser".
|
If cancelled due to this command, "result" in the on_completed event will be "WizardCancelledByUser".
|
||||||
"""
|
"""
|
||||||
if scan_wizard._scan_wizard_id not in self._scan_wizards:
|
if scan_wizard._scan_wizard_id not in self._scan_wizards:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._send_command("CmdCancelScanWizard", {"scan_wizard_id": scan_wizard._scan_wizard_id})
|
self._send_command("CmdCancelScanWizard", {"scan_wizard_id": scan_wizard._scan_wizard_id})
|
||||||
|
|
||||||
def add_connection_channel(self, channel):
|
def add_connection_channel(self, channel):
|
||||||
"""Adds a connection channel to a specific Flic button.
|
"""Adds a connection channel to a specific Flic button.
|
||||||
|
|
||||||
This will start listening for a specific Flic button's connection and button events.
|
This will start listening for a specific Flic button's connection and button events.
|
||||||
Make sure the Flic is either in public mode (by holding it down for 7 seconds) or already verified before calling this method.
|
Make sure the Flic is either in public mode (by holding it down for 7 seconds) or already verified before calling this method.
|
||||||
|
|
||||||
The on_create_connection_channel_response callback property will be called on the
|
The on_create_connection_channel_response callback property will be called on the
|
||||||
connection channel after this command has been received by the server.
|
connection channel after this command has been received by the server.
|
||||||
|
|
||||||
You may have as many connection channels as you wish for a specific Flic Button.
|
You may have as many connection channels as you wish for a specific Flic Button.
|
||||||
"""
|
"""
|
||||||
if channel._conn_id in self._connection_channels:
|
if channel._conn_id in self._connection_channels:
|
||||||
return
|
return
|
||||||
|
|
||||||
channel._client = self
|
channel._client = self
|
||||||
|
|
||||||
self._connection_channels[channel._conn_id] = channel
|
self._connection_channels[channel._conn_id] = channel
|
||||||
self._send_command("CmdCreateConnectionChannel", {"conn_id": channel._conn_id, "bd_addr": channel.bd_addr, "latency_mode": channel._latency_mode, "auto_disconnect_time": channel._auto_disconnect_time})
|
self._send_command("CmdCreateConnectionChannel", {"conn_id": channel._conn_id, "bd_addr": channel.bd_addr,
|
||||||
|
"latency_mode": channel._latency_mode,
|
||||||
|
"auto_disconnect_time": channel._auto_disconnect_time})
|
||||||
|
|
||||||
def remove_connection_channel(self, channel):
|
def remove_connection_channel(self, channel):
|
||||||
"""Remove a connection channel.
|
"""Remove a connection channel.
|
||||||
|
|
||||||
This will stop listening for new events for a specific connection channel that has previously been added.
|
This will stop listening for new events for a specific connection channel that has previously been added.
|
||||||
Note: The effect of this command will take place at the time the on_removed event arrives on the connection channel object.
|
Note: The effect of this command will take place at the time the on_removed event arrives on the connection channel object.
|
||||||
"""
|
"""
|
||||||
if channel._conn_id not in self._connection_channels:
|
if channel._conn_id not in self._connection_channels:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._send_command("CmdRemoveConnectionChannel", {"conn_id": channel._conn_id})
|
self._send_command("CmdRemoveConnectionChannel", {"conn_id": channel._conn_id})
|
||||||
|
|
||||||
def force_disconnect(self, bd_addr):
|
def force_disconnect(self, bd_addr):
|
||||||
"""Force disconnection or cancel pending connection of a specific Flic button.
|
"""Force disconnection or cancel pending connection of a specific Flic button.
|
||||||
|
|
||||||
This removes all connection channels for all clients connected to the server for this specific Flic button.
|
This removes all connection channels for all clients connected to the server for this specific Flic button.
|
||||||
"""
|
"""
|
||||||
self._send_command("CmdForceDisconnect", {"bd_addr": bd_addr})
|
self._send_command("CmdForceDisconnect", {"bd_addr": bd_addr})
|
||||||
|
|
||||||
def get_info(self):
|
def get_info(self):
|
||||||
"""Get info about the current state of the server.
|
"""Get info about the current state of the server.
|
||||||
|
|
||||||
The server will send back its information directly and the callback will be called once the response arrives.
|
The server will send back its information directly and the callback will be called once the response arrives.
|
||||||
The callback takes only one parameter: info. This info parameter is a dictionary with the following objects:
|
The callback takes only one parameter: info. This info parameter is a dictionary with the following objects:
|
||||||
bluetooth_controller_state, my_bd_addr, my_bd_addr_type, max_pending_connections, max_concurrently_connected_buttons,
|
bluetooth_controller_state, my_bd_addr, my_bd_addr_type, max_pending_connections, max_concurrently_connected_buttons,
|
||||||
current_pending_connections, currently_no_space_for_new_connection, bd_addr_of_verified_buttons (a list of bd addresses).
|
current_pending_connections, currently_no_space_for_new_connection, bd_addr_of_verified_buttons (a list of bd addresses).
|
||||||
"""
|
"""
|
||||||
self._send_command("CmdGetInfo", {})
|
self._send_command("CmdGetInfo", {})
|
||||||
|
|
||||||
def get_button_uuid(self, bd_addr):
|
def get_button_uuid(self, bd_addr):
|
||||||
"""Get button uuid for a verified button.
|
"""Get button uuid for a verified button.
|
||||||
|
|
||||||
The server will send back its information directly and the callback will be called once the response arrives.
|
The server will send back its information directly and the callback will be called once the response arrives.
|
||||||
Responses will arrive in the same order as requested.
|
Responses will arrive in the same order as requested.
|
||||||
|
|
||||||
The callback takes two parameters: bd_addr, uuid (hex string of 32 characters).
|
The callback takes two parameters: bd_addr, uuid (hex string of 32 characters).
|
||||||
|
|
||||||
Note: if the button isn't verified, the uuid sent to the callback will rather be None.
|
Note: if the button isn't verified, the uuid sent to the callback will rather be None.
|
||||||
"""
|
"""
|
||||||
self._send_command("CmdGetButtonUUID", {"bd_addr": bd_addr})
|
self._send_command("CmdGetButtonUUID", {"bd_addr": bd_addr})
|
||||||
|
|
||||||
|
|
||||||
def run_on_handle_events_thread(self, callback):
|
def run_on_handle_events_thread(self, callback):
|
||||||
"""Run a function on the thread that handles the events."""
|
"""Run a function on the thread that handles the events."""
|
||||||
if threading.get_ident() == self._handle_event_thread_ident:
|
if threading.get_ident() == self._handle_event_thread_ident:
|
||||||
callback()
|
callback()
|
||||||
else:
|
else:
|
||||||
self.set_timer(0, callback)
|
self.set_timer(0, callback)
|
||||||
|
|
||||||
def _send_command(self, name, items):
|
def _send_command(self, name, items):
|
||||||
|
|
||||||
for key, value in items.items():
|
for key, value in items.items():
|
||||||
if isinstance(value, Enum):
|
if isinstance(value, Enum):
|
||||||
items[key] = value.value
|
items[key] = value.value
|
||||||
|
|
||||||
if "bd_addr" in items:
|
if "bd_addr" in items:
|
||||||
items["bd_addr"] = FlicClient._bdaddr_string_to_bytes(items["bd_addr"])
|
items["bd_addr"] = FlicClient._bdaddr_string_to_bytes()
|
||||||
|
|
||||||
opcode = FlicClient._COMMAND_NAME_TO_OPCODE[name]
|
opcode = FlicClient._COMMAND_NAME_TO_OPCODE[name]
|
||||||
data_bytes = FlicClient._COMMAND_STRUCTS[opcode].pack(*FlicClient._COMMAND_NAMED_TUPLES[opcode](**items))
|
data_bytes = FlicClient._COMMAND_STRUCTS[opcode].pack(*FlicClient._COMMAND_NAMED_TUPLES[opcode](**items))
|
||||||
bytes = bytearray(3)
|
bytes = bytearray(3)
|
||||||
|
@ -409,83 +427,85 @@ class FlicClient(asyncio.Protocol):
|
||||||
bytes[2] = opcode
|
bytes[2] = opcode
|
||||||
bytes += data_bytes
|
bytes += data_bytes
|
||||||
self.transport.write(bytes)
|
self.transport.write(bytes)
|
||||||
|
|
||||||
def _dispatch_event(self, data):
|
def _dispatch_event(self, data):
|
||||||
if len(data) == 0:
|
if len(data) == 0:
|
||||||
return
|
return
|
||||||
opcode = data[0]
|
opcode = data[0]
|
||||||
|
|
||||||
if opcode >= len(FlicClient._EVENTS) or FlicClient._EVENTS[opcode] == None:
|
if opcode >= len(FlicClient._EVENTS) or FlicClient._EVENTS[opcode] is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
event_name = FlicClient._EVENTS[opcode][0]
|
event_name = FlicClient._EVENTS[opcode][0]
|
||||||
data_tuple = FlicClient._EVENT_STRUCTS[opcode].unpack(data[1 : 1 + FlicClient._EVENT_STRUCTS[opcode].size])
|
data_tuple = FlicClient._EVENT_STRUCTS[opcode].unpack(data[1: 1 + FlicClient._EVENT_STRUCTS[opcode].size])
|
||||||
items = FlicClient._EVENT_NAMED_TUPLES[opcode]._make(data_tuple)._asdict()
|
items = FlicClient._EVENT_NAMED_TUPLES[opcode]._make(data_tuple)._asdict()
|
||||||
|
|
||||||
# Process some kind of items whose data type is not supported by struct
|
# Process some kind of items whose data type is not supported by struct
|
||||||
if "bd_addr" in items:
|
if "bd_addr" in items:
|
||||||
items["bd_addr"] = FlicClient._bdaddr_bytes_to_string(items["bd_addr"])
|
items["bd_addr"] = FlicClient._bdaddr_bytes_to_string()
|
||||||
|
|
||||||
if "name" in items:
|
if "name" in items:
|
||||||
items["name"] = items["name"].decode("utf-8")
|
items["name"] = items["name"].decode("utf-8")
|
||||||
|
|
||||||
if event_name == "EvtCreateConnectionChannelResponse":
|
if event_name == "EvtCreateConnectionChannelResponse":
|
||||||
items["error"] = CreateConnectionChannelError(items["error"])
|
items["error"] = CreateConnectionChannelError(items["error"])
|
||||||
items["connection_status"] = ConnectionStatus(items["connection_status"])
|
items["connection_status"] = ConnectionStatus(items["connection_status"])
|
||||||
|
|
||||||
if event_name == "EvtConnectionStatusChanged":
|
if event_name == "EvtConnectionStatusChanged":
|
||||||
items["connection_status"] = ConnectionStatus(items["connection_status"])
|
items["connection_status"] = ConnectionStatus(items["connection_status"])
|
||||||
items["disconnect_reason"] = DisconnectReason(items["disconnect_reason"])
|
items["disconnect_reason"] = DisconnectReason(items["disconnect_reason"])
|
||||||
|
|
||||||
if event_name == "EvtConnectionChannelRemoved":
|
if event_name == "EvtConnectionChannelRemoved":
|
||||||
items["removed_reason"] = RemovedReason(items["removed_reason"])
|
items["removed_reason"] = RemovedReason(items["removed_reason"])
|
||||||
|
|
||||||
if event_name.startswith("EvtButton"):
|
if event_name.startswith("EvtButton"):
|
||||||
items["click_type"] = ClickType(items["click_type"])
|
items["click_type"] = ClickType(items["click_type"])
|
||||||
|
|
||||||
if event_name == "EvtGetInfoResponse":
|
if event_name == "EvtGetInfoResponse":
|
||||||
items["bluetooth_controller_state"] = BluetoothControllerState(items["bluetooth_controller_state"])
|
items["bluetooth_controller_state"] = BluetoothControllerState(items["bluetooth_controller_state"])
|
||||||
items["my_bd_addr"] = FlicClient._bdaddr_bytes_to_string(items["my_bd_addr"])
|
items["my_bd_addr"] = FlicClient._bdaddr_bytes_to_string()
|
||||||
items["my_bd_addr_type"] = BdAddrType(items["my_bd_addr_type"])
|
items["my_bd_addr_type"] = BdAddrType(items["my_bd_addr_type"])
|
||||||
items["bd_addr_of_verified_buttons"] = []
|
items["bd_addr_of_verified_buttons"] = []
|
||||||
|
|
||||||
pos = FlicClient._EVENT_STRUCTS[opcode].size
|
pos = FlicClient._EVENT_STRUCTS[opcode].size
|
||||||
for i in range(items["nb_verified_buttons"]):
|
for i in range(items["nb_verified_buttons"]):
|
||||||
items["bd_addr_of_verified_buttons"].append(FlicClient._bdaddr_bytes_to_string(data[1 + pos : 1 + pos + 6]))
|
items["bd_addr_of_verified_buttons"].append(
|
||||||
|
FlicClient._bdaddr_bytes_to_string())
|
||||||
pos += 6
|
pos += 6
|
||||||
|
|
||||||
if event_name == "EvtBluetoothControllerStateChange":
|
if event_name == "EvtBluetoothControllerStateChange":
|
||||||
items["state"] = BluetoothControllerState(items["state"])
|
items["state"] = BluetoothControllerState(items["state"])
|
||||||
|
|
||||||
if event_name == "EvtGetButtonUUIDResponse":
|
if event_name == "EvtGetButtonUUIDResponse":
|
||||||
items["uuid"] = "".join(map(lambda x: "%02x" % x, items["uuid"]))
|
items["uuid"] = "".join(map(lambda x: "%02x" % x, items["uuid"]))
|
||||||
if items["uuid"] == "00000000000000000000000000000000":
|
if items["uuid"] == "00000000000000000000000000000000":
|
||||||
items["uuid"] = None
|
items["uuid"] = None
|
||||||
|
|
||||||
if event_name == "EvtScanWizardCompleted":
|
if event_name == "EvtScanWizardCompleted":
|
||||||
items["result"] = ScanWizardResult(items["result"])
|
items["result"] = ScanWizardResult(items["result"])
|
||||||
|
|
||||||
# Process event
|
# Process event
|
||||||
if event_name == "EvtAdvertisementPacket":
|
if event_name == "EvtAdvertisementPacket":
|
||||||
scanner = self._scanners.get(items["scan_id"])
|
scanner = self._scanners.get(items["scan_id"])
|
||||||
if scanner is not None:
|
if scanner is not None:
|
||||||
scanner.on_advertisement_packet(scanner, items["bd_addr"], items["name"], items["rssi"], items["is_private"], items["already_verified"])
|
scanner.on_advertisement_packet(scanner, items["bd_addr"], items["name"], items["rssi"],
|
||||||
|
items["is_private"], items["already_verified"])
|
||||||
|
|
||||||
if event_name == "EvtCreateConnectionChannelResponse":
|
if event_name == "EvtCreateConnectionChannelResponse":
|
||||||
channel = self._connection_channels[items["conn_id"]]
|
channel = self._connection_channels[items["conn_id"]]
|
||||||
if items["error"] != CreateConnectionChannelError.NoError:
|
if items["error"] != CreateConnectionChannelError.NoError:
|
||||||
del self._connection_channels[items["conn_id"]]
|
del self._connection_channels[items["conn_id"]]
|
||||||
channel.on_create_connection_channel_response(channel, items["error"], items["connection_status"])
|
channel.on_create_connection_channel_response(channel, items["error"], items["connection_status"])
|
||||||
|
|
||||||
if event_name == "EvtConnectionStatusChanged":
|
if event_name == "EvtConnectionStatusChanged":
|
||||||
channel = self._connection_channels[items["conn_id"]]
|
channel = self._connection_channels[items["conn_id"]]
|
||||||
channel.on_connection_status_changed(channel, items["connection_status"], items["disconnect_reason"])
|
channel.on_connection_status_changed(channel, items["connection_status"], items["disconnect_reason"])
|
||||||
|
|
||||||
if event_name == "EvtConnectionChannelRemoved":
|
if event_name == "EvtConnectionChannelRemoved":
|
||||||
channel = self._connection_channels[items["conn_id"]]
|
channel = self._connection_channels[items["conn_id"]]
|
||||||
del self._connection_channels[items["conn_id"]]
|
del self._connection_channels[items["conn_id"]]
|
||||||
channel.on_removed(channel, items["removed_reason"])
|
channel.on_removed(channel, items["removed_reason"])
|
||||||
|
|
||||||
if event_name == "EvtButtonUpOrDown":
|
if event_name == "EvtButtonUpOrDown":
|
||||||
channel = self._connection_channels[items["conn_id"]]
|
channel = self._connection_channels[items["conn_id"]]
|
||||||
channel.on_button_up_or_down(channel, items["click_type"], items["was_queued"], items["time_diff"])
|
channel.on_button_up_or_down(channel, items["click_type"], items["was_queued"], items["time_diff"])
|
||||||
|
@ -494,61 +514,60 @@ class FlicClient(asyncio.Protocol):
|
||||||
channel.on_button_click_or_hold(channel, items["click_type"], items["was_queued"], items["time_diff"])
|
channel.on_button_click_or_hold(channel, items["click_type"], items["was_queued"], items["time_diff"])
|
||||||
if event_name == "EvtButtonSingleOrDoubleClick":
|
if event_name == "EvtButtonSingleOrDoubleClick":
|
||||||
channel = self._connection_channels[items["conn_id"]]
|
channel = self._connection_channels[items["conn_id"]]
|
||||||
channel.on_button_single_or_double_click(channel, items["click_type"], items["was_queued"], items["time_diff"])
|
channel.on_button_single_or_double_click(channel, items["click_type"], items["was_queued"],
|
||||||
|
items["time_diff"])
|
||||||
if event_name == "EvtButtonSingleOrDoubleClickOrHold":
|
if event_name == "EvtButtonSingleOrDoubleClickOrHold":
|
||||||
channel = self._connection_channels[items["conn_id"]]
|
channel = self._connection_channels[items["conn_id"]]
|
||||||
channel.on_button_single_or_double_click_or_hold(channel, items["click_type"], items["was_queued"], items["time_diff"])
|
channel.on_button_single_or_double_click_or_hold(channel, items["click_type"], items["was_queued"],
|
||||||
|
items["time_diff"])
|
||||||
|
|
||||||
if event_name == "EvtNewVerifiedButton":
|
if event_name == "EvtNewVerifiedButton":
|
||||||
self.on_new_verified_button(items["bd_addr"])
|
self.on_new_verified_button(items["bd_addr"])
|
||||||
|
|
||||||
if event_name == "EvtGetInfoResponse":
|
if event_name == "EvtGetInfoResponse":
|
||||||
self.on_get_info(items)
|
self.on_get_info(items)
|
||||||
|
|
||||||
if event_name == "EvtNoSpaceForNewConnection":
|
if event_name == "EvtNoSpaceForNewConnection":
|
||||||
self.on_no_space_for_new_connection(items["max_concurrently_connected_buttons"])
|
self.on_no_space_for_new_connection(items["max_concurrently_connected_buttons"])
|
||||||
|
|
||||||
if event_name == "EvtGotSpaceForNewConnection":
|
if event_name == "EvtGotSpaceForNewConnection":
|
||||||
self.on_got_space_for_new_connection(items["max_concurrently_connected_buttons"])
|
self.on_got_space_for_new_connection(items["max_concurrently_connected_buttons"])
|
||||||
|
|
||||||
if event_name == "EvtBluetoothControllerStateChange":
|
if event_name == "EvtBluetoothControllerStateChange":
|
||||||
self.on_bluetooth_controller_state_change(items["state"])
|
self.on_bluetooth_controller_state_change(items["state"])
|
||||||
|
|
||||||
if event_name == "EvtGetButtonUUIDResponse":
|
if event_name == "EvtGetButtonUUIDResponse":
|
||||||
self.on_get_button_uuid(items["bd_addr"], items["uuid"])
|
self.on_get_button_uuid(items["bd_addr"], items["uuid"])
|
||||||
|
|
||||||
if event_name == "EvtScanWizardFoundPrivateButton":
|
if event_name == "EvtScanWizardFoundPrivateButton":
|
||||||
scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
|
scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
|
||||||
scan_wizard.on_found_private_button(scan_wizard)
|
scan_wizard.on_found_private_button(scan_wizard)
|
||||||
|
|
||||||
if event_name == "EvtScanWizardFoundPublicButton":
|
if event_name == "EvtScanWizardFoundPublicButton":
|
||||||
scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
|
scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
|
||||||
scan_wizard._bd_addr = items["bd_addr"]
|
scan_wizard._bd_addr = items["bd_addr"]
|
||||||
scan_wizard._name = items["name"]
|
scan_wizard._name = items["name"]
|
||||||
scan_wizard.on_found_public_button(scan_wizard, scan_wizard._bd_addr, scan_wizard._name)
|
scan_wizard.on_found_public_button(scan_wizard, scan_wizard._bd_addr, scan_wizard._name)
|
||||||
|
|
||||||
if event_name == "EvtScanWizardButtonConnected":
|
if event_name == "EvtScanWizardButtonConnected":
|
||||||
scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
|
scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
|
||||||
scan_wizard.on_button_connected(scan_wizard, scan_wizard._bd_addr, scan_wizard._name)
|
scan_wizard.on_button_connected(scan_wizard, scan_wizard._bd_addr, scan_wizard._name)
|
||||||
|
|
||||||
if event_name == "EvtScanWizardCompleted":
|
if event_name == "EvtScanWizardCompleted":
|
||||||
scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
|
scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
|
||||||
del self._scan_wizards[items["scan_wizard_id"]]
|
del self._scan_wizards[items["scan_wizard_id"]]
|
||||||
scan_wizard.on_completed(scan_wizard, items["result"], scan_wizard._bd_addr, scan_wizard._name)
|
scan_wizard.on_completed(scan_wizard, items["result"], scan_wizard._bd_addr, scan_wizard._name)
|
||||||
|
|
||||||
|
def data_received(self, data):
|
||||||
def data_received(self,data):
|
cdata = self.buffer + data
|
||||||
cdata=self.buffer+data
|
self.buffer = b""
|
||||||
self.buffer=b""
|
|
||||||
while len(cdata):
|
while len(cdata):
|
||||||
packet_len = cdata[0] | (cdata[1] << 8)
|
packet_len = cdata[0] | (cdata[1] << 8)
|
||||||
packet_len += 2
|
packet_len += 2
|
||||||
if len(cdata)>= packet_len:
|
if len(cdata) >= packet_len:
|
||||||
self._dispatch_event(cdata[2:packet_len])
|
self._dispatch_event(cdata[2:packet_len])
|
||||||
cdata=cdata[packet_len:]
|
cdata = cdata[packet_len:]
|
||||||
else:
|
else:
|
||||||
if len(cdata):
|
if len(cdata):
|
||||||
self.buffer=cdata #unlikely to happen but.....
|
self.buffer = cdata # unlikely to happen but.....
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ class CameraPiBackend(Backend):
|
||||||
return self.value == other
|
return self.value == other
|
||||||
|
|
||||||
# noinspection PyUnresolvedReferences,PyPackageRequirements
|
# noinspection PyUnresolvedReferences,PyPackageRequirements
|
||||||
def __init__(self, listen_port, x_resolution=640, y_resolution=480,
|
def __init__(self, listen_port, bind_address='0.0.0.0', x_resolution=640, y_resolution=480,
|
||||||
redis_queue='platypush/camera/pi',
|
redis_queue='platypush/camera/pi',
|
||||||
start_recording_on_startup=True,
|
start_recording_on_startup=True,
|
||||||
framerate=24, hflip=False, vflip=False,
|
framerate=24, hflip=False, vflip=False,
|
||||||
|
@ -49,13 +49,17 @@ class CameraPiBackend(Backend):
|
||||||
|
|
||||||
:param listen_port: Port where the camera process will provide the video output while recording
|
:param listen_port: Port where the camera process will provide the video output while recording
|
||||||
:type listen_port: int
|
:type listen_port: int
|
||||||
|
|
||||||
|
:param bind_address: Bind address (default: 0.0.0.0).
|
||||||
|
:type bind_address: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
self.bind_address = bind_address
|
||||||
self.listen_port = listen_port
|
self.listen_port = listen_port
|
||||||
self.server_socket = socket.socket()
|
self.server_socket = socket.socket()
|
||||||
self.server_socket.bind(('0.0.0.0', self.listen_port))
|
self.server_socket.bind((self.bind_address, self.listen_port))
|
||||||
self.server_socket.listen(0)
|
self.server_socket.listen(0)
|
||||||
|
|
||||||
import picamera
|
import picamera
|
||||||
|
@ -134,13 +138,13 @@ class CameraPiBackend(Backend):
|
||||||
self.logger.info('Client closed connection')
|
self.logger.info('Client closed connection')
|
||||||
try:
|
try:
|
||||||
self.stop_recording()
|
self.stop_recording()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Could not stop recording: {}'.format(str(e)))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
connection.close()
|
connection.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Could not close connection: {}'.format(str(e)))
|
||||||
|
|
||||||
self.send_camera_action(self.CameraAction.START_RECORDING)
|
self.send_camera_action(self.CameraAction.START_RECORDING)
|
||||||
|
|
||||||
|
|
|
@ -51,11 +51,10 @@ class GooglePubsubBackend(Backend):
|
||||||
def _message_callback(self, topic):
|
def _message_callback(self, topic):
|
||||||
def callback(msg):
|
def callback(msg):
|
||||||
data = msg.data.decode()
|
data = msg.data.decode()
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug('Not a valid JSON: {}: {}'.format(data, str(e)))
|
||||||
|
|
||||||
msg.ack()
|
msg.ack()
|
||||||
self.bus.post(GooglePubsubMessageEvent(topic=topic, msg=data))
|
self.bus.post(GooglePubsubMessageEvent(topic=topic, msg=data))
|
||||||
|
|
|
@ -31,8 +31,8 @@ def _hook(hook_name):
|
||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
try:
|
try:
|
||||||
event_args['data'] = json.loads(event_args['data'])
|
event_args['data'] = json.loads(event_args['data'])
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
logger().warning('Not a valid JSON string: {}: {}'.format(event_args['data'], str(e)))
|
||||||
|
|
||||||
event = WebhookEvent(**event_args)
|
event = WebhookEvent(**event_args)
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ def login():
|
||||||
if session_token:
|
if session_token:
|
||||||
user, session = user_manager.authenticate_user_session(session_token)
|
user, session = user_manager.authenticate_user_session(session_token)
|
||||||
if user:
|
if user:
|
||||||
return redirect(redirect_page, 302)
|
return redirect(redirect_page, 302) # lgtm [py/url-redirection]
|
||||||
|
|
||||||
if request.form:
|
if request.form:
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
|
@ -44,7 +44,7 @@ def login():
|
||||||
expires_at=expires)
|
expires_at=expires)
|
||||||
|
|
||||||
if session:
|
if session:
|
||||||
redirect_target = redirect(redirect_page, 302)
|
redirect_target = redirect(redirect_page, 302) # lgtm [py/url-redirection]
|
||||||
response = make_response(redirect_target)
|
response = make_response(redirect_target)
|
||||||
response.set_cookie('session_token', session.session_token, expires=expires)
|
response.set_cookie('session_token', session.session_token, expires=expires)
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -25,7 +25,7 @@ def logout():
|
||||||
if not user:
|
if not user:
|
||||||
return abort(403, 'Invalid session token')
|
return abort(403, 'Invalid session token')
|
||||||
|
|
||||||
redirect_target = redirect(redirect_page, 302)
|
redirect_target = redirect(redirect_page, 302) # lgtm [py/url-redirection]
|
||||||
response = make_response(redirect_target)
|
response = make_response(redirect_target)
|
||||||
response.set_cookie('session_token', '', expires=0)
|
response.set_cookie('session_token', '', expires=0)
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -32,8 +32,8 @@ def add_media():
|
||||||
args = {}
|
args = {}
|
||||||
try:
|
try:
|
||||||
args = json.loads(request.data.decode('utf-8'))
|
args = json.loads(request.data.decode('utf-8'))
|
||||||
except:
|
except Exception as e:
|
||||||
abort(400, 'Invalid JSON request')
|
abort(400, 'Invalid JSON request: {}'.format(str(e)))
|
||||||
|
|
||||||
source = args.get('source')
|
source = args.get('source')
|
||||||
if not source:
|
if not source:
|
||||||
|
|
|
@ -40,7 +40,7 @@ def audio_feed(device, fifo, sample_rate, blocksize, latency, channels):
|
||||||
channels=channels)
|
channels=channels)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(fifo, 'rb') as f:
|
with open(fifo, 'rb') as f: # lgtm [py/path-injection]
|
||||||
send_header = True
|
send_header = True
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -31,10 +31,10 @@ def register():
|
||||||
if session_token:
|
if session_token:
|
||||||
user, session = user_manager.authenticate_user_session(session_token)
|
user, session = user_manager.authenticate_user_session(session_token)
|
||||||
if user:
|
if user:
|
||||||
return redirect(redirect_page, 302)
|
return redirect(redirect_page, 302) # lgtm [py/url-redirection]
|
||||||
|
|
||||||
if user_manager.get_user_count() > 0:
|
if user_manager.get_user_count() > 0:
|
||||||
return redirect('/login?redirect=' + redirect_page, 302)
|
return redirect('/login?redirect=' + redirect_page, 302) # lgtm [py/url-redirection]
|
||||||
|
|
||||||
if request.form:
|
if request.form:
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
|
@ -49,7 +49,7 @@ def register():
|
||||||
if not remember else None)
|
if not remember else None)
|
||||||
|
|
||||||
if session:
|
if session:
|
||||||
redirect_target = redirect(redirect_page, 302)
|
redirect_target = redirect(redirect_page, 302) # lgtm [py/url-redirection]
|
||||||
response = make_response(redirect_target)
|
response = make_response(redirect_target)
|
||||||
response.set_cookie('session_token', session.session_token)
|
response.set_cookie('session_token', session.session_token)
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -123,7 +123,8 @@ def _authenticate_token():
|
||||||
try:
|
try:
|
||||||
user_manager.validate_jwt_token(user_token)
|
user_manager.validate_jwt_token(user_token)
|
||||||
return True
|
return True
|
||||||
except:
|
except Exception as e:
|
||||||
|
logger().debug(str(e))
|
||||||
return token and user_token == token
|
return token and user_token == token
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -78,11 +78,11 @@ class HttpRequest(object):
|
||||||
|
|
||||||
def get_new_items(self, response):
|
def get_new_items(self, response):
|
||||||
""" Gets new items out of a response """
|
""" Gets new items out of a response """
|
||||||
raise ("get_new_items must be implemented in a derived class")
|
raise NotImplementedError("get_new_items must be implemented in a derived class")
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for (key, value) in self.request_args.items():
|
for (key, value) in self.request_args.items():
|
||||||
yield (key, value)
|
yield key, value
|
||||||
|
|
||||||
|
|
||||||
class JsonHttpRequest(HttpRequest):
|
class JsonHttpRequest(HttpRequest):
|
||||||
|
@ -96,7 +96,7 @@ class JsonHttpRequest(HttpRequest):
|
||||||
new_entries = []
|
new_entries = []
|
||||||
|
|
||||||
if self.path:
|
if self.path:
|
||||||
m = re.match('\$\{\s*(.*)\s*\}', self.path)
|
m = re.match(r'\${\s*(.*)\s*}', self.path)
|
||||||
response = eval(m.group(1))
|
response = eval(m.group(1))
|
||||||
|
|
||||||
for entry in response:
|
for entry in response:
|
||||||
|
|
|
@ -238,15 +238,15 @@ class RssUpdates(HttpRequest):
|
||||||
with open(digest_filename, 'w', encoding='utf-8') as f:
|
with open(digest_filename, 'w', encoding='utf-8') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
elif self.digest_format == 'pdf':
|
elif self.digest_format == 'pdf':
|
||||||
import weasyprint
|
from weasyprint import HTML, CSS
|
||||||
from weasyprint.fonts import FontConfiguration
|
from weasyprint.fonts import FontConfiguration
|
||||||
|
|
||||||
body_style = 'body { {body_style} }'.format(body_style=self.body_style)
|
body_style = 'body { ' + self.body_style + ' }'
|
||||||
font_config = FontConfiguration()
|
font_config = FontConfiguration()
|
||||||
css = [weasyprint.CSS('https://fonts.googleapis.com/css?family=Merriweather'),
|
css = [CSS('https://fonts.googleapis.com/css?family=Merriweather'),
|
||||||
weasyprint.CSS(string=body_style, font_config=font_config)]
|
CSS(string=body_style, font_config=font_config)]
|
||||||
|
|
||||||
weasyprint.HTML(string=content).write_pdf(digest_filename, stylesheets=css)
|
HTML(string=content).write_pdf(digest_filename, stylesheets=css)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Unsupported format: {}. Supported formats: ' +
|
raise RuntimeError('Unsupported format: {}. Supported formats: ' +
|
||||||
'html or pdf'.format(self.digest_format))
|
'html or pdf'.format(self.digest_format))
|
||||||
|
|
|
@ -50,15 +50,13 @@ class HttpUtils(object):
|
||||||
for name, resource_path in resource_dirs.items():
|
for name, resource_path in resource_dirs.items():
|
||||||
resource_path = os.path.abspath(os.path.expanduser(resource_path))
|
resource_path = os.path.abspath(os.path.expanduser(resource_path))
|
||||||
if directory.startswith(resource_path):
|
if directory.startswith(resource_path):
|
||||||
subdir = re.sub('^{}(.*)$'.format(resource_path),
|
|
||||||
'\\1', directory)
|
|
||||||
uri = '/resources/' + name
|
uri = '/resources/' + name
|
||||||
break
|
break
|
||||||
|
|
||||||
if not uri:
|
if not uri:
|
||||||
raise RuntimeError(('Directory {} not found among the available ' +
|
raise RuntimeError(('Directory {} not found among the available ' +
|
||||||
'static resources on the webserver').format(
|
'static resources on the webserver').format(
|
||||||
directory))
|
directory))
|
||||||
|
|
||||||
results = [
|
results = [
|
||||||
re.sub('^{}(.*)$'.format(resource_path), uri + '\\1', path)
|
re.sub('^{}(.*)$'.format(resource_path), uri + '\\1', path)
|
||||||
|
@ -92,10 +90,11 @@ class HttpUtils(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def plugin_name_to_tag(cls, module_name):
|
def plugin_name_to_tag(cls, module_name):
|
||||||
return module_name.replace('.','-')
|
return module_name.replace('.', '-')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_templates_in_dir(cls, directory):
|
def find_templates_in_dir(cls, directory):
|
||||||
|
# noinspection PyTypeChecker
|
||||||
return [
|
return [
|
||||||
os.path.join(directory, file)
|
os.path.join(directory, file)
|
||||||
for root, path, files in os.walk(os.path.abspath(os.path.join(template_folder, directory)))
|
for root, path, files in os.walk(os.path.abspath(os.path.join(template_folder, directory)))
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/js/chunk-178b19d7.1c7265c0.js.map
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/js/chunk-178b19d7.1c7265c0.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/js/chunk-1ad96db7.02188e12.js.map
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/js/chunk-1ad96db7.02188e12.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
platypush/backend/http/webapp/dist/static/js/chunk-6c14c2d1.94636887.js
vendored
Normal file
2
platypush/backend/http/webapp/dist/static/js/chunk-6c14c2d1.94636887.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
platypush/backend/http/webapp/dist/static/js/chunk-6c14c2d1.94636887.js.map
vendored
Normal file
1
platypush/backend/http/webapp/dist/static/js/chunk-6c14c2d1.94636887.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -380,20 +380,16 @@ export default {
|
||||||
|
|
||||||
this.request(this.action.name, args).then(this.onResponse).catch(this.onError).finally(this.onDone)
|
this.request(this.action.name, args).then(this.onResponse).catch(this.onError).finally(this.onDone)
|
||||||
} else {
|
} else {
|
||||||
let request = this.rawRequest
|
|
||||||
try {
|
try {
|
||||||
request = JSON.parse(this.rawRequest)
|
const request = JSON.parse(this.rawRequest)
|
||||||
|
this.execute(request).then(this.onResponse).catch(this.onError).finally(this.onDone)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.notify({
|
this.notify({
|
||||||
error: true,
|
error: true,
|
||||||
title: 'Invalid JSON request',
|
title: 'Invalid JSON request',
|
||||||
text: e.toString(),
|
text: e.toString(),
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.execute(request).then(this.onResponse).catch(this.onError).finally(this.onDone)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,7 @@ export class ColorConverter {
|
||||||
if (isNaN(blue))
|
if (isNaN(blue))
|
||||||
blue = 0;
|
blue = 0;
|
||||||
|
|
||||||
|
// lgtm [js/automatic-semicolon-insertion]
|
||||||
return [red, green, blue].map((c) => Math.min(Math.max(0, c), 255))
|
return [red, green, blue].map((c) => Math.min(Math.max(0, c), 255))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -454,9 +454,10 @@ export default {
|
||||||
brightness: hsl[2],
|
brightness: hsl[2],
|
||||||
color: {
|
color: {
|
||||||
hue: hsl[0],
|
hue: hsl[0],
|
||||||
'`${satAttr}': hsl[1],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request.value.color[satAttr] = hsl[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
|
@ -35,6 +35,7 @@ class KafkaBackend(Backend):
|
||||||
self.topic_prefix = topic
|
self.topic_prefix = topic
|
||||||
self.topic = self._topic_by_device_id(self.device_id)
|
self.topic = self._topic_by_device_id(self.device_id)
|
||||||
self.producer = None
|
self.producer = None
|
||||||
|
self.consumer = None
|
||||||
|
|
||||||
# Kafka can be veryyyy noisy
|
# Kafka can be veryyyy noisy
|
||||||
logging.getLogger('kafka').setLevel(logging.ERROR)
|
logging.getLogger('kafka').setLevel(logging.ERROR)
|
||||||
|
@ -47,8 +48,8 @@ class KafkaBackend(Backend):
|
||||||
try:
|
try:
|
||||||
msg = Message.build(msg)
|
msg = Message.build(msg)
|
||||||
is_platypush_message = True
|
is_platypush_message = True
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug(str(e))
|
||||||
|
|
||||||
self.logger.info('Received message on Kafka backend: {}'.format(msg))
|
self.logger.info('Received message on Kafka backend: {}'.format(msg))
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ class KafkaBackend(Backend):
|
||||||
def _topic_by_device_id(self, device_id):
|
def _topic_by_device_id(self, device_id):
|
||||||
return '{}.{}'.format(self.topic_prefix, device_id)
|
return '{}.{}'.format(self.topic_prefix, device_id)
|
||||||
|
|
||||||
def send_message(self, msg):
|
def send_message(self, msg, **kwargs):
|
||||||
target = msg.target
|
target = msg.target
|
||||||
kafka_plugin = get_plugin('kafka')
|
kafka_plugin = get_plugin('kafka')
|
||||||
kafka_plugin.send_message(msg=msg,
|
kafka_plugin.send_message(msg=msg,
|
||||||
|
|
|
@ -108,8 +108,8 @@ class LogEventHandler(EventHandler):
|
||||||
url = m.group(5).split(' ')[1]
|
url = m.group(5).split(' ')[1]
|
||||||
method = m.group(5).split(' ')[0]
|
method = m.group(5).split(' ')[0]
|
||||||
http_version = m.group(5).split(' ')[2].split('/')[1]
|
http_version = m.group(5).split(' ')[2].split('/')[1]
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
logger.debug(str(e))
|
||||||
|
|
||||||
if not url:
|
if not url:
|
||||||
return
|
return
|
||||||
|
|
|
@ -275,8 +275,8 @@ class MqttBackend(Backend):
|
||||||
try:
|
try:
|
||||||
data = data.decode('utf-8')
|
data = data.decode('utf-8')
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug(str(e))
|
||||||
|
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
self.bus.post(MQTTMessageEvent(host=client._host, port=client._port, topic=msg.topic, msg=data))
|
self.bus.post(MQTTMessageEvent(host=client._host, port=client._port, topic=msg.topic, msg=data))
|
||||||
|
@ -303,8 +303,8 @@ class MqttBackend(Backend):
|
||||||
try:
|
try:
|
||||||
msg = json.loads(msg)
|
msg = json.loads(msg)
|
||||||
msg = Message.build(msg)
|
msg = Message.build(msg)
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug(str(e))
|
||||||
|
|
||||||
if not msg:
|
if not msg:
|
||||||
return
|
return
|
||||||
|
|
|
@ -49,10 +49,10 @@ class PushbulletBackend(Backend):
|
||||||
from pushbullet import Pushbullet
|
from pushbullet import Pushbullet
|
||||||
self.pb = Pushbullet(self.token)
|
self.pb = Pushbullet(self.token)
|
||||||
|
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
self.device = self.pb.get_device(self.device_name)
|
self.device = self.pb.get_device(self.device_name)
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.info('Device {} does not exist: {}. Creating it'.format(self.device_name, str(e)))
|
||||||
self.device = self.pb.new_device(self.device_name)
|
self.device = self.pb.new_device(self.device_name)
|
||||||
|
|
||||||
self.pb_device_id = self.get_device_id()
|
self.pb_device_id = self.get_device_id()
|
||||||
|
|
|
@ -60,15 +60,17 @@ class RedisBackend(Backend):
|
||||||
msg = data[1].decode()
|
msg = data[1].decode()
|
||||||
try:
|
try:
|
||||||
msg = Message.build(msg)
|
msg = Message.build(msg)
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.debug(str(e))
|
||||||
try:
|
try:
|
||||||
import ast
|
import ast
|
||||||
msg = Message.build(ast.literal_eval(msg))
|
msg = Message.build(ast.literal_eval(msg))
|
||||||
except:
|
except Exception as ee:
|
||||||
|
self.logger.debug(str(ee))
|
||||||
try:
|
try:
|
||||||
msg = json.loads(msg)
|
msg = json.loads(msg)
|
||||||
except Exception as e:
|
except Exception as eee:
|
||||||
self.logger.exception(e)
|
self.logger.exception(eee)
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
|
@ -119,12 +119,11 @@ class SensorBackend(Backend):
|
||||||
if self.data is None or new_data is None:
|
if self.data is None or new_data is None:
|
||||||
return new_data
|
return new_data
|
||||||
|
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
# Scalar data case
|
# Scalar data case
|
||||||
new_data = self._get_value(new_data)
|
new_data = self._get_value(new_data)
|
||||||
return new_data if abs(new_data - self.data) >= self.tolerance else None
|
return new_data if abs(new_data - self.data) >= self.tolerance else None
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
# If it's not a scalar then it should be a dict
|
# If it's not a scalar then it should be a dict
|
||||||
assert isinstance(new_data, dict), 'Invalid type {} received for sensor data'.format(type(new_data))
|
assert isinstance(new_data, dict), 'Invalid type {} received for sensor data'.format(type(new_data))
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,9 @@ import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.message import Message
|
|
||||||
from platypush.message.event.wiimote import WiimoteEvent, \
|
from platypush.message.event.wiimote import WiimoteEvent, \
|
||||||
WiimoteConnectionEvent, WiimoteDisconnectionEvent
|
WiimoteConnectionEvent, WiimoteDisconnectionEvent
|
||||||
|
|
||||||
from platypush.message.request import Request
|
|
||||||
|
|
||||||
|
|
||||||
class WiimoteBackend(Backend):
|
class WiimoteBackend(Backend):
|
||||||
"""
|
"""
|
||||||
|
@ -110,8 +107,8 @@ class WiimoteBackend(Backend):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._wiimote.close()
|
self._wiimote.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Could not close Wiimote connection: {}'.format(str(e)))
|
||||||
|
|
||||||
self._wiimote = None
|
self._wiimote = None
|
||||||
self.bus.post(WiimoteDisconnectionEvent())
|
self.bus.post(WiimoteDisconnectionEvent())
|
||||||
|
|
|
@ -232,10 +232,9 @@ class ZigbeeMqttBackend(MqttBackend):
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if topic == 'bridge/state':
|
if topic == 'bridge/state':
|
||||||
|
|
|
@ -57,7 +57,7 @@ class Cronjob(threading.Thread):
|
||||||
self.state = CronjobState.ERROR
|
self.state = CronjobState.ERROR
|
||||||
|
|
||||||
def wait(self):
|
def wait(self):
|
||||||
now = datetime.datetime.now().replace(tzinfo=gettz())
|
now = datetime.datetime.now().replace(tzinfo=gettz()) # lgtm [py/call-to-non-callable]
|
||||||
cron = croniter.croniter(self.cron_expression, now)
|
cron = croniter.croniter(self.cron_expression, now)
|
||||||
next_run = cron.get_next()
|
next_run = cron.get_next()
|
||||||
self._should_stop.wait(next_run - now.timestamp())
|
self._should_stop.wait(next_run - now.timestamp())
|
||||||
|
|
|
@ -13,7 +13,7 @@ class EventGenerator(object):
|
||||||
logger = logging.getLogger('platypush')
|
logger = logging.getLogger('platypush')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._event_handlers = {} # Event type => callback map
|
self._event_handlers = {} # Event type => callback map
|
||||||
|
|
||||||
def fire_event(self, event):
|
def fire_event(self, event):
|
||||||
"""
|
"""
|
||||||
|
@ -25,8 +25,8 @@ class EventGenerator(object):
|
||||||
:type event: :class:`platypush.message.event.Event` or a subclass
|
:type event: :class:`platypush.message.event.Event` or a subclass
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def hndl_thread():
|
def hndl_thread(handler):
|
||||||
hndl(event)
|
handler(event)
|
||||||
|
|
||||||
from platypush.backend import Backend
|
from platypush.backend import Backend
|
||||||
from platypush.context import get_bus
|
from platypush.context import get_bus
|
||||||
|
@ -44,8 +44,7 @@ class EventGenerator(object):
|
||||||
handlers.update(self._event_handlers[cls])
|
handlers.update(self._event_handlers[cls])
|
||||||
|
|
||||||
for hndl in handlers:
|
for hndl in handlers:
|
||||||
threading.Thread(target=hndl_thread).start()
|
threading.Thread(target=hndl_thread, args=(hndl,)).start()
|
||||||
|
|
||||||
|
|
||||||
def register_handler(self, event_type, callback):
|
def register_handler(self, event_type, callback):
|
||||||
"""
|
"""
|
||||||
|
@ -64,7 +63,6 @@ class EventGenerator(object):
|
||||||
self._event_handlers[event_type] = set()
|
self._event_handlers[event_type] = set()
|
||||||
self._event_handlers[event_type].add(callback)
|
self._event_handlers[event_type].add(callback)
|
||||||
|
|
||||||
|
|
||||||
def unregister_handler(self, event_type, callback):
|
def unregister_handler(self, event_type, callback):
|
||||||
"""
|
"""
|
||||||
Unregisters a callback handler from a camera event type.
|
Unregisters a callback handler from a camera event type.
|
||||||
|
|
|
@ -61,7 +61,8 @@ class EventProcessor(object):
|
||||||
|
|
||||||
if hook.priority > max_priority:
|
if hook.priority > max_priority:
|
||||||
priority_hooks = {hook}
|
priority_hooks = {hook}
|
||||||
elif hook.priority == max_priority and max_priority > 0:
|
max_priority = hook.priority
|
||||||
|
elif hook.priority == max_priority:
|
||||||
priority_hooks.add(hook)
|
priority_hooks.add(hook)
|
||||||
|
|
||||||
matched_hooks.update(priority_hooks)
|
matched_hooks.update(priority_hooks)
|
||||||
|
|
|
@ -106,10 +106,9 @@ class Message(object):
|
||||||
if isinstance(msg, bytes) or isinstance(msg, bytearray):
|
if isinstance(msg, bytes) or isinstance(msg, bytearray):
|
||||||
msg = msg.decode('utf-8')
|
msg = msg.decode('utf-8')
|
||||||
if isinstance(msg, str):
|
if isinstance(msg, str):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
msg = json.loads(msg.strip())
|
msg = json.loads(msg.strip())
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
logger.warning('Invalid JSON message: {}'.format(msg))
|
logger.warning('Invalid JSON message: {}'.format(msg))
|
||||||
|
|
||||||
assert isinstance(msg, dict)
|
assert isinstance(msg, dict)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import json
|
|
||||||
|
|
||||||
from platypush.message.event import Event
|
from platypush.message.event import Event
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,6 @@ class Request(Message):
|
||||||
|
|
||||||
return event_args
|
return event_args
|
||||||
|
|
||||||
# noinspection PyBroadException
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def expand_value_from_context(cls, _value, **context):
|
def expand_value_from_context(cls, _value, **context):
|
||||||
for (k, v) in context.items():
|
for (k, v) in context.items():
|
||||||
|
@ -127,7 +126,8 @@ class Request(Message):
|
||||||
v = json.loads(str(v))
|
v = json.loads(str(v))
|
||||||
try:
|
try:
|
||||||
exec('{}={}'.format(k, v))
|
exec('{}={}'.format(k, v))
|
||||||
except:
|
except Exception as e:
|
||||||
|
logger.debug(str(e))
|
||||||
if isinstance(v, str):
|
if isinstance(v, str):
|
||||||
try:
|
try:
|
||||||
exec('{}="{}"'.format(k, re.sub(r'(^|[^\\])"', '\1\\"', v)))
|
exec('{}="{}"'.format(k, re.sub(r'(^|[^\\])"', '\1\\"', v)))
|
||||||
|
@ -171,7 +171,7 @@ class Request(Message):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return json.loads(parsed_value)
|
return json.loads(parsed_value)
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
return parsed_value
|
return parsed_value
|
||||||
|
|
||||||
def _send_response(self, response):
|
def _send_response(self, response):
|
||||||
|
|
|
@ -40,10 +40,9 @@ class RectModel(Mapping):
|
||||||
class ResultModel(Mapping):
|
class ResultModel(Mapping):
|
||||||
def __init__(self, result: Decoded, *args, **kwargs):
|
def __init__(self, result: Decoded, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
data = result.data.decode()
|
data = result.data.decode()
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
data = base64.encodebytes(result.data).decode()
|
data = base64.encodebytes(result.data).decode()
|
||||||
|
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
|
@ -142,7 +142,7 @@ class AssistantGooglePushtotalkPlugin(AssistantPlugin):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
audio_sink = audio_device = (
|
audio_sink = (
|
||||||
audio_device or audio_helpers.SoundDeviceStream(
|
audio_device or audio_helpers.SoundDeviceStream(
|
||||||
sample_rate=self.audio_sample_rate,
|
sample_rate=self.audio_sample_rate,
|
||||||
sample_width=self.audio_sample_width,
|
sample_width=self.audio_sample_width,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import json
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from platypush.config import Config
|
from platypush.config import Config
|
||||||
from platypush.message import Message
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +20,7 @@ class AutoremotePlugin(Plugin):
|
||||||
_AUTOREMOTE_MESSAGE_URL = _AUTOREMOTE_BASE_URL + '/sendmessage'
|
_AUTOREMOTE_MESSAGE_URL = _AUTOREMOTE_BASE_URL + '/sendmessage'
|
||||||
_AUTOREMOTE_NOTIFICATION_URL = _AUTOREMOTE_BASE_URL + '/sendnotification'
|
_AUTOREMOTE_NOTIFICATION_URL = _AUTOREMOTE_BASE_URL + '/sendnotification'
|
||||||
|
|
||||||
def __init__(self, devices=None, key=None, password=None, *args, **kwargs):
|
def __init__(self, devices=None, key=None, password=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param devices: Set this attribute if you want to control multiple AutoRemote devices.
|
:param devices: Set this attribute if you want to control multiple AutoRemote devices.
|
||||||
This will be a map in the format::
|
This will be a map in the format::
|
||||||
|
@ -47,7 +45,7 @@ class AutoremotePlugin(Plugin):
|
||||||
:type password: str
|
:type password: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
if key:
|
if key:
|
||||||
self.devices = { key: { 'key': key, 'password': password } }
|
self.devices = { key: { 'key': key, 'password': password } }
|
||||||
|
|
|
@ -404,11 +404,10 @@ class CameraPlugin(Plugin, ABC):
|
||||||
frame_queue.put(None)
|
frame_queue.put(None)
|
||||||
self.stop_preview(camera)
|
self.stop_preview(camera)
|
||||||
for output in camera.get_outputs():
|
for output in camera.get_outputs():
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
output.close()
|
output.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Could not close camera output: {}'.format(str(e)))
|
||||||
|
|
||||||
self.close_device(camera, wait_capture=False)
|
self.close_device(camera, wait_capture=False)
|
||||||
frame_processor.join(timeout=5.0)
|
frame_processor.join(timeout=5.0)
|
||||||
|
|
|
@ -114,11 +114,10 @@ class StreamWriter(VideoWriter, ABC):
|
||||||
def close(self):
|
def close(self):
|
||||||
self.buffer.close()
|
self.buffer.close()
|
||||||
if self.sock:
|
if self.sock:
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Could not close camera resource: {}'.format(str(e)))
|
||||||
|
|
||||||
super().close()
|
super().close()
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,11 @@ class CvFileWriter(FileVideoWriter):
|
||||||
if not self.writer:
|
if not self.writer:
|
||||||
return
|
return
|
||||||
|
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
if isinstance(img, ImageType):
|
if isinstance(img, ImageType):
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
img = np.array(img)
|
img = np.array(img)
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.writer.write(img)
|
self.writer.write(img)
|
||||||
|
|
|
@ -78,7 +78,7 @@ class DbPlugin(Plugin):
|
||||||
engine = self._get_engine(engine, *args, **kwargs)
|
engine = self._get_engine(engine, *args, **kwargs)
|
||||||
|
|
||||||
with engine.connect() as connection:
|
with engine.connect() as connection:
|
||||||
result = connection.execute(statement)
|
connection.execute(statement)
|
||||||
|
|
||||||
def _get_table(self, table, engine=None, *args, **kwargs):
|
def _get_table(self, table, engine=None, *args, **kwargs):
|
||||||
if not engine:
|
if not engine:
|
||||||
|
|
|
@ -188,11 +188,10 @@ class EspPlugin(Plugin):
|
||||||
|
|
||||||
def on_close(self, conn: Connection):
|
def on_close(self, conn: Connection):
|
||||||
def callback(ws):
|
def callback(ws):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
ws.close()
|
ws.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Could not close connection: {}'.format(str(e)))
|
||||||
|
|
||||||
conn.on_close()
|
conn.on_close()
|
||||||
self.logger.info('Connection to {}:{} closed'.format(conn.host, conn.port))
|
self.logger.info('Connection to {}:{} closed'.format(conn.host, conn.port))
|
||||||
|
@ -608,7 +607,8 @@ if {miso}:
|
||||||
self.execute(code, **kwargs)
|
self.execute(code, **kwargs)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def i2c_open(self, scl: Optional[int] = None, sda: Optional[int] = None, id: int = -1, baudrate: int = 400000, **kwargs):
|
def i2c_open(self, scl: Optional[int] = None, sda: Optional[int] = None, id: int = -1, baudrate: int = 400000,
|
||||||
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Open a connection to an I2C (or "two-wire") port.
|
Open a connection to an I2C (or "two-wire") port.
|
||||||
|
|
||||||
|
|
|
@ -150,11 +150,10 @@ class Connection:
|
||||||
self._echo_received.set()
|
self._echo_received.set()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
self.ws.close()
|
self.ws.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Could not close connection: {}'.format(str(e)))
|
||||||
|
|
||||||
self.on_close()
|
self.on_close()
|
||||||
|
|
||||||
|
@ -243,6 +242,7 @@ class Connection:
|
||||||
chunk = self._downloaded_chunks.get(timeout=timeout)
|
chunk = self._downloaded_chunks.get(timeout=timeout)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
self.on_timeout('File download timed out')
|
self.on_timeout('File download timed out')
|
||||||
|
break
|
||||||
|
|
||||||
if chunk is None:
|
if chunk is None:
|
||||||
break
|
break
|
||||||
|
|
|
@ -19,7 +19,7 @@ class FilePlugin(Plugin):
|
||||||
def _to_string(cls, content):
|
def _to_string(cls, content):
|
||||||
try:
|
try:
|
||||||
return json.dumps(content)
|
return json.dumps(content)
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
return str(content)
|
return str(content)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import base64
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
|
||||||
|
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.google import GooglePlugin
|
from platypush.plugins.google import GooglePlugin
|
||||||
|
@ -21,7 +19,6 @@ class GoogleCalendarPlugin(GooglePlugin, CalendarInterface):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(scopes=self.scopes, *args, **kwargs)
|
super().__init__(scopes=self.scopes, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_upcoming_events(self, max_results=10):
|
def get_upcoming_events(self, max_results=10):
|
||||||
"""
|
"""
|
||||||
|
@ -40,4 +37,3 @@ class GoogleCalendarPlugin(GooglePlugin, CalendarInterface):
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,12 @@ def get_credentials(scope):
|
||||||
credentials_file = get_credentials_filename(*sorted(scope.split(' ')))
|
credentials_file = get_credentials_filename(*sorted(scope.split(' ')))
|
||||||
if not os.path.exists(credentials_file):
|
if not os.path.exists(credentials_file):
|
||||||
raise RuntimeError(('Credentials file {} not found. Generate it through:\n' +
|
raise RuntimeError(('Credentials file {} not found. Generate it through:\n' +
|
||||||
'\tpython -m platypush.plugins.google.credentials "{}" ' +
|
'\tpython -m platypush.plugins.google.credentials "{}" ' +
|
||||||
'<path to client_secret.json>\n' +
|
'<path to client_secret.json>\n' +
|
||||||
'\t\t[--auth_host_name AUTH_HOST_NAME]\n' +
|
'\t\t[--auth_host_name AUTH_HOST_NAME]\n' +
|
||||||
'\t\t[--noauth_local_webserver]\n' +
|
'\t\t[--noauth_local_webserver]\n' +
|
||||||
'\t\t[--auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT ...]]]\n' +
|
'\t\t[--auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT ...]]]\n' +
|
||||||
'\t\t[--logging_level [DEBUG,INFO,WARNING,ERROR,CRITICAL]]\n').
|
'\t\t[--logging_level [DEBUG,INFO,WARNING,ERROR,CRITICAL]]\n').
|
||||||
format(credentials_file, scope))
|
format(credentials_file, scope))
|
||||||
|
|
||||||
store = Storage(credentials_file)
|
store = Storage(credentials_file)
|
||||||
|
@ -48,7 +48,7 @@ def generate_credentials(client_secret_path, scope):
|
||||||
flow.user_agent = 'Platypush'
|
flow.user_agent = 'Platypush'
|
||||||
flow.access_type = 'offline'
|
flow.access_type = 'offline'
|
||||||
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
|
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
|
||||||
credentials = tools.run_flow(flow, store, flags)
|
tools.run_flow(flow, store, flags)
|
||||||
print('Storing credentials to ' + credentials_file)
|
print('Storing credentials to ' + credentials_file)
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ def main():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
scope = sys.argv.pop(1) if len(sys.argv) > 1 \
|
scope = sys.argv.pop(1) if len(sys.argv) > 1 \
|
||||||
else input('Space separated list of OAuth scopes: ')
|
else input('Space separated list of OAuth scopes: ')
|
||||||
|
|
||||||
client_secret_path = os.path.expanduser(
|
client_secret_path = os.path.expanduser(
|
||||||
sys.argv.pop(1) if len(sys.argv) > 1
|
sys.argv.pop(1) if len(sys.argv) > 1
|
||||||
|
@ -76,6 +76,4 @@ def main():
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import httplib2
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -29,7 +28,6 @@ class GoogleMailPlugin(GooglePlugin):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(scopes=self.scopes, *args, **kwargs)
|
super().__init__(scopes=self.scopes, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def compose(self, sender, to, subject, body, files=None):
|
def compose(self, sender, to, subject, body, files=None):
|
||||||
"""
|
"""
|
||||||
|
@ -66,9 +64,11 @@ class GoogleMailPlugin(GooglePlugin):
|
||||||
content_type = 'application/octet-stream'
|
content_type = 'application/octet-stream'
|
||||||
|
|
||||||
main_type, sub_type = content_type.split('/', 1)
|
main_type, sub_type = content_type.split('/', 1)
|
||||||
with open(file, 'rb') as fp: content = fp.read()
|
with open(file, 'rb') as fp:
|
||||||
|
content = fp.read()
|
||||||
|
|
||||||
if main_type == 'text':
|
if main_type == 'text':
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
msg = mimetypes.MIMEText(content, _subtype=sub_type)
|
msg = mimetypes.MIMEText(content, _subtype=sub_type)
|
||||||
elif main_type == 'image':
|
elif main_type == 'image':
|
||||||
msg = MIMEImage(content, _subtype=sub_type)
|
msg = MIMEImage(content, _subtype=sub_type)
|
||||||
|
@ -86,13 +86,12 @@ class GoogleMailPlugin(GooglePlugin):
|
||||||
message.attach(msg)
|
message.attach(msg)
|
||||||
|
|
||||||
service = self.get_service('gmail', 'v1')
|
service = self.get_service('gmail', 'v1')
|
||||||
body = { 'raw': base64.urlsafe_b64encode(message.as_bytes()).decode() }
|
body = {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}
|
||||||
message = (service.users().messages().send(
|
message = (service.users().messages().send(
|
||||||
userId='me', body=body).execute())
|
userId='me', body=body).execute())
|
||||||
|
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_labels(self):
|
def get_labels(self):
|
||||||
"""
|
"""
|
||||||
|
@ -103,5 +102,4 @@ class GoogleMailPlugin(GooglePlugin):
|
||||||
labels = results.get('labels', [])
|
labels = results.get('labels', [])
|
||||||
return labels
|
return labels
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
.. moduleauthor:: Fabio Manganiello <blacklight86@gmail.com>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
|
@ -18,14 +17,14 @@ class GoogleMapsPlugin(GooglePlugin):
|
||||||
|
|
||||||
def __init__(self, api_key, *args, **kwargs):
|
def __init__(self, api_key, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param api_key: Server-side API key to be used for the requests, get one at https://console.developers.google.com
|
:param api_key: Server-side API key to be used for the requests, get one at
|
||||||
|
https://console.developers.google.com
|
||||||
:type api_key: str
|
:type api_key: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(scopes=self.scopes, *args, **kwargs)
|
super().__init__(scopes=self.scopes, *args, **kwargs)
|
||||||
self.api_key = api_key
|
self.api_key = api_key
|
||||||
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_address_from_latlng(self, latitude, longitude):
|
def get_address_from_latlng(self, latitude, longitude):
|
||||||
"""
|
"""
|
||||||
|
@ -39,7 +38,7 @@ class GoogleMapsPlugin(GooglePlugin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
response = requests.get('https://maps.googleapis.com/maps/api/geocode/json',
|
response = requests.get('https://maps.googleapis.com/maps/api/geocode/json',
|
||||||
params = {
|
params={
|
||||||
'latlng': '{},{}'.format(latitude, longitude),
|
'latlng': '{},{}'.format(latitude, longitude),
|
||||||
'key': self.api_key,
|
'key': self.api_key,
|
||||||
}).json()
|
}).json()
|
||||||
|
@ -54,7 +53,7 @@ class GoogleMapsPlugin(GooglePlugin):
|
||||||
if 'results' in response and response['results']:
|
if 'results' in response and response['results']:
|
||||||
result = response['results'][0]
|
result = response['results'][0]
|
||||||
self.logger.info('Google Maps geocode response for latlng ({},{}): {}'.
|
self.logger.info('Google Maps geocode response for latlng ({},{}): {}'.
|
||||||
format(latitude, longitude, result))
|
format(latitude, longitude, result))
|
||||||
|
|
||||||
address['address'] = result['formatted_address'].split(',')[0]
|
address['address'] = result['formatted_address'].split(',')[0]
|
||||||
for addr_component in result['address_components']:
|
for addr_component in result['address_components']:
|
||||||
|
@ -81,7 +80,7 @@ class GoogleMapsPlugin(GooglePlugin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
response = requests.get('https://maps.googleapis.com/maps/api/elevation/json',
|
response = requests.get('https://maps.googleapis.com/maps/api/elevation/json',
|
||||||
params = {
|
params={
|
||||||
'locations': '{},{}'.format(latitude, longitude),
|
'locations': '{},{}'.format(latitude, longitude),
|
||||||
'key': self.api_key,
|
'key': self.api_key,
|
||||||
}).json()
|
}).json()
|
||||||
|
@ -91,8 +90,6 @@ class GoogleMapsPlugin(GooglePlugin):
|
||||||
if response.get('results'):
|
if response.get('results'):
|
||||||
elevation = response['results'][0]['elevation']
|
elevation = response['results'][0]['elevation']
|
||||||
|
|
||||||
return { 'elevation': elevation }
|
return {'elevation': elevation}
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -78,8 +78,8 @@ class GpioSensorDistanceVl53L1XPlugin(GpioSensorPlugin):
|
||||||
try:
|
try:
|
||||||
self._device.stop_ranging()
|
self._device.stop_ranging()
|
||||||
self._device.close()
|
self._device.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Error while closing device: {}'.format(str(e)))
|
||||||
|
|
||||||
self._device = None
|
self._device = None
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
|
@ -12,7 +12,7 @@ class MCP3008Mode(enum.Enum):
|
||||||
class GpioSensorMcp3008Plugin(GpioSensorPlugin):
|
class GpioSensorMcp3008Plugin(GpioSensorPlugin):
|
||||||
"""
|
"""
|
||||||
Plugin to read analog sensor values from an MCP3008 chipset. The MCP3008
|
Plugin to read analog sensor values from an MCP3008 chipset. The MCP3008
|
||||||
chipset is a circuit that allows you to read measuremnts from multiple
|
chipset is a circuit that allows you to read measurements from multiple
|
||||||
analog sources (e.g. sensors) and multiplex them to a digital device like a
|
analog sources (e.g. sensors) and multiplex them to a digital device like a
|
||||||
Raspberry Pi or a regular laptop. See
|
Raspberry Pi or a regular laptop. See
|
||||||
https://learn.adafruit.com/raspberry-pi-analog-to-digital-converters/mcp3008
|
https://learn.adafruit.com/raspberry-pi-analog-to-digital-converters/mcp3008
|
||||||
|
@ -25,8 +25,9 @@ class GpioSensorMcp3008Plugin(GpioSensorPlugin):
|
||||||
|
|
||||||
N_CHANNELS = 8
|
N_CHANNELS = 8
|
||||||
|
|
||||||
|
# noinspection PyPep8Naming
|
||||||
def __init__(self, CLK=None, MISO=None, MOSI=None, CS=None, spi_port=None,
|
def __init__(self, CLK=None, MISO=None, MOSI=None, CS=None, spi_port=None,
|
||||||
spi_device=None, channels=None, Vdd=3.3, *args, **kwargs):
|
spi_device=None, channels=None, Vdd=3.3, **kwargs):
|
||||||
"""
|
"""
|
||||||
The MCP3008 can be connected in two modes:
|
The MCP3008 can be connected in two modes:
|
||||||
|
|
||||||
|
@ -54,8 +55,8 @@ class GpioSensorMcp3008Plugin(GpioSensorPlugin):
|
||||||
:param spi_device: (hardware SPI mode) SPI device name
|
:param spi_device: (hardware SPI mode) SPI device name
|
||||||
:type spi_device: str
|
:type spi_device: str
|
||||||
|
|
||||||
:param channels: name-value mapping between MCP3008 output PINs and sensor names. This mapping will be used when you get values through :func:`.get_measurement()`.
|
:param channels: name-value mapping between MCP3008 output PINs and sensor names. This mapping will be used
|
||||||
Example::
|
when you get values through :func:`.get_measurement()`. Example::
|
||||||
|
|
||||||
channels = {
|
channels = {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -83,7 +84,7 @@ class GpioSensorMcp3008Plugin(GpioSensorPlugin):
|
||||||
:type Vdd: float
|
:type Vdd: float
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
if CLK and MISO and MOSI and CS:
|
if CLK and MISO and MOSI and CS:
|
||||||
self.CLK = CLK
|
self.CLK = CLK
|
||||||
|
@ -154,6 +155,7 @@ class GpioSensorMcp3008Plugin(GpioSensorPlugin):
|
||||||
if i in self.channels:
|
if i in self.channels:
|
||||||
channel = self.channels[i]
|
channel = self.channels[i]
|
||||||
if 'conv_function' in channel:
|
if 'conv_function' in channel:
|
||||||
|
# noinspection PyUnusedLocal
|
||||||
x = value
|
x = value
|
||||||
value = eval(channel['conv_function'])
|
value = eval(channel['conv_function'])
|
||||||
|
|
||||||
|
|
|
@ -164,8 +164,9 @@ Warning, this new I?C address will still be used after resetting the power on th
|
||||||
print('Missing ZeroBorg at %02X' % oldAddress)
|
print('Missing ZeroBorg at %02X' % oldAddress)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
foundChip = False
|
foundChip = False
|
||||||
|
print(str(e))
|
||||||
print('Missing ZeroBorg at %02X' % oldAddress)
|
print('Missing ZeroBorg at %02X' % oldAddress)
|
||||||
if foundChip:
|
if foundChip:
|
||||||
bus.RawWrite(COMMAND_SET_I2C_ADD, [newAddress])
|
bus.RawWrite(COMMAND_SET_I2C_ADD, [newAddress])
|
||||||
|
@ -188,8 +189,9 @@ Warning, this new I?C address will still be used after resetting the power on th
|
||||||
print('Missing ZeroBorg at %02X' % newAddress)
|
print('Missing ZeroBorg at %02X' % newAddress)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
foundChip = False
|
foundChip = False
|
||||||
|
print(str(e))
|
||||||
print('Missing ZeroBorg at %02X' % newAddress)
|
print('Missing ZeroBorg at %02X' % newAddress)
|
||||||
if foundChip:
|
if foundChip:
|
||||||
print('New I?C address of %02X set successfully' % newAddress)
|
print('New I?C address of %02X set successfully' % newAddress)
|
||||||
|
@ -332,9 +334,9 @@ the current busNumber.
|
||||||
self.Print('Missing ZeroBorg at %02X' % self.i2cAddress)
|
self.Print('Missing ZeroBorg at %02X' % self.i2cAddress)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.foundChip = False
|
self.foundChip = False
|
||||||
self.Print('Missing ZeroBorg at %02X' % self.i2cAddress)
|
self.Print('Missing ZeroBorg at %02X: %s' % (self.i2cAddress, str(e)))
|
||||||
|
|
||||||
# See if we are missing chips
|
# See if we are missing chips
|
||||||
if not self.foundChip:
|
if not self.foundChip:
|
||||||
|
@ -383,8 +385,8 @@ SetMotor1(1) -> motor 1 moving forward at 100% power
|
||||||
self.RawWrite(command, [pwm])
|
self.RawWrite(command, [pwm])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed sending motor 1 drive level!')
|
self.Print('Failed sending motor 1 drive level! {}'.format(str(e)))
|
||||||
|
|
||||||
def GetMotor1(self):
|
def GetMotor1(self):
|
||||||
"""
|
"""
|
||||||
|
@ -402,8 +404,8 @@ e.g.
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_A, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_A, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading motor 1 drive level!')
|
self.Print('Failed reading motor 1 drive level! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
power = float(i2cRecv[2]) / float(PWM_MAX)
|
power = float(i2cRecv[2]) / float(PWM_MAX)
|
||||||
|
@ -444,8 +446,8 @@ SetMotor2(1) -> motor 2 moving forward at 100% power
|
||||||
self.RawWrite(command, [pwm])
|
self.RawWrite(command, [pwm])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed sending motor 2 drive level!')
|
self.Print('Failed sending motor 2 drive level! {}'.format(str(e)))
|
||||||
|
|
||||||
def GetMotor2(self):
|
def GetMotor2(self):
|
||||||
"""
|
"""
|
||||||
|
@ -463,8 +465,8 @@ e.g.
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_B, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_B, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading motor 2 drive level!')
|
self.Print('Failed reading motor 2 drive level! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
power = float(i2cRecv[2]) / float(PWM_MAX)
|
power = float(i2cRecv[2]) / float(PWM_MAX)
|
||||||
|
@ -505,8 +507,8 @@ SetMotor3(1) -> motor 3 moving forward at 100% power
|
||||||
self.RawWrite(command, [pwm])
|
self.RawWrite(command, [pwm])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed sending motor 3 drive level!')
|
self.Print('Failed sending motor 3 drive level! {}'.format(str(e)))
|
||||||
|
|
||||||
def GetMotor3(self):
|
def GetMotor3(self):
|
||||||
"""
|
"""
|
||||||
|
@ -524,8 +526,8 @@ e.g.
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_C, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_C, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading motor 3 drive level!')
|
self.Print('Failed reading motor 3 drive level! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
power = float(i2cRecv[2]) / float(PWM_MAX)
|
power = float(i2cRecv[2]) / float(PWM_MAX)
|
||||||
|
@ -566,8 +568,8 @@ SetMotor4(1) -> motor 4 moving forward at 100% power
|
||||||
self.RawWrite(command, [pwm])
|
self.RawWrite(command, [pwm])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed sending motor 4 drive level!')
|
self.Print('Failed sending motor 4 drive level! {}'.format(str(e)))
|
||||||
|
|
||||||
def GetMotor4(self):
|
def GetMotor4(self):
|
||||||
"""
|
"""
|
||||||
|
@ -585,8 +587,8 @@ e.g.
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_D, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_D, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading motor 4 drive level!')
|
self.Print('Failed reading motor 4 drive level! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
power = float(i2cRecv[2]) / float(PWM_MAX)
|
power = float(i2cRecv[2]) / float(PWM_MAX)
|
||||||
|
@ -627,8 +629,8 @@ SetMotors(1) -> all motors are moving forward at 100% power
|
||||||
self.RawWrite(command, [pwm])
|
self.RawWrite(command, [pwm])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed sending all motors drive level!')
|
self.Print('Failed sending all motors drive level! {}'.format(str(e)))
|
||||||
|
|
||||||
def MotorsOff(self):
|
def MotorsOff(self):
|
||||||
"""
|
"""
|
||||||
|
@ -641,8 +643,8 @@ Sets all motors to stopped, useful when ending a program
|
||||||
self.RawWrite(COMMAND_ALL_OFF, [0])
|
self.RawWrite(COMMAND_ALL_OFF, [0])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed sending motors off command!')
|
self.Print('Failed sending motors off command! {}'.format(str(e)))
|
||||||
|
|
||||||
def SetLed(self, state):
|
def SetLed(self, state):
|
||||||
"""
|
"""
|
||||||
|
@ -674,8 +676,8 @@ Reads the current state of the LED, False for off, True for on
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_LED, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_LED, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading LED state!')
|
self.Print('Failed reading LED state! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
||||||
|
@ -694,8 +696,8 @@ Resets the EPO latch state, use to allow movement again after the EPO has been t
|
||||||
self.RawWrite(COMMAND_RESET_EPO, [0])
|
self.RawWrite(COMMAND_RESET_EPO, [0])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed resetting EPO!')
|
self.Print('Failed resetting EPO! {}'.format(str(e)))
|
||||||
|
|
||||||
def GetEpo(self):
|
def GetEpo(self):
|
||||||
"""
|
"""
|
||||||
|
@ -711,8 +713,8 @@ If True the EPO has been tripped, movement is disabled if the EPO is not ignored
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_EPO, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_EPO, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading EPO ignore state!')
|
self.Print('Failed reading EPO ignore state! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
||||||
|
@ -736,8 +738,8 @@ Sets the system to ignore or use the EPO latch, set to False if you have an EPO
|
||||||
self.RawWrite(COMMAND_SET_EPO_IGNORE, [level])
|
self.RawWrite(COMMAND_SET_EPO_IGNORE, [level])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed sending EPO ignore state!')
|
self.Print('Failed sending EPO ignore state! {}'.format(str(e)))
|
||||||
|
|
||||||
def GetEpoIgnore(self):
|
def GetEpoIgnore(self):
|
||||||
"""
|
"""
|
||||||
|
@ -750,8 +752,8 @@ Reads the system EPO ignore state, False for using the EPO latch, True for ignor
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_EPO_IGNORE, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_EPO_IGNORE, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading EPO ignore state!')
|
self.Print('Failed reading EPO ignore state! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
||||||
|
@ -793,8 +795,8 @@ Use HasNewIrMessage() to see if there has been a new IR message since the last c
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_LAST_IR, I2C_LONG_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_LAST_IR, I2C_LONG_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading IR message!')
|
self.Print('Failed reading IR message! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
message = ''
|
message = ''
|
||||||
|
@ -818,8 +820,8 @@ Sets if IR messages control the state of the LED, False for no effect, True for
|
||||||
self.RawWrite(COMMAND_SET_LED_IR, [level])
|
self.RawWrite(COMMAND_SET_LED_IR, [level])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed sending LED state!')
|
self.Print('Failed sending LED state! {}'.format(str(e)))
|
||||||
|
|
||||||
def GetLedIr(self):
|
def GetLedIr(self):
|
||||||
"""
|
"""
|
||||||
|
@ -832,8 +834,8 @@ Reads if IR messages control the state of the LED, False for no effect, True for
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_LED_IR, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_LED_IR, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading LED state!')
|
self.Print('Failed reading LED state! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
||||||
|
@ -853,8 +855,8 @@ Returns the value as a voltage based on the 3.3 V reference pin (pin 1).
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_ANALOG_1, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_ANALOG_1, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading analog level #1!')
|
self.Print('Failed reading analog level #1! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
raw = (i2cRecv[1] << 8) + i2cRecv[2]
|
raw = (i2cRecv[1] << 8) + i2cRecv[2]
|
||||||
|
@ -873,8 +875,8 @@ Returns the value as a voltage based on the 3.3 V reference pin (pin 1).
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_ANALOG_2, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_ANALOG_2, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading analog level #2!')
|
self.Print('Failed reading analog level #2! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
raw = (i2cRecv[1] << 8) + i2cRecv[2]
|
raw = (i2cRecv[1] << 8) + i2cRecv[2]
|
||||||
|
@ -900,8 +902,8 @@ The failsafe is disabled at power on
|
||||||
self.RawWrite(COMMAND_SET_FAILSAFE, [level])
|
self.RawWrite(COMMAND_SET_FAILSAFE, [level])
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed sending communications failsafe state!')
|
self.Print('Failed sending communications failsafe state! {}'.format(str(e)))
|
||||||
|
|
||||||
def GetCommsFailsafe(self):
|
def GetCommsFailsafe(self):
|
||||||
"""
|
"""
|
||||||
|
@ -915,8 +917,8 @@ The failsafe will turn the motors off unless it is commanded at least once every
|
||||||
i2cRecv = self.RawRead(COMMAND_GET_FAILSAFE, I2C_NORM_LEN)
|
i2cRecv = self.RawRead(COMMAND_GET_FAILSAFE, I2C_NORM_LEN)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
self.Print('Failed reading communications failsafe state!')
|
self.Print('Failed reading communications failsafe state! {}'.format(str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
if i2cRecv[1] == COMMAND_VALUE_OFF:
|
||||||
|
@ -930,6 +932,7 @@ Help()
|
||||||
|
|
||||||
Displays the names and descriptions of the various functions and settings provided
|
Displays the names and descriptions of the various functions and settings provided
|
||||||
"""
|
"""
|
||||||
|
# noinspection PyTypeChecker
|
||||||
funcList = [ZeroBorg.__dict__.get(a) for a in dir(ZeroBorg) if
|
funcList = [ZeroBorg.__dict__.get(a) for a in dir(ZeroBorg) if
|
||||||
isinstance(ZeroBorg.__dict__.get(a), types.FunctionType)]
|
isinstance(ZeroBorg.__dict__.get(a), types.FunctionType)]
|
||||||
funcListSorted = sorted(funcList, key=lambda x: x.func_code.co_firstlineno)
|
funcListSorted = sorted(funcList, key=lambda x: x.func_code.co_firstlineno)
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
import datetime
|
|
||||||
import dateutil.parser
|
|
||||||
|
|
||||||
from platypush.plugins import action
|
|
||||||
from platypush.plugins.http.request import HttpRequestPlugin
|
|
||||||
|
|
||||||
class HttpRequestOtaBookingPlugin(HttpRequestPlugin):
|
|
||||||
""" Plugin to send requests to the Booking Hub API """
|
|
||||||
|
|
||||||
def __init__(self, hotel_id, token, timeout=5, **kwargs):
|
|
||||||
self.hotel_id = hotel_id
|
|
||||||
self.token = token
|
|
||||||
self.timeout = timeout
|
|
||||||
|
|
||||||
|
|
||||||
@action
|
|
||||||
def get_reservations(self, day='today'):
|
|
||||||
url = 'https://hub-api.booking.com/v1/hotels/{}/reservations' \
|
|
||||||
.format(self.hotel_id)
|
|
||||||
|
|
||||||
today = datetime.date.today().isoformat()
|
|
||||||
if day == 'today': day = today
|
|
||||||
|
|
||||||
headers = { 'X-Booking-Auth-Token': self.token }
|
|
||||||
params = { 'checkin': day }
|
|
||||||
|
|
||||||
response = self.get(url, headers=headers, params=params,
|
|
||||||
output='json', timeout=self.timeout)
|
|
||||||
|
|
||||||
reservations = [res for res in response.output
|
|
||||||
if res['status'] != 'CANCELLED']
|
|
||||||
|
|
||||||
response.output = {
|
|
||||||
'reservations': reservations,
|
|
||||||
'n_reservations': len(reservations),
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
|
||||||
|
|
|
@ -24,14 +24,12 @@ class HttpWebpagePlugin(Plugin):
|
||||||
|
|
||||||
_mercury_script = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'mercury-parser.js')
|
_mercury_script = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'mercury-parser.js')
|
||||||
|
|
||||||
def _parse(self, proc):
|
@staticmethod
|
||||||
output = ''
|
def _parse(proc):
|
||||||
|
|
||||||
with subprocess.Popen(proc, stdout=subprocess.PIPE, stderr=None) as parser:
|
with subprocess.Popen(proc, stdout=subprocess.PIPE, stderr=None) as parser:
|
||||||
output = parser.communicate()[0].decode()
|
return parser.communicate()[0].decode()
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
# noinspection PyShadowingBuiltins
|
||||||
@action
|
@action
|
||||||
def simplify(self, url, type='html', html=None, outfile=None):
|
def simplify(self, url, type='html', html=None, outfile=None):
|
||||||
"""
|
"""
|
||||||
|
@ -124,15 +122,12 @@ class HttpWebpagePlugin(Plugin):
|
||||||
|
|
||||||
weasyprint.HTML(string=content).write_pdf(outfile, stylesheets=css)
|
weasyprint.HTML(string=content).write_pdf(outfile, stylesheets=css)
|
||||||
else:
|
else:
|
||||||
content = '''
|
content = '''<html>
|
||||||
<html>
|
|
||||||
<head>
|
<head>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<style>{style}</style>
|
<style>{style}</style>
|
||||||
</head>
|
</head>'''.format(title=title, style=style, content=content) + \
|
||||||
<body>{{content}}</body>
|
'<body>{{' + content + '}}</body></html>'
|
||||||
</html>
|
|
||||||
'''.format(title=title, style=style, content=content)
|
|
||||||
|
|
||||||
with open(outfile, 'w', encoding='utf-8') as f:
|
with open(outfile, 'w', encoding='utf-8') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
|
@ -188,10 +188,10 @@ class InspectPlugin(Plugin):
|
||||||
for _, modname, _ in pkgutil.walk_packages(path=package.__path__,
|
for _, modname, _ in pkgutil.walk_packages(path=package.__path__,
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
onerror=lambda x: None):
|
onerror=lambda x: None):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
module = importlib.import_module(modname)
|
module = importlib.import_module(modname)
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.debug(f'Could not import module {modname}: {str(e)}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for _, obj in inspect.getmembers(module):
|
for _, obj in inspect.getmembers(module):
|
||||||
|
@ -207,10 +207,10 @@ class InspectPlugin(Plugin):
|
||||||
for _, modname, _ in pkgutil.walk_packages(path=package.__path__,
|
for _, modname, _ in pkgutil.walk_packages(path=package.__path__,
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
onerror=lambda x: None):
|
onerror=lambda x: None):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
module = importlib.import_module(modname)
|
module = importlib.import_module(modname)
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.debug(f'Could not import module {modname}: {str(e)}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for _, obj in inspect.getmembers(module):
|
for _, obj in inspect.getmembers(module):
|
||||||
|
@ -226,10 +226,10 @@ class InspectPlugin(Plugin):
|
||||||
for _, modname, _ in pkgutil.walk_packages(path=package.__path__,
|
for _, modname, _ in pkgutil.walk_packages(path=package.__path__,
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
onerror=lambda x: None):
|
onerror=lambda x: None):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
module = importlib.import_module(modname)
|
module = importlib.import_module(modname)
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.debug(f'Could not import module {modname}: {str(e)}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for _, obj in inspect.getmembers(module):
|
for _, obj in inspect.getmembers(module):
|
||||||
|
@ -250,10 +250,10 @@ class InspectPlugin(Plugin):
|
||||||
for _, modname, _ in pkgutil.walk_packages(path=package.__path__,
|
for _, modname, _ in pkgutil.walk_packages(path=package.__path__,
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
onerror=lambda x: None):
|
onerror=lambda x: None):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
module = importlib.import_module(modname)
|
module = importlib.import_module(modname)
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.debug(f'Could not import module {modname}: {str(e)}')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for _, obj in inspect.getmembers(module):
|
for _, obj in inspect.getmembers(module):
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
|
|
||||||
from platypush.context import get_backend
|
from platypush.context import get_backend
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
@ -19,10 +18,14 @@ class KafkaPlugin(Plugin):
|
||||||
* **kafka** (``pip install kafka-python``)
|
* **kafka** (``pip install kafka-python``)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, server=None, **kwargs):
|
def __init__(self, server=None, port=9092, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param server: Default Kafka server name or address + port (format: ``host:port``) to dispatch the messages to. If None (default), then it has to be specified upon message sending.
|
:param server: Default Kafka server name or address. If None (default), then it has to be specified upon
|
||||||
|
message sending.
|
||||||
:type server: str
|
:type server: str
|
||||||
|
|
||||||
|
:param port: Default Kafka server port (default: 9092).
|
||||||
|
:type port: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
@ -35,13 +38,17 @@ class KafkaPlugin(Plugin):
|
||||||
# Kafka can be veryyyy noisy
|
# Kafka can be veryyyy noisy
|
||||||
logging.getLogger('kafka').setLevel(logging.ERROR)
|
logging.getLogger('kafka').setLevel(logging.ERROR)
|
||||||
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def send_message(self, msg, topic, server=None, **kwargs):
|
def send_message(self, msg, topic, server=None):
|
||||||
"""
|
"""
|
||||||
:param msg: Message to send - as a string, bytes stream, JSON, Platypush message, dictionary, or anything that implements ``__str__``
|
:param msg: Message to send - as a string, bytes stream, JSON, Platypush message, dictionary, or anything
|
||||||
|
that implements ``__str__``
|
||||||
|
|
||||||
:param server: Kafka server name or address + port (format: ``host:port``). If None, then the default server will be used
|
:param topic: Topic to send the message to.
|
||||||
|
:type topic: str
|
||||||
|
|
||||||
|
:param server: Kafka server name or address + port (format: ``host:port``). If None, then the default server
|
||||||
|
will be used
|
||||||
:type server: str
|
:type server: str
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -52,8 +59,8 @@ class KafkaPlugin(Plugin):
|
||||||
try:
|
try:
|
||||||
kafka_backend = get_backend('kafka')
|
kafka_backend = get_backend('kafka')
|
||||||
server = kafka_backend.server
|
server = kafka_backend.server
|
||||||
except:
|
except Exception as e:
|
||||||
raise RuntimeError('No Kafka server nor default server specified')
|
raise RuntimeError(f'No Kafka server nor default server specified: {str(e)}')
|
||||||
else:
|
else:
|
||||||
server = self.server
|
server = self.server
|
||||||
|
|
||||||
|
@ -67,4 +74,3 @@ class KafkaPlugin(Plugin):
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@ import functools
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
import re
|
import re
|
||||||
|
import requests
|
||||||
from typing import Optional, List, Dict, Union
|
from typing import Optional, List, Dict, Union
|
||||||
|
|
||||||
import requests
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
|
@ -214,8 +214,8 @@ class MediaPlugin(Plugin):
|
||||||
try:
|
try:
|
||||||
torrents = get_plugin(self.torrent_plugin)
|
torrents = get_plugin(self.torrent_plugin)
|
||||||
torrents.quit()
|
torrents.quit()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning(f'Could not stop torrent plugin: {str(e)}')
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def play(self, resource, *args, **kwargs):
|
def play(self, resource, *args, **kwargs):
|
||||||
|
@ -423,7 +423,6 @@ class MediaPlugin(Plugin):
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import requests
|
|
||||||
|
|
||||||
http = get_backend('http')
|
http = get_backend('http')
|
||||||
if not http:
|
if not http:
|
||||||
|
@ -445,8 +444,6 @@ class MediaPlugin(Plugin):
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def stop_streaming(self, media_id):
|
def stop_streaming(self, media_id):
|
||||||
import requests
|
|
||||||
|
|
||||||
http = get_backend('http')
|
http = get_backend('http')
|
||||||
if not http:
|
if not http:
|
||||||
self.logger.warning('Cannot unregister {}: HTTP backend unavailable'.
|
self.logger.warning('Cannot unregister {}: HTTP backend unavailable'.
|
||||||
|
@ -536,9 +533,9 @@ class MediaPlugin(Plugin):
|
||||||
return functools.reduce(
|
return functools.reduce(
|
||||||
lambda t, t_i: t + t_i,
|
lambda t, t_i: t + t_i,
|
||||||
[float(t) * pow(60, i) for (i, t) in enumerate(re.search(
|
[float(t) * pow(60, i) for (i, t) in enumerate(re.search(
|
||||||
'^Duration:\s*([^,]+)', [x.decode()
|
r'^Duration:\s*([^,]+)', [x.decode()
|
||||||
for x in result.stdout.readlines()
|
for x in result.stdout.readlines()
|
||||||
if "Duration" in x.decode()].pop().strip()
|
if "Duration" in x.decode()].pop().strip()
|
||||||
).group(1).split(':')[::-1])]
|
).group(1).split(':')[::-1])]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import datetime
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from platypush.context import get_plugin, get_bus
|
from platypush.context import get_bus
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.media import MediaPlugin
|
from platypush.plugins.media import MediaPlugin
|
||||||
from platypush.utils import get_mime_type
|
from platypush.utils import get_mime_type
|
||||||
|
@ -91,6 +91,7 @@ class MediaChromecastPlugin(MediaPlugin):
|
||||||
post_event(MediaStopEvent, device=self.name)
|
post_event(MediaStopEvent, device=self.name)
|
||||||
if status.get('volume') != self.status.get('volume'):
|
if status.get('volume') != self.status.get('volume'):
|
||||||
post_event(MediaVolumeChangedEvent, volume=status.get('volume'), device=self.name)
|
post_event(MediaVolumeChangedEvent, volume=status.get('volume'), device=self.name)
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
if abs(status.get('position') - self.status.get('position')) > time.time() - self.last_status_timestamp + 5:
|
if abs(status.get('position') - self.status.get('position')) > time.time() - self.last_status_timestamp + 5:
|
||||||
post_event(MediaSeekEvent, position=status.get('position'), device=self.name)
|
post_event(MediaSeekEvent, position=status.get('position'), device=self.name)
|
||||||
|
|
||||||
|
@ -104,7 +105,7 @@ class MediaChromecastPlugin(MediaPlugin):
|
||||||
self.initialized = False
|
self.initialized = False
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def new_media_status(self, status):
|
def new_media_status(self, *_):
|
||||||
if self.subtitle_id and not self.initialized:
|
if self.subtitle_id and not self.initialized:
|
||||||
self.mc.update_status()
|
self.mc.update_status()
|
||||||
self.mc.enable_subtitle(self.subtitle_id)
|
self.mc.enable_subtitle(self.subtitle_id)
|
||||||
|
@ -327,9 +328,9 @@ class MediaChromecastPlugin(MediaPlugin):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_youtube_url(cls, url):
|
def _get_youtube_url(cls, url):
|
||||||
m = re.match('https?://www.youtube.com/watch\?v=([^&]+).*', url)
|
m = re.match(r'https?://(www\.)?youtube\.com/watch\?v=([^&]+).*', url)
|
||||||
if m:
|
if m:
|
||||||
return m.group(1)
|
return m.group(2)
|
||||||
|
|
||||||
m = re.match('youtube:video:(.*)', url)
|
m = re.match('youtube:video:(.*)', url)
|
||||||
if m:
|
if m:
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import json
|
import json
|
||||||
import re
|
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
@ -7,7 +6,7 @@ from platypush.context import get_bus
|
||||||
from platypush.plugins import action
|
from platypush.plugins import action
|
||||||
from platypush.plugins.media import MediaPlugin, PlayerState
|
from platypush.plugins.media import MediaPlugin, PlayerState
|
||||||
from platypush.message.event.media import MediaPlayEvent, MediaPauseEvent, MediaStopEvent, \
|
from platypush.message.event.media import MediaPlayEvent, MediaPauseEvent, MediaStopEvent, \
|
||||||
MediaSeekEvent, MediaVolumeChangedEvent, NewPlayingMediaEvent
|
MediaSeekEvent, MediaVolumeChangedEvent
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
|
@ -602,7 +601,8 @@ class MediaKodiPlugin(MediaPlugin):
|
||||||
try:
|
try:
|
||||||
kodi = self._get_kodi()
|
kodi = self._get_kodi()
|
||||||
players = kodi.Player.GetActivePlayers().get('result', [])
|
players = kodi.Player.GetActivePlayers().get('result', [])
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.debug(f'Could not get active players: {str(e)}')
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
ret['state'] = PlayerState.STOP.value
|
ret['state'] = PlayerState.STOP.value
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import enum
|
import enum
|
||||||
import math
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from platypush.context import get_bus
|
from platypush.context import get_bus
|
||||||
|
@ -119,8 +118,8 @@ class MediaOmxplayerPlugin(MediaPlugin):
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
self._player.stop()
|
self._player.stop()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning(f'Could not stop player: {str(e)}')
|
||||||
|
|
||||||
self._player.quit()
|
self._player.quit()
|
||||||
except OMXPlayerDeadError:
|
except OMXPlayerDeadError:
|
||||||
|
|
|
@ -3,9 +3,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from sqlalchemy import create_engine, func, or_, Column, Integer, String, \
|
from sqlalchemy import create_engine, Column, Integer, String, DateTime, PrimaryKeyConstraint, ForeignKey
|
||||||
DateTime, PrimaryKeyConstraint, ForeignKey
|
|
||||||
|
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func
|
||||||
|
@ -17,6 +15,7 @@ from platypush.plugins.media.search import MediaSearcher
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
Session = scoped_session(sessionmaker())
|
Session = scoped_session(sessionmaker())
|
||||||
|
|
||||||
|
|
||||||
class LocalMediaSearcher(MediaSearcher):
|
class LocalMediaSearcher(MediaSearcher):
|
||||||
"""
|
"""
|
||||||
This class will search for media in the local configured directories. It
|
This class will search for media in the local configured directories. It
|
||||||
|
@ -29,7 +28,7 @@ class LocalMediaSearcher(MediaSearcher):
|
||||||
* **sqlalchemy** (``pip install sqlalchemy``)
|
* **sqlalchemy** (``pip install sqlalchemy``)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_filename_separators = '[.,_\-@()\[\]\{\}\s\'\"]+'
|
_filename_separators = r'[.,_\-@()\[\]\{\}\s\'\"]+'
|
||||||
|
|
||||||
def __init__(self, dirs, *args, **kwargs):
|
def __init__(self, dirs, *args, **kwargs):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
|
@ -97,8 +97,8 @@ class MediaWebtorrentPlugin(MediaPlugin):
|
||||||
if Config.get(plugin_name):
|
if Config.get(plugin_name):
|
||||||
self._media_plugin = get_plugin(plugin_name)
|
self._media_plugin = get_plugin(plugin_name)
|
||||||
break
|
break
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug(f'Could not get media plugin {plugin_name}: {str(e)}')
|
||||||
|
|
||||||
if not self._media_plugin:
|
if not self._media_plugin:
|
||||||
raise RuntimeError(('No media player specified and no ' +
|
raise RuntimeError(('No media player specified and no ' +
|
||||||
|
@ -109,7 +109,7 @@ class MediaWebtorrentPlugin(MediaPlugin):
|
||||||
def _read_process_line(self):
|
def _read_process_line(self):
|
||||||
line = self._webtorrent_process.stdout.readline().decode().strip()
|
line = self._webtorrent_process.stdout.readline().decode().strip()
|
||||||
# Strip output of the colors
|
# Strip output of the colors
|
||||||
return re.sub('\x1b\[(([0-9]+m)|(.{1,2}))', '', line).strip()
|
return re.sub(r'\x1b\[(([0-9]+m)|(.{1,2}))', '', line).strip()
|
||||||
|
|
||||||
def _process_monitor(self, resource, download_dir, download_only,
|
def _process_monitor(self, resource, download_dir, download_only,
|
||||||
player_type, player_args):
|
player_type, player_args):
|
||||||
|
@ -142,7 +142,7 @@ class MediaWebtorrentPlugin(MediaPlugin):
|
||||||
and state == TorrentState.IDLE:
|
and state == TorrentState.IDLE:
|
||||||
# IDLE -> DOWNLOADING_METADATA
|
# IDLE -> DOWNLOADING_METADATA
|
||||||
state = TorrentState.DOWNLOADING_METADATA
|
state = TorrentState.DOWNLOADING_METADATA
|
||||||
bus.post(TorrentDownloadedMetadataEvent(resource=resource))
|
bus.post(TorrentDownloadedMetadataEvent(url=webtorrent_url, resource=resource))
|
||||||
elif 'downloading: ' in line.lower() \
|
elif 'downloading: ' in line.lower() \
|
||||||
and media_file is None:
|
and media_file is None:
|
||||||
# Find video files in torrent directory
|
# Find video files in torrent directory
|
||||||
|
@ -177,10 +177,9 @@ class MediaWebtorrentPlugin(MediaPlugin):
|
||||||
if state.value <= TorrentState.DOWNLOADING_METADATA.value \
|
if state.value <= TorrentState.DOWNLOADING_METADATA.value \
|
||||||
and media_file and webtorrent_url:
|
and media_file and webtorrent_url:
|
||||||
# DOWNLOADING_METADATA -> DOWNLOADING
|
# DOWNLOADING_METADATA -> DOWNLOADING
|
||||||
state = TorrentState.DOWNLOADING
|
|
||||||
bus.post(TorrentDownloadStartEvent(
|
bus.post(TorrentDownloadStartEvent(
|
||||||
resource=resource, media_file=media_file,
|
resource=resource, media_file=media_file,
|
||||||
stream_url=webtorrent_url))
|
stream_url=webtorrent_url, url=webtorrent_url))
|
||||||
break
|
break
|
||||||
|
|
||||||
if not output_dir:
|
if not output_dir:
|
||||||
|
@ -193,8 +192,11 @@ class MediaWebtorrentPlugin(MediaPlugin):
|
||||||
self.logger.warning('WebTorrent could not start streaming')
|
self.logger.warning('WebTorrent could not start streaming')
|
||||||
|
|
||||||
# Keep downloading but don't start the player
|
# Keep downloading but don't start the player
|
||||||
try: self._webtorrent_process.wait()
|
try:
|
||||||
except: pass
|
self._webtorrent_process.wait()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.warning(f'WebTorrent process error: {str(e)}')
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
player = None
|
player = None
|
||||||
|
@ -237,10 +239,14 @@ class MediaWebtorrentPlugin(MediaPlugin):
|
||||||
self.logger.info('Torrent player terminated')
|
self.logger.info('Torrent player terminated')
|
||||||
bus.post(TorrentDownloadCompletedEvent(resource=resource,
|
bus.post(TorrentDownloadCompletedEvent(resource=resource,
|
||||||
output_dir=output_dir,
|
output_dir=output_dir,
|
||||||
media_file=media_file))
|
media_file=media_file,
|
||||||
|
url=webtorrent_url))
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.quit()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.warning(f'Could not terminate WebTorrent process: {str(e)}')
|
||||||
|
|
||||||
try: self.quit()
|
|
||||||
except: pass
|
|
||||||
self.logger.info('WebTorrent process terminated')
|
self.logger.info('WebTorrent process terminated')
|
||||||
|
|
||||||
return _thread
|
return _thread
|
||||||
|
@ -252,6 +258,7 @@ class MediaWebtorrentPlugin(MediaPlugin):
|
||||||
media_cls = player.__class__.__name__
|
media_cls = player.__class__.__name__
|
||||||
|
|
||||||
if media_cls == 'MediaMplayerPlugin':
|
if media_cls == 'MediaMplayerPlugin':
|
||||||
|
# noinspection PyProtectedMember
|
||||||
stop_evt = player._mplayer_stopped_event
|
stop_evt = player._mplayer_stopped_event
|
||||||
elif media_cls == 'MediaMpvPlugin' or media_cls == 'MediaVlcPlugin':
|
elif media_cls == 'MediaMpvPlugin' or media_cls == 'MediaVlcPlugin':
|
||||||
stop_evt = threading.Event()
|
stop_evt = threading.Event()
|
||||||
|
@ -359,8 +366,8 @@ class MediaWebtorrentPlugin(MediaPlugin):
|
||||||
stream_url = self._torrent_stream_urls.get(resource)
|
stream_url = self._torrent_stream_urls.get(resource)
|
||||||
|
|
||||||
if not stream_url:
|
if not stream_url:
|
||||||
return (None, ('The webtorrent process hasn\'t started ' +
|
return (None, ("The webtorrent process hasn't started " +
|
||||||
'streaming after {} seconds').format(
|
"streaming after {} seconds").format(
|
||||||
self._web_stream_ready_timeout))
|
self._web_stream_ready_timeout))
|
||||||
|
|
||||||
return {'resource': resource, 'url': stream_url}
|
return {'resource': resource, 'url': stream_url}
|
||||||
|
@ -380,8 +387,10 @@ class MediaWebtorrentPlugin(MediaPlugin):
|
||||||
if self._is_process_alive():
|
if self._is_process_alive():
|
||||||
self._webtorrent_process.terminate()
|
self._webtorrent_process.terminate()
|
||||||
self._webtorrent_process.wait()
|
self._webtorrent_process.wait()
|
||||||
try: self._webtorrent_process.kill()
|
try:
|
||||||
except: pass
|
self._webtorrent_process.kill()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.warning(f'Error on WebTorrent process kill: {str(e)}')
|
||||||
|
|
||||||
self._webtorrent_process = None
|
self._webtorrent_process = None
|
||||||
|
|
||||||
|
|
|
@ -173,11 +173,10 @@ class MqttPlugin(Plugin):
|
||||||
if isinstance(msg, (dict, list)):
|
if isinstance(msg, (dict, list)):
|
||||||
msg = json.dumps(msg)
|
msg = json.dumps(msg)
|
||||||
|
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
msg = Message.build(json.loads(msg))
|
msg = Message.build(json.loads(msg))
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug(f'Not a valid JSON: {str(e)}')
|
||||||
|
|
||||||
host = host or self.host
|
host = host or self.host
|
||||||
port = port or self.port or 1883
|
port = port or self.port or 1883
|
||||||
|
@ -209,11 +208,10 @@ class MqttPlugin(Plugin):
|
||||||
response_buffer.close()
|
response_buffer.close()
|
||||||
|
|
||||||
if client:
|
if client:
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
client.loop_stop()
|
client.loop_stop()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning(f'Could not stop client loop: {e}')
|
||||||
|
|
||||||
client.disconnect()
|
client.disconnect()
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,8 @@ class MusicMpdPlugin(MusicPlugin):
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
self.client = None
|
self.client = None
|
||||||
raise error
|
if error:
|
||||||
|
raise error
|
||||||
|
|
||||||
def _exec(self, method, *args, **kwargs):
|
def _exec(self, method, *args, **kwargs):
|
||||||
error = None
|
error = None
|
||||||
|
@ -383,14 +384,14 @@ class MusicMpdPlugin(MusicPlugin):
|
||||||
if not resource:
|
if not resource:
|
||||||
return
|
return
|
||||||
|
|
||||||
m = re.search('^https://open.spotify.com/([^?]+)', resource)
|
m = re.search(r'^https?://open\.spotify\.com/([^?]+)', resource)
|
||||||
if m:
|
if m:
|
||||||
resource = 'spotify:{}'.format(m.group(1).replace('/', ':'))
|
resource = 'spotify:{}'.format(m.group(1).replace('/', ':'))
|
||||||
|
|
||||||
if resource.startswith('spotify:'):
|
if resource.startswith('spotify:'):
|
||||||
resource = resource.split('?')[0]
|
resource = resource.split('?')[0]
|
||||||
|
|
||||||
m = re.match('spotify:playlist:(.*)', resource)
|
m = re.match(r'spotify:playlist:(.*)', resource)
|
||||||
if m:
|
if m:
|
||||||
# Old Spotify URI format, convert it to new
|
# Old Spotify URI format, convert it to new
|
||||||
resource = 'spotify:user:spotify:playlist:' + m.group(1)
|
resource = 'spotify:user:spotify:playlist:' + m.group(1)
|
||||||
|
@ -423,8 +424,8 @@ class MusicMpdPlugin(MusicPlugin):
|
||||||
"""
|
"""
|
||||||
Seek to the specified position (DEPRECATED, use :meth:`.seek` instead).
|
Seek to the specified position (DEPRECATED, use :meth:`.seek` instead).
|
||||||
|
|
||||||
:param value: Seek position in seconds, or delta string (e.g. '+15' or '-15') to indicate a seek relative to the current position
|
:param value: Seek position in seconds, or delta string (e.g. '+15' or '-15') to indicate a seek relative to
|
||||||
:type value: int
|
the current position :type value: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.seek(value)
|
return self.seek(value)
|
||||||
|
@ -434,8 +435,8 @@ class MusicMpdPlugin(MusicPlugin):
|
||||||
"""
|
"""
|
||||||
Seek to the specified position
|
Seek to the specified position
|
||||||
|
|
||||||
:param position: Seek position in seconds, or delta string (e.g. '+15' or '-15') to indicate a seek relative to the current position
|
:param position: Seek position in seconds, or delta string (e.g. '+15' or '-15') to indicate a seek relative
|
||||||
:type position: int
|
to the current position :type position: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._exec('seekcur', position)
|
return self._exec('seekcur', position)
|
||||||
|
@ -486,7 +487,8 @@ class MusicMpdPlugin(MusicPlugin):
|
||||||
try:
|
try:
|
||||||
n_tries -= 1
|
n_tries -= 1
|
||||||
self._connect()
|
self._connect()
|
||||||
return self.client.status()
|
if self.client:
|
||||||
|
return self.client.status()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error = e
|
error = e
|
||||||
self.logger.warning('Exception while getting MPD status: {}'.
|
self.logger.warning('Exception while getting MPD status: {}'.
|
||||||
|
@ -523,7 +525,7 @@ class MusicMpdPlugin(MusicPlugin):
|
||||||
or not track['artist']
|
or not track['artist']
|
||||||
or re.search('^https?://', track['file'])
|
or re.search('^https?://', track['file'])
|
||||||
or re.search('^tunein:', track['file'])):
|
or re.search('^tunein:', track['file'])):
|
||||||
m = re.match('^\s*(.+?)\s+-\s+(.*)\s*$', track['title'])
|
m = re.match(r'^\s*(.+?)\s+-\s+(.*)\s*$', track['title'])
|
||||||
if m and m.group(1) and m.group(2):
|
if m and m.group(1) and m.group(2):
|
||||||
track['artist'] = m.group(1)
|
track['artist'] = m.group(1)
|
||||||
track['title'] = m.group(2)
|
track['title'] = m.group(2)
|
||||||
|
@ -781,10 +783,7 @@ class MusicMpdPlugin(MusicPlugin):
|
||||||
items = self._exec('search', *filter, *args, return_status=False, **kwargs)
|
items = self._exec('search', *filter, *args, return_status=False, **kwargs)
|
||||||
|
|
||||||
# Spotify results first
|
# Spotify results first
|
||||||
items = sorted(items, key=lambda item:
|
return sorted(items, key=lambda item: 0 if item['file'].startswith('spotify:') else 1)
|
||||||
0 if item['file'].startswith('spotify:') else 1)
|
|
||||||
|
|
||||||
return items
|
|
||||||
|
|
||||||
# noinspection PyShadowingBuiltins
|
# noinspection PyShadowingBuiltins
|
||||||
@action
|
@action
|
||||||
|
|
|
@ -96,6 +96,7 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
'method': 'Server.GetStatus'
|
'method': 'Server.GetStatus'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
self._send(sock, request)
|
self._send(sock, request)
|
||||||
return (self._recv(sock) or {}).get('server', {})
|
return (self._recv(sock) or {}).get('server', {})
|
||||||
|
|
||||||
|
@ -214,8 +215,8 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning(f'Error on socket close: {e}')
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def mute(self, client=None, group=None, mute=None, host=None, port=None):
|
def mute(self, client=None, group=None, mute=None, host=None, port=None):
|
||||||
|
@ -266,13 +267,14 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
request['params']['volume']['percent'] = client['config']['volume']['percent']
|
request['params']['volume']['percent'] = client['config']['volume']['percent']
|
||||||
request['params']['volume']['muted'] = not cur_muted if mute is None else mute
|
request['params']['volume']['muted'] = not cur_muted if mute is None else mute
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
self._send(sock, request)
|
self._send(sock, request)
|
||||||
return self._recv(sock)
|
return self._recv(sock)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Error on socket close', e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def volume(self, client, volume=None, delta=None, mute=None, host=None,
|
def volume(self, client, volume=None, delta=None, mute=None, host=None,
|
||||||
|
@ -336,13 +338,14 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
request['params']['volume'] = {}
|
request['params']['volume'] = {}
|
||||||
request['params']['volume']['percent'] = volume
|
request['params']['volume']['percent'] = volume
|
||||||
request['params']['volume']['muted'] = mute
|
request['params']['volume']['muted'] = mute
|
||||||
|
# noinspection PyTypeChecker
|
||||||
self._send(sock, request)
|
self._send(sock, request)
|
||||||
return self._recv(sock)
|
return self._recv(sock)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Error on socket close', e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def set_client_name(self, client, name, host=None, port=None):
|
def set_client_name(self, client, name, host=None, port=None):
|
||||||
|
@ -376,13 +379,14 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
client = self._get_client(sock, client)
|
client = self._get_client(sock, client)
|
||||||
request['params']['id'] = client['id']
|
request['params']['id'] = client['id']
|
||||||
request['params']['name'] = name
|
request['params']['name'] = name
|
||||||
|
# noinspection PyTypeChecker
|
||||||
self._send(sock, request)
|
self._send(sock, request)
|
||||||
return self._recv(sock)
|
return self._recv(sock)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Error on socket close', e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def set_group_name(self, group, name, host=None, port=None):
|
def set_group_name(self, group, name, host=None, port=None):
|
||||||
|
@ -416,13 +420,14 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
self._send(sock, request)
|
self._send(sock, request)
|
||||||
return self._recv(sock)
|
return self._recv(sock)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Error on socket close', e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def set_latency(self, client, latency, host=None, port=None):
|
def set_latency(self, client, latency, host=None, port=None):
|
||||||
|
@ -457,13 +462,14 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
|
|
||||||
client = self._get_client(sock, client)
|
client = self._get_client(sock, client)
|
||||||
request['params']['id'] = client['id']
|
request['params']['id'] = client['id']
|
||||||
|
# noinspection PyTypeChecker
|
||||||
self._send(sock, request)
|
self._send(sock, request)
|
||||||
return self._recv(sock)
|
return self._recv(sock)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Error on socket close', e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def delete_client(self, client, host=None, port=None):
|
def delete_client(self, client, host=None, port=None):
|
||||||
|
@ -493,13 +499,14 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
|
|
||||||
client = self._get_client(sock, client)
|
client = self._get_client(sock, client)
|
||||||
request['params']['id'] = client['id']
|
request['params']['id'] = client['id']
|
||||||
|
# noinspection PyTypeChecker
|
||||||
self._send(sock, request)
|
self._send(sock, request)
|
||||||
return self._recv(sock)
|
return self._recv(sock)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Error on socket close', e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def group_set_clients(self, group, clients, host=None, port=None):
|
def group_set_clients(self, group, clients, host=None, port=None):
|
||||||
|
@ -538,13 +545,14 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
client = self._get_client(sock, client)
|
client = self._get_client(sock, client)
|
||||||
request['params']['clients'].append(client['id'])
|
request['params']['clients'].append(client['id'])
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
self._send(sock, request)
|
self._send(sock, request)
|
||||||
return self._recv(sock)
|
return self._recv(sock)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Error on socket close', e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def group_set_stream(self, group, stream_id, host=None, port=None):
|
def group_set_stream(self, group, stream_id, host=None, port=None):
|
||||||
|
@ -579,13 +587,14 @@ class MusicSnapcastPlugin(Plugin):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
self._send(sock, request)
|
self._send(sock, request)
|
||||||
return self._recv(sock)
|
return self._recv(sock)
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
sock.close()
|
sock.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Error on socket close', e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_backend_hosts(self):
|
def get_backend_hosts(self):
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -101,6 +100,7 @@ class PushbulletPlugin(Plugin):
|
||||||
kwargs['type'] = 'link' if url else 'note'
|
kwargs['type'] = 'link' if url else 'note'
|
||||||
|
|
||||||
if device:
|
if device:
|
||||||
|
# noinspection PyTypeChecker
|
||||||
kwargs['device_iden'] = device['iden']
|
kwargs['device_iden'] = device['iden']
|
||||||
|
|
||||||
resp = requests.post('https://api.pushbullet.com/v2/pushes',
|
resp = requests.post('https://api.pushbullet.com/v2/pushes',
|
||||||
|
@ -143,6 +143,7 @@ class PushbulletPlugin(Plugin):
|
||||||
raise Exception('Pushbullet file upload failed with status {}'.
|
raise Exception('Pushbullet file upload failed with status {}'.
|
||||||
format(resp.status_code))
|
format(resp.status_code))
|
||||||
|
|
||||||
|
# noinspection PyTypeChecker
|
||||||
resp = requests.post('https://api.pushbullet.com/v2/pushes',
|
resp = requests.post('https://api.pushbullet.com/v2/pushes',
|
||||||
headers={'Authorization': 'Bearer ' + self.token,
|
headers={'Authorization': 'Bearer ' + self.token,
|
||||||
'Content-Type': 'application/json'},
|
'Content-Type': 'application/json'},
|
||||||
|
|
|
@ -19,13 +19,12 @@ class RedisPlugin(Plugin):
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
|
||||||
if not kwargs:
|
if not kwargs:
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
redis_backend = get_backend('redis')
|
redis_backend = get_backend('redis')
|
||||||
if redis_backend and redis_backend.redis_args:
|
if redis_backend and redis_backend.redis_args:
|
||||||
self.kwargs = redis_backend.redis_args
|
self.kwargs = redis_backend.redis_args
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug(e)
|
||||||
|
|
||||||
def _get_redis(self):
|
def _get_redis(self):
|
||||||
return Redis(*self.args, **self.kwargs)
|
return Redis(*self.args, **self.kwargs)
|
||||||
|
|
|
@ -8,7 +8,6 @@ from platypush.plugins import action
|
||||||
from platypush.plugins.sensor import SensorPlugin
|
from platypush.plugins.sensor import SensorPlugin
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyBroadException
|
|
||||||
class SerialPlugin(SensorPlugin):
|
class SerialPlugin(SensorPlugin):
|
||||||
"""
|
"""
|
||||||
The serial plugin can read data from a serial device, as long as the serial
|
The serial plugin can read data from a serial device, as long as the serial
|
||||||
|
@ -129,7 +128,8 @@ class SerialPlugin(SensorPlugin):
|
||||||
if serial_available:
|
if serial_available:
|
||||||
try:
|
try:
|
||||||
ser = self._get_serial(device=device, baud_rate=baud_rate)
|
ser = self._get_serial(device=device, baud_rate=baud_rate)
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.debug(e)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
ser = self._get_serial(device=device, baud_rate=baud_rate, reset=True)
|
ser = self._get_serial(device=device, baud_rate=baud_rate, reset=True)
|
||||||
|
|
||||||
|
@ -137,16 +137,15 @@ class SerialPlugin(SensorPlugin):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
self.logger.warning('Invalid JSON message from {}: {}'.
|
self.logger.warning('Invalid JSON message from {}: {}'.format(self.device, data))
|
||||||
format(self.device, data))
|
|
||||||
else:
|
else:
|
||||||
data = self.last_measurement
|
data = self.last_measurement
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
self.serial_lock.release()
|
self.serial_lock.release()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug(e)
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
self.last_measurement = data
|
self.last_measurement = data
|
||||||
|
@ -194,7 +193,8 @@ class SerialPlugin(SensorPlugin):
|
||||||
if serial_available:
|
if serial_available:
|
||||||
try:
|
try:
|
||||||
ser = self._get_serial(device=device, baud_rate=baud_rate)
|
ser = self._get_serial(device=device, baud_rate=baud_rate)
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.debug(e)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
ser = self._get_serial(device=device, baud_rate=baud_rate, reset=True)
|
ser = self._get_serial(device=device, baud_rate=baud_rate, reset=True)
|
||||||
|
|
||||||
|
@ -216,12 +216,12 @@ class SerialPlugin(SensorPlugin):
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
self.serial_lock.release()
|
self.serial_lock.release()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug(e)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = data.decode()
|
data = data.decode()
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
data = base64.encodebytes(data)
|
data = base64.encodebytes(data)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -261,7 +261,8 @@ class SerialPlugin(SensorPlugin):
|
||||||
if serial_available:
|
if serial_available:
|
||||||
try:
|
try:
|
||||||
ser = self._get_serial(device=device, baud_rate=baud_rate)
|
ser = self._get_serial(device=device, baud_rate=baud_rate)
|
||||||
except:
|
except Exception as e:
|
||||||
|
self.logger.debug(e)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
ser = self._get_serial(device=device, baud_rate=baud_rate, reset=True)
|
ser = self._get_serial(device=device, baud_rate=baud_rate, reset=True)
|
||||||
|
|
||||||
|
@ -270,8 +271,8 @@ class SerialPlugin(SensorPlugin):
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
self.serial_lock.release()
|
self.serial_lock.release()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.debug(e)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -166,8 +166,7 @@ class SoundPlugin(Plugin):
|
||||||
|
|
||||||
is_raw_stream = streamtype == sd.RawOutputStream
|
is_raw_stream = streamtype == sd.RawOutputStream
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
def audio_callback(outdata, frames, *, status):
|
||||||
def audio_callback(outdata, frames, frame_time, status):
|
|
||||||
if self._get_playback_state(stream_index) == PlaybackState.STOPPED:
|
if self._get_playback_state(stream_index) == PlaybackState.STOPPED:
|
||||||
raise sd.CallbackStop
|
raise sd.CallbackStop
|
||||||
|
|
||||||
|
@ -181,7 +180,7 @@ class SoundPlugin(Plugin):
|
||||||
|
|
||||||
if status.output_underflow:
|
if status.output_underflow:
|
||||||
self.logger.warning('Output underflow: increase blocksize?')
|
self.logger.warning('Output underflow: increase blocksize?')
|
||||||
outdata = (b'\x00' if is_raw_stream else 0.) * len(outdata)
|
outdata[:] = (b'\x00' if is_raw_stream else 0.) * len(outdata)
|
||||||
return
|
return
|
||||||
|
|
||||||
if status:
|
if status:
|
||||||
|
@ -397,7 +396,6 @@ class SoundPlugin(Plugin):
|
||||||
finally:
|
finally:
|
||||||
if f and not f.closed:
|
if f and not f.closed:
|
||||||
f.close()
|
f.close()
|
||||||
f = None
|
|
||||||
|
|
||||||
self.stop_playback([stream_index])
|
self.stop_playback([stream_index])
|
||||||
|
|
||||||
|
|
|
@ -221,10 +221,9 @@ class SshPlugin(Plugin):
|
||||||
client = self._connect(**kwargs)
|
client = self._connect(**kwargs)
|
||||||
|
|
||||||
def decode(buf: bytes) -> str:
|
def decode(buf: bytes) -> str:
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
buf = buf.decode()
|
buf = buf.decode()
|
||||||
except:
|
except (ValueError, TypeError):
|
||||||
buf = base64.encodebytes(buf).decode()
|
buf = base64.encodebytes(buf).decode()
|
||||||
|
|
||||||
if buf.endswith('\n'):
|
if buf.endswith('\n'):
|
||||||
|
@ -341,11 +340,10 @@ class SshPlugin(Plugin):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if os.path.isdir(local_path):
|
if os.path.isdir(local_path):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
sftp.mkdir(remote_path)
|
sftp.mkdir(remote_path)
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning(f'mkdir {remote_path}: {e}')
|
||||||
|
|
||||||
assert recursive, '{} is a directory but recursive has been set to False'.format(local_path)
|
assert recursive, '{} is a directory but recursive has been set to False'.format(local_path)
|
||||||
assert self.is_directory(sftp, remote_path), \
|
assert self.is_directory(sftp, remote_path), \
|
||||||
|
@ -355,11 +353,10 @@ class SshPlugin(Plugin):
|
||||||
os.chdir(local_path)
|
os.chdir(local_path)
|
||||||
|
|
||||||
for path, folders, files in os.walk('.'):
|
for path, folders, files in os.walk('.'):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
sftp.mkdir(path)
|
sftp.mkdir(path)
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning(f'mkdir {remote_path}: {e}')
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
src = os.path.join(path, file)
|
src = os.path.join(path, file)
|
||||||
|
|
|
@ -47,12 +47,11 @@ def reverse_tunnel(server_port, remote_host, remote_port, transport, bind_addr='
|
||||||
should_run[key] = True
|
should_run[key] = True
|
||||||
|
|
||||||
while should_run.get(key):
|
while should_run.get(key):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
chan = transport.accept(1)
|
chan = transport.accept(1)
|
||||||
if chan is None:
|
assert chan is not None
|
||||||
raise AssertionError
|
except Exception as e:
|
||||||
except:
|
logger.warning(e)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
thr = threading.Thread(
|
thr = threading.Thread(
|
||||||
|
|
|
@ -128,7 +128,6 @@ class SwitchSwitchbotPlugin(SwitchPlugin, BluetoothBlePlugin):
|
||||||
compatible_devices = {}
|
compatible_devices = {}
|
||||||
|
|
||||||
for dev in devices:
|
for dev in devices:
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
characteristics = [
|
characteristics = [
|
||||||
chrc for chrc in self.discover_characteristics(
|
chrc for chrc in self.discover_characteristics(
|
||||||
|
@ -139,8 +138,8 @@ class SwitchSwitchbotPlugin(SwitchPlugin, BluetoothBlePlugin):
|
||||||
|
|
||||||
if characteristics:
|
if characteristics:
|
||||||
compatible_devices[dev['addr']] = None
|
compatible_devices[dev['addr']] = None
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.warning('Device scan error', e)
|
||||||
|
|
||||||
return BluetoothScanResponse(devices=compatible_devices)
|
return BluetoothScanResponse(devices=compatible_devices)
|
||||||
|
|
||||||
|
|
|
@ -83,11 +83,10 @@ class TensorflowPlugin(Plugin):
|
||||||
assert success, 'Unable to acquire the model lock'
|
assert success, 'Unable to acquire the model lock'
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
self._model_locks[model_name].release()
|
self._model_locks[model_name].release()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
self.logger.info(f'Model {model_name} lock release error: {e}')
|
||||||
|
|
||||||
def _load_model(self, model_name: str, reload: bool = False) -> Model:
|
def _load_model(self, model_name: str, reload: bool = False) -> Model:
|
||||||
if model_name in self.models and not reload:
|
if model_name in self.models and not reload:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import requests
|
import requests
|
||||||
from typing import Callable, Dict, Any, List, Optional
|
from typing import Callable, Dict, Any, List
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
import json
|
|
||||||
import urllib3
|
|
||||||
import urllib.request
|
|
||||||
import urllib.parse
|
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
|
||||||
from platypush.plugins.media import PlayerState
|
|
||||||
|
|
||||||
class VideoTorrentcastPlugin(Plugin):
|
|
||||||
def __init__(self, server='localhost', port=9090, *args, **kwargs):
|
|
||||||
self.server = server
|
|
||||||
self.port = port
|
|
||||||
self.state = PlayerState.STOP.value
|
|
||||||
|
|
||||||
@action
|
|
||||||
def play(self, url):
|
|
||||||
request = urllib.request.urlopen(
|
|
||||||
'http://{}:{}/play/'.format(self.server, self.port),
|
|
||||||
data=urllib.parse.urlencode({
|
|
||||||
'url': url
|
|
||||||
}).encode()
|
|
||||||
)
|
|
||||||
|
|
||||||
self.state = PlayerState.PLAY.value
|
|
||||||
return request.read()
|
|
||||||
|
|
||||||
@action
|
|
||||||
def pause(self):
|
|
||||||
http = urllib3.PoolManager()
|
|
||||||
request = http.request('POST',
|
|
||||||
'http://{}:{}/pause/'.format(self.server, self.port))
|
|
||||||
|
|
||||||
self.state = PlayerState.PAUSE.value
|
|
||||||
return request.read()
|
|
||||||
|
|
||||||
@action
|
|
||||||
def stop(self):
|
|
||||||
http = urllib3.PoolManager()
|
|
||||||
request = http.request('POST',
|
|
||||||
'http://{}:{}/stop/'.format(self.server, self.port))
|
|
||||||
|
|
||||||
self.state = PlayerState.STOP.value
|
|
||||||
return request.read()
|
|
||||||
|
|
||||||
@action
|
|
||||||
def search(self, query):
|
|
||||||
request = urllib.request.urlopen(urllib.request.Request(
|
|
||||||
'https://api.apidomain.info/list?' + urllib.parse.urlencode({
|
|
||||||
'sort': 'relevance',
|
|
||||||
'quality': '720p,1080p,3d',
|
|
||||||
'page': 1,
|
|
||||||
'keywords': query,
|
|
||||||
}),
|
|
||||||
headers = {
|
|
||||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 ' +
|
|
||||||
'(KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
results = json.loads(request.read())
|
|
||||||
return results
|
|
||||||
|
|
||||||
@action
|
|
||||||
def search_and_play(self, query):
|
|
||||||
response = self.search(query)
|
|
||||||
if not response.output['MovieList']:
|
|
||||||
self.logger.info('No torrent results found for {}'.format(query))
|
|
||||||
|
|
||||||
item = response.output['MovieList'][0]
|
|
||||||
magnet = item['items'][0]['torrent_magnet']
|
|
||||||
self.logger.info('Playing torrent "{}" from {}'
|
|
||||||
.format(item['title'], magnet))
|
|
||||||
|
|
||||||
return self.play(magnet)
|
|
||||||
|
|
||||||
@action
|
|
||||||
def voldown(self): raise NotImplementedError()
|
|
||||||
|
|
||||||
@action
|
|
||||||
def volup(self): raise NotImplementedError()
|
|
||||||
|
|
||||||
@action
|
|
||||||
def back(self): raise NotImplementedError()
|
|
||||||
|
|
||||||
@action
|
|
||||||
def forward(self): raise NotImplementedError()
|
|
||||||
|
|
||||||
@action
|
|
||||||
def status(self): return { 'state': self.state }
|
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
|
||||||
|
|
|
@ -3,8 +3,7 @@ import socket
|
||||||
import time
|
import time
|
||||||
from typing import List, Dict, Any, Optional, Union
|
from typing import List, Dict, Any, Optional, Union
|
||||||
|
|
||||||
import zeroconf
|
from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser, ServiceListener, ZeroconfServiceTypes
|
||||||
from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser
|
|
||||||
|
|
||||||
from platypush.context import get_bus
|
from platypush.context import get_bus
|
||||||
from platypush.message.event.zeroconf import ZeroconfServiceAddedEvent, ZeroconfServiceRemovedEvent, \
|
from platypush.message.event.zeroconf import ZeroconfServiceAddedEvent, ZeroconfServiceRemovedEvent, \
|
||||||
|
@ -12,7 +11,7 @@ from platypush.message.event.zeroconf import ZeroconfServiceAddedEvent, Zeroconf
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
class ZeroconfListener(zeroconf.ServiceListener):
|
class ZeroconfListener(ServiceListener):
|
||||||
def __init__(self, evt_queue: queue.Queue):
|
def __init__(self, evt_queue: queue.Queue):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.evt_queue = evt_queue
|
self.evt_queue = evt_queue
|
||||||
|
@ -79,7 +78,7 @@ class ZeroconfPlugin(Plugin):
|
||||||
:param timeout: Discovery timeout in seconds (default: 5).
|
:param timeout: Discovery timeout in seconds (default: 5).
|
||||||
:return: List of the services as strings.
|
:return: List of the services as strings.
|
||||||
"""
|
"""
|
||||||
return list(zeroconf.ZeroconfServiceTypes.find(timeout=timeout))
|
return list(ZeroconfServiceTypes.find(timeout=timeout))
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def discover_service(self, service: Union[str, list], timeout: Optional[int] = 5) -> Dict[str, Any]:
|
def discover_service(self, service: Union[str, list], timeout: Optional[int] = 5) -> Dict[str, Any]:
|
||||||
|
|
|
@ -125,9 +125,10 @@ class ZigbeeMqttPlugin(MqttPlugin, SwitchPlugin):
|
||||||
:param password: If the connection requires user authentication, specify the password (default: None)
|
:param password: If the connection requires user authentication, specify the password (default: None)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(host=host, port=port, tls_certfile=tls_certfile, tls_keyfile=tls_keyfile,
|
SwitchPlugin.__init__(self)
|
||||||
tls_version=tls_version, tls_ciphers=tls_ciphers, username=username,
|
MqttPlugin.__init__(self, host=host, port=port, tls_certfile=tls_certfile, tls_keyfile=tls_keyfile,
|
||||||
password=password, **kwargs)
|
tls_version=tls_version, tls_ciphers=tls_ciphers, username=username,
|
||||||
|
password=password, **kwargs)
|
||||||
|
|
||||||
self.base_topic = base_topic
|
self.base_topic = base_topic
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
|
@ -270,12 +270,12 @@ class ForProcedure(LoopProcedure):
|
||||||
self.iterable = iterable
|
self.iterable = iterable
|
||||||
|
|
||||||
def execute(self, _async=None, **context):
|
def execute(self, _async=None, **context):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
iterable = eval(self.iterable)
|
iterable = eval(self.iterable)
|
||||||
assert hasattr(iterable, '__iter__'), 'Object of type {} is not iterable: {}'.\
|
assert hasattr(iterable, '__iter__'), 'Object of type {} is not iterable: {}'.\
|
||||||
format(type(iterable), iterable)
|
format(type(iterable), iterable)
|
||||||
except:
|
except Exception as e:
|
||||||
|
logger.debug(f'Iterable {self.iterable} expansion error: {e}')
|
||||||
iterable = Request.expand_value_from_context(self.iterable, **context)
|
iterable = Request.expand_value_from_context(self.iterable, **context)
|
||||||
|
|
||||||
response = Response()
|
response = Response()
|
||||||
|
@ -334,10 +334,10 @@ class WhileProcedure(LoopProcedure):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_context(**context):
|
def _get_context(**context):
|
||||||
for (k, v) in context.items():
|
for (k, v) in context.items():
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
context[k] = eval(v)
|
context[k] = eval(v)
|
||||||
except:
|
except Exception as e:
|
||||||
|
logger.debug(f'Evaluation error for {v}: {e}')
|
||||||
if isinstance(v, str):
|
if isinstance(v, str):
|
||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
try:
|
try:
|
||||||
|
@ -356,8 +356,8 @@ class WhileProcedure(LoopProcedure):
|
||||||
for k, v in context.items():
|
for k, v in context.items():
|
||||||
try:
|
try:
|
||||||
exec('{}={}'.format(k, v))
|
exec('{}={}'.format(k, v))
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
logger.debug(f'Evaluation error: {k}={v}: {e}')
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
condition_true = eval(self.condition)
|
condition_true = eval(self.condition)
|
||||||
|
@ -386,8 +386,8 @@ class WhileProcedure(LoopProcedure):
|
||||||
for k, v in new_context.items():
|
for k, v in new_context.items():
|
||||||
try:
|
try:
|
||||||
exec('{}={}'.format(k, v))
|
exec('{}={}'.format(k, v))
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
logger.debug(f'Evaluation error: {k}={v}: {e}')
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -453,7 +453,8 @@ class IfProcedure(Procedure):
|
||||||
for (k, v) in context.items():
|
for (k, v) in context.items():
|
||||||
try:
|
try:
|
||||||
exec('{}={}'.format(k, v))
|
exec('{}={}'.format(k, v))
|
||||||
except:
|
except Exception as e:
|
||||||
|
logger.debug(f'Evaluation error: {k}={v}: {e}')
|
||||||
if isinstance(v, str):
|
if isinstance(v, str):
|
||||||
try:
|
try:
|
||||||
exec('{}="{}"'.format(k, re.sub(r'(^|[^\\])"', '\1\\"', v)))
|
exec('{}="{}"'.format(k, re.sub(r'(^|[^\\])"', '\1\\"', v)))
|
||||||
|
|
|
@ -169,14 +169,6 @@ class UserManager:
|
||||||
"""
|
"""
|
||||||
session = self._get_db_session()
|
session = self._get_db_session()
|
||||||
return session.query(User).join(UserSession).filter_by(session_token=session_token).first()
|
return session.query(User).join(UserSession).filter_by(session_token=session_token).first()
|
||||||
if not user:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return {
|
|
||||||
'user_id': user.user_id,
|
|
||||||
'username': user.username,
|
|
||||||
'created_at': user.created_at,
|
|
||||||
}
|
|
||||||
|
|
||||||
def generate_jwt_token(self, username: str, password: str, expires_at: Optional[datetime.datetime] = None) -> str:
|
def generate_jwt_token(self, username: str, password: str, expires_at: Optional[datetime.datetime] = None) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue