Skip to content

fix: round energy/power values to avoid total_increasing violations#1918

Open
rishabhvaish wants to merge 3 commits intolbbrhzn:mainfrom
rishabhvaish:fix/energy-session-float-precision
Open

fix: round energy/power values to avoid total_increasing violations#1918
rishabhvaish wants to merge 3 commits intolbbrhzn:mainfrom
rishabhvaish:fix/energy-session-float-precision

Conversation

@rishabhvaish
Copy link
Copy Markdown

@rishabhvaish rishabhvaish commented Mar 17, 2026

Some chargers report energy readings with floating-point noise (e.g. 65.9999999998035 Wh instead of 66 Wh). After the Wh-to-kWh division this produces values like 0.0659999999998035 instead of 0.066, which can appear to decrease slightly and triggers the HA recorder warning:

Entity sensor.charger_energy_session from integration ocpp has state class total_increasing, but its state is not strictly increasing

I've added round(..., 3) (1 Wh / 1 W precision) to all the Wh-to-kWh and W-to-kW conversion paths in chargepoint.py. This is consistent with the rounding already used for the session-energy derivation on the meter_start path (line 998).

Fixes #1916

Summary by CodeRabbit

  • Bug Fixes
    • Improved precision of energy and power readings by rounding values to three decimal places for display.
    • Standardized unit conversions so reported kWh and kW values are consistent across the app.
    • Reduced floating-point artifacts, yielding more stable and predictable energy/power values in the UI.

Some chargers report energy readings with floating-point noise
(e.g. 65.9999999998035 Wh instead of 66 Wh). After the Wh-to-kWh
conversion this produces values like 0.0659999999998035 instead of
0.066, which can appear to decrease and triggers HA warnings for
sensors with state_class total_increasing.

Round all Wh/W → kWh/kW conversions to 3 decimal places (1 Wh / 1 W
precision), consistent with the rounding already used for the
session-energy derivation on the meter_start path.

Fixes lbbrhzn#1916
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

Introduce a KILO_UNIT_PRECISION constant and apply rounding to 3 decimal places for kW/kWh conversions in the chargepoint module: per-phase aggregation, measurand normalization, and the get_energy_kwh output are now rounded to 3 decimals.

Changes

Cohort / File(s) Summary
Energy/Power Conversion Rounding
custom_components/ocpp/chargepoint.py
Add KILO_UNIT_PRECISION = 3; replace raw division-by-1000 with round(..., KILO_UNIT_PRECISION) in _sum_l123 and process_measurands; get_energy_kwh now returns values rounded to 3 decimals and its docstring updated to document the rounding contract. Watch for changes in numeric equality, ordering, and tests relying on prior precision.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I nibble digits, tender and bright,
I trim them thrice to set values right.
No jitter now where currents flow,
Small hops of three decimals — steady glow. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: rounding energy/power values to prevent total_increasing violations due to floating-point precision issues.
Linked Issues check ✅ Passed The pull request directly addresses issue #1916 by implementing rounding to 3 decimals for all W→kW and Wh→kWh conversions, which prevents floating-point noise from causing apparent state decreases.
Out of Scope Changes check ✅ Passed All changes are focused on rounding conversions in chargepoint.py; no unrelated modifications are present outside the stated objectives of fixing floating-point precision issues.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
custom_components/ocpp/chargepoint.py (1)

824-833: Consider centralizing precision to avoid drift across conversion paths.

3 is now repeated in multiple conversion branches. A shared constant (or helper) would keep behavior consistent if precision policy changes.

♻️ Suggested refactor
+KILO_UNIT_ROUND_DIGITS = 3
...
- self._metrics[(target_cid, metric)].value = round(metric_value / 1000, 3)
+ self._metrics[(target_cid, metric)].value = round(
+     metric_value / 1000, KILO_UNIT_ROUND_DIGITS
+ )
...
- return round(measurand_value.value / 1000, 3)
+ return round(measurand_value.value / 1000, KILO_UNIT_ROUND_DIGITS)
...
- return round(measurand_value.value, 3)
+ return round(measurand_value.value, KILO_UNIT_ROUND_DIGITS)
...
- value = round(value / 1000, 3)
+ value = round(value / 1000, KILO_UNIT_ROUND_DIGITS)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@custom_components/ocpp/chargepoint.py` around lines 824 - 833, Centralize the
rounding precision used in the energy conversion code by replacing the
duplicated literal 3 with a shared constant or helper used by the conversion
function that handles measurand_value (the function that "Convert energy value
from charger to kWh"); introduce a single PRECISION (e.g., PRECISION_KWH) or a
helper like round_kwh(value) and use it in both branches where
measurand_value.unit is "Wh" or None and the fallback branch so future precision
changes affect all paths consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@custom_components/ocpp/chargepoint.py`:
- Around line 824-833: Centralize the rounding precision used in the energy
conversion code by replacing the duplicated literal 3 with a shared constant or
helper used by the conversion function that handles measurand_value (the
function that "Convert energy value from charger to kWh"); introduce a single
PRECISION (e.g., PRECISION_KWH) or a helper like round_kwh(value) and use it in
both branches where measurand_value.unit is "Wh" or None and the fallback branch
so future precision changes affect all paths consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d8a8f4a9-45c9-4736-b3ac-37790a25d07b

📥 Commits

Reviewing files that changed from the base of the PR and between bd8e8f6 and d174ae5.

📒 Files selected for processing (1)
  • custom_components/ocpp/chargepoint.py

@rishabhvaish rishabhvaish temporarily deployed to continuous-integration March 17, 2026 16:16 — with GitHub Actions Inactive
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.92%. Comparing base (bd8e8f6) to head (d174ae5).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1918   +/-   ##
=======================================
  Coverage   94.92%   94.92%           
=======================================
  Files          12       12           
  Lines        2957     2957           
=======================================
  Hits         2807     2807           
  Misses        150      150           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
custom_components/ocpp/chargepoint.py (1)

828-836: Centralize the precision constant to avoid future drift.

The logic is good, but the repeated 3 precision literal across conversion paths is easy to desynchronize later. Consider a shared constant (e.g., KILO_UNIT_PRECISION = 3) reused here and in other conversion branches.

♻️ Suggested refactor
+KILO_UNIT_PRECISION = 3
+
 `@staticmethod`
 def get_energy_kwh(measurand_value: MeasurandValue) -> float:
@@
-    if (measurand_value.unit == "Wh") or (measurand_value.unit is None):
-        return round(measurand_value.value / 1000, 3)
-    return round(measurand_value.value, 3)
+    if (measurand_value.unit == "Wh") or (measurand_value.unit is None):
+        return round(measurand_value.value / 1000, KILO_UNIT_PRECISION)
+    return round(measurand_value.value, KILO_UNIT_PRECISION)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@custom_components/ocpp/chargepoint.py` around lines 828 - 836, Add a
module-level constant (e.g., KILO_UNIT_PRECISION = 3) and replace the hardcoded
precision literal in the energy conversion function that contains the "Convert
energy value from charger to kWh" docstring with that constant; update both
branches that call round(measurand_value.value / 1000, 3) and
round(measurand_value.value, 3) to use KILO_UNIT_PRECISION, and also reuse that
constant in other related conversion branches in chargepoint.py to keep
precision centralized and consistent (refer to the measurand_value usage in the
conversion function to locate the code).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@custom_components/ocpp/chargepoint.py`:
- Around line 828-836: Add a module-level constant (e.g., KILO_UNIT_PRECISION =
3) and replace the hardcoded precision literal in the energy conversion function
that contains the "Convert energy value from charger to kWh" docstring with that
constant; update both branches that call round(measurand_value.value / 1000, 3)
and round(measurand_value.value, 3) to use KILO_UNIT_PRECISION, and also reuse
that constant in other related conversion branches in chargepoint.py to keep
precision centralized and consistent (refer to the measurand_value usage in the
conversion function to locate the code).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6587efe5-c2bd-497a-bd39-b49cf11fe9b1

📥 Commits

Reviewing files that changed from the base of the PR and between d174ae5 and a4db620.

📒 Files selected for processing (1)
  • custom_components/ocpp/chargepoint.py

Centralizes the repeated precision literal (3) used when converting
W→kW and Wh→kWh into a single KILO_UNIT_PRECISION constant to
prevent future drift.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
custom_components/ocpp/chargepoint.py (1)

1005-1011: Consider using KILO_UNIT_PRECISION for consistency.

Line 1008 computes session energy using round(1000 * (value - ms_metric.value)) / 1000, which achieves the same 3-decimal precision but with a different expression. For maintainability and to leverage the new constant:

♻️ Suggested refactor for consistency
                             elif ms_metric.unit == unit:
                                 self._metrics[
                                     (target_cid, csess.session_energy.value)
-                                ].value = round(1000 * (value - ms_metric.value)) / 1000
+                                ].value = round(value - ms_metric.value, KILO_UNIT_PRECISION)
                                 self._metrics[
                                     (target_cid, csess.session_energy.value)
                                 ].unit = unit
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@custom_components/ocpp/chargepoint.py` around lines 1005 - 1011, Replace the
hard-coded 3-decimal rounding expression in the block that assigns session
energy (inside the ms_metric.unit == unit branch where
self._metrics[(target_cid, csess.session_energy.value)].value is set) with the
existing KILO_UNIT_PRECISION constant to standardize precision; update the
assignment to round to KILO_UNIT_PRECISION (e.g., using round((value -
ms_metric.value), KILO_UNIT_PRECISION) or equivalent) and leave the unit
assignment unchanged so the metric uses the shared precision constant.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@custom_components/ocpp/chargepoint.py`:
- Around line 1005-1011: Replace the hard-coded 3-decimal rounding expression in
the block that assigns session energy (inside the ms_metric.unit == unit branch
where self._metrics[(target_cid, csess.session_energy.value)].value is set) with
the existing KILO_UNIT_PRECISION constant to standardize precision; update the
assignment to round to KILO_UNIT_PRECISION (e.g., using round((value -
ms_metric.value), KILO_UNIT_PRECISION) or equivalent) and leave the unit
assignment unchanged so the metric uses the shared precision constant.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4bb68bb0-455f-4eb1-b63f-b6620bbb3748

📥 Commits

Reviewing files that changed from the base of the PR and between a4db620 and 42d7b85.

📒 Files selected for processing (1)
  • custom_components/ocpp/chargepoint.py

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.

Entity sensor.charger_energy_session from integration ocpp has state class total_increasing, but its state is not strictly increasing

1 participant