Skip to content

Conversation

@upsuper
Copy link
Contributor

@upsuper upsuper commented Dec 7, 2025

Proposed change

Ecowtt products produce many rain count sensors, there are largely three types:

  • Total sensors - lifetime accumulating
  • Resetting sensors - resetting to zero in certain condition, e.g. daily or after 24h no rain
  • Rolling window sensors - total for the last X period

Total sensors and resetting sensors should have state class of TOTAL_INCREASING, and the rolling window ones should have MEASUREMENT as their state class correspondingly.

Previously, the code only counted hourly sensors as rolling window sensors, and #155321 was raised because 24h sensors are also rolling window but the code didn't take that into consideration.

As described in #157882 (comment), #155358 by @ogruendel was raised to fix the above issue, however, it was done in a wrong approach by changing all resetting sensors to TOTAL, and subsequent PRs #155812 and #157409 are heading down the wrong way further by completely removing the state class, causing long-term statistics for those resetting sensors to be deleted.

This PR reverts the three aforementioned PRs, and tries to assign the right state class for all the sensors.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

Copilot AI review requested due to automatic review settings December 7, 2025 21:37
@upsuper upsuper requested a review from pvizeli as a code owner December 7, 2025 21:37
@home-assistant
Copy link

home-assistant bot commented Dec 7, 2025

Hey there @pvizeli, mind taking a look at this pull request as it has been labeled with an integration (ecowitt) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of ecowitt can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign ecowitt Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component) on the pull request.

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 PR fixes incorrect state class assignments for Ecowitt rain count sensors by reverting three previous PRs and implementing proper logic to distinguish between sensor types. Rain sensors are categorized as: total/resetting sensors (TOTAL_INCREASING) and rolling window sensors (MEASUREMENT). The fix adds default TOTAL_INCREASING state class to rain count sensor descriptions, then overrides it to MEASUREMENT for sensors matching a rolling window pattern (hourly, 24h, and piezo variants).

Key changes:

  • Adds regex pattern to identify rolling window rain sensors (hourly, last24h, and piezo variants)
  • Sets TOTAL_INCREASING as default state class for RAIN_COUNT_MM and RAIN_COUNT_INCHES sensor descriptions
  • Implements logic to override state class to MEASUREMENT for rolling window sensors during sensor setup

Comment on lines +298 to 303
if _ROLLING_WINDOW_RAIN_COUNT_SENSOR.fullmatch(sensor.key):
description = dataclasses.replace(
description,
state_class=SensorStateClass.TOTAL_INCREASING,
state_class=SensorStateClass.MEASUREMENT,
)

Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

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

This logic for detecting and overriding state classes for rolling window sensors lacks test coverage. Given the critical nature of state class assignments for long-term statistics (as mentioned in the PR description), tests should be added to verify:

  1. Rolling window sensors (matching the regex) correctly get MEASUREMENT state class
  2. Non-rolling window rain count sensors correctly retain TOTAL_INCREASING state class
  3. The regex pattern matches all expected sensor key formats (hourlyrainmm, hourlyrainin, last24hrainmm, last24hrainin, hrain_piezo variants, etc.)

Consider adding tests in tests/components/ecowitt/test_sensor.py following the Home Assistant testing patterns with fixtures and snapshots.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

For sure ecowitt tests are extremely limited!
I strongly suggest to add snapshot tests (maybe in a preliminary PR?) to ensure this is properly tested and follow-up PRs do not accidentaly mess it up.


# Hourly and 24h rain count sensors are rolling window sensors
_ROLLING_WINDOW_RAIN_COUNT_SENSOR = re.compile(
"(?:hourly|last24h)rain(?:in|mm)|(?:last24)?hrain_piezo(?:mm)?"
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider using a raw string (r"...") for the regex pattern to improve readability and make escape sequences more explicit. Also, adding parentheses around the entire alternation could make the pattern structure clearer:

_ROLLING_WINDOW_RAIN_COUNT_SENSOR = re.compile(
    r"(?:hourly|last24h)rain(?:in|mm)|(?:last24)?hrain_piezo(?:mm)?"
)

This doesn't change functionality but improves code maintainability.

Suggested change
"(?:hourly|last24h)rain(?:in|mm)|(?:last24)?hrain_piezo(?:mm)?"
r"((?:hourly|last24h)rain(?:in|mm)|(?:last24)?hrain_piezo(?:mm)?)"

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is nothing need to be escaped, so raw string isn't useful.

)


# Hourly and 24h rain count sensors are rolling window sensors
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

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

The comment mentions "Hourly and 24h rain count sensors" but the regex also matches piezo rain sensor variants (hrain_piezo, last24hrain_piezo). Consider updating the comment to be more comprehensive:

# Hourly, 24h, and piezo rain count sensors are rolling window sensors

This makes it clearer that piezo sensors are also included in the pattern.

Suggested change
# Hourly and 24h rain count sensors are rolling window sensors
# Hourly, 24h, and piezo rain count sensors are rolling window sensors

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whether the data comes from piezo or not is orthogonal to whether its time period is hourly or 24h.

@il77781
Copy link

il77781 commented Dec 8, 2025

I suggest using the SensorStateClass.MEASUREMENT everywhere as it is more correct (in my opinion).

@frenck
Copy link
Member

frenck commented Dec 8, 2025

Also related #158070

@frenck
Copy link
Member

frenck commented Dec 8, 2025

Assigning @sairon for review.

@frenck frenck requested a review from sairon December 8, 2025 07:54
@frenck
Copy link
Member

frenck commented Dec 8, 2025

I suggest using the SensorStateClass.MEASUREMENT everywhere as it is more correct (in my opinion).

That one may not be used on aggregations, thus not a valid suggestion; see also our developer documentation.

@BJReplay
Copy link

BJReplay commented Dec 8, 2025

This will also fix #157903

Copy link
Member

@sairon sairon left a comment

Choose a reason for hiding this comment

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

To give some context, when I opened my PR, it was aimed at fixing the removal of total rain from the long term statistics. At that point it wasn't known that this sensor is not always present. But now I agree it is reasonable to keep the other values in LTS as TOTAL_INCREASING too, unless they cause any problems.

Not entirely sure if it makes sense to preserve/reintroduce the rolling window ones in long-term statistics - the consensus from the previous discussions was to remove them from there, which my change only made consistent for hourly sensors as well.

The major change I'd do though is that I'd use an explicit list of the sensors instead of the regexp, the only advantage of the regexp is that it's slightly more concise, but it's harder to read and orders of magnitude slower. And in longer run, I think it would be best if the difference between rolling window sensors was implemented on the aioecowitt library level, but I'd not go through that complexity for the purpose of this fix.

Comment on lines +62 to +64
_ROLLING_WINDOW_RAIN_COUNT_SENSOR = re.compile(
"(?:hourly|last24h)rain(?:in|mm)|(?:last24)?hrain_piezo(?:mm)?"
)
Copy link
Member

Choose a reason for hiding this comment

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

The list is not that extensive, I'd prefer this, as it's not only much easier to comprehend (even with the comment, it took me some time to understand what sensors it matches), it's less complex as well.

Suggested change
_ROLLING_WINDOW_RAIN_COUNT_SENSOR = re.compile(
"(?:hourly|last24h)rain(?:in|mm)|(?:last24)?hrain_piezo(?:mm)?"
)
_ROLLING_WINDOW_RAIN_COUNT_SENSORS = {
"hourlyrainin",
"hourlyrainmm",
"last24hrainin",
"last24hrainmm",
"hrain_piezo",
"hrain_piezomm",
"last24hrain_piezo",
"last24hrain_piezomm",
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Would it not make more sense to have the opposite: a list of "total increasing" sensor?

Copy link
Member

Choose a reason for hiding this comment

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

It kind of depends on which ones the stats should be preserved but note there are currently in total 18 + 17 rain sensors.

Ideally the distinction should be really done there when the sensor is defined in the mapping. But for that we still need an agreement on what should have which state classes.

Copy link
Member

@zweckj zweckj Dec 8, 2025

Choose a reason for hiding this comment

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

according to the PR description and this comment all the 24h & hourly sensors are indeed moving windows, which would mean they are not qualifying for any StateClass. The rest appears to be resetting at fixed intervals, which would make them TOTAL_INCREASING. If that is true, we'd need to remove the state class from those 8 sensors and keep the rest as TOTAL_INCREASING. But I own no Ecowitt so I have no way of verifying if that claim is really true.

Copy link

@Getiem Getiem Dec 8, 2025

Choose a reason for hiding this comment

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

according to the PR description and this comment all the 24h & daily sensors are indeed moving windows, which would mean they are not qualifying for any StateClass. The rest appears to be resetting at fixed intervals, which would make them TOTAL_INCREASING. If that is true, we'd need to remove the state class from those 8 sensors and keep the rest as TOTAL_INCREASING. But I own no Ecowitt so I have no way of verifying if that claim is really true.

image

Event = sum of all rain in consecutive blocks of 24 hours. Resets at 24 hours with 0mm rain. So if last rain stops at 9am, it will reset next day at 9am. Will sum up all rain events if they happen within 24h of each other, and so will sum up multiple days.
The others reset at the end of the period of their given name.

Copy link

@Getiem Getiem Dec 8, 2025

Choose a reason for hiding this comment

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

Also mind, that the name and source of information may differ according to Ecowitt sensor devices.... see post #157882 (comment)

"totalrainmm",
):
# Rolling window sensors must use measurement state classes
if _ROLLING_WINDOW_RAIN_COUNT_SENSOR.fullmatch(sensor.key):
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if _ROLLING_WINDOW_RAIN_COUNT_SENSOR.fullmatch(sensor.key):
if sensor.key in _ROLLING_WINDOW_RAIN_COUNT_SENSORS:

description = dataclasses.replace(
description,
state_class=SensorStateClass.TOTAL_INCREASING,
state_class=SensorStateClass.MEASUREMENT,
Copy link
Member

Choose a reason for hiding this comment

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

The consensus from previous discussions was to remove these from the LTS, given that they're largely redundant with the remaining data. I don't want to be the one to call the shots here again (with my PR I basically just wanted to avoid the rain data from being completely removed) so I'd like to leave the decision to someone else.


import dataclasses
from datetime import datetime
import re
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't be needed with the explicit set.

Suggested change
import re

@sairon sairon requested review from joostlek and zweckj December 8, 2025 10:47
@sairon
Copy link
Member

sairon commented Dec 8, 2025

Adding @zweckj and @joostlek as the reviewers of the previous PRs.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ecowitt GW2000B: "The entity no longer has a state class"

9 participants