Daily checkup

This commit is contained in:
2026-05-20 08:49:14 +03:00
parent 2fbeae26a6
commit b22cdd93eb
46 changed files with 5478 additions and 680 deletions
+76 -2
View File
@@ -12,6 +12,8 @@ import json
import os
import queue
import random
import re
import subprocess
import threading
import struct
import sys
@@ -1340,7 +1342,9 @@ class DataCharacteristic(Characteristic):
class StatusCharacteristic(Characteristic):
def __init__(self, bus, index, service: Service, bridge: AisHubBridge):
Characteristic.__init__(self, bus, index, AIS_HUB_STATUS_UUID, ["read", "notify"], service)
# Только read: notify не используется клиентом; лишний notify увеличивает
# поверхность ATT без пользы. Шифрование не требуется (без encrypt-*).
Characteristic.__init__(self, bus, index, AIS_HUB_STATUS_UUID, ["read"], service)
self.bridge = bridge
self.notifying = False
@@ -1417,6 +1421,74 @@ def find_adapter(bus):
return None
def _hci_index_from_adapter_path(adapter_path: str) -> str | None:
m = re.search(r'hci(\d+)$', adapter_path)
return m.group(1) if m else None
def configure_adapter_for_open_le(bus, adapter_path: str) -> None:
"""
Снижает вероятность системного диалога pairing на Android.
Наши характеристики без encrypt-* флагов, но bluetoothd по умолчанию:
- шлёт SMP Security Request (bonding) при LE connect;
- делает reverse GATT discovery к central (читает Battery и т.п. на
телефоне) -> Insufficient Authentication -> Android показывает pairing.
См. bluez/bluez#851, ukBaz/python-bluezero#390.
"""
props = dbus.Interface(
bus.get_object(BLUEZ_SERVICE_NAME, adapter_path),
DBUS_PROP_IFACE,
)
try:
props.Set('org.bluez.Adapter1', 'Pairable', dbus.Boolean(False))
log_info('[Adapter] Pairable=false (no incoming pairing UI from adapter)')
except Exception as e:
log_warn(f'[Adapter] Pairable=false failed: {e}')
# btmgmt: bondable off + NoInputNoOutput — peripheral не инициирует bonding.
hci = _hci_index_from_adapter_path(adapter_path)
if hci is None:
log_warn('[Adapter] cannot parse hci index from ' + adapter_path)
return
for args in (
['bondable', 'off'],
['io-cap', '3'], # NoInputNoOutput
):
cmd = ['btmgmt', '-i', hci] + args
try:
r = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
if r.returncode == 0:
log_info(f'[Adapter] btmgmt {" ".join(args)}: ok')
else:
err = (r.stderr or r.stdout or '').strip()
log_warn(f'[Adapter] btmgmt {" ".join(args)} failed ({r.returncode}): {err}')
except FileNotFoundError:
log_warn('[Adapter] btmgmt not found; set ReverseServiceDiscovery=false in /etc/bluetooth/main.conf')
break
except Exception as e:
log_warn(f'[Adapter] btmgmt {" ".join(args)}: {e}')
log_info(
'[Adapter] Рекомендуется в /etc/bluetooth/main.conf: '
'ReverseServiceDiscovery=false и [GATT] Client=false (перезапуск bluetoothd)'
)
def trust_connected_device(bus, device_path: str) -> None:
"""Помечает central как Trusted — на части стеков убирает повторный pairing prompt."""
if not device_path or device_path == 'unknown':
return
try:
dev = bus.get_object(BLUEZ_SERVICE_NAME, device_path)
props = dbus.Interface(dev, DBUS_PROP_IFACE)
props.Set('org.bluez.Device1', 'Trusted', dbus.Boolean(True))
log_debug(f'[BlueZ] Device Trusted=true: {device_path}')
except Exception as e:
log_debug(f'[BlueZ] Trusted=true failed for {device_path}: {e}')
# ============ main() ============
def main():
@@ -1432,6 +1504,7 @@ def main():
return 1
log_info(f'Используем адаптер: {adapter_path}')
configure_adapter_for_open_le(bus, adapter_path)
service_manager = dbus.Interface(
bus.get_object(BLUEZ_SERVICE_NAME, adapter_path),
@@ -1455,11 +1528,12 @@ def main():
if "Connected" not in changed:
return
connected = bool(changed.get("Connected"))
dev_path = str(path or "")
dev_path = "" if path is None else str(path)
if not dev_path:
return
if connected:
log_info(f"[BlueZ] Device connected: {dev_path} sessions={bridge.session_count()}")
trust_connected_device(bus, dev_path)
bridge.get_or_create_session(dev_path)
else:
log_info(f"[BlueZ] Device disconnected: {dev_path} -> removing session")