forked from viewit/KX-Bridge-Release
Discovered while testing the smart-plug power-switch feature: unplugging the printer left the dashboard stuck showing it as online/"ready" indefinitely. Live-tested against a real printer to isolate two independent, compounding causes: 1. The MQTT socket had no TCP keepalive. A connection killed without a clean TCP close (unplugged, not a graceful shutdown) looks alive to the OS for as long as its default dead-connection timeout - 15+ minutes on Linux - since a send on a half-open connection is buffered by the kernel and doesn't fail immediately. Fixed with SO_KEEPALIVE (short idle/interval/count) plus TCP_USER_TIMEOUT, since keepalive probes alone only fire on an idle connection - verified live that a printer disappearing while a send was still in flight (the common case, since the poll loop sends every few seconds) instead falls back to the far slower normal TCP retransmission timer, which keepalive settings don't affect at all. 2. Even after the socket was correctly detected as dead, the status poll loop could hang indefinitely inside publish() waiting for a reconnect attempt already running on the MQTT reader thread (the Issue #105 reconnect-lock serialization), and therefore never reached the is_connected() check that flips kobra_state to "offline". _reconnect() now takes wait_if_in_progress/persist flags so the poll loop's call returns immediately with at most one attempt instead of blocking through someone else's multi-minute backoff loop - persistent retrying stays the reader thread's job. A disconnected printer is now detected and reflected on the dashboard within about 15 seconds. Live-verified across repeated disconnect/ reconnect cycles that no sockets, threads, or file descriptors are left behind (checked via /proc/<pid>/fd and /proc/<pid>/task) - the transient FIN-WAIT-2 entries seen while the printer's TLS service is still booting belong to the kernel's own connection teardown, not to processes held by the bridge, and clear on their own.