diff --git a/.coveragerc b/.coveragerc index 5eab1aab00e..f788c917e82 100644 --- a/.coveragerc +++ b/.coveragerc @@ -182,11 +182,9 @@ omit = homeassistant/components/denonavr/media_player.py homeassistant/components/denonavr/receiver.py homeassistant/components/deutsche_bahn/sensor.py - homeassistant/components/devolo_home_control/binary_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/devolo_device.py homeassistant/components/devolo_home_control/devolo_multi_level_switch.py homeassistant/components/devolo_home_control/light.py homeassistant/components/devolo_home_control/sensor.py diff --git a/tests/components/devolo_home_control/mocks.py b/tests/components/devolo_home_control/mocks.py new file mode 100644 index 00000000000..d2ba69d9440 --- /dev/null +++ b/tests/components/devolo_home_control/mocks.py @@ -0,0 +1,91 @@ +"""Mocks for tests.""" + +from unittest.mock import MagicMock + +from devolo_home_control_api.publisher.publisher import Publisher + + +class BinarySensorPropertyMock: + """devolo Home Control binary sensor mock.""" + + element_uid = "Test" + key_count = 1 + sensor_type = "door" + sub_type = "" + state = False + + +class SettingsMock: + """devolo Home Control settings mock.""" + + name = "Test" + zone = "Test" + + +class DeviceMock: + """devolo Home Control device mock.""" + + available = True + brand = "devolo" + name = "Test Device" + uid = "Test" + settings_property = {"general_device_settings": SettingsMock()} + + def is_online(self): + """Mock online state of the device.""" + return DeviceMock.available + + +class BinarySensorMock(DeviceMock): + """devolo Home Control binary sensor device mock.""" + + binary_sensor_property = {"Test": BinarySensorPropertyMock()} + + +class RemoteControlMock(DeviceMock): + """devolo Home Control remote control device mock.""" + + remote_control_property = {"Test": BinarySensorPropertyMock()} + + +class DisabledBinarySensorMock(DeviceMock): + """devolo Home Control disabled binary sensor device mock.""" + + binary_sensor_property = {"devolo.WarningBinaryFI:Test": BinarySensorPropertyMock()} + + +class HomeControlMock: + """devolo Home Control gateway mock.""" + + binary_sensor_devices = [] + binary_switch_devices = [] + multi_level_sensor_devices = [] + multi_level_switch_devices = [] + devices = {} + publisher = MagicMock() + + def websocket_disconnect(self): + """Mock disconnect of the websocket.""" + pass + + +class HomeControlMockBinarySensor(HomeControlMock): + """devolo Home Control gateway mock with binary sensor device.""" + + binary_sensor_devices = [BinarySensorMock()] + devices = {"Test": BinarySensorMock()} + publisher = Publisher(devices.keys()) + publisher.unregister = MagicMock() + + +class HomeControlMockRemoteControl(HomeControlMock): + """devolo Home Control gateway mock with remote control device.""" + + devices = {"Test": RemoteControlMock()} + publisher = Publisher(devices.keys()) + + +class HomeControlMockDisabledBinarySensor(HomeControlMock): + """devolo Home Control gateway mock with disabled device.""" + + binary_sensor_devices = [DisabledBinarySensorMock()] diff --git a/tests/components/devolo_home_control/test_binary_sensor.py b/tests/components/devolo_home_control/test_binary_sensor.py new file mode 100644 index 00000000000..022cd4a1578 --- /dev/null +++ b/tests/components/devolo_home_control/test_binary_sensor.py @@ -0,0 +1,112 @@ +"""Tests for the devolo Home Control binary sensors.""" +from unittest.mock import patch + +import pytest + +from homeassistant.components.binary_sensor import DOMAIN +from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant + +from . import configure_integration +from .mocks import ( + DeviceMock, + HomeControlMock, + HomeControlMockBinarySensor, + HomeControlMockDisabledBinarySensor, + HomeControlMockRemoteControl, +) + + +@pytest.mark.usefixtures("mock_zeroconf") +async def test_binary_sensor(hass: HomeAssistant): + """Test setup and state change of a binary sensor device.""" + entry = configure_integration(hass) + DeviceMock.available = True + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[HomeControlMockBinarySensor, 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 == STATE_OFF + + # Emulate websocket message: sensor turned on + HomeControlMockBinarySensor.publisher.dispatch("Test", ("Test", True)) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test").state == STATE_ON + + # Emulate websocket message: device went offline + DeviceMock.available = False + HomeControlMockBinarySensor.publisher.dispatch("Test", ("Status", False, "status")) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test").state == STATE_UNAVAILABLE + + +@pytest.mark.usefixtures("mock_zeroconf") +async def test_remote_control(hass: HomeAssistant): + """Test setup and state change of a remote control device.""" + entry = configure_integration(hass) + DeviceMock.available = True + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[HomeControlMockRemoteControl, 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 == STATE_OFF + + # Emulate websocket message: button pressed + HomeControlMockRemoteControl.publisher.dispatch("Test", ("Test", 1)) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test").state == STATE_ON + + # Emulate websocket message: button released + HomeControlMockRemoteControl.publisher.dispatch("Test", ("Test", 0)) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test").state == STATE_OFF + + # Emulate websocket message: device went offline + DeviceMock.available = False + HomeControlMockRemoteControl.publisher.dispatch("Test", ("Status", False, "status")) + await hass.async_block_till_done() + assert hass.states.get(f"{DOMAIN}.test").state == STATE_UNAVAILABLE + + +@pytest.mark.usefixtures("mock_zeroconf") +async def test_disabled(hass: HomeAssistant): + """Test setup of a disabled device.""" + entry = configure_integration(hass) + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[HomeControlMockDisabledBinarySensor, HomeControlMock], + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert hass.states.get(f"{DOMAIN}.devolo.WarningBinaryFI:Test") is None + + +@pytest.mark.usefixtures("mock_zeroconf") +async def test_remove_from_hass(hass: HomeAssistant): + """Test removing entity.""" + entry = configure_integration(hass) + with patch( + "homeassistant.components.devolo_home_control.HomeControl", + side_effect=[HomeControlMockBinarySensor, 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 + HomeControlMockBinarySensor.publisher.unregister.assert_called_once()