forked from platypush/platypush
Refactored backends to be more robust by wrapping the core logic into a try-except logic with sleep and retry
This commit is contained in:
parent
0a7722d858
commit
6ce348365f
25 changed files with 265 additions and 259 deletions
|
@ -7,6 +7,7 @@ import importlib
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ class Backend(Thread):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_default_response_timeout = 5
|
_default_response_timeout = 5
|
||||||
|
_backend_reload_timeout = 10
|
||||||
|
|
||||||
def __init__(self, bus=None, **kwargs):
|
def __init__(self, bus=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -211,9 +213,38 @@ class Backend(Thread):
|
||||||
redis.send_message(msg, queue_name=queue_name)
|
redis.send_message(msg, queue_name=queue_name)
|
||||||
|
|
||||||
|
|
||||||
|
def exec(self):
|
||||||
|
""" Backend thread logic. To be implemented in the derived classes """
|
||||||
|
raise RuntimeError('Backend class {} does not implement the exec() method'.
|
||||||
|
format(self.__class__.__name__))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
""" Starts the backend thread. To be implemented in the derived classes """
|
""" Thread runner. It wraps the exec method in a retry block """
|
||||||
|
|
||||||
|
super().run()
|
||||||
self.thread_id = threading.get_ident()
|
self.thread_id = threading.get_ident()
|
||||||
|
error = None
|
||||||
|
|
||||||
|
while not self.should_stop():
|
||||||
|
try:
|
||||||
|
self.exec()
|
||||||
|
except Exception as e:
|
||||||
|
error = e
|
||||||
|
|
||||||
|
if not self.should_stop():
|
||||||
|
if error:
|
||||||
|
self.logger.error(('Backend {} terminated with an exception, ' +
|
||||||
|
'reloading in {} seconds').format(
|
||||||
|
self.__class__.__name__,
|
||||||
|
self._backend_reload_timeout))
|
||||||
|
self.logger.exception(error)
|
||||||
|
else:
|
||||||
|
self.logger.warning(('Backend {} unexpectedly terminated, ' +
|
||||||
|
'reloading in {} seconds').format(
|
||||||
|
self.__class__.__name__,
|
||||||
|
self._backend_reload_timeout))
|
||||||
|
|
||||||
|
time.sleep(self._backend_reload_timeout)
|
||||||
|
|
||||||
def on_stop(self):
|
def on_stop(self):
|
||||||
""" Callback invoked when the process stops """
|
""" Callback invoked when the process stops """
|
||||||
|
|
|
@ -110,8 +110,7 @@ class AssistantGoogleBackend(Backend):
|
||||||
self.assistant.stop_conversation()
|
self.assistant.stop_conversation()
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
with Assistant(self.credentials, self.device_model_id) as assistant:
|
with Assistant(self.credentials, self.device_model_id) as assistant:
|
||||||
self.assistant = assistant
|
self.assistant = assistant
|
||||||
|
|
|
@ -176,9 +176,8 @@ class AssistantGooglePushtotalkBackend(Backend):
|
||||||
""" Speech recognized handler """
|
""" Speech recognized handler """
|
||||||
self.bus.post(SpeechRecognizedEvent(phrase=speech))
|
self.bus.post(SpeechRecognizedEvent(phrase=speech))
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
""" Backend executor """
|
""" Backend executor """
|
||||||
super().run()
|
|
||||||
|
|
||||||
with SampleAssistant(self.lang, self.device_model_id, self.device_id,
|
with SampleAssistant(self.lang, self.device_model_id, self.device_id,
|
||||||
self.conversation_stream,
|
self.conversation_stream,
|
||||||
|
|
|
@ -72,8 +72,7 @@ class AssistantSnowboyBackend(Backend):
|
||||||
self.bus.post(HotwordDetectedEvent(hotword=self.hotword))
|
self.bus.post(HotwordDetectedEvent(hotword=self.hotword))
|
||||||
return callback
|
return callback
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
self.detector.start(self.hotword_detected())
|
self.detector.start(self.hotword_detected())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -112,8 +112,7 @@ class ButtonFlicBackend(Backend):
|
||||||
|
|
||||||
return _f
|
return _f
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
self.client.handle_events()
|
self.client.handle_events()
|
||||||
|
|
||||||
|
|
|
@ -36,12 +36,12 @@ 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):
|
||||||
|
@ -77,22 +77,22 @@ class ScanWizardResult(Enum):
|
||||||
|
|
||||||
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 +101,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
|
||||||
|
@ -115,31 +115,31 @@ class ScanWizard:
|
||||||
|
|
||||||
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,21 +147,21 @@ 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})
|
||||||
|
@ -169,39 +169,39 @@ class ButtonConnectionChannel:
|
||||||
@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"),
|
||||||
|
@ -225,7 +225,7 @@ class FlicClient(asyncio.Protocol):
|
||||||
]
|
]
|
||||||
_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 == 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 == None else namedtuple(x[0], x[2]), _EVENTS))
|
||||||
|
|
||||||
_COMMANDS = [
|
_COMMANDS = [
|
||||||
("CmdGetInfo", "", ""),
|
("CmdGetInfo", "", ""),
|
||||||
("CmdCreateScanner", "<I", "scan_id"),
|
("CmdCreateScanner", "<I", "scan_id"),
|
||||||
|
@ -239,18 +239,18 @@ 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))
|
||||||
|
|
||||||
|
|
||||||
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)))
|
||||||
|
|
||||||
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""
|
||||||
|
@ -260,147 +260,147 @@ class FlicClient(asyncio.Protocol):
|
||||||
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(items["bd_addr"])
|
||||||
|
|
||||||
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 +409,83 @@ 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] == 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(items["bd_addr"])
|
||||||
|
|
||||||
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"])
|
||||||
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(data[1 + pos : 1 + pos + 6]))
|
||||||
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"])
|
||||||
|
@ -498,45 +498,45 @@ class FlicClient(asyncio.Protocol):
|
||||||
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""
|
||||||
|
@ -550,5 +550,5 @@ class FlicClient(asyncio.Protocol):
|
||||||
if len(cdata):
|
if len(cdata):
|
||||||
self.buffer=cdata #unlikely to happen but.....
|
self.buffer=cdata #unlikely to happen but.....
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,12 +40,12 @@ 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):
|
||||||
|
@ -81,22 +81,22 @@ class ScanWizardResult(Enum):
|
||||||
|
|
||||||
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: ...
|
||||||
|
@ -105,9 +105,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
|
||||||
|
@ -119,31 +119,31 @@ class ScanWizard:
|
||||||
|
|
||||||
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
|
||||||
|
@ -151,36 +151,36 @@ 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
|
||||||
|
|
||||||
with self._client._lock:
|
with self._client._lock:
|
||||||
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
|
||||||
|
|
||||||
with self._client._lock:
|
with self._client._lock:
|
||||||
self._auto_disconnect_time = auto_disconnect_time
|
self._auto_disconnect_time = auto_disconnect_time
|
||||||
if not self._client._closed:
|
if not self._client._closed:
|
||||||
|
@ -188,26 +188,26 @@ class ButtonConnectionChannel:
|
||||||
|
|
||||||
class FlicClient:
|
class FlicClient:
|
||||||
"""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"),
|
||||||
|
@ -231,7 +231,7 @@ class FlicClient:
|
||||||
]
|
]
|
||||||
_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 == 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 == None else namedtuple(x[0], x[2]), _EVENTS))
|
||||||
|
|
||||||
_COMMANDS = [
|
_COMMANDS = [
|
||||||
("CmdGetInfo", "", ""),
|
("CmdGetInfo", "", ""),
|
||||||
("CmdCreateScanner", "<I", "scan_id"),
|
("CmdCreateScanner", "<I", "scan_id"),
|
||||||
|
@ -245,17 +245,17 @@ class FlicClient:
|
||||||
("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))
|
||||||
|
|
||||||
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)))
|
||||||
|
|
||||||
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, host, port = 5551):
|
def __init__(self, host, port = 5551):
|
||||||
self._sock = socket.create_connection((host, port), None)
|
self._sock = socket.create_connection((host, port), None)
|
||||||
self._lock = threading.RLock()
|
self._lock = threading.RLock()
|
||||||
|
@ -267,113 +267,113 @@ class FlicClient:
|
||||||
self._timers = queue.PriorityQueue()
|
self._timers = queue.PriorityQueue()
|
||||||
self._handle_event_thread_ident = None
|
self._handle_event_thread_ident = None
|
||||||
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
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Closes the client. The handle_events() method will return."""
|
"""Closes the client. The handle_events() method will return."""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if self._closed:
|
if self._closed:
|
||||||
return
|
return
|
||||||
|
|
||||||
if threading.get_ident() != self._handle_event_thread_ident:
|
if threading.get_ident() != self._handle_event_thread_ident:
|
||||||
self._send_command("CmdPing", {"ping_id": 0}) # To unblock socket select
|
self._send_command("CmdPing", {"ping_id": 0}) # To unblock socket select
|
||||||
|
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
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".
|
||||||
"""
|
"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
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, callback):
|
def get_info(self, callback):
|
||||||
"""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,
|
||||||
|
@ -381,47 +381,47 @@ class FlicClient:
|
||||||
"""
|
"""
|
||||||
self._get_info_response_queue.put(callback)
|
self._get_info_response_queue.put(callback)
|
||||||
self._send_command("CmdGetInfo", {})
|
self._send_command("CmdGetInfo", {})
|
||||||
|
|
||||||
def get_button_uuid(self, bd_addr, callback):
|
def get_button_uuid(self, bd_addr, callback):
|
||||||
"""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.
|
||||||
"""
|
"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._get_button_uuid_queue.put(callback)
|
self._get_button_uuid_queue.put(callback)
|
||||||
self._send_command("CmdGetButtonUUID", {"bd_addr": bd_addr})
|
self._send_command("CmdGetButtonUUID", {"bd_addr": bd_addr})
|
||||||
|
|
||||||
def set_timer(self, timeout_millis, callback):
|
def set_timer(self, timeout_millis, callback):
|
||||||
"""Set a timer
|
"""Set a timer
|
||||||
|
|
||||||
This timer callback will run after the specified timeout_millis on the thread that handles the events.
|
This timer callback will run after the specified timeout_millis on the thread that handles the events.
|
||||||
"""
|
"""
|
||||||
point_in_time = time.monotonic() + timeout_millis / 1000.0
|
point_in_time = time.monotonic() + timeout_millis / 1000.0
|
||||||
self._timers.put((point_in_time, callback))
|
self._timers.put((point_in_time, callback))
|
||||||
|
|
||||||
if threading.get_ident() != self._handle_event_thread_ident:
|
if threading.get_ident() != self._handle_event_thread_ident:
|
||||||
self._send_command("CmdPing", {"ping_id": 0}) # To unblock socket select
|
self._send_command("CmdPing", {"ping_id": 0}) # To unblock socket select
|
||||||
|
|
||||||
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(items["bd_addr"])
|
||||||
|
|
||||||
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)
|
||||||
|
@ -432,83 +432,83 @@ class FlicClient:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if not self._closed:
|
if not self._closed:
|
||||||
self._sock.sendall(bytes)
|
self._sock.sendall(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] == 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(items["bd_addr"])
|
||||||
|
|
||||||
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"])
|
||||||
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(data[1 + pos : 1 + pos + 6]))
|
||||||
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"])
|
||||||
|
@ -521,44 +521,44 @@ class FlicClient:
|
||||||
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._get_info_response_queue.get()(items)
|
self._get_info_response_queue.get()(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._get_button_uuid_queue.get()(items["bd_addr"], items["uuid"])
|
self._get_button_uuid_queue.get()(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 _handle_one_event(self):
|
def _handle_one_event(self):
|
||||||
if len(self._timers.queue) > 0:
|
if len(self._timers.queue) > 0:
|
||||||
current_timer = self._timers.queue[0]
|
current_timer = self._timers.queue[0]
|
||||||
|
@ -568,10 +568,10 @@ class FlicClient:
|
||||||
return True
|
return True
|
||||||
if len(select.select([self._sock], [], [], timeout)[0]) == 0:
|
if len(select.select([self._sock], [], [], timeout)[0]) == 0:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
len_arr = bytearray(2)
|
len_arr = bytearray(2)
|
||||||
view = memoryview(len_arr)
|
view = memoryview(len_arr)
|
||||||
|
|
||||||
toread = 2
|
toread = 2
|
||||||
while toread > 0:
|
while toread > 0:
|
||||||
nbytes = self._sock.recv_into(view, toread)
|
nbytes = self._sock.recv_into(view, toread)
|
||||||
|
@ -579,7 +579,7 @@ class FlicClient:
|
||||||
return False
|
return False
|
||||||
view = view[nbytes:]
|
view = view[nbytes:]
|
||||||
toread -= nbytes
|
toread -= nbytes
|
||||||
|
|
||||||
packet_len = len_arr[0] | (len_arr[1] << 8)
|
packet_len = len_arr[0] | (len_arr[1] << 8)
|
||||||
data = bytearray(packet_len)
|
data = bytearray(packet_len)
|
||||||
view = memoryview(data)
|
view = memoryview(data)
|
||||||
|
@ -590,13 +590,13 @@ class FlicClient:
|
||||||
return False
|
return False
|
||||||
view = view[nbytes:]
|
view = view[nbytes:]
|
||||||
toread -= nbytes
|
toread -= nbytes
|
||||||
|
|
||||||
self._dispatch_event(data)
|
self._dispatch_event(data)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def handle_events(self):
|
def handle_events(self):
|
||||||
"""Start the main loop for this client.
|
"""Start the main loop for this client.
|
||||||
|
|
||||||
This method will not return until the socket has been closed.
|
This method will not return until the socket has been closed.
|
||||||
Once it has returned, any use of this FlicClient is illegal.
|
Once it has returned, any use of this FlicClient is illegal.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -160,8 +160,7 @@ class CameraPiBackend(Backend):
|
||||||
self.logger.warning('Failed to stop recording')
|
self.logger.warning('Failed to stop recording')
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
if not self.redis:
|
if not self.redis:
|
||||||
self.redis = get_backend('redis')
|
self.redis = get_backend('redis')
|
||||||
|
|
|
@ -377,8 +377,7 @@ class HttpBackend(Backend):
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
os.putenv('FLASK_APP', 'platypush')
|
os.putenv('FLASK_APP', 'platypush')
|
||||||
os.putenv('FLASK_ENV', 'production')
|
os.putenv('FLASK_ENV', 'production')
|
||||||
self.logger.info('Initialized HTTP backend on port {}'.format(self.port))
|
self.logger.info('Initialized HTTP backend on port {}'.format(self.port))
|
||||||
|
|
|
@ -72,8 +72,7 @@ class HttpPollBackend(Backend):
|
||||||
self.requests.append(request)
|
self.requests.append(request)
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
while not self.should_stop():
|
while not self.should_stop():
|
||||||
for request in self.requests:
|
for request in self.requests:
|
||||||
|
|
|
@ -48,8 +48,7 @@ class InotifyBackend(Backend):
|
||||||
|
|
||||||
self.inotify_watch = None
|
self.inotify_watch = None
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
self.inotify_watch = inotify.adapters.Inotify()
|
self.inotify_watch = inotify.adapters.Inotify()
|
||||||
for path in self.watch_paths:
|
for path in self.watch_paths:
|
||||||
|
|
|
@ -29,8 +29,7 @@ class JoystickBackend(Backend):
|
||||||
|
|
||||||
self.device = device
|
self.device = device
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
self.logger.info('Initialized joystick backend on device {}'.format(self.device))
|
self.logger.info('Initialized joystick backend on device {}'.format(self.device))
|
||||||
|
|
||||||
while not self.should_stop():
|
while not self.should_stop():
|
||||||
|
|
|
@ -81,8 +81,7 @@ class KafkaBackend(Backend):
|
||||||
self.logger.warning('Exception occurred while closing Kafka connection')
|
self.logger.warning('Exception occurred while closing Kafka connection')
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
self.consumer = KafkaConsumer(self.topic, bootstrap_servers=self.server)
|
self.consumer = KafkaConsumer(self.topic, bootstrap_servers=self.server)
|
||||||
self.logger.info('Initialized kafka backend - server: {}, topic: {}'
|
self.logger.info('Initialized kafka backend - server: {}, topic: {}'
|
||||||
|
|
|
@ -61,8 +61,7 @@ class LocalBackend(Backend):
|
||||||
return Message.build(msg) if len(msg) else None
|
return Message.build(msg) if len(msg) else None
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
self.logger.info('Initialized local backend on {} and {}'.
|
self.logger.info('Initialized local backend on {} and {}'.
|
||||||
format(self.request_fifo, self.response_fifo))
|
format(self.request_fifo, self.response_fifo))
|
||||||
|
|
||||||
|
|
|
@ -102,8 +102,7 @@ class MidiBackend(Backend):
|
||||||
return callback
|
return callback
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
self.midi.open_port(self.port_number)
|
self.midi.open_port(self.port_number)
|
||||||
self.logger.info('Initialized MIDI backend, listening for events on device {}'.
|
self.logger.info('Initialized MIDI backend, listening for events on device {}'.
|
||||||
|
|
|
@ -45,7 +45,7 @@ class MqttBackend(Backend):
|
||||||
publisher.single(self.topic, str(msg), hostname=self.host, port=self.port)
|
publisher.single(self.topic, str(msg), hostname=self.host, port=self.port)
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
def on_connect(client, userdata, flags, rc):
|
def on_connect(client, userdata, flags, rc):
|
||||||
client.subscribe(self.topic)
|
client.subscribe(self.topic)
|
||||||
|
|
||||||
|
@ -57,7 +57,6 @@ class MqttBackend(Backend):
|
||||||
self.logger.info('Received message on the MQTT backend: {}'.format(msg))
|
self.logger.info('Received message on the MQTT backend: {}'.format(msg))
|
||||||
self.on_message(msg)
|
self.on_message(msg)
|
||||||
|
|
||||||
super().run()
|
|
||||||
client = mqtt.Client()
|
client = mqtt.Client()
|
||||||
client.on_connect = on_connect
|
client.on_connect = on_connect
|
||||||
client.on_message = on_message
|
client.on_message = on_message
|
||||||
|
|
|
@ -39,8 +39,7 @@ class MusicMpdBackend(Backend):
|
||||||
self.poll_seconds = poll_seconds
|
self.poll_seconds = poll_seconds
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
last_status = {}
|
last_status = {}
|
||||||
last_state = None
|
last_state = None
|
||||||
|
|
|
@ -175,8 +175,7 @@ class PushbulletBackend(Backend):
|
||||||
def on_stop(self):
|
def on_stop(self):
|
||||||
self.ws.close()
|
self.ws.close()
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
self._init_socket()
|
self._init_socket()
|
||||||
self.logger.info('Initialized Pushbullet backend - device_id: {}'
|
self.logger.info('Initialized Pushbullet backend - device_id: {}'
|
||||||
|
|
|
@ -60,8 +60,7 @@ class RedisBackend(Backend):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
self.logger.info('Initialized Redis backend on queue {} with arguments {}'.
|
self.logger.info('Initialized Redis backend on queue {} with arguments {}'.
|
||||||
format(self.queue, self.redis_args))
|
format(self.queue, self.redis_args))
|
||||||
|
|
|
@ -50,8 +50,7 @@ class ScardBackend(Backend):
|
||||||
self.cardtype = AnyCardType()
|
self.cardtype = AnyCardType()
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
self.logger.info('Initialized smart card reader backend - ATR filter: {}'.
|
self.logger.info('Initialized smart card reader backend - ATR filter: {}'.
|
||||||
format(self.ATRs))
|
format(self.ATRs))
|
||||||
|
|
|
@ -40,8 +40,7 @@ class SensorBackend(Backend):
|
||||||
""" To be implemented in the derived classes """
|
""" To be implemented in the derived classes """
|
||||||
raise NotImplementedError('To be implemented in a derived class')
|
raise NotImplementedError('To be implemented in a derived class')
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
self.logger.info('Initialized {} sensor backend'.format(self.__class__.__name__))
|
self.logger.info('Initialized {} sensor backend'.format(self.__class__.__name__))
|
||||||
|
|
||||||
while not self.should_stop():
|
while not self.should_stop():
|
||||||
|
|
|
@ -32,9 +32,7 @@ class SensorIrZeroborgBackend(Backend):
|
||||||
self.logger.info('Initialized Zeroborg infrared sensor backend')
|
self.logger.info('Initialized Zeroborg infrared sensor backend')
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self.zb.GetIrMessage()
|
self.zb.GetIrMessage()
|
||||||
|
|
|
@ -61,9 +61,7 @@ class SensorLeapBackend(Backend):
|
||||||
self.position_tolerance = position_tolerance
|
self.position_tolerance = position_tolerance
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
listener = LeapListener(position_ranges=self.position_ranges,
|
listener = LeapListener(position_ranges=self.position_ranges,
|
||||||
position_tolerance=self.position_tolerance)
|
position_tolerance=self.position_tolerance)
|
||||||
|
|
||||||
|
|
|
@ -90,9 +90,7 @@ class TcpBackend(Backend):
|
||||||
|
|
||||||
threading.Thread(target=_f_wrapper).run()
|
threading.Thread(target=_f_wrapper).run()
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
|
|
||||||
serv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
serv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
serv_sock.bind((self.bind_address, self.port))
|
serv_sock.bind((self.bind_address, self.port))
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,7 @@ class WeatherForecastBackend(Backend):
|
||||||
def send_message(self, msg):
|
def send_message(self, msg):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def run(self):
|
def exec(self):
|
||||||
super().run()
|
|
||||||
weather = get_plugin('weather.forecast')
|
weather = get_plugin('weather.forecast')
|
||||||
self.logger.info('Initialized weather forecast backend')
|
self.logger.info('Initialized weather forecast backend')
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue