Always define msg so the function doesn't fail on exception

Reverted the previous retry logic for backend - it didn't really work

This reverts commit 4e0e4863a0.
This reverts commit 964c7b5cf0.
This reverts commit 6ce348365f.
This commit is contained in:
Fabio Manganiello 2018-10-25 20:45:58 +02:00
parent 4e0e4863a0
commit 284e0638f8
28 changed files with 267 additions and 273 deletions

View File

@ -7,7 +7,6 @@ import importlib
import logging import logging
import sys import sys
import threading import threading
import time
from threading import Thread from threading import Thread
@ -35,7 +34,6 @@ 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):
""" """
@ -213,38 +211,9 @@ 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):
""" Thread runner. It wraps the exec method in a retry block """ """ Starts the backend thread. To be implemented in the derived classes """
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 """

View File

@ -110,7 +110,8 @@ class AssistantGoogleBackend(Backend):
self.assistant.stop_conversation() self.assistant.stop_conversation()
def exec(self): def run(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

View File

@ -176,8 +176,9 @@ class AssistantGooglePushtotalkBackend(Backend):
""" Speech recognized handler """ """ Speech recognized handler """
self.bus.post(SpeechRecognizedEvent(phrase=speech)) self.bus.post(SpeechRecognizedEvent(phrase=speech))
def exec(self): def run(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,

View File

@ -72,7 +72,8 @@ class AssistantSnowboyBackend(Backend):
self.bus.post(HotwordDetectedEvent(hotword=self.hotword)) self.bus.post(HotwordDetectedEvent(hotword=self.hotword))
return callback return callback
def exec(self): def run(self):
super().run()
self.detector.start(self.hotword_detected()) self.detector.start(self.hotword_detected())

View File

@ -112,7 +112,8 @@ class ButtonFlicBackend(Backend):
return _f return _f
def exec(self): def run(self):
super().run()
self.client.handle_events() self.client.handle_events()

View File

@ -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

View File

@ -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.
""" """

View File

@ -160,7 +160,8 @@ 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 exec(self): def run(self):
super().run()
if not self.redis: if not self.redis:
self.redis = get_backend('redis') self.redis = get_backend('redis')

View File

@ -377,7 +377,8 @@ class HttpBackend(Backend):
loop.run_forever() loop.run_forever()
def exec(self): def run(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))

View File

@ -72,7 +72,8 @@ class HttpPollBackend(Backend):
self.requests.append(request) self.requests.append(request)
def exec(self): def run(self):
super().run()
while not self.should_stop(): while not self.should_stop():
for request in self.requests: for request in self.requests:

View File

@ -48,7 +48,8 @@ class InotifyBackend(Backend):
self.inotify_watch = None self.inotify_watch = None
def exec(self): def run(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:

View File

@ -29,7 +29,8 @@ class JoystickBackend(Backend):
self.device = device self.device = device
def exec(self): def run(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():

View File

@ -81,7 +81,8 @@ 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 exec(self): def run(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: {}'

View File

@ -61,7 +61,8 @@ class LocalBackend(Backend):
return Message.build(msg) if len(msg) else None return Message.build(msg) if len(msg) else None
def exec(self): def run(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))

View File

@ -102,7 +102,8 @@ class MidiBackend(Backend):
return callback return callback
def exec(self): def run(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 {}'.

View File

@ -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 exec(self): def run(self):
def on_connect(client, userdata, flags, rc): def on_connect(client, userdata, flags, rc):
client.subscribe(self.topic) client.subscribe(self.topic)
@ -57,6 +57,7 @@ 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

View File

@ -39,7 +39,8 @@ class MusicMpdBackend(Backend):
self.poll_seconds = poll_seconds self.poll_seconds = poll_seconds
def exec(self): def run(self):
super().run()
last_status = {} last_status = {}
last_state = None last_state = None

View File

@ -175,7 +175,8 @@ class PushbulletBackend(Backend):
def on_stop(self): def on_stop(self):
self.ws.close() self.ws.close()
def exec(self): def run(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: {}'

View File

@ -31,21 +31,19 @@ class RedisBackend(Backend):
self.queue = queue self.queue = queue
self.redis_args = redis_args self.redis_args = redis_args
self.redis = None self.redis = Redis(**self.redis_args)
def _get_redis(self):
return Redis(**self.redis_args)
def send_message(self, msg, queue_name=None): def send_message(self, msg, queue_name=None):
if queue_name: if queue_name:
self._get_redis().rpush(queue_name, msg) self.redis.rpush(queue_name, msg)
else: else:
self._get_redis().rpush(self.queue, msg) self.redis.rpush(self.queue, msg)
def get_message(self, queue_name=None): def get_message(self, queue_name=None):
queue = queue_name or self.queue queue = queue_name or self.queue
msg = self._get_redis().blpop(queue)[1].decode('utf-8') msg = self.redis.blpop(queue)[1].decode('utf-8')
try: try:
msg = Message.build(json.loads(msg)) msg = Message.build(json.loads(msg))
@ -62,7 +60,8 @@ class RedisBackend(Backend):
return msg return msg
def exec(self): def run(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))

View File

@ -50,7 +50,8 @@ class ScardBackend(Backend):
self.cardtype = AnyCardType() self.cardtype = AnyCardType()
def exec(self): def run(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))

View File

@ -40,7 +40,8 @@ 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 exec(self): def run(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():

View File

@ -32,7 +32,9 @@ class SensorIrZeroborgBackend(Backend):
self.logger.info('Initialized Zeroborg infrared sensor backend') self.logger.info('Initialized Zeroborg infrared sensor backend')
def exec(self): def run(self):
super().run()
while True: while True:
try: try:
self.zb.GetIrMessage() self.zb.GetIrMessage()

View File

@ -61,7 +61,9 @@ class SensorLeapBackend(Backend):
self.position_tolerance = position_tolerance self.position_tolerance = position_tolerance
def exec(self): def run(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)

View File

@ -90,7 +90,9 @@ class TcpBackend(Backend):
threading.Thread(target=_f_wrapper).run() threading.Thread(target=_f_wrapper).run()
def exec(self): def run(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))

View File

@ -27,7 +27,8 @@ class WeatherForecastBackend(Backend):
def send_message(self, msg): def send_message(self, msg):
pass pass
def exec(self): def run(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')

View File

@ -24,6 +24,8 @@ class RedisBus(Bus):
def get(self): def get(self):
""" Reads one message from the Redis queue """ """ Reads one message from the Redis queue """
msg = None
try: try:
msg = self.redis.blpop(self.redis_queue) msg = self.redis.blpop(self.redis_queue)
if msg and msg[1]: if msg and msg[1]:

View File

@ -75,7 +75,7 @@ class AdafruitIoPlugin(Plugin):
def _get_redis(self): def _get_redis(self):
from redis import Redis from redis import Redis
redis_args = get_backend('redis').redis_args.copy() redis_args = get_backend('redis').redis_args
redis_args['socket_timeout'] = 1 redis_args['socket_timeout'] = 1
return Redis(**redis_args) return Redis(**redis_args)

View File

@ -706,7 +706,7 @@ class LightHuePlugin(LightPlugin):
self.animation_thread = None self.animation_thread = None
self.redis = None self.redis = None
redis_args = get_backend('redis').redis_args.copy() redis_args = get_backend('redis').redis_args
redis_args['socket_timeout'] = transition_seconds redis_args['socket_timeout'] = transition_seconds
self.redis = Redis(**redis_args) self.redis = Redis(**redis_args)