MetricFormatter
Centralized metric formatting for display strings. Eliminates duplicated formatString patterns across Node, NodeItem, and metric screens.
Every number here reads in the user's locale; the unit symbols are fixed. Units that vary by locale (wind speed, rainfall, weight) pick their symbol from MeasureUnitKind via formatMeasure; the universal ones (V, mA, dB, dBm, hPa, %, °C) carry theirs inline. Either way the value goes through NumberFormatter.format, so a German reader sees "3,85 V" and "0,0°C".
The one thing that stays locale-independent is anything another system parses — see NumberFormatter.formatInvariant.
Functions
The degree symbol for the display unit, for callers that already hold a converted value and only need to label it — chart axes, and the telemetry rows fed by an upstream conversion. Defined here so a symbol has one definition: wind drifted between screens precisely because its unit was written out twice.
Formats a received signal strength, or UNKNOWN_VALUE when the radio reported none. 0 dBm is a legitimate reading on some radios, so it must never stand in for a missing one.
Wind arrives from the sensor in m/s and is shown in the unit a reader expects for weather: km/h for metric, mph for imperial. m/s is the meteorological observation standard, but public forecasts across most metric regions quote km/h, so that is what the app displays. The charts convert the same way — see chartValue — so a reading reads identically on the node card and on its graph.