v2.14.0 — Cleanup pass · CI linter · dep refresh
First minor bump since v2.13. No new features — a foundation pass that pays down accumulated debt, adds a Python linter to CI, refreshes dependencies, and quietens a cosmetic startup warning. The path is clear for the feature backlog (#24–#37) to land cleanly on top of this.
🧹 Code cleanup (PR #44)
switchTabconsolidation — was three nested function-replace wrappers stacked at parse time (base → currentTab tracking → chat-render hook). Collapsed into one function with a doc-block listing all five responsibilities. ~30 lines shorter, much easier to follow.- Dead JS removed —
loadPersistedHistory(defined but never called) andupdateSliderByAbsAddr(3-line alias wrapper with no callers). - New tests (+31):
tests/test_xml_escape.py— 10 tests covering all 5 XML entities, ordering rule (&substituted first or&would re-escape), unicode passthrough, realistic mixed content with quotes/apostrophes/ampersands. Matters because scene XML injection corrupts.qxwfiles if entities are wrong.tests/test_absolute_channel.py— 11 tests covering universe-boundary math (channel 512 vs 513), the+11-based-vs-0-based conversion, plus rig-specific pinning for the Riversway SlimPAR Pro + SlimPAR 56 addressing.
✅ Ruff linter + CI gate (PR #45)
New fourth CI job catches style drift, dead imports, deprecated typing, and small footguns at PR time.
- Config in repo-root
pyproject.toml: line-length 120, target Python 3.11, rulesE F W I B UP(pycodestyle + pyflakes + isort + bugbear + pyupgrade) - 67 violations fixed: 35 auto-fixed (import sorting,
Dict→dict,Optional[X]→X | None, unusedemitimport), 6 E701 split, 4 B904 (exception chains preserved viafrom e/from Nonein AI provider wrappers), 2 E402, 2 W293, 18 E501 (long tool-registry descriptions wrapped using paren-grouped string concat) - CI step runs once across both servers (~150ms) — slots in before pytest
📦 Dependency refresh (PR #46)
All five runtime deps were pinned to versions ~6 months old.
| Package | From | To |
|---|---|---|
| Flask | 3.0.0 | 3.1.3 |
| flask-cors | 4.0.0 | 6.0.2 |
| flask-socketio | 5.3.5 | 5.6.1 |
| requests | 2.31.0 | 2.34.2 |
| websockets | 12.0 | 15.0.1 |
Tested in a fresh venv against the full pytest suite. Notable: requests 2.34 includes security fixes for PoolManager-related CVEs. Websockets kept one minor back from latest 16.0 for stability.
🤫 Silence Werkzeug dev-server warning (PR #48)
Werkzeug's "WARNING: This is a development server" line in journalctl is cosmetic noise for the single-user studio LAN deploy. Silenced via a targeted logging.Filter on the werkzeug logger — other startup output (request access logs, * Running on http://... banner) is preserved.
The proper production-WSGI migration (gunicorn + eventlet/gevent worker) is queued as #47. That migration needs Pi-side testing because of the persistent QLC+ asyncio loop in a dedicated thread — out of scope for a cleanup pass.
Numbers
- Tests: 195 → 226 (+31)
- CI jobs: 3 → 4 (added ruff)
- Lint violations: 67 → 0
- Dead code removed: ~40 lines of JS
- Switch-tab layers: 3 → 1
Upgrade
From your Mac:
git pull origin main
bash scripts/deploy.shThen on the Pi (one-time, to pick up dep bumps):
ssh lights.local
cd ~/lights-pi/control-server
source ~/venv/bin/activate # wherever the venv lives
pip install --upgrade -r requirements.txt
sudo systemctl restart lighting-control.service
journalctl -u lighting-control.service -f # watch startupYou should no longer see "WARNING: This is a development server" in the log.
What's next
Foundation pass is wrapped. The feature backlog (#24–#37) is the main thrust from here. MIDI controller input (#26) is still the one I'd cut next — the single biggest UX upgrade for daily use, and ~$40 of hardware + a couple days of code.