63 lines
2.2 KiB
Python
63 lines
2.2 KiB
Python
|
import asyncio
|
||
|
import contextlib
|
||
|
import logging
|
||
|
import re
|
||
|
from typing import Optional
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
async def _get_bluetoothctl_version() -> Optional[re.Match]:
|
||
|
"""Get the version of bluetoothctl."""
|
||
|
with contextlib.suppress(Exception):
|
||
|
proc = await asyncio.create_subprocess_exec(
|
||
|
"bluetoothctl", "--version", stdout=asyncio.subprocess.PIPE
|
||
|
)
|
||
|
out = await proc.stdout.read()
|
||
|
version = re.search(b"(\\d+).(\\d+)", out.strip(b"'"))
|
||
|
await proc.wait()
|
||
|
return version
|
||
|
return None
|
||
|
|
||
|
|
||
|
class BlueZFeatures:
|
||
|
"""Check which features are supported by the BlueZ backend."""
|
||
|
|
||
|
checked_bluez_version = False
|
||
|
supported_version = True
|
||
|
can_write_without_response = True
|
||
|
write_without_response_workaround_needed = False
|
||
|
hides_battery_characteristic = True
|
||
|
hides_device_name_characteristic = True
|
||
|
_check_bluez_event: Optional[asyncio.Event] = None
|
||
|
|
||
|
@classmethod
|
||
|
async def check_bluez_version(cls) -> None:
|
||
|
"""Check the bluez version."""
|
||
|
if cls._check_bluez_event:
|
||
|
# If there is already a check in progress
|
||
|
# it wins, wait for it instead
|
||
|
await cls._check_bluez_event.wait()
|
||
|
return
|
||
|
cls._check_bluez_event = asyncio.Event()
|
||
|
version_output = await _get_bluetoothctl_version()
|
||
|
if version_output:
|
||
|
major, minor = tuple(map(int, version_output.groups()))
|
||
|
cls.supported_version = major == 5 and minor >= 34
|
||
|
cls.can_write_without_response = major == 5 and minor >= 46
|
||
|
cls.write_without_response_workaround_needed = not (
|
||
|
major == 5 and minor >= 51
|
||
|
)
|
||
|
cls.hides_battery_characteristic = major == 5 and minor >= 48 and minor < 55
|
||
|
cls.hides_device_name_characteristic = major == 5 and minor >= 48
|
||
|
else:
|
||
|
# Its possible they may be running inside a container where
|
||
|
# bluetoothctl is not available and they only have access to the
|
||
|
# BlueZ D-Bus API.
|
||
|
logging.warning(
|
||
|
"Could not determine BlueZ version, bluetoothctl not available, assuming 5.51+"
|
||
|
)
|
||
|
|
||
|
cls._check_bluez_event.set()
|
||
|
cls.checked_bluez_version = True
|