Skip to content

Commit

Permalink
feat: add support for getting callbacks on slot allocation change (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco authored Jan 18, 2025
1 parent c5f661d commit ae21ecb
Show file tree
Hide file tree
Showing 3 changed files with 302 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/bleak_retry_connector/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from bleak.exc import BleakDBusError, BleakDeviceNotFoundError, BleakError

from .bluez import ( # noqa: F401
AllocationChange,
AllocationChangeEvent,
BleakSlotManager,
_get_properties,
clear_cache,
Expand Down
54 changes: 53 additions & 1 deletion src/bleak_retry_connector/bluez.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import contextlib
import logging
import time
from collections.abc import Generator
from collections.abc import Callable, Generator
from dataclasses import dataclass
from enum import Enum
from functools import partial
from typing import Any

from bleak.backends.device import BLEDevice
Expand Down Expand Up @@ -35,6 +38,22 @@
)


class AllocationChange(Enum):
"""Allocation change."""

ALLOCATED = 1
RELEASED = 2


@dataclass(slots=True)
class AllocationChangeEvent:

change: AllocationChange
path: str | None # D-Bus object path of the device
adapter: str
address: str


def device_source(device: BLEDevice) -> str | None:
"""Return the device source."""
return _device_details_value_or_none(device, "source")
Expand All @@ -54,6 +73,11 @@ def adapter_from_path(path: str) -> str:
return path.split("/")[3]


def address_from_path(path: str) -> str:
"""Get the address from a ble device path."""
return path.split("/")[-1].removeprefix("dev_").replace("_", ":").upper()


def path_from_ble_device(device: BLEDevice) -> str | None:
"""Get the adapter from a ble device."""
return _device_details_value_or_none(device, "path")
Expand All @@ -71,6 +95,7 @@ def __init__(self) -> None:
self._adapter_slots: dict[str, int] = {}
self._allocations_by_adapter: dict[str, dict[str, DeviceWatcher]] = {}
self._manager: BlueZManager | None = None
self._callbacks: set[Callable[[AllocationChangeEvent], None]] = set()

async def async_setup(self) -> None:
"""Set up the class."""
Expand Down Expand Up @@ -103,6 +128,19 @@ def remove_adapter(self, adapter: str) -> None:
self._manager.remove_device_watcher(watcher)
del self._allocations_by_adapter[adapter]

def register_allocation_callback(
self, callback: Callable[[AllocationChangeEvent], None]
) -> Callable[[], None]:
"""Register a callback for when allocations change."""
self._callbacks.add(callback)
return partial(self.unregister_allocation_callback, callback)

def unregister_allocation_callback(
self, callback: Callable[[AllocationChangeEvent], None]
) -> None:
"""Unregister a callback."""
self._callbacks.discard(callback)

def register_adapter(self, adapter: str, slots: int) -> None:
"""Register an adapter."""
self._allocations_by_adapter[adapter] = {}
Expand Down Expand Up @@ -132,6 +170,7 @@ def _on_device_connected_changed(connected: bool) -> None:
on_connected_changed=_on_device_connected_changed,
on_characteristic_value_changed=_on_characteristic_value_changed,
)
self._call_callbacks(AllocationChange.ALLOCATED, path)

def release_slot(self, device: BLEDevice) -> None:
"""Release a slot."""
Expand All @@ -150,6 +189,19 @@ def _release_slot(self, path: str) -> None:
allocations = self._allocations_by_adapter[adapter]
if watcher := allocations.pop(path, None):
self._manager.remove_device_watcher(watcher)
self._call_callbacks(AllocationChange.RELEASED, path)

def _call_callbacks(self, change: AllocationChange, path: str) -> None:
"""Call the callbacks."""
for callback_ in self._callbacks:
try:
callback_(
AllocationChangeEvent(
change, path, adapter_from_path(path), address_from_path(path)
)
)
except Exception: # pylint
_LOGGER.exception("Error in callback")

def allocate_slot(self, device: BLEDevice) -> bool:
"""Allocate a slot."""
Expand Down
Loading

0 comments on commit ae21ecb

Please sign in to comment.