GUI
- Full keyboard and screen-reader accessibility pass — every interactive widget (icon buttons, color swatches, minimap, split bars, version label) now has an accessible name and visible focus ring; toggle buttons (HOLD, REL, LIVE, …) announce on/off state; the main reading is a polite live region with active status flags spoken alongside the value; the graph exposes a one-line text summary; top bar / main / status are marked as landmarks; arrow keys drive custom widgets (minimap pan, split-bar resize, color sliders); the
?overlay traps focus. - Hover tooltips on every control — every button, toggle, link, and setting shows a descriptive tooltip with its keyboard shortcut. Device-command tooltips (HOLD, REL, RANGE, …) use device-agnostic wording so they stay accurate across meter families.
- Graph box zoom — Shift-drag to draw a rectangle; on release the view zooms to that time and value range. Escape cancels mid-drag. A new Reset Zoom toolbar button (also bound to double-click on the plot) returns the graph to live follow with auto Y.
- Help overlay covers mouse gestures — the
?popup (renamed "Keyboard & Mouse") documents drag pan, Shift-drag zoom, wheel zoom, double-click reset, cursor placement, and minimap drag. - Reconnect progress is visible — the connection-status button shows the current attempt counter ("Reconnecting (attempt 3)…") with the latest failure reason in its tooltip, and a Disconnect click is picked up immediately instead of after the next retry interval (up to 2 s).
- VC-890 LoZ and VOID badges — the GUI and CLI now surface the meter's low-impedance measurement indicator and the "reading voided" marker alongside the other status flags, and the screen-reader live region announces them when they toggle.
Bug fixes
- Status text colors now meet WCAG AA contrast in every theme/preset combination — six status colors fell short on one background (e.g. success toasts and the LIVE indicator in light mode, error toasts in dark mode); a new automated check verifies every text color numerically in both themes.
- Settings and CSV exports can no longer be corrupted by a crash mid-write — both are written to a temporary file and atomically renamed into place, so a kill or full disk leaves the previous file intact instead of a truncated one.
- UT803/UT804 protocol overhaul — negative readings now show as negative (the sign bit was previously misread as HOLD), values get the right decimal point (the position tables were inverted), most UT804 mode names and units are corrected (e.g. the µA/mA/A and temperature dial positions were mislabeled), overload displays as OL instead of a bogus number, and the UT803 — which speaks a different frame layout than the UT804 — gets its own parser instead of one that rejected every frame.
- UT181A mode names and temperature units fixed — AC+DC current modes are no longer mislabeled "Hz", mV DC Peak / continuity-open / diode-alarm modes are named correctly, the temperature display arrangements (T1/T2 and differentials) are distinguished, and °C/°F unit strings display properly instead of "�C"/"�F". COMP mode limits now show the right number of decimal places, and the HOLD command uses the button-code form expected by the meter.
- UT171 frame decoding fixed — the frame length and checksum were computed with an off-by-one model, so a real UT171 could never deliver a valid reading; framing now matches the meter's actual format (identical to UT181A). Resistance readings now carry the right magnitude (kΩ/MΩ on upper ranges), and the second display on AC voltage modes is labeled as a frequency in kHz.
- UT61E+ recovers from a corrupted frame on the next reading — corrupt data is discarded after a checksum error (matching the vendor software); previously the bad frame stayed buffered and every subsequent reading failed until reconnect.
- UT8802/UT8803 connections no longer stall on unexpected frames — a non-measurement or corrupt-length frame is now skipped immediately instead of blocking measurement extraction until an internal buffer filled (~65 s of lost readings).
- UT8803 readings now show the correct unit magnitude — the displayed unit carries the range's prefix (mV, kΩ, MΩ, mH, nF/µF/mF, kHz/MHz) as the meter's value is range-relative; previously a 5.999 kΩ reading showed as "5.999 Ω". The ESR sub-modes (Inductance R, Capacitance R) now report Ω instead of H/F, the Q/D factor sub-modes are unitless, and the DC indicator follows the dial mode.
- UT8802 and UT8803 no longer send a spurious 0x5A byte on connect — the vendor software never sends it to these meters (it belongs to a different meter family's init); they stream unprompted.
- UT8802 readings no longer have their digits reversed — the display digits were decoded in the opposite order from the meter's wire format (e.g. a reading of 1234.5 displayed as 5432.1). Overload is now detected from the meter's overload flag instead of a display-digit heuristic, and the AC/DC indicator now follows the dial position (the byte previously read as "AC/DC coupling" is actually the diode/SCR probe-direction indicator).
- VC-890 low-pass (ACV LPF) mode now reports its fixed 1000V range — the vendor protocol ignores the range byte in LPF mode, so the range label no longer depends on an undefined byte (previously it could show 6V–1000V or nothing).
- Graph frame rate no longer degrades as history grows — per-frame work now scales with the visible window instead of the full 10 000-point buffer, and the minimap keeps a steady frame rate at full history.
- Graph drag-to-pan works in live mode — starting a drag in live mode snaps the view to the current end of data and drops out of live, matching the scroll-wheel behaviour. Previously drag was silently inert while following live data.
- Graph X-axis labels gain sub-second precision — tight zooms (e.g. a 0.5 s span) no longer produce duplicate "9 s" labels; labels now use the grid step to add decimals when it's sub-second (e.g. "9.25 s", "1m 30.5s").
- Graph X axis reflects acquisition time, not UI drain time — each point carries the timestamp captured at the device read, so UI work or CPU load no longer warps the X axis during drag/zoom/toasts.
- Measurement pacing no longer drifts — the GUI acquisition thread and the CLI
read/debugloops sleep until the next tick boundary instead of adding a fixed delay after each read, so sample cadence stays on schedule under load. - Recorded and exported timestamps reflect acquisition time — recorded samples and CLI CSV/JSON rows now use each measurement's acquisition time instead of the moment the row was formatted, so exported timestamps line up with when the device produced each reading.
- Integrator flags skipped intervals — intervals longer than the 2 s limit are still skipped (to avoid post-disconnect spikes), but the CLI
read --integratesummary and the GUI stats panel now show the skipped count instead of silently reporting0, and the first skip logs a warning. - Mock meter no longer shows impossible states — Peak and MIN/MAX can't be active at once (each ends the other, as on the real meter), and Peak presses are ignored in DC modes like the real UT61E+.
- Mock waveforms are smoother and loop seamlessly — scenario waveforms are now driven by elapsed time and are periodic over their duration, so scheduling jitter no longer shows as visible noise and pinned mock modes no longer snap at the loop point. The capacitance and temperature sawtooths became triangles for the same reason.
Full Changelog: v0.4.0...v0.5.0