From a7465fba2e3541ec2b085a9a9741c2c6a7851ad1 Mon Sep 17 00:00:00 2001 From: Jean-Marc Collin Date: Sat, 18 Mar 2023 11:40:28 +0100 Subject: [PATCH] Issue #56 - Exception when undeerlying thermostat is not found at startup --- .../versatile_thermostat/climate.py | 20 ++++- .../versatile_thermostat/tests/test_bugs.py | 84 +++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 custom_components/versatile_thermostat/tests/test_bugs.py diff --git a/custom_components/versatile_thermostat/climate.py b/custom_components/versatile_thermostat/climate.py index f93dfe1..4a0a6e0 100644 --- a/custom_components/versatile_thermostat/climate.py +++ b/custom_components/versatile_thermostat/climate.py @@ -598,7 +598,11 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): self.async_on_remove(self.async_remove_thermostat) - await self.async_startup() + try: + await self.async_startup() + except UnknownEntity: + # Ingore this error which is possible if underlying climate is not found temporary + pass # starts a cycle if we are in over_climate type if self._is_over_climate: @@ -651,7 +655,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): self, self._climate_entity_id, ) - self._is_over_climate = False + # #56 keep the over_climate and try periodically to find the underlying climate + # self._is_over_climate = False raise UnknownEntity( f"Underlying thermostat {self._climate_entity_id} not found" ) @@ -2229,6 +2234,17 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): self._attr_preset_mode, ) + # Issue 56 in over_climate mode, if the underlying climate is not initialized, try to initialize it + if self._is_over_climate and self._underlying_climate is None: + _LOGGER.info( + "%s - Underlying climate is not initialized. Try to initialize it", self + ) + try: + await self.async_startup() + except UnknownEntity as err: + # still not found, we an stop here + raise err + # Check overpowering condition overpowering: bool = await self.check_overpowering() if overpowering: diff --git a/custom_components/versatile_thermostat/tests/test_bugs.py b/custom_components/versatile_thermostat/tests/test_bugs.py new file mode 100644 index 0000000..d8f55c8 --- /dev/null +++ b/custom_components/versatile_thermostat/tests/test_bugs.py @@ -0,0 +1,84 @@ +""" Test the Window management """ +from unittest.mock import patch +from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import + +import logging + +logging.getLogger().setLevel(logging.DEBUG) + + +async def test_bug_56( + hass: HomeAssistant, + skip_hass_states_is_state, + skip_turn_on_off_heater, + skip_send_event, +): + """Test that in over_climate mode there is no error when underlying climate is not available""" + + the_mock_underlying = MagicMockClimate() + with patch( + "custom_components.versatile_thermostat.climate.VersatileThermostat.find_underlying_climate", + return_value=None, # dont find the underlying climate + ): + entry = MockConfigEntry( + domain=DOMAIN, + title="TheOverClimateMockName", + unique_id="uniqueId", + data={ + CONF_NAME: "TheOverClimateMockName", + CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE, + CONF_TEMP_SENSOR: "sensor.mock_temp_sensor", + CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor", + CONF_CYCLE_MIN: 5, + CONF_TEMP_MIN: 15, + CONF_TEMP_MAX: 30, + "eco_temp": 17, + "comfort_temp": 18, + "boost_temp": 19, + CONF_USE_WINDOW_FEATURE: False, + CONF_USE_MOTION_FEATURE: False, + CONF_USE_POWER_FEATURE: False, + CONF_USE_PRESENCE_FEATURE: False, + CONF_CLIMATE: "climate.mock_climate", + CONF_MINIMAL_ACTIVATION_DELAY: 30, + CONF_SECURITY_DELAY_MIN: 5, + CONF_SECURITY_MIN_ON_PERCENT: 0.3, + }, + ) + + entity: VersatileThermostat = await create_thermostat( + hass, entry, "climate.theoverclimatemockname" + ) + assert entity + # cause the underlying climate was not found + assert entity.is_over_climate is True + assert entity._underlying_climate is None + + # Should not failed + entity.update_custom_attributes() + + # try to call _async_control_heating + try: + await entity._async_control_heating() + # an exception should be send + assert False + except UnknownEntity: + pass + except Exception: # pylint: disable=broad-exception-caught + assert False + + # This time the underlying will be found + with patch( + "custom_components.versatile_thermostat.climate.VersatileThermostat.find_underlying_climate", + return_value=the_mock_underlying, # dont find the underlying climate + ): + # try to call _async_control_heating + try: + await entity._async_control_heating() + except UnknownEntity: + assert False + except Exception: # pylint: disable=broad-exception-caught + assert False + + # Should not failed + entity.update_custom_attributes()