Browse Source

Start scan in separate thread (to not disconnect connected devices), fix mac address if entered in incorrect format. (#24)

RenierM26 2 years ago
parent
commit
a072d9c5cd
2 changed files with 45 additions and 15 deletions
  1. 1 1
      setup.py
  2. 44 14
      switchbot/__init__.py

+ 1 - 1
setup.py

@@ -4,7 +4,7 @@ setup(
     name = 'PySwitchbot',
     packages = ['switchbot'],
     install_requires=['bluepy'],
-    version = '0.11.0',
+    version = '0.11.1',
     description = 'A library to communicate with Switchbot',
     author='Daniel Hjelseth Hoyer',
     url='https://github.com/Danielhiversen/pySwitchbot/',

+ 44 - 14
switchbot/__init__.py

@@ -3,7 +3,7 @@ from __future__ import annotations
 
 import binascii
 import logging
-import threading
+from multiprocessing import Manager, Process
 import time
 
 import bluepy
@@ -31,7 +31,6 @@ OFF_KEY_SUFFIX = "02"
 PRESS_KEY_SUFFIX = "00"
 
 _LOGGER = logging.getLogger(__name__)
-CONNECT_LOCK = threading.Lock()
 
 
 def _process_wohand(data) -> dict:
@@ -93,6 +92,34 @@ def _process_wosensorth(data) -> dict:
     return _wosensorth_data
 
 
+class BLEScanner:
+    """Helper for bluepy device scanning."""
+
+    def __init__(self, scan_timeout=DEFAULT_SCAN_TIMEOUT, interface=None) -> None:
+        """Init class constructor."""
+        self._scan_timeout = scan_timeout
+        self._interface = interface
+        self._devices = None
+
+    def start(self) -> dict | None:
+        """Start scan in seperate process."""
+        with Manager() as manager:
+            _devices = manager.dict()
+            process = Process(target=self._scan, args=(_devices,))
+            process.start()
+            process.join()
+
+            return _devices.get(0, None)
+
+    def _scan(self, devices) -> dict | None:
+        """Scan for advertisement data."""
+        try:
+            devices[0] = bluepy.btle.Scanner(self._interface).scan(self._scan_timeout)
+
+        except bluepy.btle.BTLEDisconnectError:
+            pass
+
+
 class GetSwitchbotDevices:
     """Scan for all Switchbot devices and return by type."""
 
@@ -109,7 +136,9 @@ class GetSwitchbotDevices:
         devices = None
 
         try:
-            devices = bluepy.btle.Scanner(self._interface).scan(scan_timeout)
+            devices = BLEScanner(
+                scan_timeout=scan_timeout, interface=self._interface
+            ).start()
 
         except bluepy.btle.BTLEManagementError:
             _LOGGER.error("Error scanning for switchbot devices", exc_info=True)
@@ -199,7 +228,7 @@ class GetSwitchbotDevices:
         _switchbot_data = {}
 
         for item in self._all_services_data:
-            if self._all_services_data[item]["mac_address"] == mac:
+            if self._all_services_data[item]["mac_address"] == mac.replace("-", ":").lower():
                 _switchbot_data = self._all_services_data[item]
 
         return _switchbot_data
@@ -211,7 +240,7 @@ class SwitchbotDevice:
     def __init__(self, mac, password=None, interface=None, **kwargs) -> None:
         """Switchbot base class constructor."""
         self._interface = interface
-        self._mac = mac
+        self._mac = mac.replace("-", ":").lower()
         self._device = None
         self._switchbot_device_data = {}
         self._scan_timeout = kwargs.pop("scan_timeout", DEFAULT_SCAN_TIMEOUT)
@@ -277,14 +306,13 @@ class SwitchbotDevice:
         send_success = False
         command = self._commandkey(key)
         _LOGGER.debug("Sending command to switchbot %s", command)
-        with CONNECT_LOCK:
-            try:
-                self._connect()
-                send_success = self._writekey(command)
-            except bluepy.btle.BTLEException:
-                _LOGGER.warning("Error talking to Switchbot", exc_info=True)
-            finally:
-                self._disconnect()
+        try:
+            self._connect()
+            send_success = self._writekey(command)
+        except bluepy.btle.BTLEException:
+            _LOGGER.warning("Error talking to Switchbot", exc_info=True)
+        finally:
+            self._disconnect()
         if send_success:
             return True
         if retry < 1:
@@ -316,7 +344,9 @@ class SwitchbotDevice:
         devices = None
 
         try:
-            devices = bluepy.btle.Scanner(_interface).scan(self._scan_timeout)
+            devices = BLEScanner(
+                scan_timeout=self._scan_timeout, interface=_interface
+            ).start()
 
         except bluepy.btle.BTLEManagementError:
             _LOGGER.error("Error scanning for switchbot devices", exc_info=True)