Skip to content

NumberWidget: color the displayed value by its Tag's thresholds (alarm-aware KPI tiles) #219

Description

@HanSur94

Problem / motivation

The big-number NumberWidget is the canonical dashboard KPI tile — it shows a single live scalar bound to a Tag (e.g. "Pressure: 152 psi"). But it renders that value in a fixed foreground color regardless of whether the reading is nominal or in alarm. A value that crosses its MonitorTag limit looks identical to a healthy one, so the most-glanced-at widget on a dashboard cannot answer the core sensor-analysis question: is this reading in alarm?

This is a clean feature asymmetry. Every other value-display widget already colors its value by the bound Tag's thresholds:

Widget colors value by Sensor.Thresholds?
GaugeWidget ✅ (getValueColor, GaugeWidget.m:252; threshold walk :290)
StatusWidget ✅ (threshold walk StatusWidget.m:391-392)
MultiStatusWidget
IconCardWidget
NumberWidget
SparklineCardWidget

grep -ciE "threshold|zone" confirms the gap: Status 43, MultiStatus 41, IconCard 41, Gauge 36, vs NumberWidget 3 (all in the trend-arrow slope test computeTrend, NumberWidget.m:222-225) and SparklineCard 1 (a layout doc-comment). Neither actually colors its value.

Proposed feature

Add an opt-in ColorByThreshold mode to NumberWidget. When enabled and the bound Tag carries thresholds, color the value text by the active threshold zone — reusing the same Sensor.Thresholds evaluation Gauge/Status already perform. Default off, so every existing dashboard renders byte-identically.

Rough sketch

  • Lib/class: libs/Dashboard/NumberWidget.m (primary). SparklineCardWidget is a natural sibling follow-up, kept out of scope here.
  • Public API: new property ColorByThreshold = false (logical). Optionally allow an explicit override color map later, but the MVP just reads the Tag.
  • Where: in refresh() after obj.CurrentValue is computed (NumberWidget.m:110-148), if ColorByThreshold && ~isempty(obj.Sensor) && ~isempty(obj.Sensor.Thresholds), resolve the zone color for CurrentValue and set(obj.hValueText, 'ForegroundColor', zoneColor); otherwise keep the current fgColor path (render, :81).
  • Reuse: factor the value→zone-color logic so Gauge/Status/Number share one helper rather than three copies (the threshold-walk already exists in GaugeWidget.getValueColor / StatusWidget).
  • Serialization: add s.colorByThreshold to toStruct/fromStruct (:188, :234), omitting it when default so legacy serialized dashboards stay diff-invisible.

Value

High. Alarm/threshold ergonomics on the most-used KPI tile — a pressure/temperature reading that turns red (or amber) the moment it crosses its MonitorTag limit is exactly what a sensor-analysis engineer expects from a number tile, and today it's impossible without dropping to a Gauge/Status widget.

Constraints check

  • Toolbox-free: ✅ — only set(..., 'ForegroundColor', ...) and the existing threshold evaluation; no toolboxes.
  • Backward-compatible: ✅ — ColorByThreshold defaults false; omitted from toStruct when default, so existing scripts and serialized dashboards are unaffected.
  • Pure MATLAB/Octave: ✅.
  • Widget contract: ✅ — works entirely through the existing DashboardWidget/Sensor (Tag) binding; no engine or contract change.

Effort estimate

S–M — single file plus a small shared threshold→color helper (the threshold-walk pattern already exists in Gauge/Status). No new dependencies, no public-surface migration.


AI-proposed via /feature-scout — needs a human product decision before implementation.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions