From 5b4fdb081ec765cd5a2b6da2550f88d9b1099853 Mon Sep 17 00:00:00 2001 From: Guido Schmitz Date: Mon, 23 May 2022 17:24:28 +0200 Subject: [PATCH] Add climate tests for devolo_home_control (#72230) --- .coveragerc | 3 - tests/components/devolo_home_control/mocks.py | 52 ++++++++++++ .../devolo_home_control/test_climate.py | 81 +++++++++++++++++++ 3 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 tests/components/devolo_home_control/test_climate.py diff --git a/.coveragerc b/.coveragerc index ed1e0b0bce4..1eb922b52e5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -207,12 +207,9 @@ omit = homeassistant/components/denonavr/media_player.py homeassistant/components/denonavr/receiver.py homeassistant/components/deutsche_bahn/sensor.py - homeassistant/components/devolo_home_control/climate.py - homeassistant/components/devolo_home_control/const.py homeassistant/components/devolo_home_control/cover.py homeassistant/components/devolo_home_control/light.py homeassistant/components/devolo_home_control/sensor.py - homeassistant/components/devolo_home_control/subscriber.py homeassistant/components/devolo_home_control/switch.py homeassistant/components/digital_ocean/* homeassistant/components/discogs/sensor.py diff --git a/tests/components/devolo_home_control/mocks.py b/tests/components/devolo_home_control/mocks.py index 79bf94b8fc3..b43cb77ad71 100644 --- a/tests/components/devolo_home_control/mocks.py +++ b/tests/components/devolo_home_control/mocks.py @@ -8,6 +8,9 @@ from devolo_home_control_api.homecontrol import HomeControl from devolo_home_control_api.properties.binary_sensor_property import ( BinarySensorProperty, ) +from devolo_home_control_api.properties.multi_level_sensor_property import ( + MultiLevelSensorProperty, +) from devolo_home_control_api.properties.multi_level_switch_property import ( MultiLevelSwitchProperty, ) @@ -28,6 +31,31 @@ class BinarySensorPropertyMock(BinarySensorProperty): self.state = False +class MultiLevelSensorPropertyMock(MultiLevelSensorProperty): + """devolo Home Control multi level sensor mock.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + self.element_uid = "Test" + self.sensor_type = "temperature" + self._unit = "°C" + self._value = 20 + self._logger = MagicMock() + + +class MultiLevelSwitchPropertyMock(MultiLevelSwitchProperty): + """devolo Home Control multi level switch mock.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + self.element_uid = "Test" + self.min = 4 + self.max = 24 + self.switch_type = "temperature" + self._value = 20 + self._logger = MagicMock() + + class SirenPropertyMock(MultiLevelSwitchProperty): """devolo Home Control siren mock.""" @@ -84,6 +112,17 @@ class BinarySensorMockOverload(DeviceMock): self.binary_sensor_property["Overload"].sensor_type = "overload" +class ClimateMock(DeviceMock): + """devolo Home Control climate device mock.""" + + def __init__(self) -> None: + """Initialize the mock.""" + super().__init__() + self.device_model_uid = "devolo.model.Room:Thermostat" + self.multi_level_switch_property = {"Test": MultiLevelSwitchPropertyMock()} + self.multi_level_sensor_property = {"Test": MultiLevelSensorPropertyMock()} + + class RemoteControlMock(DeviceMock): """devolo Home Control remote control device mock.""" @@ -143,6 +182,19 @@ class HomeControlMockBinarySensor(HomeControlMock): self.publisher.unregister = MagicMock() +class HomeControlMockClimate(HomeControlMock): + """devolo Home Control gateway mock with climate devices.""" + + def __init__(self, **kwargs: Any) -> None: + """Initialize the mock.""" + super().__init__() + self.devices = { + "Test": ClimateMock(), + } + self.publisher = Publisher(self.devices.keys()) + self.publisher.unregister = MagicMock() + + class HomeControlMockRemoteControl(HomeControlMock): """devolo Home Control gateway mock with remote control device.""" diff --git a/tests/components/devolo_home_control/test_climate.py b/tests/components/devolo_home_control/test_climate.py new file mode 100644 index 00000000000..4fca1825459 --- /dev/null +++ b/tests/components/devolo_home_control/test_climate.py @@ -0,0 +1,81 @@ +"""Tests for the devolo Home Control climate.""" +from unittest.mock import patch + +from homeassistant.components.climate import DOMAIN +from homeassistant.components.climate.const import ( + ATTR_HVAC_MODE, + SERVICE_SET_TEMPERATURE, + HVACMode, +) +from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant + +from . import configure_integration +from .mocks import HomeControlMock, HomeControlMockClimate + + +async def test_climate(hass: HomeAssistant): + """Test setup and state change of a climate device.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockClimate() + test_gateway.devices["Test"].value = 20 + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{DOMAIN}.test") + assert state is not None + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == test_gateway.devices["Test"].value + + # Emulate websocket message: temperature changed + test_gateway.publisher.dispatch("Test", ("Test", 21.0)) + await hass.async_block_till_done() + state = hass.states.get(f"{DOMAIN}.test") + assert state.state == HVACMode.HEAT + assert state.attributes[ATTR_TEMPERATURE] == 21.0 + + # Test setting temperature + with patch( + "devolo_home_control_api.properties.multi_level_switch_property.MultiLevelSwitchProperty.set" + ) as set_value: + await hass.services.async_call( + DOMAIN, + SERVICE_SET_TEMPERATURE, + { + ATTR_ENTITY_ID: f"{DOMAIN}.test", + ATTR_HVAC_MODE: HVACMode.HEAT, + ATTR_TEMPERATURE: 20.0, + }, + blocking=True, + ) # In reality, this leads to a websocket message like already tested above + set_value.assert_called_once_with(20.0) + + # Emulate websocket message: device went offline + test_gateway.devices["Test"].status = 1 + test_gateway.publisher.dispatch("Test", ("Status", False, "status")) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test").state == STATE_UNAVAILABLE + + +async def test_remove_from_hass(hass: HomeAssistant): + """Test removing entity.""" + entry = configure_integration(hass) + test_gateway = HomeControlMockClimate() + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[test_gateway, HomeControlMock()], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + state = hass.states.get(f"{DOMAIN}.test") + assert state is not None + await hass.config_entries.async_remove(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + assert test_gateway.publisher.unregister.call_count == 2