Skip to content

v2.0.0

Choose a tag to compare

@coddingtonbear coddingtonbear released this 17 Jul 23:05
· 20 commits to main since this release

2.0.0

A major rewrite of the core transport and operator experience. Upgrading requires re-pairing (see Breaking changes).

Breaking changes

  • Dropped the solaar dependency. flow-server/flow-client now talk to /dev/hidraw* directly through a new logitech_flow_kvm.hidpp package that owns the HID++ wire protocol (Bolt and Unifying receivers), with no dependency beyond the stdlib. Solaar's actuator path was built for its interactive config UI and asserted when a device was unreachable — the normal steady state while the reconciler retries — so it couldn't be reused safely.
  • Devices are now identified by serial number instead of hidraw path. Paths aren't stable across reboots; serials are. If you have scripts or config pinning a specific device path, update them to use the serial shown by list-devices.
  • Requires Python 3.10+.
  • Regenerating the server's TLS certificate (e.g. after adding --hostname) invalidates every already-paired client's cached copy. A freshly started flow-client re-pairs automatically; one already running does not and needs a restart.

New features

  • Interactive TUI. flow-server/flow-client now show a live status panel and scrolling log panel (via Textual) when run from a terminal, including connected-guest names on the server side. Pairing prompts moved into a proper modal instead of blocking on stdin, which no longer conflicts with the TUI owning the terminal. Non-interactive runs (e.g. under systemd) are unaffected and keep the plain-log fallback.
  • Real logging, with a rotating log file. Every message now goes through the standard logging pipeline and is always mirrored to a rotating log file under your platform's per-app log directory (via platformdirs), so you can check what happened even after it's scrolled off-screen.
  • SSE-driven reconciliation loop. Replaced the old disconnect-triggered, guess-the-new-host-and-sleep dance with a shared Reconciler that retries on positive evidence (a real connect notification) until convergence, fed by a new /events SSE endpoint that pushes the leader's current host to every client. Transient failures (e.g. a device unreachable mid-roam) no longer kill the reconciler loop.
  • --hostname/-H flag for flow-server. Declare the name(s) clients will reach the server as (mDNS .local/.lan, etc.) so the generated certificate's SAN list actually matches, instead of guessing via socket.gethostname().
  • Pairing requests are now serialized, so two concurrent attempts no longer interleave prompts on a shared console.

Fixes

  • Fixed missing CA:TRUE basic constraint on the self-signed certificate, which modern OpenSSL requires to use it as a trust anchor — this was silently breaking flow-client's TLS verification.
  • Fixed stale README examples showing device paths where matching has always been by serial number.
  • Restored werkzeug's startup banner (the line telling you the server's bind address), which had been accidentally silenced along with its per-request access log.
  • Clipboard data is now explicitly encoded as UTF-8 bytes.
  • Stopped enumerating theoretical devices past a receiver's actual maximum, and sped up enumeration by assuming lower device IDs are used first.

Packaging & tooling

  • Migrated from setup.py/setup.cfg/MANIFEST.in to PEP 621 pyproject.toml (hatchling).
  • Replaced appdirs → platformdirs, netifaces → psutil, pyOpenSSL → cryptography; dropped the unused hid_parser dependency.
  • Moved to Flask 3, rich 13+, and switched linting/formatting from black/flake8/isort to ruff.
  • CI now runs on a uv-based matrix across Python 3.10–3.14.
  • Added a real test suite (hidpp protocol/discovery/receiver, reconciler, SSE, logging setup, flow-server/flow-client, clipboard/cert utilities).