Skip to content

Add drafty human friendly current metrics#4433

Open
NeimadTL wants to merge 17 commits intomeshtastic:mainfrom
NeimadTL:add-human-friendly-metrics
Open

Add drafty human friendly current metrics#4433
NeimadTL wants to merge 17 commits intomeshtastic:mainfrom
NeimadTL:add-human-friendly-metrics

Conversation

@NeimadTL
Copy link
Contributor

@NeimadTL NeimadTL commented Feb 3, 2026

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

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
Copy link

codecov bot commented Feb 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (5fc7e46) to head (77cb56b).

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.
📢 Have feedback on the report? Share it here.

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.
@NeimadTL NeimadTL marked this pull request as ready for review February 6, 2026 14:14
@NeimadTL
Copy link
Contributor Author

NeimadTL commented Feb 6, 2026

@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 feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt. Thank you.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_UNITS and AMPERE_UNITS constant maps to support unit conversion
  • Updated current display in EnvironmentMetrics.kt to 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

@DaneEvans DaneEvans marked this pull request as draft February 7, 2026 01:53
Copy link
Collaborator

@DaneEvans DaneEvans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@NeimadTL
Copy link
Contributor Author

NeimadTL commented Feb 9, 2026

Thanks for your feedback @DaneEvans. Made the changes but looks like there other issue which I haven't figured out yet. Any suggestion please ?
Screenshot

FYI, the same warning appears the last CI run:

Configure project :core:strings
w: ⚠️ Unused Kotlin Source Sets
The Kotlin source set commonTest was configured but not added to any Kotlin compilation.
Solution: You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.
See https://kotl.in/connecting-source-sets

NeimadTL and others added 2 commits February 9, 2026 13:44
Spotless have been applied sucessfully but not sure issues are
resolved (attempt#1).
Copy link
Collaborator

@jamesarich jamesarich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

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),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@NeimadTL NeimadTL marked this pull request as ready for review March 4, 2026 23:37
@NeimadTL
Copy link
Contributor Author

NeimadTL commented Mar 4, 2026

Thank your help and feedback @jamesarich. Whenever you've got some time, please a look again, thanks.

@NeimadTL NeimadTL requested a review from jamesarich March 4, 2026 23:39
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 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.
/*

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +52 to +58
add(
VectorMetricInfo(
Res.string.channel_1,
"%.1fA".format(ch1_current?.milliToBase),
Icons.Rounded.Power,
),
)
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
VerticalAxis.rememberStart(
label = ChartStyling.rememberAxisLabel(color = currentColor),
valueFormatter = { _, value, _ -> "%.0f mA".format(value) },
valueFormatter = { _, value, _ -> "%.0f A".format(value.toFloat().milliToBase) },
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
valueFormatter = { _, value, _ -> "%.0f A".format(value.toFloat().milliToBase) },
valueFormatter = { _, value, _ -> "%.1f A".format(value.toFloat().milliToBase) },

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +63
/**
* 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
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
items.add {
PowerInfo(
value = "%.1fmA".format(env.current ?: 0f),
value = "%.1fA".format(env.current?.milliToBase ?: 0f),
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
@jamesarich
Copy link
Collaborator

@NeimadTL take a look at copilot's review. I'm also a bit unsure about this normalization - forcing everything into amps with %sA kinda seems like a bad plan, especially as locales may have different ways of displaying unit labels.

maybe you can take a look at https://github.com/jacobras/Human-Readable for inspiration as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants