Adds an optional per-printer integration with KXGauge
(https://gitea.it-drui.de/viewit/kxgauge), a small ESP32 round-face
display that shows printer status as an emotion and hotend temperature
as a color ring. KXGauge only exposes a GET-only HTTP API with no
push/websocket, so the bridge actively pushes to it from the existing
MQTT callbacks (_on_temp for the heat ring, _on_print + offline
transitions for the emotion) whenever state actually changes, with a
built-in dedupe so it doesn't spam the device every poll tick.
- kxgauge_client.py: thin synchronous HTTP client (mirrors
spoolman_client.py's shape - called from the MQTT reader thread).
- New [kxgauge]/[kxgauge_mapping] config.ini sections; the mapping
(kobra_state -> KXGauge emotion) is user-editable with a sane
default and falls back per-key if only partially configured.
- Settings UI: new card under Integrations with enable/URL/target-temp
fields, a per-state emotion mapping list, and a connection-test
button (/api/kxgauge/test).
- Multi-printer aware: kxgauge_url/enabled/heat_peak merge per
[printer_N] like the existing power-switch settings.
Also fixes a real bug found while testing this: _find_config_path()
resolves to the live project config/config.ini, not a sandboxed path,
so any test hitting /api/settings POST without stubbing it out will
silently overwrite the real printer config. test_settings.py already
guards against this - test_kxgauge.py now does too.
The Dockerfile still only COPYed the pre-refactor module list, so the
testing image built from the split-module branch crashed immediately
with ModuleNotFoundError: No module named 'spoolman_client'. Add the
new bridge_*.py mixins plus spoolman_client/gcode_store/gcode_meta/
camera/credentials. Verified with a local docker build + run against
the real printer.