work: Отладил переподключение к Tion. Раньше при разрыве соединения в библиотеке btle соединение зависало. Сейчас при сбросе устройства явно вызывается disconnect библиотеки, чтобы сессия Bluetooth освобождалась.

This commit is contained in:
Fedorov Dmitriy
2026-09-13 21:35:37 +03:00
parent 5cc125db3f
commit fdae9dec72
7 changed files with 557 additions and 18 deletions
+74 -18
View File
@@ -1,6 +1,6 @@
import asyncio
from typing import Any
from bleak import BleakScanner
from tion_btle import TionS4
from .models import TionState
@@ -18,7 +18,7 @@ class TionController:
def __init__(self, mac: str):
self._mac = mac
self._device = TionS4(mac)
self._device: TionS4 | None = None
# Не позволяем двум частям программы одновременно работать с BLE.
self._lock = asyncio.Lock()
@@ -35,27 +35,88 @@ class TionController:
@property
def connected(self) -> bool:
return self._device.connection_status == "connected"
return (
self._device is not None
and self._device.connection_status == "connected"
)
@property
def state(self) -> TionState | None:
async def _reset_device(self) -> None:
"""
Последнее известное состояние без обращения к Bluetooth.
Полностью уничтожить текущий BLE transport.
Важно:
tion-btle не вызывает BleakClient.disconnect(),
если WinRT уже считает устройство disconnected.
Поэтому при полном reset принудительно закрываем
BleakClient напрямую.
"""
return self._state
old_device = self._device
# Controller сразу больше не считает старый объект рабочим.
self._device = None
self._started = False
if old_device is None:
return
try:
# Нам нужен именно настоящий BleakClient.disconnect().
#
# Не old_device.disconnect(), потому что tion-btle
# может пропустить физический cleanup при
# connection_status == "disc".
await old_device._btle.disconnect()
except Exception:
# Старый transport всё равно больше использоваться
# не будет.
pass
"""
Соединение
"""
async def connect(self) -> None:
"""
Открыть постоянное BLE-соединение.
"""
async with self._lock:
if self._started:
if self.connected:
self._started = True
return
await self._device.connect()
# Полностью закрываем всё, что осталось
# от предыдущего соединения.
await self._reset_device()
# Получаем свежий BLEDevice.
ble_device = await BleakScanner.find_device_by_address(
self._mac,
timeout=5.0,
)
if ble_device is None:
raise ConnectionError(
f"Tion {self._mac} not found"
)
# Новый TionS4 = новый BleakClient.
device = TionS4(ble_device)
try:
await device.connect()
except Exception:
# ВАЖНО:
# освобождаем даже частично созданную
# WinRT/GATT-сессию.
try:
await device._btle.disconnect()
except Exception:
pass
raise
self._device = device
self._started = True
async def disconnect(self) -> None:
@@ -63,13 +124,8 @@ class TionController:
Закрыть BLE-соединение.
"""
async with self._lock:
if not self._started:
return
await self._reset_device()
try:
await self._device.disconnect()
finally:
self._started = False
"""
Состояние