Skip to content

fix(sensor): use TOTAL state class for computed e_consumption_today#143

Merged
dewet22-claude merged 1 commit into
mainfrom
fix/consumption-today-state-class
Jun 8, 2026
Merged

fix(sensor): use TOTAL state class for computed e_consumption_today#143
dewet22-claude merged 1 commit into
mainfrom
fix/consumption-today-state-class

Conversation

@dewet22

@dewet22 dewet22 commented Jun 8, 2026

Copy link
Copy Markdown
Owner

What

Changes e_consumption_today from SensorStateClass.TOTAL_INCREASING to SensorStateClass.TOTAL.

Why

e_consumption_today is a computed field — givenergy-modbus derives it as PV gen + grid-in - grid-out - AC-charge from several registers polled at slightly different times. When one component updates before the others, the result can transiently dip by a few Wh, triggering HA's strictly-increasing guard and logging the warning reported in #142.

The other daily energy sensors (e_pv_day, e_grid_in_day, e_grid_out_day, the battery day totals) are direct hardware counters that only increment, so they correctly stay TOTAL_INCREASING. Only the computed sensor needs the relaxed class.

Both TOTAL and TOTAL_INCREASING produce sum-type long-term statistics in HA's recorder, so existing history is unaffected.

Fixes #142

The sensor is derived (PV gen + grid-in - grid-out - AC-charge) from
registers polled at slightly different times, so a reading can
transiently dip by a few Wh when one component updates before the others.
TOTAL_INCREASING's strictly-increasing guard fires a spurious HA warning
in that case. TOTAL is the correct class for a computed daily cumulative
that may briefly fluctuate; direct hardware counters (PV, grid, battery
day totals) are unaffected and stay TOTAL_INCREASING.

Fixes #142

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request changes the state class of the 'House Consumption Today' (e_consumption_today) sensor from SensorStateClass.TOTAL_INCREASING to SensorStateClass.TOTAL. This prevents transient dips in computed values, caused by registers being polled at slightly different times, from triggering Home Assistant's strictly-increasing guard. There are no review comments, and I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@dewet22-claude dewet22-claude merged commit 6879db5 into main Jun 8, 2026
11 checks passed
@dewet22-claude dewet22-claude deleted the fix/consumption-today-state-class branch June 8, 2026 17:58
@dewet22-codex

Copy link
Copy Markdown
Collaborator

I’ve started reviewing this PR.

native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
state_class=SensorStateClass.TOTAL,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

TOTAL only starts a new recorder statistics cycle when the entity supplies a changed last_reset; this entity does not supply one. With this change, the normal midnight drop from the day’s final consumption to zero is therefore recorded as a negative delta and subtracts the whole day from the accumulated sum, whereas TOTAL_INCREASING currently detects that drop as a reset automatically. Brief intra-day dips would cancel once the derived value catches up, but the daily reset will not. I think this needs a matching daily last_reset implementation (or a different approach that preserves reset detection) before switching the state class.

dewet22 added a commit that referenced this pull request Jun 8, 2026
…sion

The sensor is computed from several registers (PV gen + grid-in - grid-out -
AC-charge) polled at slightly different times. When one component updates
before the others the result can transiently dip by a few Wh, tripping
HA's strictly-increasing guard (#142).

Adds a `monotonic: bool` flag to `GivEnergyInverterSensorDescription` and
tracks a `_monotonic_max` in the entity. When set, `native_value` returns
`max(new, session_max)` so intra-day dips are invisible to HA's recorder.

Keeping `TOTAL_INCREASING` is important: switching to `TOTAL` (as in the
reverted #143) silently corrupts the accumulated `sum` on the midnight reset
to zero, because `TOTAL` requires an explicit `last_reset` attribute to
distinguish a reset from a decrease.

Fixes #142

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dewet22 added a commit that referenced this pull request Jun 8, 2026
…umption_today

The sensor is computed from several registers (PV gen + grid-in - grid-out -
AC-charge) polled at slightly different times. When one component updates
before the others the result can transiently dip by a few Wh, tripping
HA's strictly-increasing guard (#142).

Adds a `monotonic: bool` flag to `GivEnergyInverterSensorDescription` and
tracks `_monotonic_max` and `_monotonic_date` on the entity. When set,
`native_value`:
- resets both fields when the calendar day changes (in HA's timezone), so
  the legitimate midnight drop to zero is emitted as a real decrease and
  TOTAL_INCREASING starts a new recorder cycle;
- otherwise returns `max(new, session_max)`, hiding intra-day dips.

Keeping `TOTAL_INCREASING` is important: switching to `TOTAL` (as in the
reverted #143) silently corrupts the accumulated `sum` on the midnight reset
to zero, because `TOTAL` requires an explicit `last_reset` attribute to
distinguish a reset from a decrease.

Fixes #142

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dewet22 added a commit that referenced this pull request Jun 8, 2026
…ore for e_consumption_today

The sensor is computed from several registers (PV gen + grid-in - grid-out -
AC-charge) polled at slightly different times. When one component updates
before the others the result can transiently dip by a few Wh, tripping
HA's strictly-increasing guard (#142).

Adds a `monotonic: bool` flag to `GivEnergyInverterSensorDescription` and
tracks `_monotonic_max` and `_monotonic_date` on the entity. When set,
`native_value`:
- resets both fields when the calendar day changes (in HA's timezone), so
  the legitimate midnight drop to zero is emitted as a real decrease and
  TOTAL_INCREASING starts a new recorder cycle;
- otherwise returns `max(new, session_max)`, hiding intra-day dips.

`GivEnergyInverterSensor` now also inherits `RestoreEntity` and seeds
`_monotonic_max` from the last persisted state in `async_added_to_hass`
(only when the persisted state is from today). Without this, the first
transient-dip reading after an HA restart or integration reload would
become the new baseline, allowing the TOTAL_INCREASING warning to recur
until consumption caught up naturally.

Keeping `TOTAL_INCREASING` is important: switching to `TOTAL` (as in the
reverted #143) silently corrupts the accumulated `sum` on the midnight reset
to zero, because `TOTAL` requires an explicit `last_reset` attribute to
distinguish a reset from a decrease.

Fixes #142

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dewet22 added a commit that referenced this pull request Jun 8, 2026
…, and reset-threshold

The sensor is computed from several registers (PV gen + grid-in - grid-out -
AC-charge) polled at slightly different times. When one component updates
before the others the result can transiently dip by a few Wh, tripping
HA's strictly-increasing guard (#142).

Adds a `monotonic: bool` flag to `GivEnergyInverterSensorDescription` and
tracks `_monotonic_max` and `_monotonic_date` on the entity. When set,
`native_value` applies three layers of logic:

1. Day boundary: when HA's local date changes, reset both fields so the
   midnight drop to zero passes through as a real decrease and
   TOTAL_INCREASING starts a new recorder cycle.

2. Large-drop reset (clock-drift guard): if the value drops by more than
   _MONOTONIC_RESET_THRESHOLD (0.5 kWh) on the same HA date, treat it as a
   genuine source reset. This handles inverter clocks that lag HA's midnight
   by one scan interval — the actual counter reset arrives on a subsequent
   read after the day boundary has already been committed.

3. Intra-day clamp: otherwise, return max(new, session_max) to hide the
   transient few-Wh dips that triggered the original warning.

`GivEnergyInverterSensor` also inherits `RestoreEntity` and seeds
`_monotonic_max` from the last persisted state in `async_added_to_hass`
(only when the persisted state is from today) so a transient-dip first
reading after an HA restart does not undercut the recorder's prior value.

Keeping `TOTAL_INCREASING` is important: switching to `TOTAL` (as in the
reverted #143) silently corrupts the accumulated `sum` on the midnight reset
to zero, because `TOTAL` requires an explicit `last_reset` attribute to
distinguish a reset from a decrease.

Fixes #142

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dewet22-claude pushed a commit that referenced this pull request Jun 8, 2026
…, and reset-threshold (#144)

The sensor is computed from several registers (PV gen + grid-in - grid-out -
AC-charge) polled at slightly different times. When one component updates
before the others the result can transiently dip by a few Wh, tripping
HA's strictly-increasing guard (#142).

Adds a `monotonic: bool` flag to `GivEnergyInverterSensorDescription` and
tracks `_monotonic_max` and `_monotonic_date` on the entity. When set,
`native_value` applies three layers of logic:

1. Day boundary: when HA's local date changes, reset both fields so the
   midnight drop to zero passes through as a real decrease and
   TOTAL_INCREASING starts a new recorder cycle.

2. Large-drop reset (clock-drift guard): if the value drops by more than
   _MONOTONIC_RESET_THRESHOLD (0.5 kWh) on the same HA date, treat it as a
   genuine source reset. This handles inverter clocks that lag HA's midnight
   by one scan interval — the actual counter reset arrives on a subsequent
   read after the day boundary has already been committed.

3. Intra-day clamp: otherwise, return max(new, session_max) to hide the
   transient few-Wh dips that triggered the original warning.

`GivEnergyInverterSensor` also inherits `RestoreEntity` and seeds
`_monotonic_max` from the last persisted state in `async_added_to_hass`
(only when the persisted state is from today) so a transient-dip first
reading after an HA restart does not undercut the recorder's prior value.

Keeping `TOTAL_INCREASING` is important: switching to `TOTAL` (as in the
reverted #143) silently corrupts the accumulated `sum` on the midnight reset
to zero, because `TOTAL` requires an explicit `last_reset` attribute to
distinguish a reset from a decrease.

Fixes #142

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

Has state class total_increasing, but its state is not strictly increasing

3 participants