collector: survive bluetoothd hangs and restarts

- every BlueZ call runs off the asyncio thread with a timeout; if bluetoothd
  stays unresponsive for 30 s the collector restarts bluetooth.service and
  exits for a clean re-registration
- Type=notify + WatchdogSec for buttontask-collector, never give up restarting
- bluetooth.service drop-in: Restart=always (BlueZ 5.55 crashes on assert/SEGV),
  installed by device-install.sh and OTA (bt-update)
- bt-service: bluez-restart action for the collector

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-10 07:10:02 +00:00
co-authored by Claude Opus 5.5
parent 588c2e0135
commit 115879ab67
10 changed files with 265 additions and 89 deletions
+4 -13
View File
@@ -115,20 +115,11 @@ class BlessAdvertiser:
except Exception as e:
log.debug("stop_advertising: %s", e)
# Do NOT unregister/re-register the GATT application here: doing it on
# every adv update (~1/min) is a suspect for bluetoothd 5.55 crashes
# (GLib assert / SEGV). If bluetoothd restarts, main.py exits and
# systemd restarts the collector, which registers everything afresh.
try:
try:
gatt_manager = adapter.get_interface("org.bluez.GattManager1")
# Сначала убираем старую регистрацию, чтобы не было конфликта
await gatt_manager.call_unregister_application(app.base_path)
except Exception:
pass
try:
# Регистрируем GATT-сервер (сервисы и характеристики) заново
await gatt_manager.call_register_application(app.base_path, {})
log.debug("GATT application re-registered successfully")
except Exception as e:
log.debug("GATT re-register failed (already active?): %s", e)
await app.set_name(adapter, LOCAL_NAME)
advertisement = BlueZLEAdvertisement(
Type.PERIPHERAL, len(app.advertisements) + 1, app