Fix utility_meter on DST changes (#129862)

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
This commit is contained in:
Diogo Gomes 2024-11-08 17:44:15 +00:00 committed by GitHub
parent a7be76ba0a
commit e4aaaf10c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 38 additions and 9 deletions

View File

@ -8,5 +8,5 @@
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["croniter"], "loggers": ["croniter"],
"quality_scale": "internal", "quality_scale": "internal",
"requirements": ["croniter==2.0.2"] "requirements": ["cronsim==2.6"]
} }

View File

@ -9,7 +9,7 @@ from decimal import Decimal, DecimalException, InvalidOperation
import logging import logging
from typing import Any, Self from typing import Any, Self
from croniter import croniter from cronsim import CronSim
import voluptuous as vol import voluptuous as vol
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
@ -405,6 +405,16 @@ class UtilityMeterSensor(RestoreSensor):
self._tariff = tariff self._tariff = tariff
self._tariff_entity = tariff_entity self._tariff_entity = tariff_entity
self._next_reset = None self._next_reset = None
self.scheduler = (
CronSim(
self._cron_pattern,
dt_util.now(
dt_util.get_default_time_zone()
), # we need timezone for DST purposes (see issue #102984)
)
if self._cron_pattern
else None
)
def start(self, attributes: Mapping[str, Any]) -> None: def start(self, attributes: Mapping[str, Any]) -> None:
"""Initialize unit and state upon source initial update.""" """Initialize unit and state upon source initial update."""
@ -543,11 +553,10 @@ class UtilityMeterSensor(RestoreSensor):
async def _program_reset(self): async def _program_reset(self):
"""Program the reset of the utility meter.""" """Program the reset of the utility meter."""
if self._cron_pattern is not None: if self.scheduler:
tz = dt_util.get_default_time_zone() self._next_reset = next(self.scheduler)
self._next_reset = croniter(self._cron_pattern, dt_util.now(tz)).get_next(
datetime _LOGGER.debug("Next reset of %s is %s", self.entity_id, self._next_reset)
) # we need timezone for DST purposes (see issue #102984)
self.async_on_remove( self.async_on_remove(
async_track_point_in_time( async_track_point_in_time(
self.hass, self.hass,

View File

@ -702,7 +702,7 @@ connect-box==0.3.1
construct==2.10.68 construct==2.10.68
# homeassistant.components.utility_meter # homeassistant.components.utility_meter
croniter==2.0.2 cronsim==2.6
# homeassistant.components.crownstone # homeassistant.components.crownstone
crownstone-cloud==1.4.11 crownstone-cloud==1.4.11

View File

@ -598,7 +598,7 @@ colorthief==0.2.1
construct==2.10.68 construct==2.10.68
# homeassistant.components.utility_meter # homeassistant.components.utility_meter
croniter==2.0.2 cronsim==2.6
# homeassistant.components.crownstone # homeassistant.components.crownstone
crownstone-cloud==1.4.11 crownstone-cloud==1.4.11

View File

@ -1804,6 +1804,26 @@ async def test_self_reset_hourly_dst(hass: HomeAssistant) -> None:
) )
async def test_self_reset_hourly_dst2(hass: HomeAssistant) -> None:
"""Test weekly reset of meter in DST change conditions."""
hass.config.time_zone = "Europe/Berlin"
dt_util.set_default_time_zone(dt_util.get_time_zone(hass.config.time_zone))
await _test_self_reset(
hass, gen_config("daily"), "2024-10-26T23:59:00.000000+02:00"
)
state = hass.states.get("sensor.energy_bill")
last_reset = dt_util.parse_datetime("2024-10-27T00:00:00.000000+02:00")
assert (
dt_util.as_local(dt_util.parse_datetime(state.attributes.get("last_reset")))
== last_reset
)
next_reset = dt_util.parse_datetime("2024-10-28T00:00:00.000000+01:00").isoformat()
assert state.attributes.get("next_reset") == next_reset
async def test_self_reset_daily(hass: HomeAssistant) -> None: async def test_self_reset_daily(hass: HomeAssistant) -> None:
"""Test daily reset of meter.""" """Test daily reset of meter."""
await _test_self_reset( await _test_self_reset(