diff --git a/CHANGELOG.md b/CHANGELOG.md index b0d44cd93..c2b0ad019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # Changelog -## Ongoing +## v0.38.1 +- Add missing exception-handling for set-function in `__init__.py` +- Add call_request() functions combining all common exception-handling for all set-functions +- Update and improve test code - Implementing code improvements as suggested in #567 ## v0.38.0 diff --git a/plugwise/__init__.py b/plugwise/__init__.py index 4ff7fea2b..7faef69a6 100644 --- a/plugwise/__init__.py +++ b/plugwise/__init__.py @@ -324,7 +324,10 @@ async def set_select( state: str | None = None, ) -> None: """Set the selected option for the applicable Select.""" - await self._smile_api.set_select(key, loc_id, option, state) + try: + await self._smile_api.set_select(key, loc_id, option, state) + except ConnectionFailedError as exc: + raise ConnectionFailedError(f"Failed to set select option '{option}': {str(exc)}") from exc async def set_schedule_state( self, @@ -333,15 +336,25 @@ async def set_schedule_state( name: str | None = None, ) -> None: """Activate/deactivate the Schedule, with the given name, on the relevant Thermostat.""" - await self._smile_api.set_schedule_state(loc_id, state, name) + try: + await self._smile_api.set_schedule_state(loc_id, state, name) + except ConnectionFailedError as exc: # pragma no cover + raise ConnectionFailedError(f"Failed to set schedule state: {str(exc)}") from exc # pragma no cover + async def set_preset(self, loc_id: str, preset: str) -> None: """Set the given Preset on the relevant Thermostat.""" - await self._smile_api.set_preset(loc_id, preset) + try: + await self._smile_api.set_preset(loc_id, preset) + except ConnectionFailedError as exc: + raise ConnectionFailedError(f"Failed to set preset: {str(exc)}") from exc async def set_temperature(self, loc_id: str, items: dict[str, float]) -> None: """Set the given Temperature on the relevant Thermostat.""" - await self._smile_api.set_temperature(loc_id, items) + try: + await self._smile_api.set_temperature(loc_id, items) + except ConnectionFailedError as exc: + raise ConnectionFailedError(f"Failed to set temperature: {str(exc)}") from exc async def set_number( self, @@ -350,40 +363,58 @@ async def set_number( temperature: float, ) -> None: """Set the maximum boiler- or DHW-setpoint on the Central Heating boiler or the temperature-offset on a Thermostat.""" - await self._smile_api.set_number(dev_id, key, temperature) + try: + await self._smile_api.set_number(dev_id, key, temperature) + except ConnectionFailedError as exc: + raise ConnectionFailedError(f"Failed to set number '{key}': {str(exc)}") from exc async def set_temperature_offset(self, dev_id: str, offset: float) -> None: """Set the Temperature offset for thermostats that support this feature.""" - await self._smile_api.set_offset(dev_id, offset) # pragma: no cover + try: # pragma no cover + await self._smile_api.set_offset(dev_id, offset) # pragma: no cover + except ConnectionFailedError as exc: # pragma no cover + raise ConnectionFailedError(f"Failed to set temperature offset: {str(exc)}") from exc # pragma no cover async def set_switch_state( self, appl_id: str, members: list[str] | None, model: str, state: str ) -> None: """Set the given State of the relevant Switch.""" - await self._smile_api.set_switch_state(appl_id, members, model, state) + try: + await self._smile_api.set_switch_state(appl_id, members, model, state) + except ConnectionFailedError as exc: + raise ConnectionFailedError(f"Failed to set switch state: {str(exc)}") from exc async def set_gateway_mode(self, mode: str) -> None: """Set the gateway mode.""" - await self._smile_api.set_gateway_mode(mode) # pragma: no cover + try: # pragma no cover + await self._smile_api.set_gateway_mode(mode) # pragma: no cover + except ConnectionFailedError as exc: # pragma no cover + raise ConnectionFailedError(f"Failed to set gateway mode: {str(exc)}") from exc # pragma no cover async def set_regulation_mode(self, mode: str) -> None: """Set the heating regulation mode.""" - await self._smile_api.set_regulation_mode(mode) # pragma: no cover + try: # pragma no cover + await self._smile_api.set_regulation_mode(mode) # pragma: no cover + except ConnectionFailedError as exc: # pragma no cover + raise ConnectionFailedError(f"Failed to set regulation mode: {str(exc)}") from exc # pragma no cover async def set_dhw_mode(self, mode: str) -> None: """Set the domestic hot water heating regulation mode.""" - await self._smile_api.set_dhw_mode(mode) # pragma: no cover + try: # pragma no cover + await self._smile_api.set_dhw_mode(mode) # pragma: no cover + except ConnectionFailedError as exc: # pragma no cover + raise ConnectionFailedError(f"Failed to set dhw mode: {str(exc)}") from exc # pragma no cover async def delete_notification(self) -> None: """Delete the active Plugwise Notification.""" try: await self._smile_api.delete_notification() except ConnectionFailedError as exc: - raise PlugwiseError(f"Failed to delete notification: {str(exc)}") from exc + raise ConnectionFailedError(f"Failed to delete notification: {str(exc)}") from exc async def reboot_gateway(self) -> None: """Reboot the Plugwise Gateway.""" try: await self._smile_api.reboot_gateway() except ConnectionFailedError as exc: - raise PlugwiseError(f"Failed to reboot gateway: {str(exc)}") from exc + raise ConnectionFailedError(f"Failed to reboot gateway: {str(exc)}") from exc diff --git a/plugwise/legacy/smile.py b/plugwise/legacy/smile.py index 9048fa7e5..e72621b82 100644 --- a/plugwise/legacy/smile.py +++ b/plugwise/legacy/smile.py @@ -5,6 +5,7 @@ from __future__ import annotations import datetime as dt +from typing import Any from plugwise.constants import ( APPLIANCES, @@ -23,7 +24,7 @@ PlugwiseData, ThermoLoc, ) -from plugwise.exceptions import PlugwiseError +from plugwise.exceptions import ConnectionFailedError, PlugwiseError from plugwise.helper import SmileComm from plugwise.legacy.data import SmileLegacyData @@ -180,7 +181,7 @@ async def set_preset(self, _: str, preset: str) -> None: rule = self._domain_objects.find(locator) data = f'true' - await self._request(RULES, method="put", data=data) + await self.call_request(RULES, method="put", data=data) async def set_regulation_mode(self, mode: str) -> None: """Set-function placeholder for legacy devices.""" @@ -226,7 +227,7 @@ async def set_schedule_state(self, _: str, state: str | None, name: str | None) f' id="{template_id}" />{new_state}' ) - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def set_switch_state( self, appl_id: str, members: list[str] | None, model: str, state: str @@ -254,7 +255,7 @@ async def set_switch_state( if self._appliances.find(locator).text == "true": raise PlugwiseError("Plugwise: the locked Relay was not switched.") - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def _set_groupswitch_member_state( self, members: list[str], state: str, switch: Munch @@ -267,7 +268,7 @@ async def _set_groupswitch_member_state( uri = f"{APPLIANCES};id={member}/{switch.func_type}" data = f"<{switch.func_type}><{switch.func}>{state}" - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def set_temperature(self, _: str, items: dict[str, float]) -> None: """Set the given Temperature on the relevant Thermostat.""" @@ -287,4 +288,13 @@ async def set_temperature(self, _: str, items: dict[str, float]) -> None: f"{temperature}" ) - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) + + async def call_request(self, uri: str, **kwargs: Any) -> None: + """ConnectionFailedError wrapper for calling _request().""" + method: str = kwargs["method"] + data: str | None = kwargs.get("data") + try: + await self._request(uri, method=method, data=data) + except ConnectionFailedError as exc: + raise ConnectionFailedError from exc diff --git a/plugwise/smile.py b/plugwise/smile.py index c2d0ba5cf..ef3e13d55 100644 --- a/plugwise/smile.py +++ b/plugwise/smile.py @@ -149,14 +149,6 @@ async def async_update(self) -> PlugwiseData: ### API Set and HA Service-related Functions ### ######################################################################################################## - async def call_request(self, uri: str, **kwargs: Any) -> None: - """ConnectionFailedError wrapper for calling _request().""" - method: str = kwargs["method"] - try: - await self._request(uri, method=method) - except ConnectionFailedError as exc: - raise ConnectionFailedError from exc - async def delete_notification(self) -> None: """Delete the active Plugwise Notification.""" await self.call_request(NOTIFICATIONS, method="delete") @@ -189,7 +181,7 @@ async def set_number( uri = f"{APPLIANCES};id={self._heater_id}/thermostat;id={thermostat_id}" data = f"{temp}" - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def set_offset(self, dev_id: str, offset: float) -> None: """Set the Temperature offset for thermostats that support this feature.""" @@ -202,7 +194,7 @@ async def set_offset(self, dev_id: str, offset: float) -> None: uri = f"{APPLIANCES};id={dev_id}/offset;type=temperature_offset" data = f"{value}" - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def set_preset(self, loc_id: str, preset: str) -> None: """Set the given Preset on the relevant Thermostat - from LOCATIONS.""" @@ -222,7 +214,7 @@ async def set_preset(self, loc_id: str, preset: str) -> None: f"{preset}" ) - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def set_select(self, key: str, loc_id: str, option: str, state: str | None) -> None: """Set a dhw/gateway/regulation mode or the thermostat schedule option.""" @@ -245,7 +237,7 @@ async def set_dhw_mode(self, mode: str) -> None: uri = f"{APPLIANCES};type=heater_central/domestic_hot_water_mode_control" data = f"{mode}" - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def set_gateway_mode(self, mode: str) -> None: """Set the gateway mode.""" @@ -268,7 +260,7 @@ async def set_gateway_mode(self, mode: str) -> None: uri = f"{APPLIANCES};id={self.gateway_id}/gateway_mode_control" data = f"{mode}{valid}" - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def set_regulation_mode(self, mode: str) -> None: """Set the heating regulation mode.""" @@ -281,7 +273,7 @@ async def set_regulation_mode(self, mode: str) -> None: duration = "300" data = f"{duration}{mode}" - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def set_schedule_state( self, @@ -335,7 +327,7 @@ async def set_schedule_state( f"{template}{contexts}" ) - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) self._schedule_old_states[loc_id][name] = new_state def determine_contexts( @@ -404,7 +396,7 @@ async def set_switch_state( if self._domain_objects.find(locator).text == "true": raise PlugwiseError("Plugwise: the locked Relay was not switched.") - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def _set_groupswitch_member_state( self, members: list[str], state: str, switch: Munch @@ -419,7 +411,7 @@ async def _set_groupswitch_member_state( uri = f"{APPLIANCES};id={member}/{switch.device};id={switch_id}" data = f"<{switch.func_type}><{switch.func}>{state}" - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) async def set_temperature(self, loc_id: str, items: dict[str, float]) -> None: """Set the given Temperature on the relevant Thermostat.""" @@ -460,4 +452,13 @@ async def set_temperature(self, loc_id: str, items: dict[str, float]) -> None: f"{temperature}" ) - await self._request(uri, method="put", data=data) + await self.call_request(uri, method="put", data=data) + + async def call_request(self, uri: str, **kwargs: Any) -> None: + """ConnectionFailedError wrapper for calling _request().""" + method: str = kwargs["method"] + data: str | None = kwargs.get("data") + try: + await self._request(uri, method=method, data=data) + except ConnectionFailedError as exc: + raise ConnectionFailedError from exc diff --git a/pyproject.toml b/pyproject.toml index 8b0a29d33..e8cfc8121 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise" -version = "0.38.0" +version = "0.38.1" license = {file = "LICENSE"} description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3." readme = "README.md" diff --git a/tests/test_adam.py b/tests/test_adam.py index 17c2d563d..0a97cb10f 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -63,7 +63,7 @@ async def test_connect_adam_zone_per_device(self): await self.disconnect(server, client) server, smile, client = await self.connect_wrapper(raise_timeout=True) - await self.device_test(smile, "2022-05-16 00:00:01", testdata) + await self.device_test(smile, "2022-05-16 00:00:01", testdata, skip_testing=True) result = await self.tinker_thermostat( smile, "c50f167537524366a5af7aa3942feb1e", @@ -79,15 +79,18 @@ async def test_connect_adam_zone_per_device(self): ) assert result + tinkered = await self.tinker_max_boiler_temp(smile, unhappy=True) + assert not tinkered + try: await smile.delete_notification() notification_deletion = False # pragma: no cover - except pw_exceptions.PlugwiseError: + except pw_exceptions.ConnectionFailedError: notification_deletion = True assert notification_deletion reboot = await self.tinker_reboot(smile, unhappy=True) - assert not reboot + assert reboot await smile.close_connection() await self.disconnect(server, client) @@ -212,7 +215,7 @@ async def test_connect_adam_plus_anna(self): await self.disconnect(server, client) server, smile, client = await self.connect_wrapper(raise_timeout=True) - await self.device_test(smile, "2020-03-22 00:00:01", testdata) + await self.device_test(smile, "2020-03-22 00:00:01", testdata, skip_testing=True) result = await self.tinker_thermostat( smile, "009490cc2f674ce6b576863fbb64f867", @@ -322,9 +325,14 @@ async def test_connect_adam_plus_anna_new(self): ) assert not switch_change - await self.tinker_gateway_mode(smile) - await self.tinker_regulation_mode(smile) - await self.tinker_max_boiler_temp(smile) + tinkered = await self.tinker_gateway_mode(smile) + assert not tinkered + + tinkered = await self.tinker_regulation_mode(smile) + assert not tinkered + + tinkered = await self.tinker_max_boiler_temp(smile) + assert not tinkered # Now change some data and change directory reading xml from # emulating reading newer dataset after an update_interval @@ -353,6 +361,23 @@ async def test_connect_adam_plus_anna_new(self): await smile.close_connection() await self.disconnect(server, client) + self.smile_setup = "adam_plus_anna_new" + testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + server, smile, client = await self.connect_wrapper(raise_timeout=True) + await self.device_test(smile, "2023-12-17 00:00:01", testdata, skip_testing=True) + + tinkered = await self.tinker_max_boiler_temp(smile, unhappy=True) + assert tinkered + + tinkered = await self.tinker_gateway_mode(smile, unhappy=True) + assert tinkered + + tinkered = await self.tinker_regulation_mode(smile, unhappy=True) + assert tinkered + + await smile.close_connection() + await self.disconnect(server, client) + @pytest.mark.asyncio async def test_adam_plus_jip(self): """Test Adam with Jip setup.""" diff --git a/tests/test_anna.py b/tests/test_anna.py index c2b9d9bd5..a05cd48a2 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -70,7 +70,7 @@ async def test_connect_anna_v4(self): server, smile, client = await self.connect_wrapper(raise_timeout=True) # Reset self.smile_setup self.smile_setup = "anna_v4" - await self.device_test(smile, "2020-04-05 00:00:01", testdata) + await self.device_test(smile, "2020-04-05 00:00:01", testdata, skip_testing=True) result = await self.tinker_thermostat( smile, "eb5309212bf5407bb143e5bfa3b18aee", @@ -79,6 +79,12 @@ async def test_connect_anna_v4(self): unhappy=True, ) assert result + + result = await self.tinker_temp_offset( + smile, "01b85360fdd243d0aaad4d6ac2a5ba7e", unhappy=True, + ) + assert result + await smile.close_connection() await self.disconnect(server, client) @@ -458,11 +464,22 @@ async def test_connect_anna_loria_heating_idle(self): "ERROR raised setting block cooling: %s", exc.value ) # pragma: no cover - await self.tinker_dhw_mode(smile) + tinkered = await self.tinker_dhw_mode(smile) + assert not tinkered await smile.close_connection() await self.disconnect(server, client) + server, smile, client = await self.connect_wrapper(raise_timeout=True) + await self.device_test(smile, "2022-05-16 00:00:01", testdata, skip_testing=True) + + tinkered = await self.tinker_dhw_mode(smile, unhappy=True) + assert tinkered + + await smile.close_connection() + await self.disconnect(server, client) + + @pytest.mark.asyncio async def test_connect_anna_loria_cooling_active(self): """Test an Anna with a Loria in heating mode - state idle.""" diff --git a/tests/test_init.py b/tests/test_init.py index 3407a67a0..b6c0dc47f 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -536,6 +536,7 @@ async def device_test( test_time=None, testdata=None, initialize=True, + skip_testing=False, ): """Perform basic device tests.""" bsw_list = ["binary_sensors", "central", "climate", "sensors", "switches"] @@ -588,6 +589,9 @@ async def device_test( _LOGGER.info("Device list = %s", data.devices) self.show_setup(location_list, data.devices) + if skip_testing: + return + # Perform tests and asserts tests = 0 asserts = 0 @@ -644,10 +648,10 @@ async def tinker_reboot(self, smile, unhappy=False): await smile.reboot_gateway() _LOGGER.info(" + worked as intended") return True - except pw_exceptions.PlugwiseError: + except pw_exceptions.ConnectionFailedError: if unhappy: _LOGGER.info(" + failed as expected") - return False + return True else: # pragma: no cover _LOGGER.info(" - failed unexpectedly") return False @@ -659,8 +663,8 @@ async def tinker_switch( """Turn a Switch on and off to test functionality.""" _LOGGER.info("Asserting modifying settings for switch devices:") _LOGGER.info("- Devices (%s):", dev_id) + tinker_switch_passed = False for new_state in ["false", "true", "false"]: - tinker_switch_passed = False _LOGGER.info("- Switching %s", new_state) try: await smile.set_switch_state(dev_id, members, model, new_state) @@ -669,9 +673,9 @@ async def tinker_switch( except pw_exceptions.PlugwiseError: _LOGGER.info(" + locked, not switched as expected") return False - except pw_exceptions.ConnectionFailedError: + except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error if unhappy: - tinker_switch_passed = True # test is pass! + return True # test is pass! _LOGGER.info(" + failed as expected") else: # pragma: no cover _LOGGER.info(" - failed unexpectedly") @@ -697,18 +701,18 @@ async def tinker_thermostat_temp( except pw_exceptions.ConnectionFailedError: if unhappy: _LOGGER.info(" + tinker_thermostat_temp failed as expected") - tinker_temp_passed = True + return True else: # pragma: no cover _LOGGER.info(" - tinker_thermostat_temp failed unexpectedly") - tinker_temp_passed = False + return False return tinker_temp_passed @pytest.mark.asyncio async def tinker_thermostat_preset(self, smile, loc_id, unhappy=False): """Toggle preset to test functionality.""" + tinker_preset_passed = False for new_preset in ["asleep", "home", BOGUS]: - tinker_preset_passed = False warning = "" if new_preset[0] == "!": warning = " TTP Negative test" @@ -721,10 +725,10 @@ async def tinker_thermostat_preset(self, smile, loc_id, unhappy=False): except pw_exceptions.PlugwiseError: _LOGGER.info(" + found invalid preset, as expected") tinker_preset_passed = True - except pw_exceptions.ConnectionFailedError: + except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error if unhappy: - tinker_preset_passed = True _LOGGER.info(" + tinker_thermostat_preset failed as expected") + return True else: # pragma: no cover _LOGGER.info(" - tinker_thermostat_preset failed unexpectedly") return False @@ -740,8 +744,9 @@ async def tinker_thermostat_schedule( if good_schedules != []: if not single and ("!VeryBogusSchedule" not in good_schedules): good_schedules.append("!VeryBogusSchedule") + + tinker_schedule_passed = False for new_schedule in good_schedules: - tinker_schedule_passed = False warning = "" if new_schedule is not None and new_schedule[0] == "!": warning = " TTS Negative test" @@ -754,11 +759,11 @@ async def tinker_thermostat_schedule( except pw_exceptions.PlugwiseError: _LOGGER.info(" + failed as expected") tinker_schedule_passed = True - except pw_exceptions.ConnectionFailedError: + except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error tinker_schedule_passed = False if unhappy: _LOGGER.info(" + failed as expected before intended failure") - tinker_schedule_passed = True + return True else: # pragma: no cover _LOGGER.info(" - succeeded unexpectedly for some reason") return False @@ -772,6 +777,7 @@ async def tinker_thermostat_schedule( async def tinker_legacy_thermostat_schedule(self, smile, unhappy=False): """Toggle schedules to test functionality.""" states = ["on", "off", "!Bogus"] + tinker_schedule_passed = False for state in states: _LOGGER.info("- Adjusting schedule to state %s", state) try: @@ -781,11 +787,11 @@ async def tinker_legacy_thermostat_schedule(self, smile, unhappy=False): except pw_exceptions.PlugwiseError: _LOGGER.info(" + failed as expected") tinker_schedule_passed = True - except pw_exceptions.ConnectionFailedError: + except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error tinker_schedule_passed = False if unhappy: _LOGGER.info(" + failed as expected before intended failure") - tinker_schedule_passed = True + return True else: # pragma: no cover _LOGGER.info(" - succeeded unexpectedly for some reason") return False @@ -833,7 +839,7 @@ async def tinker_legacy_thermostat( smile, schedule_on=True, block_cooling=False, - unhappy=False + unhappy=False, ): """Toggle various climate settings to test functionality.""" result_1 = await self.tinker_thermostat_temp( @@ -847,8 +853,9 @@ async def tinker_legacy_thermostat( return result_1 and result_2 and result_3 @staticmethod - async def tinker_dhw_mode(smile): + async def tinker_dhw_mode(smile, unhappy=False): """Toggle dhw to test functionality.""" + tinker_dhw_mode_passed = False for mode in ["auto", "boost", BOGUS]: warning = "" if mode[0] == "!": @@ -858,12 +865,24 @@ async def tinker_dhw_mode(smile): try: await smile.set_select("select_dhw_mode", "dummy", mode) _LOGGER.info(" + tinker_dhw_mode worked as intended") + tinker_dhw_mode_passed = True except pw_exceptions.PlugwiseError: _LOGGER.info(" + tinker_dhw_mode found invalid mode, as expected") + tinker_dhw_mode_passed = False + except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error + if unhappy: + _LOGGER.info(" + failed as expected before intended failure") + return True + else: # pragma: no cover + _LOGGER.info(" - succeeded unexpectedly for some reason") + return False + + return tinker_dhw_mode_passed @staticmethod - async def tinker_regulation_mode(smile): + async def tinker_regulation_mode(smile, unhappy=False): """Toggle regulation_mode to test functionality.""" + tinker_reg_mode_passed = False for mode in ["off", "heating", "bleeding_cold", BOGUS]: warning = "" if mode[0] == "!": @@ -873,25 +892,49 @@ async def tinker_regulation_mode(smile): try: await smile.set_select("select_regulation_mode", "dummy", mode) _LOGGER.info(" + tinker_regulation_mode worked as intended") + tinker_reg_mode_passed = True except pw_exceptions.PlugwiseError: _LOGGER.info( " + tinker_regulation_mode found invalid mode, as expected" ) + tinker_reg_mode_passed = False + except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error + if unhappy: + _LOGGER.info(" + failed as expected before intended failure") + return True + else: # pragma: no cover + _LOGGER.info(" - succeeded unexpectedly for some reason") + return False + + return tinker_reg_mode_passed @staticmethod - async def tinker_max_boiler_temp(smile): + async def tinker_max_boiler_temp(smile, unhappy=False): """Change max boiler temp setpoint to test functionality.""" + tinker_max_boiler_temp_passed = False new_temp = 60.0 _LOGGER.info("- Adjusting temperature to %s", new_temp) for test in ["maximum_boiler_temperature", "bogus_temperature"]: + _LOGGER.info(" + for %s", test) try: await smile.set_number("dummy", test, new_temp) _LOGGER.info(" + tinker_max_boiler_temp worked as intended") + tinker_max_boiler_temp_passed = True except pw_exceptions.PlugwiseError: _LOGGER.info(" + tinker_max_boiler_temp failed as intended") + tinker_max_boiler_temp_passed = False + except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error + if unhappy: + _LOGGER.info(" + failed as expected before intended failure") + return True + else: # pragma: no cover + _LOGGER.info(" - succeeded unexpectedly for some reason") + return False + + return tinker_max_boiler_temp_passed @staticmethod - async def tinker_temp_offset(smile, dev_id): + async def tinker_temp_offset(smile, dev_id, unhappy=False): """Change temperature_offset to test functionality.""" new_offset = 1.0 _LOGGER.info("- Adjusting temperature offset to %s", new_offset) @@ -902,10 +945,18 @@ async def tinker_temp_offset(smile, dev_id): except pw_exceptions.PlugwiseError: _LOGGER.info(" + tinker_temp_offset failed as intended") return False + except pw_exceptions.ConnectionFailedError: + if unhappy: + _LOGGER.info(" + failed as expected before intended failure") + return True + else: # pragma: no cover + _LOGGER.info(" - succeeded unexpectedly for some reason") + return False @staticmethod - async def tinker_gateway_mode(smile): + async def tinker_gateway_mode(smile, unhappy=False): """Toggle gateway_mode to test functionality.""" + tinker_gateway_mode_passed = False for mode in ["away", "full", "vacation", "!bogus"]: warning = "" if mode[0] == "!": @@ -915,8 +966,19 @@ async def tinker_gateway_mode(smile): try: await smile.set_select("select_gateway_mode", "dummy", mode) _LOGGER.info(" + worked as intended") + tinker_gateway_mode_passed = True except pw_exceptions.PlugwiseError: _LOGGER.info(" + found invalid mode, as expected") + tinker_gateway_mode_passed = False + except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error + if unhappy: + _LOGGER.info(" + failed as expected before intended failure") + return True + else: # pragma: no cover + _LOGGER.info(" - succeeded unexpectedly for some reason") + return False + + return tinker_gateway_mode_passed @staticmethod def validate_test_basics( diff --git a/tests/test_legacy_anna.py b/tests/test_legacy_anna.py index cf6a62a70..2475aff83 100644 --- a/tests/test_legacy_anna.py +++ b/tests/test_legacy_anna.py @@ -39,7 +39,7 @@ async def test_connect_legacy_anna(self): await self.disconnect(server, client) server, smile, client = await self.connect_legacy_wrapper(raise_timeout=True) - await self.device_test(smile, "2020-03-22 00:00:01", testdata) + await self.device_test(smile, "2020-03-22 00:00:01", testdata, skip_testing=True) result = await self.tinker_legacy_thermostat(smile, unhappy=True) assert result await smile.close_connection()