Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ packages = [
[tool.poetry.dependencies]
python = ">=3.7,<4.0"

bleak = "^0.14.3"
cryptography = "^37.0.2"
bleak = "^0.20.2"
cryptography = ">=37.0.2"

typing-extensions = { version = "^4.2.0", python = "<3.8" }
importlib-metadata = {version = "^4.11.4", python = "<3.8"}
Expand Down
65 changes: 34 additions & 31 deletions pysesameos2/ble.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from bleak import BleakScanner
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from bleak.exc import BleakError

from pysesameos2.const import (
Expand Down Expand Up @@ -291,15 +292,15 @@ def getPayload(self) -> bytes:


class BLEAdvertisement:
def __init__(self, dev: BLEDevice, manufacturer_data: dict) -> None:
def __init__(self, dev: BLEDevice, manufacturer_data: dict, rssi: int) -> None:
if not isinstance(dev, BLEDevice):
raise TypeError("Invalid dev")
if not isinstance(manufacturer_data, dict):
raise TypeError("Invalid manufacturer_data")

self._address = dev.address
self._device = dev
self._rssi = dev.rssi
self._rssi = rssi

self._advBytes = next(iter(manufacturer_data.values()))
self._productModel = CHProductModel.getByValue(self._advBytes[0])
Expand Down Expand Up @@ -334,32 +335,31 @@ def isRegistered(self) -> bool:


class CHBleManager:
def device_factory(self, dev: BLEDevice) -> Union["CHSesame2", "CHSesameBot"]:
"""Return a device object corresponding to a BLE advertisement.
def device_factory(
self, dev: BLEDevice, adv_data: AdvertisementData
) -> Union["CHSesame2", "CHSesameBot"]:
"""Return a device object corresponding to a BLEDevice and AdvertisementData.

Args:
dev (BLEDevice): The discovered BLE device.
adv_data (AdvertisementData): break's AdvertisementData when discovered.

Returns:
Union[CHSesame2, CHSesameBot]: The candyhouse device.
"""
if not isinstance(dev, BLEDevice):
if not isinstance(dev, BLEDevice) and not isinstance(
adv_data, AdvertisementData
):
raise TypeError("Invalid dev")

if dev.name is None:
raise ValueError("Failed to find the device name")

# `BLEDevice.metadata` should return device specific details in OS-agnostically way.
# https://bleak.readthedocs.io/en/latest/api.html#bleak.backends.device.BLEDevice.metadata
if (
dev.metadata is None
or "uuids" not in dev.metadata
or "manufacturer_data" not in dev.metadata
):
raise ValueError("Failed to find the device metadata")
if adv_data.service_uuids is None or adv_data.manufacturer_data is None:
raise ValueError("Failed to find the uuid/manufacture data")

if SERVICE_UUID in dev.metadata["uuids"]:
adv = BLEAdvertisement(dev, dev.metadata["manufacturer_data"])
if SERVICE_UUID in adv_data.service_uuids:
adv = BLEAdvertisement(dev, adv_data.manufacturer_data, adv_data.rssi)
device = adv.getProductModel().deviceFactory()()
device.setAdvertisement(adv)

Expand All @@ -381,11 +381,15 @@ async def scan(
logger.info("Starting scan for SESAME devices...")
ret = {}
try:
devices = await asyncio.wait_for(BleakScanner.discover(), scan_duration)
discovers = await asyncio.wait_for(
BleakScanner.discover(return_adv=True), scan_duration
)

for device in devices:
for discover in discovers.values():
device = discover[0]
adv_data = discover[1]
try:
obj = self.device_factory(device)
obj = self.device_factory(device, adv_data)
except NotImplementedError:
logger.warning("Unsupported SESAME device is found, skip.")
continue
Expand Down Expand Up @@ -421,30 +425,29 @@ async def scan_by_address(
# `BleakScanner.find_device_by_address`.
#
# The problem is, the reposence (`BLEDevice`) of `find_device_by_address` does not
# contain a proper `matadata` which is heavily utilized in `device_factory`.
#
# `discover` internally calls `discovered_devices` which provides
# OS-agnostic `metadata` of the device.
# https://github.com/hbldh/bleak/blob/55a2d34cc96bb842be278485794806704caa2d2c/bleak/backends/scanner.py#L101
# https://github.com/hbldh/bleak/blob/ce63ed4d92430f154ce33ab812e313961b26f7a4/bleak/backends/bluezdbus/scanner.py#L213-L237
# provide the advertisement data. So we cannot identify the device model etc.

devices = await asyncio.wait_for(BleakScanner.discover(), scan_duration)
discovers = await asyncio.wait_for(
BleakScanner.discover(return_adv=True), scan_duration
)

device = next(
(d for d in devices if d.address.lower() == ble_device_identifier.lower()),
discovered = next(
(
d
for d in discovers.values()
if d[0].address.lower() == ble_device_identifier.lower()
),
None,
)
if device is None:
if discovered is None:
raise ConnectionRefusedError("Scan completed: the device not found")

try:
obj = self.device_factory(device)
obj = self.device_factory(discovered[0], discovered[1])
except NotImplementedError:
raise NotImplementedError("This device is not supported.")
except ValueError:
raise ValueError("This is not a SESAME device.")

obj = self.device_factory(device)

logger.info("Scan completed: found the device")
return obj
2 changes: 1 addition & 1 deletion pysesameos2/chsesame2.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ async def connect(self) -> None:

logger.info(f"Connected to the device: {self.getDeviceUUID()}")
self.setDeviceStatus(CHSesame2Status.WaitingGatt)
services = await self._client.get_services()
services = await self._client.services
for s in services:
if s.uuid == SERVICE_UUID:
self.setCharacteristicTX(s.get_characteristic(TX_UUID))
Expand Down
Loading