diff --git a/custom_components/versatile_thermostat/climate.py b/custom_components/versatile_thermostat/climate.py index 6c25e45..745e4c2 100644 --- a/custom_components/versatile_thermostat/climate.py +++ b/custom_components/versatile_thermostat/climate.py @@ -407,8 +407,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): or DEFAULT_SECURITY_DEFAULT_ON_PERCENT ) self._minimal_activation_delay = entry_infos.get(CONF_MINIMAL_ACTIVATION_DELAY) - self._last_temperature_mesure = datetime.now() - self._last_ext_temperature_mesure = datetime.now() + self._last_temperature_mesure = datetime.now(tz=self._current_tz) + self._last_ext_temperature_mesure = datetime.now(tz=self._current_tz) self._security_state = False self._saved_hvac_mode = None @@ -1166,7 +1166,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): ): self._last_temperature_mesure = ( self._last_ext_temperature_mesure - ) = datetime.now() + ) = datetime.now(tz=self._current_tz) def find_preset_temp(self, preset_mode): """Find the right temperature of a preset considering the presence if configured""" @@ -1315,8 +1315,6 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): self._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 async def try_window_condition(_): @@ -1357,6 +1355,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): await self.async_set_hvac_mode(HVACMode.OFF) 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: self._window_call_cancel() self._window_call_cancel = None @@ -1479,9 +1480,20 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): if math.isnan(cur_temp) or math.isinf(cur_temp): raise ValueError(f"Sensor has illegal state {state.state}") self._cur_temp = cur_temp + 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 if self._security_state: await self.check_security() @@ -1498,8 +1510,18 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): raise ValueError(f"Sensor has illegal state {state.state}") self._cur_ext_temp = cur_ext_temp 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 if self._security_state: await self.check_security() @@ -2105,7 +2127,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): """Update the custom extra attributes for the entity""" 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, "eco_temp": self._presets[PRESET_ECO], "boost_temp": self._presets[PRESET_BOOST], @@ -2133,11 +2156,11 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): "security_delay_min": self._security_delay_min, "security_min_on_percent": self._security_min_on_percent, "security_default_on_percent": self._security_default_on_percent, - "last_temperature_datetime": self._last_temperature_mesure.replace( - tzinfo=self._current_tz + "last_temperature_datetime": self._last_temperature_mesure.astimezone( + self._current_tz ).isoformat(), - "last_ext_temperature_datetime": self._last_ext_temperature_mesure.replace( - tzinfo=self._current_tz + "last_ext_temperature_datetime": self._last_ext_temperature_mesure.astimezone( + self._current_tz ).isoformat(), "security_state": self._security_state, "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_TOTAL_ENERGY: self.total_energy, "last_update_datetime": datetime.now() - .replace(tzinfo=self._current_tz) + .astimezone(self._current_tz) .isoformat(), "timezone": str(self._current_tz), } diff --git a/custom_components/versatile_thermostat/tests/commons.py b/custom_components/versatile_thermostat/tests/commons.py index e95da7a..39646c3 100644 --- a/custom_components/versatile_thermostat/tests/commons.py +++ b/custom_components/versatile_thermostat/tests/commons.py @@ -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) -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""" window_event = Event( EVENT_STATE_CHANGED, @@ -162,7 +164,7 @@ async def send_window_change_event(entity: VersatileThermostat, new_state: bool, ), "old_state": State( 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_updated=date, ), diff --git a/custom_components/versatile_thermostat/tests/test_security.py b/custom_components/versatile_thermostat/tests/test_security.py index 9c0fa34..1468560 100644 --- a/custom_components/versatile_thermostat/tests/test_security.py +++ b/custom_components/versatile_thermostat/tests/test_security.py @@ -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 - now: datetime = datetime.now() + now: datetime = datetime.now(tz=tz) entity: VersatileThermostat = await create_thermostat( 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_temperature_mesure is not None - assert (entity._last_temperature_mesure - now).total_seconds() < 1 - assert (entity._last_ext_temperature_mesure - now).total_seconds() < 1 + assert (entity._last_temperature_mesure.astimezone(tz) - now).total_seconds() < 1 + assert ( + entity._last_ext_temperature_mesure.astimezone(tz) - now + ).total_seconds() < 1 # set a preset 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( EventType.TEMPERATURE_EVENT, { - "last_temperature_mesure": event_timestamp.replace( - tzinfo=tz - ).isoformat(), - "last_ext_temperature_mesure": entity._last_ext_temperature_mesure.replace( - tzinfo=tz - ).isoformat(), + "last_temperature_mesure": event_timestamp.isoformat(), + "last_ext_temperature_mesure": entity._last_ext_temperature_mesure.isoformat(), "current_temp": 15, "current_ext_temp": None, "target_temp": 18, @@ -119,12 +117,8 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): EventType.SECURITY_EVENT, { "type": "start", - "last_temperature_mesure": event_timestamp.replace( - tzinfo=tz - ).isoformat(), - "last_ext_temperature_mesure": entity._last_ext_temperature_mesure.replace( - tzinfo=tz - ).isoformat(), + "last_temperature_mesure": event_timestamp.isoformat(), + "last_ext_temperature_mesure": entity._last_ext_temperature_mesure.isoformat(), "current_temp": 15, "current_ext_temp": None, "target_temp": 18, @@ -176,11 +170,11 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): EventType.SECURITY_EVENT, { "type": "end", - "last_temperature_mesure": event_timestamp.replace( - tzinfo=tz + "last_temperature_mesure": event_timestamp.astimezone( + tz ).isoformat(), - "last_ext_temperature_mesure": entity._last_ext_temperature_mesure.replace( - tzinfo=tz + "last_ext_temperature_mesure": entity._last_ext_temperature_mesure.astimezone( + tz ).isoformat(), "current_temp": 15.2, "current_ext_temp": None, diff --git a/custom_components/versatile_thermostat/tests/test_window.py b/custom_components/versatile_thermostat/tests/test_window.py index 1f500eb..b9de792 100644 --- a/custom_components/versatile_thermostat/tests/test_window.py +++ b/custom_components/versatile_thermostat/tests/test_window.py @@ -74,7 +74,7 @@ async def test_window_management_time_not_enough( ) as mock_condition: await send_temperature_change_event(entity, 15, datetime.now()) try_window_condition = await send_window_change_event( - entity, True, datetime.now() + entity, True, False, datetime.now() ) # simulate the call to try_window_condition await try_window_condition(None) @@ -88,7 +88,7 @@ async def test_window_management_time_not_enough( # Close the window try_window_condition = await send_window_change_event( - entity, False, datetime.now() + entity, False, False, datetime.now() ) # simulate the call to try_window_condition 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()) try_window_condition = await send_window_change_event( - entity, True, datetime.now() + entity, True, False, datetime.now() ) # simulate the call to try_window_condition await try_window_condition(None) @@ -181,7 +181,7 @@ async def test_window_management_time_enough( # Close the window try_window_condition = await send_window_change_event( - entity, False, datetime.now() + entity, False, True, datetime.now() ) # simulate the call to try_window_condition await try_window_condition(None)