platypush/platypush/plugins/bluetooth/_legacy/_manager/_service.py

65 lines
2.2 KiB
Python

from concurrent.futures import ProcessPoolExecutor
from logging import getLogger
from typing import Dict, List, Optional
import bluetooth
from platypush.entities.bluetooth import BluetoothService
from .._model import BluetoothServicesBuilder
# pylint: disable=too-few-public-methods
class ServiceDiscoverer:
"""
Runs the service discovery processes in a pool.
"""
def __init__(self, max_workers: int = 4, timeout: float = 30):
self._max_workers = max_workers
self._timeout = timeout
self.logger = getLogger(__name__)
def _discover(self, address: str) -> List[BluetoothService]:
"""
Inner implementation of the service discovery for a specific device.
"""
self.logger.info("Discovering services for %s...", address)
try:
return BluetoothServicesBuilder.build(
bluetooth.find_service(address=address)
)
except Exception as e:
self.logger.warning(
"Failed to discover services for the device %s: %s", address, e
)
return []
finally:
self.logger.info("Service discovery for %s completed", address)
def discover(
self, *addresses: str, timeout: Optional[float] = None
) -> Dict[str, List[BluetoothService]]:
"""
Discover the services for the given addresses. Discovery will happen in
parallel through a process pool.
:param addresses: The addresses to scan.
:param timeout: The timeout in seconds.
:return: An ``{address: [services]}`` dictionary with the discovered
services per device.
"""
discovered_services: Dict[str, List[BluetoothService]] = {}
with ProcessPoolExecutor(max_workers=self._max_workers) as executor:
try:
for i, services in enumerate(
executor.map(
self._discover, addresses, timeout=timeout or self._timeout
)
):
discovered_services[addresses[i]] = services
except TimeoutError:
self.logger.warning("Service discovery timed out.")
return discovered_services