Compare commits

..

1 Commits

Author SHA1 Message Date
Jean-Marc Collin
46278ca9a3 In certain case, temperature event are registred with an offset of one hour #52 2023-02-19 22:42:02 +01:00
4 changed files with 57 additions and 38 deletions

View File

@@ -407,8 +407,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
or DEFAULT_SECURITY_DEFAULT_ON_PERCENT or DEFAULT_SECURITY_DEFAULT_ON_PERCENT
) )
self._minimal_activation_delay = entry_infos.get(CONF_MINIMAL_ACTIVATION_DELAY) self._minimal_activation_delay = entry_infos.get(CONF_MINIMAL_ACTIVATION_DELAY)
self._last_temperature_mesure = datetime.now() self._last_temperature_mesure = datetime.now(tz=self._current_tz)
self._last_ext_temperature_mesure = datetime.now() self._last_ext_temperature_mesure = datetime.now(tz=self._current_tz)
self._security_state = False self._security_state = False
self._saved_hvac_mode = None self._saved_hvac_mode = None
@@ -1166,7 +1166,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
): ):
self._last_temperature_mesure = ( self._last_temperature_mesure = (
self._last_ext_temperature_mesure self._last_ext_temperature_mesure
) = datetime.now() ) = datetime.now(tz=self._current_tz)
def find_preset_temp(self, preset_mode): def find_preset_temp(self, preset_mode):
"""Find the right temperature of a preset considering the presence if configured""" """Find the right temperature of a preset considering the presence if configured"""
@@ -1315,8 +1315,6 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._hvac_mode, self._hvac_mode,
self._saved_hvac_mode, self._saved_hvac_mode,
) )
if new_state is None or old_state is None or new_state.state == old_state.state:
return
# Check delay condition # Check delay condition
async def try_window_condition(_): async def try_window_condition(_):
@@ -1357,6 +1355,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
await self.async_set_hvac_mode(HVACMode.OFF) await self.async_set_hvac_mode(HVACMode.OFF)
self.update_custom_attributes() self.update_custom_attributes()
if new_state is None or old_state is None or new_state.state == old_state.state:
return try_window_condition
if self._window_call_cancel: if self._window_call_cancel:
self._window_call_cancel() self._window_call_cancel()
self._window_call_cancel = None self._window_call_cancel = None
@@ -1479,9 +1480,20 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
if math.isnan(cur_temp) or math.isinf(cur_temp): if math.isnan(cur_temp) or math.isinf(cur_temp):
raise ValueError(f"Sensor has illegal state {state.state}") raise ValueError(f"Sensor has illegal state {state.state}")
self._cur_temp = cur_temp self._cur_temp = cur_temp
self._last_temperature_mesure = ( self._last_temperature_mesure = (
state.last_changed if state.last_changed is not None else datetime.now() state.last_changed.astimezone(self._current_tz)
if state.last_changed is not None
else datetime.now(tz=self._current_tz)
) )
_LOGGER.debug(
"%s - After setting _last_temperature_mesure %s , state.last_changed.replace=%s",
self,
self._last_temperature_mesure,
state.last_changed.astimezone(self._current_tz),
)
# try to restart if we were in security mode # try to restart if we were in security mode
if self._security_state: if self._security_state:
await self.check_security() await self.check_security()
@@ -1498,8 +1510,18 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
raise ValueError(f"Sensor has illegal state {state.state}") raise ValueError(f"Sensor has illegal state {state.state}")
self._cur_ext_temp = cur_ext_temp self._cur_ext_temp = cur_ext_temp
self._last_ext_temperature_mesure = ( self._last_ext_temperature_mesure = (
state.last_changed if state.last_changed is not None else datetime.now() state.last_changed.astimezone(self._current_tz)
if state.last_changed is not None
else datetime.now(tz=self._current_tz)
) )
_LOGGER.debug(
"%s - After setting _last_ext_temperature_mesure %s , state.last_changed.replace=%s",
self,
self._last_ext_temperature_mesure,
state.last_changed.astimezone(self._current_tz),
)
# try to restart if we were in security mode # try to restart if we were in security mode
if self._security_state: if self._security_state:
await self.check_security() await self.check_security()
@@ -2105,7 +2127,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
"""Update the custom extra attributes for the entity""" """Update the custom extra attributes for the entity"""
self._attr_extra_state_attributes: dict(str, str) = { self._attr_extra_state_attributes: dict(str, str) = {
"hvac_mode": self._hvac_mode, "hvac_mode": self.hvac_mode,
"preset_mode": self.preset_mode,
"type": self._thermostat_type, "type": self._thermostat_type,
"eco_temp": self._presets[PRESET_ECO], "eco_temp": self._presets[PRESET_ECO],
"boost_temp": self._presets[PRESET_BOOST], "boost_temp": self._presets[PRESET_BOOST],
@@ -2133,11 +2156,11 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
"security_delay_min": self._security_delay_min, "security_delay_min": self._security_delay_min,
"security_min_on_percent": self._security_min_on_percent, "security_min_on_percent": self._security_min_on_percent,
"security_default_on_percent": self._security_default_on_percent, "security_default_on_percent": self._security_default_on_percent,
"last_temperature_datetime": self._last_temperature_mesure.replace( "last_temperature_datetime": self._last_temperature_mesure.astimezone(
tzinfo=self._current_tz self._current_tz
).isoformat(), ).isoformat(),
"last_ext_temperature_datetime": self._last_ext_temperature_mesure.replace( "last_ext_temperature_datetime": self._last_ext_temperature_mesure.astimezone(
tzinfo=self._current_tz self._current_tz
).isoformat(), ).isoformat(),
"security_state": self._security_state, "security_state": self._security_state,
"minimal_activation_delay_sec": self._minimal_activation_delay, "minimal_activation_delay_sec": self._minimal_activation_delay,
@@ -2145,7 +2168,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
ATTR_MEAN_POWER_CYCLE: self.mean_cycle_power, ATTR_MEAN_POWER_CYCLE: self.mean_cycle_power,
ATTR_TOTAL_ENERGY: self.total_energy, ATTR_TOTAL_ENERGY: self.total_energy,
"last_update_datetime": datetime.now() "last_update_datetime": datetime.now()
.replace(tzinfo=self._current_tz) .astimezone(self._current_tz)
.isoformat(), .isoformat(),
"timezone": str(self._current_tz), "timezone": str(self._current_tz),
} }

View File

@@ -149,7 +149,9 @@ async def send_max_power_change_event(entity: VersatileThermostat, new_power_max
return await entity._async_max_power_changed(power_event) return await entity._async_max_power_changed(power_event)
async def send_window_change_event(entity: VersatileThermostat, new_state: bool, date): async def send_window_change_event(
entity: VersatileThermostat, new_state: bool, old_state: bool, date
):
"""Sending a new window event simulating a change on the window state""" """Sending a new window event simulating a change on the window state"""
window_event = Event( window_event = Event(
EVENT_STATE_CHANGED, EVENT_STATE_CHANGED,
@@ -162,7 +164,7 @@ async def send_window_change_event(entity: VersatileThermostat, new_state: bool,
), ),
"old_state": State( "old_state": State(
entity_id=entity.entity_id, entity_id=entity.entity_id,
state=STATE_ON if not new_state else STATE_OFF, state=STATE_ON if old_state else STATE_OFF,
last_changed=date, last_changed=date,
last_updated=date, last_updated=date,
), ),

View File

@@ -52,7 +52,7 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state):
) )
# 1. creates a thermostat and check that security is off # 1. creates a thermostat and check that security is off
now: datetime = datetime.now() now: datetime = datetime.now(tz=tz)
entity: VersatileThermostat = await create_thermostat( entity: VersatileThermostat = await create_thermostat(
hass, entry, "climate.theoverswitchmockname" hass, entry, "climate.theoverswitchmockname"
) )
@@ -68,8 +68,10 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state):
] ]
assert entity._last_ext_temperature_mesure is not None assert entity._last_ext_temperature_mesure is not None
assert entity._last_temperature_mesure is not None assert entity._last_temperature_mesure is not None
assert (entity._last_temperature_mesure - now).total_seconds() < 1 assert (entity._last_temperature_mesure.astimezone(tz) - now).total_seconds() < 1
assert (entity._last_ext_temperature_mesure - now).total_seconds() < 1 assert (
entity._last_ext_temperature_mesure.astimezone(tz) - now
).total_seconds() < 1
# set a preset # set a preset
assert entity.preset_mode is PRESET_NONE assert entity.preset_mode is PRESET_NONE
@@ -104,12 +106,8 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state):
call.send_event( call.send_event(
EventType.TEMPERATURE_EVENT, EventType.TEMPERATURE_EVENT,
{ {
"last_temperature_mesure": event_timestamp.replace( "last_temperature_mesure": event_timestamp.isoformat(),
tzinfo=tz "last_ext_temperature_mesure": entity._last_ext_temperature_mesure.isoformat(),
).isoformat(),
"last_ext_temperature_mesure": entity._last_ext_temperature_mesure.replace(
tzinfo=tz
).isoformat(),
"current_temp": 15, "current_temp": 15,
"current_ext_temp": None, "current_ext_temp": None,
"target_temp": 18, "target_temp": 18,
@@ -119,12 +117,8 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state):
EventType.SECURITY_EVENT, EventType.SECURITY_EVENT,
{ {
"type": "start", "type": "start",
"last_temperature_mesure": event_timestamp.replace( "last_temperature_mesure": event_timestamp.isoformat(),
tzinfo=tz "last_ext_temperature_mesure": entity._last_ext_temperature_mesure.isoformat(),
).isoformat(),
"last_ext_temperature_mesure": entity._last_ext_temperature_mesure.replace(
tzinfo=tz
).isoformat(),
"current_temp": 15, "current_temp": 15,
"current_ext_temp": None, "current_ext_temp": None,
"target_temp": 18, "target_temp": 18,
@@ -176,11 +170,11 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state):
EventType.SECURITY_EVENT, EventType.SECURITY_EVENT,
{ {
"type": "end", "type": "end",
"last_temperature_mesure": event_timestamp.replace( "last_temperature_mesure": event_timestamp.astimezone(
tzinfo=tz tz
).isoformat(), ).isoformat(),
"last_ext_temperature_mesure": entity._last_ext_temperature_mesure.replace( "last_ext_temperature_mesure": entity._last_ext_temperature_mesure.astimezone(
tzinfo=tz tz
).isoformat(), ).isoformat(),
"current_temp": 15.2, "current_temp": 15.2,
"current_ext_temp": None, "current_ext_temp": None,

View File

@@ -74,7 +74,7 @@ async def test_window_management_time_not_enough(
) as mock_condition: ) as mock_condition:
await send_temperature_change_event(entity, 15, datetime.now()) await send_temperature_change_event(entity, 15, datetime.now())
try_window_condition = await send_window_change_event( try_window_condition = await send_window_change_event(
entity, True, datetime.now() entity, True, False, datetime.now()
) )
# simulate the call to try_window_condition # simulate the call to try_window_condition
await try_window_condition(None) await try_window_condition(None)
@@ -88,7 +88,7 @@ async def test_window_management_time_not_enough(
# Close the window # Close the window
try_window_condition = await send_window_change_event( try_window_condition = await send_window_change_event(
entity, False, datetime.now() entity, False, False, datetime.now()
) )
# simulate the call to try_window_condition # simulate the call to try_window_condition
await try_window_condition(None) await try_window_condition(None)
@@ -163,7 +163,7 @@ async def test_window_management_time_enough(
): ):
await send_temperature_change_event(entity, 15, datetime.now()) await send_temperature_change_event(entity, 15, datetime.now())
try_window_condition = await send_window_change_event( try_window_condition = await send_window_change_event(
entity, True, datetime.now() entity, True, False, datetime.now()
) )
# simulate the call to try_window_condition # simulate the call to try_window_condition
await try_window_condition(None) await try_window_condition(None)
@@ -181,7 +181,7 @@ async def test_window_management_time_enough(
# Close the window # Close the window
try_window_condition = await send_window_change_event( try_window_condition = await send_window_change_event(
entity, False, datetime.now() entity, False, True, datetime.now()
) )
# simulate the call to try_window_condition # simulate the call to try_window_condition
await try_window_condition(None) await try_window_condition(None)