From e963628158683347c6cc276fe34ee9cfb0ac242f Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 31 Mar 2026 23:54:39 +0300 Subject: [PATCH] refresh when pair --- screens/setting/bluetooth_screen.py | 13 ++++++-- services/bluetooth_service.py | 46 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/screens/setting/bluetooth_screen.py b/screens/setting/bluetooth_screen.py index 7fec3af..0ea6045 100644 --- a/screens/setting/bluetooth_screen.py +++ b/screens/setting/bluetooth_screen.py @@ -49,16 +49,18 @@ class BluetoothScreen(QWidget): actions.setContentsMargins(0, 0, 0, 0) actions.setSpacing(12) + # Кнопка скрыта, видимость включается автоматически при входе в меню self.btn_visible = QPushButton("Сделать видимым") self.btn_visible.setObjectName("BluetoothActionBtn") self.btn_visible.setMinimumHeight(56) self.btn_visible.clicked.connect(self._make_visible) - self.btn_visible.setVisible(False) # временная кнопка, скрыта + self.btn_visible.setVisible(False) self.btn_refresh = QPushButton("Обновить список") self.btn_refresh.setObjectName("BluetoothActionBtn") self.btn_refresh.setMinimumHeight(56) self.btn_refresh.clicked.connect(self.refresh_list) + self.btn_refresh.setVisible(False) # скрываем, обновляемся автоматически self.btn_connect = QPushButton("Подключить") self.btn_connect.setObjectName("BluetoothActionBtnPrimary") @@ -81,6 +83,9 @@ class BluetoothScreen(QWidget): root.addWidget(self.list, 1) root.addLayout(actions) + # Подписка на событие успешного сопряжения + self._bt_service.pairing_completed.connect(self.refresh_list) + self.refresh_list() QTimer.singleShot(200, self._auto_connect_last) @@ -174,11 +179,13 @@ class BluetoothScreen(QWidget): self.status.setText(self._bt_service.get_status_text(mac)) def showEvent(self, event): - """Экран показан — запускаем режим сопряжения.""" + """Экран показан — запускаем режим сопряжения и мониторинг.""" super().showEvent(event) self._refresh_discoverable() + self._bt_service.start_pairing_monitor() def hideEvent(self, event): - """Экран скрыт — останавливаем таймер.""" + """Экран скрыт — останавливаем таймер и мониторинг.""" super().hideEvent(event) self._discoverable_timer.stop() + self._bt_service.stop_pairing_monitor() diff --git a/services/bluetooth_service.py b/services/bluetooth_service.py index 9a5a4b8..526369e 100644 --- a/services/bluetooth_service.py +++ b/services/bluetooth_service.py @@ -24,11 +24,13 @@ class BluetoothService(QObject): status_changed = Signal(str) devices_changed = Signal(list) connected_changed = Signal(str, bool) # mac, connected + pairing_completed = Signal() # новое устройство сопряжено def __init__(self, parent=None): super().__init__(parent) self._log_path = Path("~/.cache/car_ui/bluetooth.log").expanduser() self._last_error = "" + self._monitor_process: subprocess.Popen | None = None # === Public API === @@ -182,6 +184,50 @@ class BluetoothService(QObject): """Предыдущий трек.""" self._run_btctl(["menu player", "previous"]) + def start_pairing_monitor(self) -> None: + """Запустить мониторинг событий сопряжения.""" + if self._monitor_process is not None: + return + try: + self._monitor_process = subprocess.Popen( + ["bluetoothctl", "events"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, + ) + # Запускаем поток для чтения событий + import threading + threading.Thread(target=self._read_events, daemon=True).start() + except (subprocess.SubprocessError, OSError): + self._monitor_process = None + + def stop_pairing_monitor(self) -> None: + """Остановить мониторинг событий.""" + if self._monitor_process is not None: + self._monitor_process.terminate() + self._monitor_process = None + + def _read_events(self) -> None: + """Чтение событий из bluetoothctl events.""" + if self._monitor_process is None: + return + try: + for line in iter(self._monitor_process.stdout.readline, ''): + if line: + self._process_event(line.strip()) + except (ValueError, OSError): + pass + + def _process_event(self, line: str) -> None: + """Обработка события bluetoothctl.""" + # Событие успешного сопряжения + if "Pairing successful" in line or "Pairing complete" in line: + self.pairing_completed.emit() + # Событие нового устройства + elif "Device" in line and "connected" in line.lower(): + pass # можно обработать подключение + # === Private methods === def _run_cmd(self, args: list[str]) -> str: