Add drafty human friendly current metrics#4433
Add drafty human friendly current metrics#4433NeimadTL wants to merge 17 commits intomeshtastic:mainfrom
Conversation
Currently, the metircs are always shown with the in milliampere (e.i, using the "mA" unit). A nicer way would be to have thoses metrics shown in more friendly way (e.g, 1A -> 1A, 1000A -> 1kA, 0.000001A -> 1μA...etc). At this point, this is just a draft of how it would be implemented.
The ranges were wrong and therefore gave wrong result so they've been fixed. Also, there's possiblity of the function using custom units instead of the default ones. This new parameter is optional as one may be just fine the default units.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4433 +/- ##
=====================================
Coverage 0.00% 0.00%
=====================================
Files 3 3
Lines 231 231
Branches 34 34
=====================================
Misses 231 231 ☔ View full report in Codecov by Sentry. |
Spotless has been apply to fix formatting and linting errors.
Both base and ampere units have been change into constants as gradle suggested it and also because it makes more sense.
|
@DaneEvans @jamesarich, could you please have look when you'll get some time please (couldn't assign it you for some reason). Also, I've got to do the same for the distance metric but couldn't find it in |
There was a problem hiding this comment.
Pull request overview
This pull request implements human-friendly formatting for electrical current metrics in the Environment Metrics display. Instead of always showing values in milliamperes (mA), it introduces intelligent unit scaling (μA, mA, A, kA, etc.) based on the magnitude of the value. This addresses issue #2944, which requests more readable display of large metric values (e.g., showing "18.2 A" instead of "18200 mA").
Changes:
- Added
numberToHuman()utility function to convert numeric values to human-readable format with appropriate SI prefixes - Added
BASE_UNITSandAMPERE_UNITSconstant maps to support unit conversion - Updated current display in
EnvironmentMetrics.ktto use the new formatting function
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| core/model/src/main/kotlin/org/meshtastic/core/model/util/UnitConversions.kt | Adds numberToHuman function and unit conversion constants for intelligent unit prefix selection |
| feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt | Updates current value display to use numberToHuman instead of fixed mA formatting |
core/model/src/main/kotlin/org/meshtastic/core/model/util/UnitConversions.kt
Outdated
Show resolved
Hide resolved
core/model/src/main/kotlin/org/meshtastic/core/model/util/UnitConversions.kt
Outdated
Show resolved
Hide resolved
core/model/src/commonMain/kotlin/org/meshtastic/core/model/util/UnitConversions.kt
Show resolved
Hide resolved
core/model/src/main/kotlin/org/meshtastic/core/model/util/UnitConversions.kt
Outdated
Show resolved
Hide resolved
core/model/src/main/kotlin/org/meshtastic/core/model/util/UnitConversions.kt
Outdated
Show resolved
Hide resolved
core/model/src/main/kotlin/org/meshtastic/core/model/util/UnitConversions.kt
Outdated
Show resolved
Hide resolved
core/model/src/main/kotlin/org/meshtastic/core/model/util/UnitConversions.kt
Outdated
Show resolved
Hide resolved
core/model/src/main/kotlin/org/meshtastic/core/model/util/UnitConversions.kt
Outdated
Show resolved
Hide resolved
DaneEvans
left a comment
There was a problem hiding this comment.
Go through the copilot stuff, most of it seems sensible.
Remember we want this generalised for all units / areas eventually (windspeed, distance etc.)
The mappings should be unnecessary, especially to amps in particular.
A string insertion ala (python) f"{f0.2 value}{SIprefix}{ Unit}" should be adequate.
The original formatting was the number with two decimal places (e.i, "% 2f") so the returned value is formatted that way now. The exponent logic wasn't straightforward and complex? It's been replaced the following formulae `(exponent / 3) * 3` which will round down to the nearest multiple of 3 in the exponent calculation is not a multiple of 3. The base unit map was inverted a second time to assign the unit so it's been updated.
This makes sure that number less than or equal to 0 are handle before attempting calculations and "N/A" will be returned in that case. Metrics like current and distance are stored respectively in milliampere (mA) and millimeters (mm) so before doing calculations and determining the units, the metrics have to be converted in base unit (e.i, divided by 1000) first.
We eventually decided to display metrics in base unit to make things simpler so therefore mapping exponent to unit is no longer needed.
Tests cases have been added to make sure the fonction works as expected.
|
Thanks for your feedback @DaneEvans. Made the changes but looks like there other issue which I haven't figured out yet. Any suggestion please ? FYI, the same warning appears the last CI run:
|
Spotless have been applied sucessfully but not sure issues are resolved (attempt#1).
jamesarich
left a comment
There was a problem hiding this comment.
one thing you missed - you updated the EnvironmentMetrics chart to use base Amperes, but there are still several places hardcoded to display the raw telemetry values as mA.
for the app to be consistent, you need to wrap the raw values in your new convertToBaseUnit() and swap the string formatting over to A in these files:
- feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt (lines ~280 and ~386)
- feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt (line ~51, and the other channels)
- feature/node/src/main/kotlin/org/meshtastic/feature/node/component/EnvironmentMetrics.kt (line ~103)
- feature/node/src/main/kotlin/org/meshtastic/feature/node/component/NodeItem.kt (line ~365)
core/model/src/commonMain/kotlin/org/meshtastic/core/model/util/UnitConversions.kt
Outdated
Show resolved
Hide resolved
feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt
Outdated
Show resolved
Hide resolved
| val current = convertToBaseUnit(envMetrics.current!!) | ||
| Text( | ||
| text = "%s %.2f mA".format(stringResource(Res.string.current), current), | ||
| text = "%s %.2f A".format(stringResource(Res.string.current), current), |
There was a problem hiding this comment.
Move String formatting into Compose Resources. Rather than doing "%s %.2f A".format(stringResource(Res.string.current), current) inside the Composable, it is best practice to move the format specifier directly into the strings.xml. e.g., <string name="current_value_format">%1$s %2$.2f A</string> And then in Compose: stringResource(Res.string.current_value_format, stringResource(Res.string.current), current) This is safer for localization and keeps your UI code much cleaner.
There was a problem hiding this comment.
Ok, thanks. Though, I did it only that specific file, please let me know it's need to been done in the other files as well. If so, I'll need to tweak the newly added current_value_format.
An Float extension was made out of the previous `convertToBaseUnit` method as it's more Kotlin-ish and the remaining files using ampere value have been updated.
For best pratice, the current's formatting has been added to the strings resources.
This gets rid of the double bang operator `!!` in favour of `let` which is more Kotlin-ish.
|
Thank your help and feedback @jamesarich. Whenever you've got some time, please a look again, thanks. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
feature/node/src/main/kotlin/org/meshtastic/feature/node/component/PowerMetrics.kt:1
String.format("%.1fA", ...)will throw at runtime if the argument isnull(because%frequires a numeric type, notnull). Use a non-null value (e.g.,ch1_current?.milliToBase ?: 0f) or avoid adding the current metric when the current isnull(or show a placeholder like"--"). Also note line 67 duplicatesIcons.Rounded.Powerin the snippet—if that’s not a transcription issue, it should be removed to keep the argument list correct.
/*
You can also share your feedback on Copilot code review. Take the survey.
| add( | ||
| VectorMetricInfo( | ||
| Res.string.channel_1, | ||
| "%.1fA".format(ch1_current?.milliToBase), | ||
| Icons.Rounded.Power, | ||
| ), | ||
| ) |
There was a problem hiding this comment.
String.format("%.1fA", ...) will throw at runtime if the argument is null (because %f requires a numeric type, not null). Use a non-null value (e.g., ch1_current?.milliToBase ?: 0f) or avoid adding the current metric when the current is null (or show a placeholder like "--"). Also note line 67 duplicates Icons.Rounded.Power in the snippet—if that’s not a transcription issue, it should be removed to keep the argument list correct.
| VerticalAxis.rememberStart( | ||
| label = ChartStyling.rememberAxisLabel(color = currentColor), | ||
| valueFormatter = { _, value, _ -> "%.0f mA".format(value) }, | ||
| valueFormatter = { _, value, _ -> "%.0f A".format(value.toFloat().milliToBase) }, |
There was a problem hiding this comment.
Formatting the converted amperage with "%.0f" will incorrectly round many common values to 0 A (e.g., 500 mA → 0.5 A → "0 A"). Use a formatter with decimals (e.g., 1–2dp) or an adaptive formatter that preserves sub-amp values.
| valueFormatter = { _, value, _ -> "%.0f A".format(value.toFloat().milliToBase) }, | |
| valueFormatter = { _, value, _ -> "%.1f A".format(value.toFloat().milliToBase) }, |
| /** | ||
| * Converts numbers from milli to unit. examples: | ||
| * - 1000 milliamperes will be converted into 1 ampere, | ||
| * - 100 millimeters to 0.1 meters | ||
| */ | ||
| @Suppress("MagicNumber") | ||
| val Float.milliToBase: Float | ||
| get() = this / 1000f |
There was a problem hiding this comment.
milliToBase is new behavior used in multiple UI surfaces but isn’t covered by unit tests. Add a small set of tests (positive, zero, negative, and fractional milli values) to validate the conversion and guard against regression.
| items.add { | ||
| PowerInfo( | ||
| value = "%.1fmA".format(env.current ?: 0f), | ||
| value = "%.1fA".format(env.current?.milliToBase ?: 0f), |
There was a problem hiding this comment.
This hardcodes both formatting and the "A" unit in code, while feature/node/.../metrics/EnvironmentMetrics.kt uses a string resource (current_value_format). Consider centralizing current formatting via a shared string resource (or a single formatter helper) to keep unit spacing/precision consistent and make localization easier.
|
@NeimadTL take a look at copilot's review. I'm also a bit unsure about this normalization - forcing everything into amps with maybe you can take a look at https://github.com/jacobras/Human-Readable for inspiration as well. |

Currently, the metrics are always shown with the in milliampere (e.i, using the "mA" unit). A nicer way would be to have those metrics shown in more friendly way (e.g, 1A -> 1A, 1000A -> 1kA, 0.000001A -> 1μA...etc). At this point, this is just a draft of how it would be implemented.
Related to #2944