from __future__ import annotations from PySide6.QtCore import Qt, QTimer, QSettings, QEvent from PySide6.QtGui import QFont from PySide6.QtWidgets import ( QWidget, QLabel, QVBoxLayout, QHBoxLayout, QPushButton, QListWidget, QListWidgetItem, QScroller, ) import build_info from services.bluetooth_service import BluetoothService, BluetoothDevice class BluetoothScreen(QWidget): def __init__(self, on_back): super().__init__() self._on_back = on_back self._settings = QSettings("car_ui", "ui") self._bt_service = BluetoothService(self) self._discoverable_timer = QTimer(self) self._discoverable_timer.timeout.connect(self._refresh_discoverable) self._list_refresh_timer = QTimer(self) self._list_refresh_timer.timeout.connect(self.refresh_list) root = QVBoxLayout(self) root.setContentsMargins(0, 0, 0, 0) root.setSpacing(12) self.status = QLabel("Статус: —") self.status.setObjectName("BluetoothStatus") self.status.setFont(QFont("", 12)) self.list = QListWidget() self.list.setObjectName("BluetoothList") self.list.setSpacing(6) self.list.setSelectionMode(QListWidget.SingleSelection) self.list.itemSelectionChanged.connect(self._on_select) QScroller.scroller(self.list.viewport()).grabGesture( self.list.viewport(), QScroller.LeftMouseButtonGesture, ) actions = QHBoxLayout() 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_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") self.btn_connect.setMinimumHeight(56) self.btn_connect.setEnabled(False) self.btn_connect.clicked.connect(self._connect_selected) self.btn_disconnect = QPushButton("Отключить") self.btn_disconnect.setObjectName("BluetoothActionBtn") self.btn_disconnect.setMinimumHeight(56) self.btn_disconnect.setEnabled(False) self.btn_disconnect.clicked.connect(self._disconnect_selected) actions.addWidget(self.btn_visible, 1) actions.addWidget(self.btn_refresh, 1) actions.addWidget(self.btn_connect, 1) actions.addWidget(self.btn_disconnect, 1) root.addWidget(self.status) root.addWidget(self.list, 1) root.addLayout(actions) self.refresh_list() QTimer.singleShot(200, self._auto_connect_last) def refresh_list(self): """Обновить список сопряженных устройств.""" devices = self._bt_service.get_paired_devices() self.list.clear() for dev in devices: label = f"{dev.name} ({dev.mac})" if dev.name else dev.mac item = QListWidgetItem(label) item.setData(Qt.UserRole, dev.mac) self.list.addItem(item) self._update_status() def _on_select(self): """Обработчик выбора устройства.""" has_selection = self._selected_mac() is not None self.btn_connect.setEnabled(has_selection) self.btn_disconnect.setEnabled(has_selection) self._update_status() def _selected_mac(self) -> str | None: """Получить MAC-адрес выбранного устройства.""" items = self.list.selectedItems() if not items: return None return items[0].data(Qt.UserRole) def _auto_connect_last(self): """Автоподключение к последнему устройству.""" last_mac = self._settings.value("bluetooth/last_mac", "") if not last_mac: return self._connect_device(last_mac, silent=True) def _make_visible(self): """Сделать устройство видимым для сопряжения.""" success = self._bt_service.make_discoverable() if self._bt_service.last_error == "power_off": self.status.setText("Статус: питание BT выключено (проверьте rfkill)") elif self._bt_service.last_error == "max_devices": self.status.setText( f"Статус: достигнуто макс. число устройств ({build_info.BLUETOOTH_MAX_PAIRED_DEVICES})" ) elif not success: self.status.setText(f"Статус: ошибка ({self._bt_service.last_error})") else: self.status.setText("Статус: видим для сопряжения (10 сек)") self._discoverable_timer.start(9000) # 9 секунд def _refresh_discoverable(self): """Продлить режим сопряжения.""" self._bt_service.make_discoverable() self._discoverable_timer.start(9000) # 9 секунд def _connect_selected(self): """Подключить выбранное устройство.""" mac = self._selected_mac() if not mac: return self._connect_device(mac, silent=False) def _connect_device(self, mac: str, silent: bool): """Подключиться к устройству по MAC.""" success = self._bt_service.connect_device(mac) if not silent: if not success: self.status.setText(f"Статус: ошибка ({self._bt_service.last_error})") else: self.status.setText(f"Статус: подключаемся к {mac}") self._settings.setValue("bluetooth/last_mac", mac) QTimer.singleShot(300, self._update_status) def _disconnect_selected(self): """Отключить выбранное устройство.""" mac = self._selected_mac() if not mac: return success = self._bt_service.disconnect_device(mac) if not success: self.status.setText(f"Статус: ошибка ({self._bt_service.last_error})") else: self.status.setText(f"Статус: отключено от {mac}") QTimer.singleShot(300, self._update_status) def _update_status(self): """Обновить текстовый статус.""" mac = self._selected_mac() self.status.setText(self._bt_service.get_status_text(mac)) def showEvent(self, event): """Экран показан — запускаем режим сопряжения и обновление списка.""" super().showEvent(event) self._refresh_discoverable() # Обновляем список каждые 2 секунды для отслеживания новых устройств self._list_refresh_timer.start(2000) def hideEvent(self, event): """Экран скрыт — останавливаем таймеры.""" super().hideEvent(event) self._discoverable_timer.stop() self._list_refresh_timer.stop()