from typing import Callable, Optional, Tuple from PIL import ImageDraw, Image from .base import View from .button_interface import ButtonInterface from .draw_utils import draw_bluetooth_icon class BluetoothPairingView(View, ButtonInterface): """Shows a 'waiting for connection' screen while BT pairing is active. ``start_command`` is called once when the view is first rendered, to trigger pairing mode on the hardware side. ``stop_command`` is called when the user cancels (left long press). ``on_connected(device_name)`` is called by external code when a device has paired; it transitions back by calling ``deactivate_command``. ``deactivate_command`` returns to the previous screen. """ def __init__(self, parent, im_size, center, start_command: Optional[Callable] = None, stop_command: Optional[Callable] = None, on_connected: Optional[Callable[[str], None]] = None, deactivate_command: Optional[Callable] = None): self.start_command = start_command self.stop_command = stop_command self.on_connected = on_connected self.deactivate_command = deactivate_command self._frame = 0 # used for the waiting animation self._started = False super().__init__(parent, im_size, center) # ------------------------------------------------------------------ def update_weight(self, weight: float) -> Image.Image: if not self._started: self._started = True if self.start_command: self.start_command() im = self.bkg_im.copy() draw = ImageDraw.Draw(im) cx = self.size[0] // 2 # Large BT icon centred draw_bluetooth_icon(draw, (cx, 52), size=20) draw.text((cx - 28, 80), "Pairing", fill='black') # Three animated dots self._frame = (self._frame + 1) % 6 for i in range(3): r = 3 if i < self._frame // 2 + 1 else 1 draw.circle((cx - 8 + i * 8, 100), r, fill='black') return im def notify_connected(self, device_name: str = ''): """Call this from the outside when a BT device has connected.""" if self.on_connected: self.on_connected(device_name) self._cancel() # ------------------------------------------------------------------ def _cancel(self): if self.stop_command: self.stop_command() if self.deactivate_command: self.deactivate_command() def left_long_press(self): self._cancel() def has_button(self) -> Tuple[bool, bool, bool, bool]: return False, True, False, False def render_left_long_press(self, draw, x, y): draw.text((x, y - 5), 'Cancel', fill='black')