This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
pyflic-ble is a Python library for communicating with Flic smart buttons (Flic 2, Flic Duo, Flic Twist) over BLE. It handles pairing, authentication, button/rotation events, firmware updates, and name management. Primary consumer is the Home Assistant Flic integration.
pip install -e . # Install in editable mode
python -m pytest tests/ # Run all tests
python -m pytest tests/test_rotate_tracker.py::test_multi_mode_tracker_mode_12_bounded # Run single testDependencies: bleak (BLE), bleak-retry-connector, cryptography (Ed25519/X25519). Python >=3.12.
Three device types (DeviceType enum in const.py: FLIC2, DUO, TWIST) each have a protocol handler in src/pyflic_ble/handlers/. All extend DeviceProtocolHandler (ABC in handlers/base.py). Factory function create_handler() in handlers/__init__.py instantiates the correct handler by device type.
- Flic 2 (
flic2.py): Single button, no rotation. Uses Flic 2/Duo framing with frame headers. - Duo (
duo.py): Two buttons, rotation dial, swipe gestures. Shares Flic 2 BLE service UUIDs and framing. - Twist (
twist.py): Single button, rotation with 12 selector modes. Uses its own BLE service UUIDs and a completely different protocol (no frame headers, different opcodes prefixedTWIST_OPCODE_*).
Central class that orchestrates BLE connections via bleak. Manages session state machine (SessionState enum), pairing (full verify) and reconnection (quick verify), packet framing/signing, and delegates device-specific logic to the handler. Exposes callbacks: on_button_event, on_rotate_event, on_firmware_progress, on_disconnect.
protocol.py: Message dataclasses withto_bytes()/from_bytes()serialization for all request/response types (Flic 2/Duo and Twist variants).security.py: Chaskey-LTS MAC (packet signing), Ed25519 signature verification (4-variant twist), X25519 ECDH key exchange, HMAC-SHA256 key derivation.const.py: All BLE UUIDs, opcodes, event types, and protocol constants. Flic 2/Duo opcodes are plain integers; Twist opcodes useTWIST_OPCODE_*prefix with hex values.
RotateTracker: Single-mode tracker with velocity/acceleration, backlash suppression, configurable full-range units and clamping.MultiModeRotateTracker: Wraps 13RotateTrackerinstances for Twist's 12 selector modes + 1 free rotation mode (index 12). Implements SDK-compatible min-boundary tracking for bounded position (0-100%).
- All protocol constants use
typing.Finalinconst.py. - Twist protocol is structurally different from Flic 2/Duo: separate BLE UUIDs, no frame header byte, different opcode numbering. Always check which protocol path you're in.
- Device type is detected from serial number prefix:
T=Twist,D=Duo, else Flic 2 (DeviceType.from_serial_number()). - Event type strings (e.g.,
"click","rotate_clockwise") are defined as constants inconst.pyand used throughout handlers and callbacks.