From 36582f9ac28f6e6d61a795fe7336e280867ea594 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 3 Jan 2025 12:44:47 +0100 Subject: [PATCH] Refactor all Sensibo tests (#134478) * Add me json * Mods * Mods * More * Mods * Mods * clean * last bits * Fix * unique id * return_value * remove blocking * Fix rebase --- tests/components/sensibo/conftest.py | 100 +- tests/components/sensibo/fixtures/me.json | 13 + .../sensibo/snapshots/test_entity.ambr | 137 ++ .../components/sensibo/test_binary_sensor.py | 27 +- tests/components/sensibo/test_button.py | 94 +- tests/components/sensibo/test_climate.py | 1552 +++++++---------- tests/components/sensibo/test_config_flow.py | 382 ++-- tests/components/sensibo/test_coordinator.py | 115 +- tests/components/sensibo/test_entity.py | 62 +- tests/components/sensibo/test_init.py | 115 +- tests/components/sensibo/test_number.py | 86 +- tests/components/sensibo/test_select.py | 144 +- tests/components/sensibo/test_sensor.py | 25 +- tests/components/sensibo/test_switch.py | 406 ++--- tests/components/sensibo/test_update.py | 20 +- 15 files changed, 1398 insertions(+), 1880 deletions(-) create mode 100644 tests/components/sensibo/fixtures/me.json create mode 100644 tests/components/sensibo/snapshots/test_entity.ambr diff --git a/tests/components/sensibo/conftest.py b/tests/components/sensibo/conftest.py index eaa42e47257..980c761a369 100644 --- a/tests/components/sensibo/conftest.py +++ b/tests/components/sensibo/conftest.py @@ -2,16 +2,15 @@ from __future__ import annotations +from collections.abc import AsyncGenerator, Generator import json from typing import Any -from unittest.mock import patch +from unittest.mock import AsyncMock, MagicMock, patch -from pysensibo import SensiboClient -from pysensibo.model import SensiboData +from pysensibo import APIV1, APIV2, SensiboClient, SensiboData import pytest from homeassistant.components.sensibo.const import DOMAIN, PLATFORMS -from homeassistant.config_entries import SOURCE_USER from homeassistant.const import Platform from homeassistant.core import HomeAssistant @@ -21,6 +20,15 @@ from tests.common import MockConfigEntry, load_fixture from tests.test_util.aiohttp import AiohttpClientMocker +@pytest.fixture +def mock_setup_entry() -> Generator[AsyncMock]: + """Override async_setup_entry.""" + with patch( + "homeassistant.components.sensibo.async_setup_entry", return_value=True + ) as mock_setup_entry: + yield mock_setup_entry + + @pytest.fixture(name="load_platforms") async def patch_platform_constant() -> list[Platform]: """Return list of platforms to load.""" @@ -30,63 +38,81 @@ async def patch_platform_constant() -> list[Platform]: @pytest.fixture async def load_int( hass: HomeAssistant, - get_data: SensiboData, + mock_client: SensiboClient, load_platforms: list[Platform], ) -> MockConfigEntry: - """Set up the Sensibo integration in Home Assistant.""" + """Set up the Sensibo integration.""" config_entry = MockConfigEntry( domain=DOMAIN, - source=SOURCE_USER, data=ENTRY_CONFIG, entry_id="1", - unique_id="username", + unique_id="firstnamelastname", version=2, ) config_entry.add_to_hass(hass) - with ( - patch("homeassistant.components.sensibo.PLATFORMS", load_platforms), - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ), - ): + with patch("homeassistant.components.sensibo.PLATFORMS", load_platforms): await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() + await hass.async_block_till_done() return config_entry +@pytest.fixture(name="mock_client") +async def get_client( + hass: HomeAssistant, + get_data: tuple[SensiboData, dict[str, Any]], +) -> AsyncGenerator[MagicMock]: + """Mock SensiboClient.""" + + with patch( + "homeassistant.components.sensibo.coordinator.SensiboClient", + autospec=True, + ) as mock_client: + client = mock_client.return_value + client.async_get_devices_data.return_value = get_data[0] + client.async_get_me.return_value = get_data[1] + yield client + + @pytest.fixture(name="get_data") async def get_data_from_library( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, load_json: dict[str, Any] -) -> SensiboData: - """Retrieve data from upstream Sensibo library.""" - - client = SensiboClient("123467890", aioclient_mock.create_session(hass.loop)) - with patch("pysensibo.SensiboClient.async_get_devices", return_value=load_json): - output = await client.async_get_devices_data() + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + load_json: tuple[dict[str, Any], dict[str, Any]], +) -> AsyncGenerator[tuple[SensiboData, dict[str, Any]]]: + """Get data from api.""" + aioclient_mock.request( + "GET", + url=APIV1 + "/users/me", + params={"apiKey": "1234567890"}, + json=load_json[1], + ) + aioclient_mock.request( + "GET", + url=APIV2 + "/users/me/pods", + params={"apiKey": "1234567890", "fields": "*"}, + json=load_json[0], + ) + client = SensiboClient("1234567890", aioclient_mock.create_session(hass.loop)) + me = await client.async_get_me() + data = await client.async_get_devices_data() + yield (data, me) await client._session.close() - return output @pytest.fixture(name="load_json") -def load_json_from_fixture(load_data: str) -> SensiboData: +def load_json_from_fixture( + load_data: tuple[str, str], +) -> tuple[dict[str, Any], dict[str, Any]]: """Load fixture with json data and return.""" - json_data: dict[str, Any] = json.loads(load_data) - return json_data + json_data: dict[str, Any] = json.loads(load_data[0]) + json_me: dict[str, Any] = json.loads(load_data[1]) + return (json_data, json_me) @pytest.fixture(name="load_data", scope="package") -def load_data_from_fixture() -> str: +def load_data_from_fixture() -> tuple[str, str]: """Load fixture with fixture data and return.""" - return load_fixture("data.json", "sensibo") + return (load_fixture("data.json", "sensibo"), load_fixture("me.json", "sensibo")) diff --git a/tests/components/sensibo/fixtures/me.json b/tests/components/sensibo/fixtures/me.json new file mode 100644 index 00000000000..1b06a524966 --- /dev/null +++ b/tests/components/sensibo/fixtures/me.json @@ -0,0 +1,13 @@ +{ + "status": "success", + "result": { + "username": "firstnamelastname", + "email": "first.last@domain.com", + "firstName": "Firstname", + "lastName": "Lastname", + "temperatureUnit": "C", + "appRegistrationSource": "Sensibo", + "organization": null, + "availableOrganizations": [] + } +} diff --git a/tests/components/sensibo/snapshots/test_entity.ambr b/tests/components/sensibo/snapshots/test_entity.ambr new file mode 100644 index 00000000000..23ead2f6d96 --- /dev/null +++ b/tests/components/sensibo/snapshots/test_entity.ambr @@ -0,0 +1,137 @@ +# serializer version: 1 +# name: test_device + list([ + DeviceRegistryEntrySnapshot({ + 'area_id': 'hallway', + 'config_entries': , + 'configuration_url': 'https://home.sensibo.com/', + 'connections': set({ + tuple( + 'mac', + '00:02:00:b6:00:00', + ), + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': 'esp8266ex', + 'id': , + 'identifiers': set({ + tuple( + 'sensibo', + 'ABC999111', + ), + }), + 'is_new': False, + 'labels': set({ + }), + 'manufacturer': 'Sensibo', + 'model': 'skyv2', + 'model_id': None, + 'name': 'Hallway', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': '1234567890', + 'suggested_area': 'Hallway', + 'sw_version': 'SKY30046', + 'via_device_id': None, + }), + DeviceRegistryEntrySnapshot({ + 'area_id': 'kitchen', + 'config_entries': , + 'configuration_url': 'https://home.sensibo.com/', + 'connections': set({ + tuple( + 'mac', + '00:01:00:01:00:01', + ), + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': 'pure-esp32', + 'id': , + 'identifiers': set({ + tuple( + 'sensibo', + 'AAZZAAZZ', + ), + }), + 'is_new': False, + 'labels': set({ + }), + 'manufacturer': 'Sensibo', + 'model': 'pure', + 'model_id': None, + 'name': 'Kitchen', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': '0987654321', + 'suggested_area': 'Kitchen', + 'sw_version': 'PUR00111', + 'via_device_id': None, + }), + DeviceRegistryEntrySnapshot({ + 'area_id': 'bedroom', + 'config_entries': , + 'configuration_url': 'https://home.sensibo.com/', + 'connections': set({ + tuple( + 'mac', + '00:03:00:03:00:03', + ), + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': 'pure-esp32', + 'id': , + 'identifiers': set({ + tuple( + 'sensibo', + 'BBZZBBZZ', + ), + }), + 'is_new': False, + 'labels': set({ + }), + 'manufacturer': 'Sensibo', + 'model': 'pure', + 'model_id': None, + 'name': 'Bedroom', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': '0987654329', + 'suggested_area': 'Bedroom', + 'sw_version': 'PUR00111', + 'via_device_id': None, + }), + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'configuration_url': 'https://home.sensibo.com/', + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': 'nrf52', + 'id': , + 'identifiers': set({ + tuple( + 'sensibo', + 'AABBCC', + ), + }), + 'is_new': False, + 'labels': set({ + }), + 'manufacturer': 'Sensibo', + 'model': 'motion_sensor', + 'model_id': None, + 'name': 'Hallway Motion Sensor', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'suggested_area': None, + 'sw_version': 'V17', + 'via_device_id': , + }), + ]) +# --- diff --git a/tests/components/sensibo/test_binary_sensor.py b/tests/components/sensibo/test_binary_sensor.py index 41772f07f96..292fe52de40 100644 --- a/tests/components/sensibo/test_binary_sensor.py +++ b/tests/components/sensibo/test_binary_sensor.py @@ -3,9 +3,11 @@ from __future__ import annotations from datetime import timedelta +from typing import Any from unittest.mock import patch -from pysensibo.model import SensiboData +from freezegun.api import FrozenDateTimeFactory +from pysensibo import SensiboData import pytest from syrupy.assertion import SnapshotAssertion @@ -13,7 +15,6 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_OFF, STATE_ON, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er -from homeassistant.util import dt as dt_util from tests.common import async_fire_time_changed, snapshot_platform @@ -27,29 +28,31 @@ async def test_binary_sensor( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + get_data: tuple[SensiboData, dict[str, Any]], entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion, + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo binary sensor.""" await snapshot_platform(hass, entity_registry, snapshot, load_int.entry_id) monkeypatch.setattr( - get_data.parsed["ABC999111"].motion_sensors["AABBCC"], "motion", False + get_data[0].parsed["ABC999111"].motion_sensors["AABBCC"], "motion", False ) with patch( "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", return_value=get_data, ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - state1 = hass.states.get("binary_sensor.hallway_motion_sensor_connectivity") - state3 = hass.states.get("binary_sensor.hallway_motion_sensor_motion") - assert state1.state == STATE_ON - assert state3.state == STATE_OFF + assert ( + hass.states.get("binary_sensor.hallway_motion_sensor_connectivity").state + == STATE_ON + ) + assert ( + hass.states.get("binary_sensor.hallway_motion_sensor_motion").state == STATE_OFF + ) diff --git a/tests/components/sensibo/test_button.py b/tests/components/sensibo/test_button.py index 5c36fe9e94d..556c7ac344d 100644 --- a/tests/components/sensibo/test_button.py +++ b/tests/components/sensibo/test_button.py @@ -2,12 +2,13 @@ from __future__ import annotations -from datetime import datetime, timedelta -from unittest.mock import patch +from datetime import timedelta +from typing import Any +from unittest.mock import MagicMock from freezegun import freeze_time from freezegun.api import FrozenDateTimeFactory -from pysensibo.model import SensiboData +from pysensibo import SensiboData import pytest from syrupy.assertion import SnapshotAssertion @@ -45,14 +46,20 @@ async def test_button( await snapshot_platform(hass, entity_registry, snapshot, load_int.entry_id) +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +@pytest.mark.parametrize( + "load_platforms", + [[Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SENSOR]], +) async def test_button_update( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], freezer: FrozenDateTimeFactory, ) -> None: - """Test the Sensibo button.""" + """Test the Sensibo button press.""" state_button = hass.states.get("button.hallway_reset_filter") state_filter_clean = hass.states.get("binary_sensor.hallway_filter_clean_required") @@ -62,48 +69,32 @@ async def test_button_update( assert state_filter_clean.state is STATE_ON assert state_filter_last_reset.state == "2022-03-12T15:24:26+00:00" - today = datetime(datetime.now().year + 1, 6, 19, 20, 0, 0).replace( - tzinfo=dt_util.UTC - ) - today_str = today.isoformat() + today = dt_util.utcnow() + timedelta(minutes=10) + today = today.replace(microsecond=0) + today_str = today.isoformat(timespec="seconds") freezer.move_to(today) - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_reset_filter", - return_value={"status": "success"}, - ), - ): - await hass.services.async_call( - BUTTON_DOMAIN, - SERVICE_PRESS, - { - ATTR_ENTITY_ID: state_button.entity_id, - }, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_reset_filter.return_value = {"status": "success"} - monkeypatch.setattr(get_data.parsed["ABC999111"], "filter_clean", False) + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + { + ATTR_ENTITY_ID: state_button.entity_id, + }, + blocking=True, + ) + + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "filter_clean", False) monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "filter_last_reset", today, ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() state_button = hass.states.get("button.hallway_reset_filter") state_filter_clean = hass.states.get("binary_sensor.hallway_filter_clean_required") @@ -116,31 +107,22 @@ async def test_button_update( async def test_button_failure( hass: HomeAssistant, load_int: ConfigEntry, - monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, ) -> None: - """Test the Sensibo button fails.""" + """Test the Sensibo button failure.""" - state_button = hass.states.get("button.hallway_reset_filter") + state = hass.states.get("button.hallway_reset_filter") - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_reset_filter", - return_value={"status": "failure"}, - ), - pytest.raises( - HomeAssistantError, - ), + mock_client.async_reset_filter.return_value = {"status": "failure"} + + with pytest.raises( + HomeAssistantError, ): await hass.services.async_call( BUTTON_DOMAIN, SERVICE_PRESS, { - ATTR_ENTITY_ID: state_button.entity_id, + ATTR_ENTITY_ID: state.entity_id, }, blocking=True, ) diff --git a/tests/components/sensibo/test_climate.py b/tests/components/sensibo/test_climate.py index dbc420812ae..028dd602226 100644 --- a/tests/components/sensibo/test_climate.py +++ b/tests/components/sensibo/test_climate.py @@ -1,10 +1,12 @@ -"""The test for the sensibo binary sensor platform.""" +"""The test for the sensibo climate platform.""" from __future__ import annotations from datetime import datetime, timedelta -from unittest.mock import patch +from typing import Any +from unittest.mock import MagicMock +from freezegun.api import FrozenDateTimeFactory from pysensibo.model import SensiboData import pytest from syrupy.assertion import SnapshotAssertion @@ -21,6 +23,7 @@ from homeassistant.components.climate import ( SERVICE_SET_HVAC_MODE, SERVICE_SET_SWING_MODE, SERVICE_SET_TEMPERATURE, + HVACMode, ) from homeassistant.components.sensibo.climate import ( ATTR_AC_INTEGRATION, @@ -53,6 +56,8 @@ from homeassistant.const import ( ATTR_TEMPERATURE, SERVICE_TURN_OFF, SERVICE_TURN_ON, + STATE_OFF, + STATE_ON, STATE_UNKNOWN, Platform, ) @@ -86,7 +91,6 @@ async def test_climate_find_valid_targets() -> None: async def test_climate( hass: HomeAssistant, caplog: pytest.LogCaptureFixture, - get_data: SensiboData, load_int: ConfigEntry, entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion, @@ -95,37 +99,33 @@ async def test_climate( await snapshot_platform(hass, entity_registry, snapshot, load_int.entry_id) - found_log = False logs = caplog.get_records("setup") - for log in logs: - if ( - log.message - == "Device Bedroom not correctly registered with remote on Sensibo cloud." - ): - found_log = True - break - assert found_log + assert "Device Bedroom not correctly registered with remote on Sensibo cloud." in [ + log.message for log in logs + ] async def test_climate_fan( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate fan service.""" - state1 = hass.states.get("climate.hallway") - assert state1.attributes["fan_mode"] == "high" + state = hass.states.get("climate.hallway") + assert state.attributes["fan_mode"] == "high" monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "fan_modes", ["quiet", "low", "medium", "not_in_ha"], ) monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "fan_modes_translated", { "low": "low", @@ -141,33 +141,26 @@ async def test_climate_fan( await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_FAN_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_FAN_MODE: "not_in_ha"}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_FAN_MODE: "not_in_ha"}, blocking=True, ) - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_FAN_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_FAN_MODE: "low"}, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Success"} + } - state2 = hass.states.get("climate.hallway") - assert state2.attributes["fan_mode"] == "low" + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_FAN_MODE: "low"}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.attributes["fan_mode"] == "low" monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "active_features", [ "timestamp", @@ -179,52 +172,43 @@ async def test_climate_fan( "light", ], ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - ), - pytest.raises(HomeAssistantError), - ): + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + with pytest.raises(HomeAssistantError): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_FAN_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_FAN_MODE: "low"}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_FAN_MODE: "low"}, blocking=True, ) - await hass.async_block_till_done() - state3 = hass.states.get("climate.hallway") - assert state3.attributes["fan_mode"] == "low" + state = hass.states.get("climate.hallway") + assert state.attributes["fan_mode"] == "low" async def test_climate_swing( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate swing service.""" - state1 = hass.states.get("climate.hallway") - assert state1.attributes["swing_mode"] == "stopped" + state = hass.states.get("climate.hallway") + assert state.attributes["swing_mode"] == "stopped" monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "swing_modes", ["stopped", "fixedtop", "fixedmiddletop", "not_in_ha"], ) monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "swing_modes_translated", { "fixedmiddletop": "fixedMiddleTop", @@ -233,15 +217,9 @@ async def test_climate_swing( "not_in_ha": "not_in_ha", }, ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() with pytest.raises( HomeAssistantError, @@ -250,33 +228,26 @@ async def test_climate_swing( await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_SWING_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_SWING_MODE: "not_in_ha"}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_SWING_MODE: "not_in_ha"}, blocking=True, ) - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_SWING_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_SWING_MODE: "fixedtop"}, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Success"} + } - state2 = hass.states.get("climate.hallway") - assert state2.attributes["swing_mode"] == "fixedtop" + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_SWING_MODE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_SWING_MODE: "fixedtop"}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.attributes["swing_mode"] == "fixedtop" monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "active_features", [ "timestamp", @@ -287,185 +258,120 @@ async def test_climate_swing( "light", ], ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - ), - pytest.raises(HomeAssistantError), - ): + with pytest.raises(HomeAssistantError): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_SWING_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_SWING_MODE: "fixedtop"}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_SWING_MODE: "fixedtop"}, blocking=True, ) - await hass.async_block_till_done() - state3 = hass.states.get("climate.hallway") - assert state3.attributes["swing_mode"] == "fixedtop" + state = hass.states.get("climate.hallway") + assert state.attributes["swing_mode"] == "fixedtop" async def test_climate_temperatures( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate temperature service.""" - state1 = hass.states.get("climate.hallway") - assert state1.attributes["temperature"] == 25 + state = hass.states.get("climate.hallway") + assert state.attributes["temperature"] == 25 - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_TEMPERATURE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 20}, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Success"} + } - state2 = hass.states.get("climate.hallway") - assert state2.attributes["temperature"] == 20 + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_TEMPERATURE: 20}, + blocking=True, + ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_set_ac_state_property", - ) as mock_call: - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_TEMPERATURE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 20}, - blocking=True, - ) - assert not mock_call.called + state = hass.states.get("climate.hallway") + assert state.attributes["temperature"] == 20 - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_TEMPERATURE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 15}, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_ac_state_property.reset_mock() - state2 = hass.states.get("climate.hallway") - assert state2.attributes["temperature"] == 16 + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_TEMPERATURE: 20}, + blocking=True, + ) + assert not mock_client.async_set_ac_state_property.called - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_TEMPERATURE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 18.5}, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Success"} + } + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_TEMPERATURE: 15}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.attributes["temperature"] == 16 + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_TEMPERATURE: 18.5}, + blocking=True, + ) state2 = hass.states.get("climate.hallway") assert state2.attributes["temperature"] == 19 - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - pytest.raises( - ServiceValidationError, - match="Provided temperature 24.0 is not valid. Accepted range is 10 to 20", - ), + with pytest.raises( + ServiceValidationError, + match="Provided temperature 24.0 is not valid. Accepted range is 10 to 20", ): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 24}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_TEMPERATURE: 24}, blocking=True, ) - await hass.async_block_till_done() - state2 = hass.states.get("climate.hallway") - assert state2.attributes["temperature"] == 19 + state = hass.states.get("climate.hallway") + assert state.attributes["temperature"] == 19 - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_TEMPERATURE: 20}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.attributes["temperature"] == 20 + + with pytest.raises(MultipleInvalid): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 20}, + {ATTR_ENTITY_ID: state.entity_id}, blocking=True, ) - await hass.async_block_till_done() - state2 = hass.states.get("climate.hallway") - assert state2.attributes["temperature"] == 20 - - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - pytest.raises(MultipleInvalid), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_TEMPERATURE, - {ATTR_ENTITY_ID: state1.entity_id}, - blocking=True, - ) - await hass.async_block_till_done() - - state2 = hass.states.get("climate.hallway") - assert state2.attributes["temperature"] == 20 + state = hass.states.get("climate.hallway") + assert state.attributes["temperature"] == 20 monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "active_features", [ "timestamp", @@ -476,45 +382,35 @@ async def test_climate_temperatures( "light", ], ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - pytest.raises(HomeAssistantError), - ): + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + with pytest.raises(HomeAssistantError): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_TEMPERATURE: 20}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_TEMPERATURE: 20}, blocking=True, ) - await hass.async_block_till_done() - state2 = hass.states.get("climate.hallway") - assert state2.attributes["temperature"] == 20 + state = hass.states.get("climate.hallway") + assert state.attributes["temperature"] == 20 async def test_climate_temperature_is_none( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate temperature service no temperature provided.""" monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "active_features", [ "timestamp", @@ -528,55 +424,46 @@ async def test_climate_temperature_is_none( ], ) monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "target_temp", 25, ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - state1 = hass.states.get("climate.hallway") - assert state1.attributes["temperature"] == 25 + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - ), - pytest.raises(ServiceValidationError), - ): + state = hass.states.get("climate.hallway") + assert state.attributes["temperature"] == 25 + + with pytest.raises(ServiceValidationError): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, { - ATTR_ENTITY_ID: state1.entity_id, + ATTR_ENTITY_ID: state.entity_id, ATTR_TARGET_TEMP_HIGH: 30, ATTR_TARGET_TEMP_LOW: 20, }, blocking=True, ) - await hass.async_block_till_done() - state2 = hass.states.get("climate.hallway") - assert state2.attributes["temperature"] == 25 + state = hass.states.get("climate.hallway") + assert state.attributes["temperature"] == 25 async def test_climate_hvac_mode( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate hvac mode service.""" monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "active_features", [ "timestamp", @@ -589,254 +476,182 @@ async def test_climate_hvac_mode( "light", ], ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - state1 = hass.states.get("climate.hallway") - assert state1.state == "heat" - - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_HVAC_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_HVAC_MODE: "off"}, - blocking=True, - ) - await hass.async_block_till_done() - - state2 = hass.states.get("climate.hallway") - assert state2.state == "off" - - monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", False) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_HVAC_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_HVAC_MODE: "heat"}, - blocking=True, - ) + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - state2 = hass.states.get("climate.hallway") - assert state2.state == "heat" + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT + + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Success"} + } + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_HVAC_MODE: "off"}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.OFF + + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "device_on", False) + + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_HVAC_MODE: "heat"}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT async def test_climate_on_off( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate on/off service.""" - monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "device_on", True) - state1 = hass.states.get("climate.hallway") - assert state1.state == "heat" - - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: state1.entity_id}, - blocking=True, - ) - await hass.async_block_till_done() - - state2 = hass.states.get("climate.hallway") - assert state2.state == "off" - - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_TURN_ON, - {ATTR_ENTITY_ID: state1.entity_id}, - blocking=True, - ) + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - state2 = hass.states.get("climate.hallway") - assert state2.state == "heat" + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT + + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Success"} + } + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: state.entity_id}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.OFF + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: state.entity_id}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT async def test_climate_service_failed( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate service failed.""" - monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "device_on", True) - state1 = hass.states.get("climate.hallway") - assert state1.state == "heat" + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={ - "result": {"status": "Error", "failureReason": "Did not work"} - }, - ), - pytest.raises(HomeAssistantError), - ): + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT + + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Error", "failureReason": "Did not work"} + } + + with pytest.raises(HomeAssistantError): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: state1.entity_id}, + {ATTR_ENTITY_ID: state.entity_id}, blocking=True, ) - await hass.async_block_till_done() - state2 = hass.states.get("climate.hallway") - assert state2.state == "heat" + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT async def test_climate_assumed_state( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate assumed state service.""" - monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "device_on", True) - state1 = hass.states.get("climate.hallway") - assert state1.state == "heat" - - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - DOMAIN, - SERVICE_ASSUME_STATE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_STATE: "off"}, - blocking=True, - ) + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - state2 = hass.states.get("climate.hallway") - assert state2.state == "off" + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT + + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Success"} + } + + await hass.services.async_call( + DOMAIN, + SERVICE_ASSUME_STATE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_STATE: "off"}, + blocking=True, + ) + + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.OFF async def test_climate_no_fan_no_swing( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: - """Test the Sensibo climate fan service.""" + """Test the Sensibo climate fan.""" state = hass.states.get("climate.hallway") assert state.attributes["fan_mode"] == "high" assert state.attributes["swing_mode"] == "stopped" - monkeypatch.setattr(get_data.parsed["ABC999111"], "fan_mode", None) - monkeypatch.setattr(get_data.parsed["ABC999111"], "swing_mode", None) - monkeypatch.setattr(get_data.parsed["ABC999111"], "fan_modes", None) - monkeypatch.setattr(get_data.parsed["ABC999111"], "swing_modes", None) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "fan_mode", None) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "swing_mode", None) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "fan_modes", None) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "swing_modes", None) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() state = hass.states.get("climate.hallway") assert state.attributes["fan_mode"] is None @@ -850,109 +665,69 @@ async def test_climate_set_timer( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate Set Timer service.""" - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state_climate = hass.states.get("climate.hallway") + state = hass.states.get("climate.hallway") assert hass.states.get("sensor.hallway_timer_end_time").state == STATE_UNKNOWN - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_timer", - return_value={"status": "failure"}, - ), - pytest.raises( - MultipleInvalid, - ), - ): + mock_client.async_set_timer.return_value = {"status": "failure"} + + with pytest.raises(MultipleInvalid): await hass.services.async_call( DOMAIN, SERVICE_ENABLE_TIMER, { - ATTR_ENTITY_ID: state_climate.entity_id, + ATTR_ENTITY_ID: state.entity_id, }, blocking=True, ) - await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_timer", - return_value={"status": "failure"}, - ), - pytest.raises( - HomeAssistantError, - ), - ): + with pytest.raises(HomeAssistantError): await hass.services.async_call( DOMAIN, SERVICE_ENABLE_TIMER, { - ATTR_ENTITY_ID: state_climate.entity_id, + ATTR_ENTITY_ID: state.entity_id, ATTR_MINUTES: 30, }, blocking=True, ) - await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_timer", - return_value={"status": "success", "result": {"id": "SzTGE4oZ4D"}}, - ), - ): - await hass.services.async_call( - DOMAIN, - SERVICE_ENABLE_TIMER, - { - ATTR_ENTITY_ID: state_climate.entity_id, - ATTR_MINUTES: 30, - }, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_timer.return_value = { + "status": "success", + "result": {"id": "SzTGE4oZ4D"}, + } - monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_on", True) - monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_id", "SzTGE4oZ4D") - monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_state_on", False) + await hass.services.async_call( + DOMAIN, + SERVICE_ENABLE_TIMER, + { + ATTR_ENTITY_ID: state.entity_id, + ATTR_MINUTES: 30, + }, + blocking=True, + ) + + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "timer_on", True) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "timer_id", "SzTGE4oZ4D") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "timer_state_on", False) monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "timer_time", datetime(2022, 6, 6, 12, 00, 00, tzinfo=dt_util.UTC), ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() assert ( hass.states.get("sensor.hallway_timer_end_time").state @@ -965,110 +740,86 @@ async def test_climate_pure_boost( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: - """Test the Sensibo climate assumed state service.""" + """Test the Sensibo climate pure boost service.""" - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state_climate = hass.states.get("climate.kitchen") + state = hass.states.get("climate.kitchen") state2 = hass.states.get("switch.kitchen_pure_boost") - assert state2.state == "off" + assert state2.state == STATE_OFF - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_pureboost", - ), - pytest.raises( - MultipleInvalid, - ), + with pytest.raises( + MultipleInvalid, ): await hass.services.async_call( DOMAIN, SERVICE_ENABLE_PURE_BOOST, { - ATTR_ENTITY_ID: state_climate.entity_id, + ATTR_ENTITY_ID: state.entity_id, ATTR_INDOOR_INTEGRATION: True, ATTR_OUTDOOR_INTEGRATION: True, ATTR_SENSITIVITY: "sensitive", }, blocking=True, ) + + mock_client.async_set_pureboost.return_value = { + "status": "success", + "result": { + "enabled": True, + "sensitivity": "S", + "measurements_integration": True, + "ac_integration": False, + "geo_integration": False, + "prime_integration": True, + }, + } + + await hass.services.async_call( + DOMAIN, + SERVICE_ENABLE_PURE_BOOST, + { + ATTR_ENTITY_ID: state.entity_id, + ATTR_AC_INTEGRATION: False, + ATTR_GEO_INTEGRATION: False, + ATTR_INDOOR_INTEGRATION: True, + ATTR_OUTDOOR_INTEGRATION: True, + ATTR_SENSITIVITY: "sensitive", + }, + blocking=True, + ) + + monkeypatch.setattr(get_data[0].parsed["AAZZAAZZ"], "pure_boost_enabled", True) + monkeypatch.setattr(get_data[0].parsed["AAZZAAZZ"], "pure_sensitivity", "s") + monkeypatch.setattr( + get_data[0].parsed["AAZZAAZZ"], "pure_measure_integration", True + ) + monkeypatch.setattr(get_data[0].parsed["AAZZAAZZ"], "pure_prime_integration", True) + + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_pureboost", - return_value={ - "status": "success", - "result": { - "enabled": True, - "sensitivity": "S", - "measurements_integration": True, - "ac_integration": False, - "geo_integration": False, - "prime_integration": True, - }, - }, - ), - ): - await hass.services.async_call( - DOMAIN, - SERVICE_ENABLE_PURE_BOOST, - { - ATTR_ENTITY_ID: state_climate.entity_id, - ATTR_AC_INTEGRATION: False, - ATTR_GEO_INTEGRATION: False, - ATTR_INDOOR_INTEGRATION: True, - ATTR_OUTDOOR_INTEGRATION: True, - ATTR_SENSITIVITY: "sensitive", - }, - blocking=True, - ) - await hass.async_block_till_done() - - monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_boost_enabled", True) - monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_sensitivity", "s") - monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_measure_integration", True) - monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_prime_integration", True) - - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - - state1 = hass.states.get("switch.kitchen_pure_boost") - state2 = hass.states.get( - "binary_sensor.kitchen_pure_boost_linked_with_indoor_air_quality" + assert hass.states.get("switch.kitchen_pure_boost").state == STATE_ON + assert ( + hass.states.get( + "binary_sensor.kitchen_pure_boost_linked_with_indoor_air_quality" + ).state + == STATE_ON ) - state3 = hass.states.get( - "binary_sensor.kitchen_pure_boost_linked_with_outdoor_air_quality" + assert ( + hass.states.get( + "binary_sensor.kitchen_pure_boost_linked_with_outdoor_air_quality" + ).state + == STATE_ON ) - state4 = hass.states.get("sensor.kitchen_pure_sensitivity") - assert state1.state == "on" - assert state2.state == "on" - assert state3.state == "on" - assert state4.state == "s" + assert hass.states.get("sensor.kitchen_pure_sensitivity").state == "s" @pytest.mark.usefixtures("entity_registry_enabled_by_default") @@ -1076,32 +827,20 @@ async def test_climate_climate_react( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate react custom service.""" - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() state_climate = hass.states.get("climate.hallway") - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_climate_react", - ), - pytest.raises( - MultipleInvalid, - ), + with pytest.raises( + MultipleInvalid, ): await hass.services.async_call( DOMAIN, @@ -1114,86 +853,80 @@ async def test_climate_climate_react( }, blocking=True, ) - await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_climate_react", - return_value={ - "status": "success", - "result": { - "enabled": True, - "deviceUid": "ABC999111", - "highTemperatureState": { - "on": True, - "targetTemperature": 15, - "temperatureUnit": "C", - "mode": "cool", - "fanLevel": "high", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - "highTemperatureThreshold": 30.5, - "lowTemperatureState": { - "on": True, - "targetTemperature": 25, - "temperatureUnit": "C", - "mode": "heat", - "fanLevel": "low", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - "lowTemperatureThreshold": 5.5, - "type": "temperature", - }, + mock_client.async_set_climate_react.return_value = { + "status": "success", + "result": { + "enabled": True, + "deviceUid": "ABC999111", + "highTemperatureState": { + "on": True, + "targetTemperature": 15, + "temperatureUnit": "C", + "mode": "cool", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", }, - ), - ): - await hass.services.async_call( - DOMAIN, - SERVICE_ENABLE_CLIMATE_REACT, - { - ATTR_ENTITY_ID: state_climate.entity_id, - ATTR_LOW_TEMPERATURE_THRESHOLD: 5.5, - ATTR_HIGH_TEMPERATURE_THRESHOLD: 30.5, - ATTR_LOW_TEMPERATURE_STATE: { - "on": True, - "targetTemperature": 25, - "temperatureUnit": "C", - "mode": "heat", - "fanLevel": "low", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - ATTR_HIGH_TEMPERATURE_STATE: { - "on": True, - "targetTemperature": 15, - "temperatureUnit": "C", - "mode": "cool", - "fanLevel": "high", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - ATTR_SMART_TYPE: "temperature", + "highTemperatureThreshold": 30.5, + "lowTemperatureState": { + "on": True, + "targetTemperature": 25, + "temperatureUnit": "C", + "mode": "heat", + "fanLevel": "low", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", }, - blocking=True, - ) - await hass.async_block_till_done() + "lowTemperatureThreshold": 5.5, + "type": "temperature", + }, + } - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_on", True) - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_type", "temperature") - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_low_temp_threshold", 5.5) - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_high_temp_threshold", 30.5) + await hass.services.async_call( + DOMAIN, + SERVICE_ENABLE_CLIMATE_REACT, + { + ATTR_ENTITY_ID: state_climate.entity_id, + ATTR_LOW_TEMPERATURE_THRESHOLD: 5.5, + ATTR_HIGH_TEMPERATURE_THRESHOLD: 30.5, + ATTR_LOW_TEMPERATURE_STATE: { + "on": True, + "targetTemperature": 25, + "temperatureUnit": "C", + "mode": "heat", + "fanLevel": "low", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + ATTR_HIGH_TEMPERATURE_STATE: { + "on": True, + "targetTemperature": 15, + "temperatureUnit": "C", + "mode": "cool", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + ATTR_SMART_TYPE: "temperature", + }, + blocking=True, + ) + + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "smart_on", True) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "smart_type", "temperature") monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "smart_low_temp_threshold", 5.5 + ) + monkeypatch.setattr( + get_data[0].parsed["ABC999111"], "smart_high_temp_threshold", 30.5 + ) + monkeypatch.setattr( + get_data[0].parsed["ABC999111"], "smart_low_state", { "on": True, @@ -1207,7 +940,7 @@ async def test_climate_climate_react( }, ) monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "smart_high_state", { "on": True, @@ -1221,24 +954,20 @@ async def test_climate_climate_react( }, ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state1 = hass.states.get("switch.hallway_climate_react") - state2 = hass.states.get("sensor.hallway_climate_react_low_temperature_threshold") - state3 = hass.states.get("sensor.hallway_climate_react_high_temperature_threshold") - state4 = hass.states.get("sensor.hallway_climate_react_type") - assert state1.state == "on" - assert state2.state == "5.5" - assert state3.state == "30.5" - assert state4.state == "temperature" + assert hass.states.get("switch.hallway_climate_react").state == STATE_ON + assert ( + hass.states.get("sensor.hallway_climate_react_low_temperature_threshold").state + == "5.5" + ) + assert ( + hass.states.get("sensor.hallway_climate_react_high_temperature_threshold").state + == "30.5" + ) + assert hass.states.get("sensor.hallway_climate_react_type").state == "temperature" @pytest.mark.usefixtures("entity_registry_enabled_by_default") @@ -1246,100 +975,89 @@ async def test_climate_climate_react_fahrenheit( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate react custom service with fahrenheit.""" - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - - state_climate = hass.states.get("climate.hallway") - - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_climate_react", - return_value={ - "status": "success", - "result": { - "enabled": True, - "deviceUid": "ABC999111", - "highTemperatureState": { - "on": True, - "targetTemperature": 65, - "temperatureUnit": "F", - "mode": "cool", - "fanLevel": "high", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - "highTemperatureThreshold": 77, - "lowTemperatureState": { - "on": True, - "targetTemperature": 85, - "temperatureUnit": "F", - "mode": "heat", - "fanLevel": "low", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - "lowTemperatureThreshold": 32, - "type": "temperature", - }, - }, - ), - ): - await hass.services.async_call( - DOMAIN, - SERVICE_ENABLE_CLIMATE_REACT, - { - ATTR_ENTITY_ID: state_climate.entity_id, - ATTR_LOW_TEMPERATURE_THRESHOLD: 32.0, - ATTR_HIGH_TEMPERATURE_THRESHOLD: 77.0, - ATTR_LOW_TEMPERATURE_STATE: { - "on": True, - "targetTemperature": 85, - "temperatureUnit": "F", - "mode": "heat", - "fanLevel": "low", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - ATTR_HIGH_TEMPERATURE_STATE: { - "on": True, - "targetTemperature": 65, - "temperatureUnit": "F", - "mode": "cool", - "fanLevel": "high", - "swing": "stopped", - "horizontalSwing": "stopped", - "light": "on", - }, - ATTR_SMART_TYPE: "temperature", - }, - blocking=True, - ) + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_on", True) - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_type", "temperature") - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_low_temp_threshold", 0) - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_high_temp_threshold", 25) + state = hass.states.get("climate.hallway") + + mock_client.async_set_climate_react.return_value = { + "status": "success", + "result": { + "enabled": True, + "deviceUid": "ABC999111", + "highTemperatureState": { + "on": True, + "targetTemperature": 65, + "temperatureUnit": "F", + "mode": "cool", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + "highTemperatureThreshold": 77, + "lowTemperatureState": { + "on": True, + "targetTemperature": 85, + "temperatureUnit": "F", + "mode": "heat", + "fanLevel": "low", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + "lowTemperatureThreshold": 32, + "type": "temperature", + }, + } + + await hass.services.async_call( + DOMAIN, + SERVICE_ENABLE_CLIMATE_REACT, + { + ATTR_ENTITY_ID: state.entity_id, + ATTR_LOW_TEMPERATURE_THRESHOLD: 32.0, + ATTR_HIGH_TEMPERATURE_THRESHOLD: 77.0, + ATTR_LOW_TEMPERATURE_STATE: { + "on": True, + "targetTemperature": 85, + "temperatureUnit": "F", + "mode": "heat", + "fanLevel": "low", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + ATTR_HIGH_TEMPERATURE_STATE: { + "on": True, + "targetTemperature": 65, + "temperatureUnit": "F", + "mode": "cool", + "fanLevel": "high", + "swing": "stopped", + "horizontalSwing": "stopped", + "light": "on", + }, + ATTR_SMART_TYPE: "temperature", + }, + blocking=True, + ) + + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "smart_on", True) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "smart_type", "temperature") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "smart_low_temp_threshold", 0) monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "smart_high_temp_threshold", 25 + ) + monkeypatch.setattr( + get_data[0].parsed["ABC999111"], "smart_low_state", { "on": True, @@ -1353,7 +1071,7 @@ async def test_climate_climate_react_fahrenheit( }, ) monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "smart_high_state", { "on": True, @@ -1367,24 +1085,20 @@ async def test_climate_climate_react_fahrenheit( }, ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state1 = hass.states.get("switch.hallway_climate_react") - state2 = hass.states.get("sensor.hallway_climate_react_low_temperature_threshold") - state3 = hass.states.get("sensor.hallway_climate_react_high_temperature_threshold") - state4 = hass.states.get("sensor.hallway_climate_react_type") - assert state1.state == "on" - assert state2.state == "0" - assert state3.state == "25" - assert state4.state == "temperature" + assert hass.states.get("switch.hallway_climate_react").state == STATE_ON + assert ( + hass.states.get("sensor.hallway_climate_react_low_temperature_threshold").state + == "0" + ) + assert ( + hass.states.get("sensor.hallway_climate_react_high_temperature_threshold").state + == "25" + ) + assert hass.states.get("sensor.hallway_climate_react_type").state == "temperature" @pytest.mark.usefixtures("entity_registry_enabled_by_default") @@ -1392,145 +1106,101 @@ async def test_climate_full_ac_state( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate Full AC state service.""" - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state_climate = hass.states.get("climate.hallway") - assert state_climate.state == "heat" + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_states", - ), - pytest.raises( - MultipleInvalid, - ), + with pytest.raises( + MultipleInvalid, ): await hass.services.async_call( DOMAIN, SERVICE_FULL_STATE, { - ATTR_ENTITY_ID: state_climate.entity_id, + ATTR_ENTITY_ID: state.entity_id, ATTR_TARGET_TEMPERATURE: 22, }, blocking=True, ) - await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_states", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - DOMAIN, - SERVICE_FULL_STATE, - { - ATTR_ENTITY_ID: state_climate.entity_id, - ATTR_MODE: "cool", - ATTR_TARGET_TEMPERATURE: 22, - ATTR_FAN_MODE: "high", - ATTR_SWING_MODE: "stopped", - ATTR_HORIZONTAL_SWING_MODE: "stopped", - ATTR_LIGHT: "on", - }, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_ac_states.return_value = {"result": {"status": "Success"}} - monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "cool") - monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) - monkeypatch.setattr(get_data.parsed["ABC999111"], "target_temp", 22) - monkeypatch.setattr(get_data.parsed["ABC999111"], "fan_mode", "high") - monkeypatch.setattr(get_data.parsed["ABC999111"], "swing_mode", "stopped") - monkeypatch.setattr( - get_data.parsed["ABC999111"], "horizontal_swing_mode", "stopped" + await hass.services.async_call( + DOMAIN, + SERVICE_FULL_STATE, + { + ATTR_ENTITY_ID: state.entity_id, + ATTR_MODE: "cool", + ATTR_TARGET_TEMPERATURE: 22, + ATTR_FAN_MODE: "high", + ATTR_SWING_MODE: "stopped", + ATTR_HORIZONTAL_SWING_MODE: "stopped", + ATTR_LIGHT: "on", + }, + blocking=True, ) - monkeypatch.setattr(get_data.parsed["ABC999111"], "light_mode", "on") - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "hvac_mode", "cool") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "device_on", True) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "target_temp", 22) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "fan_mode", "high") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "swing_mode", "stopped") + monkeypatch.setattr( + get_data[0].parsed["ABC999111"], "horizontal_swing_mode", "stopped" + ) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "light_mode", "on") + + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() state = hass.states.get("climate.hallway") - assert state.state == "cool" + assert state.state == HVACMode.COOL assert state.attributes["temperature"] == 22 async def test_climate_fan_mode_and_swing_mode_not_supported( hass: HomeAssistant, load_int: ConfigEntry, - monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo climate fan_mode and swing_mode not supported is raising error.""" - state1 = hass.states.get("climate.hallway") - assert state1.attributes["fan_mode"] == "high" - assert state1.attributes["swing_mode"] == "stopped" + state = hass.states.get("climate.hallway") + assert state.attributes["fan_mode"] == "high" + assert state.attributes["swing_mode"] == "stopped" - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - ), - pytest.raises(ServiceValidationError), - ): + with pytest.raises(ServiceValidationError): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_SWING_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_SWING_MODE: "faulty_swing_mode"}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_SWING_MODE: "faulty_swing_mode"}, blocking=True, ) - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - ), - pytest.raises(ServiceValidationError), - ): + with pytest.raises(ServiceValidationError): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_FAN_MODE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_FAN_MODE: "faulty_fan_mode"}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_FAN_MODE: "faulty_fan_mode"}, blocking=True, ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state2 = hass.states.get("climate.hallway") - assert state2.attributes["fan_mode"] == "high" - assert state2.attributes["swing_mode"] == "stopped" + state = hass.states.get("climate.hallway") + assert state.attributes["fan_mode"] == "high" + assert state.attributes["swing_mode"] == "stopped" diff --git a/tests/components/sensibo/test_config_flow.py b/tests/components/sensibo/test_config_flow.py index 4180eddb2be..7a2ffee0067 100644 --- a/tests/components/sensibo/test_config_flow.py +++ b/tests/components/sensibo/test_config_flow.py @@ -3,24 +3,26 @@ from __future__ import annotations from typing import Any -from unittest.mock import patch +from unittest.mock import AsyncMock, MagicMock, patch -import aiohttp -from pysensibo.exceptions import AuthenticationError, SensiboError +from pysensibo import AuthenticationError, SensiboError import pytest from homeassistant import config_entries +from homeassistant.components.sensibo.const import DOMAIN from homeassistant.const import CONF_API_KEY from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry -DOMAIN = "sensibo" +pytestmark = pytest.mark.usefixtures("mock_setup_entry") -async def test_form(hass: HomeAssistant) -> None: - """Test we get the form.""" +async def test_basic_setup( + hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_client: MagicMock +) -> None: + """Test we get and complete the form.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} @@ -29,32 +31,20 @@ async def test_form(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["errors"] == {} - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ), - patch( - "homeassistant.components.sensibo.async_setup_entry", - return_value=True, - ) as mock_setup_entry, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - CONF_API_KEY: "1234567890", - }, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_API_KEY: "1234567890", + }, + ) + await hass.async_block_till_done() - assert result2["type"] is FlowResultType.CREATE_ENTRY - assert result2["version"] == 2 - assert result2["data"] == { - "api_key": "1234567890", + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["version"] == 2 + assert result["title"] == "firstnamelastname" + assert result["result"].unique_id == "firstnamelastname" + assert result["data"] == { + CONF_API_KEY: "1234567890", } assert len(mock_setup_entry.mock_calls) == 1 @@ -63,14 +53,12 @@ async def test_form(hass: HomeAssistant) -> None: @pytest.mark.parametrize( ("error_message", "p_error"), [ - (aiohttp.ClientConnectionError, "cannot_connect"), - (TimeoutError, "cannot_connect"), (AuthenticationError, "invalid_auth"), (SensiboError, "cannot_connect"), ], ) async def test_flow_fails( - hass: HomeAssistant, error_message: Exception, p_error: str + hass: HomeAssistant, mock_client: MagicMock, error_message: Exception, p_error: str ) -> None: """Test config flow errors.""" @@ -85,44 +73,30 @@ async def test_flow_fails( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", side_effect=error_message, ): - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={ CONF_API_KEY: "1234567890", }, ) - assert result2["errors"] == {"base": p_error} + assert result["errors"] == {"base": p_error} - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ), - patch( - "homeassistant.components.sensibo.async_setup_entry", - return_value=True, - ), - ): - result3 = await hass.config_entries.flow.async_configure( - result["flow_id"], - user_input={ - CONF_API_KEY: "1234567891", - }, - ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_API_KEY: "1234567890", + }, + ) - assert result3["type"] is FlowResultType.CREATE_ENTRY - assert result3["title"] == "username" - assert result3["data"] == { - "api_key": "1234567891", + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "firstnamelastname" + assert result["data"] == { + CONF_API_KEY: "1234567890", } -async def test_flow_get_no_devices(hass: HomeAssistant) -> None: +async def test_flow_get_no_devices(hass: HomeAssistant, mock_client: MagicMock) -> None: """Test config flow get no devices from api.""" result = await hass.config_entries.flow.async_init( @@ -132,27 +106,36 @@ async def test_flow_get_no_devices(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["step_id"] == config_entries.SOURCE_USER - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": []}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {}}, - ), + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", + return_value={"result": {}}, ): - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={ CONF_API_KEY: "1234567890", }, ) - assert result2["errors"] == {"base": "no_devices"} + assert result["errors"] == {"base": "no_devices"} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_API_KEY: "1234567890", + }, + ) + + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "firstnamelastname" + assert result["data"] == { + CONF_API_KEY: "1234567890", + } -async def test_flow_get_no_username(hass: HomeAssistant) -> None: +async def test_flow_get_no_username( + hass: HomeAssistant, mock_client: MagicMock +) -> None: """Test config flow get no username from api.""" result = await hass.config_entries.flow.async_init( @@ -162,15 +145,9 @@ async def test_flow_get_no_username(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["step_id"] == config_entries.SOURCE_USER - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {}}, - ), + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_me", + return_value={"result": {}}, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -181,14 +158,27 @@ async def test_flow_get_no_username(hass: HomeAssistant) -> None: assert result2["errors"] == {"base": "no_username"} + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_API_KEY: "1234567890", + }, + ) -async def test_reauth_flow(hass: HomeAssistant) -> None: + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "firstnamelastname" + assert result["data"] == { + CONF_API_KEY: "1234567890", + } + + +async def test_reauth_flow(hass: HomeAssistant, mock_client: MagicMock) -> None: """Test a reauthentication flow.""" entry = MockConfigEntry( version=2, domain=DOMAIN, - unique_id="username", - data={"api_key": "1234567890"}, + unique_id="firstnamelastname", + data={CONF_API_KEY: "1234567890"}, ) entry.add_to_hass(hass) @@ -197,52 +187,33 @@ async def test_reauth_flow(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["errors"] == {} - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ) as mock_sensibo, - patch( - "homeassistant.components.sensibo.async_setup_entry", - return_value=True, - ) as mock_setup_entry, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_API_KEY: "1234567891"}, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_API_KEY: "1234567890"}, + ) + await hass.async_block_till_done() - assert result2["type"] is FlowResultType.ABORT - assert result2["reason"] == "reauth_successful" - assert entry.data == {"api_key": "1234567891"} - - assert len(mock_sensibo.mock_calls) == 1 - assert len(mock_setup_entry.mock_calls) == 1 + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert entry.data == {CONF_API_KEY: "1234567890"} @pytest.mark.parametrize( ("sideeffect", "p_error"), [ - (aiohttp.ClientConnectionError, "cannot_connect"), - (TimeoutError, "cannot_connect"), (AuthenticationError, "invalid_auth"), (SensiboError, "cannot_connect"), ], ) async def test_reauth_flow_error( - hass: HomeAssistant, sideeffect: Exception, p_error: str + hass: HomeAssistant, sideeffect: Exception, p_error: str, mock_client: MagicMock ) -> None: """Test a reauthentication flow with error.""" entry = MockConfigEntry( version=2, domain=DOMAIN, - unique_id="username", - data={"api_key": "1234567890"}, + unique_id="firstnamelastname", + data={CONF_API_KEY: "1234567890"}, ) entry.add_to_hass(hass) @@ -252,39 +223,25 @@ async def test_reauth_flow_error( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", side_effect=sideeffect, ): - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_API_KEY: "1234567890"}, ) await hass.async_block_till_done() - assert result2["step_id"] == "reauth_confirm" - assert result2["type"] is FlowResultType.FORM - assert result2["errors"] == {"base": p_error} + assert result["step_id"] == "reauth_confirm" + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": p_error} - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ), - patch( - "homeassistant.components.sensibo.async_setup_entry", - return_value=True, - ), - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_API_KEY: "1234567891"}, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_API_KEY: "1234567890"}, + ) + await hass.async_block_till_done() - assert result2["type"] is FlowResultType.ABORT - assert result2["reason"] == "reauth_successful" - assert entry.data == {"api_key": "1234567891"} + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert entry.data == {CONF_API_KEY: "1234567890"} @pytest.mark.parametrize( @@ -297,12 +254,12 @@ async def test_reauth_flow_error( ), ( {"result": []}, - {"result": {"username": "username"}}, + {"result": {"username": "firstnamelastname"}}, "no_devices", ), ( {"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - {"result": {"username": "username2"}}, + {"result": {"username": "firstnamelastname2"}}, "incorrect_api_key", ), ], @@ -312,13 +269,14 @@ async def test_flow_reauth_no_username_or_device( get_devices: dict[str, Any], get_me: dict[str, Any], p_error: str, + mock_client: MagicMock, ) -> None: - """Test config flow get no username from api.""" + """Test reauth flow with errors from api.""" entry = MockConfigEntry( version=2, domain=DOMAIN, - unique_id="username", - data={"api_key": "1234567890"}, + unique_id="firstnamelastname", + data={CONF_API_KEY: "1234567890"}, ) entry.add_to_hass(hass) @@ -337,7 +295,7 @@ async def test_flow_reauth_no_username_or_device( return_value=get_me, ), ): - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={ CONF_API_KEY: "1234567890", @@ -345,18 +303,28 @@ async def test_flow_reauth_no_username_or_device( ) await hass.async_block_till_done() - assert result2["step_id"] == "reauth_confirm" - assert result2["type"] is FlowResultType.FORM - assert result2["errors"] == {"base": p_error} + assert result["step_id"] == "reauth_confirm" + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": p_error} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_API_KEY: "1234567890"}, + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert entry.data == {CONF_API_KEY: "1234567890"} -async def test_reconfigure_flow(hass: HomeAssistant) -> None: +async def test_reconfigure_flow(hass: HomeAssistant, mock_client: MagicMock) -> None: """Test a reconfigure flow.""" entry = MockConfigEntry( version=2, domain=DOMAIN, - unique_id="username", - data={"api_key": "1234567890"}, + unique_id="firstnamelastname", + data={CONF_API_KEY: "1234567890"}, ) entry.add_to_hass(hass) @@ -365,52 +333,33 @@ async def test_reconfigure_flow(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["errors"] == {} - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ) as mock_sensibo, - patch( - "homeassistant.components.sensibo.async_setup_entry", - return_value=True, - ) as mock_setup_entry, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_API_KEY: "1234567891"}, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_API_KEY: "1234567890"}, + ) + await hass.async_block_till_done() - assert result2["type"] is FlowResultType.ABORT - assert result2["reason"] == "reconfigure_successful" - assert entry.data == {"api_key": "1234567891"} - - assert len(mock_sensibo.mock_calls) == 1 - assert len(mock_setup_entry.mock_calls) == 1 + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" + assert entry.data == {CONF_API_KEY: "1234567890"} @pytest.mark.parametrize( ("sideeffect", "p_error"), [ - (aiohttp.ClientConnectionError, "cannot_connect"), - (TimeoutError, "cannot_connect"), (AuthenticationError, "invalid_auth"), (SensiboError, "cannot_connect"), ], ) async def test_reconfigure_flow_error( - hass: HomeAssistant, sideeffect: Exception, p_error: str + hass: HomeAssistant, sideeffect: Exception, p_error: str, mock_client: MagicMock ) -> None: """Test a reconfigure flow with error.""" entry = MockConfigEntry( version=2, domain=DOMAIN, - unique_id="username", - data={"api_key": "1234567890"}, + unique_id="firstnamelastname", + data={CONF_API_KEY: "1234567890"}, ) entry.add_to_hass(hass) @@ -420,39 +369,25 @@ async def test_reconfigure_flow_error( "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", side_effect=sideeffect, ): - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_API_KEY: "1234567890"}, ) await hass.async_block_till_done() - assert result2["step_id"] == "reconfigure" - assert result2["type"] is FlowResultType.FORM - assert result2["errors"] == {"base": p_error} + assert result["step_id"] == "reconfigure" + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": p_error} - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ), - patch( - "homeassistant.components.sensibo.async_setup_entry", - return_value=True, - ), - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {CONF_API_KEY: "1234567891"}, - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_API_KEY: "1234567890"}, + ) + await hass.async_block_till_done() - assert result2["type"] is FlowResultType.ABORT - assert result2["reason"] == "reconfigure_successful" - assert entry.data == {"api_key": "1234567891"} + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" + assert entry.data == {CONF_API_KEY: "1234567890"} @pytest.mark.parametrize( @@ -465,12 +400,12 @@ async def test_reconfigure_flow_error( ), ( {"result": []}, - {"result": {"username": "username"}}, + {"result": {"username": "firstnamelastname"}}, "no_devices", ), ( {"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - {"result": {"username": "username2"}}, + {"result": {"username": "firstnamelastname2"}}, "incorrect_api_key", ), ], @@ -480,13 +415,14 @@ async def test_flow_reconfigure_no_username_or_device( get_devices: dict[str, Any], get_me: dict[str, Any], p_error: str, + mock_client: MagicMock, ) -> None: - """Test config flow get no username from api.""" + """Test reconfigure flow with errors from api.""" entry = MockConfigEntry( version=2, domain=DOMAIN, - unique_id="username", - data={"api_key": "1234567890"}, + unique_id="firstnamelastname", + data={CONF_API_KEY: "1234567890"}, ) entry.add_to_hass(hass) @@ -505,7 +441,7 @@ async def test_flow_reconfigure_no_username_or_device( return_value=get_me, ), ): - result2 = await hass.config_entries.flow.async_configure( + result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={ CONF_API_KEY: "1234567890", @@ -513,6 +449,16 @@ async def test_flow_reconfigure_no_username_or_device( ) await hass.async_block_till_done() - assert result2["step_id"] == "reconfigure" - assert result2["type"] is FlowResultType.FORM - assert result2["errors"] == {"base": p_error} + assert result["step_id"] == "reconfigure" + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": p_error} + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_API_KEY: "1234567890"}, + ) + await hass.async_block_till_done() + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reconfigure_successful" + assert entry.data == {CONF_API_KEY: "1234567890"} diff --git a/tests/components/sensibo/test_coordinator.py b/tests/components/sensibo/test_coordinator.py index d81b7fd613c..62dd2f91997 100644 --- a/tests/components/sensibo/test_coordinator.py +++ b/tests/components/sensibo/test_coordinator.py @@ -3,17 +3,18 @@ from __future__ import annotations from datetime import timedelta -from unittest.mock import patch +from typing import Any +from unittest.mock import MagicMock +from freezegun.api import FrozenDateTimeFactory from pysensibo.exceptions import AuthenticationError, SensiboError from pysensibo.model import SensiboData import pytest +from homeassistant.components.climate import HVACMode from homeassistant.components.sensibo.const import DOMAIN -from homeassistant.config_entries import SOURCE_USER from homeassistant.const import STATE_UNAVAILABLE from homeassistant.core import HomeAssistant -from homeassistant.util import dt as dt_util from . import ENTRY_CONFIG @@ -21,75 +22,71 @@ from tests.common import MockConfigEntry, async_fire_time_changed async def test_coordinator( - hass: HomeAssistant, monkeypatch: pytest.MonkeyPatch, get_data: SensiboData + hass: HomeAssistant, + monkeypatch: pytest.MonkeyPatch, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo coordinator with errors.""" config_entry = MockConfigEntry( domain=DOMAIN, - source=SOURCE_USER, data=ENTRY_CONFIG, entry_id="1", - unique_id="username", + unique_id="firstnamelastname", version=2, ) config_entry.add_to_hass(hass) - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - ) as mock_data, - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ), - ): - monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) - mock_data.return_value = get_data - await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - mock_data.assert_called_once() - state = hass.states.get("climate.hallway") - assert state.state == "heat" - mock_data.reset_mock() + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "device_on", True) - mock_data.side_effect = SensiboError("info") - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=1)) - await hass.async_block_till_done() - mock_data.assert_called_once() - state = hass.states.get("climate.hallway") - assert state.state == STATE_UNAVAILABLE - mock_data.reset_mock() + mock_data = mock_client.async_get_devices_data + mock_data.return_value = get_data[0] + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT + mock_data.reset_mock() - mock_data.return_value = SensiboData(raw={}, parsed={}) - mock_data.side_effect = None - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=3)) - await hass.async_block_till_done() - mock_data.assert_called_once() - state = hass.states.get("climate.hallway") - assert state.state == STATE_UNAVAILABLE - mock_data.reset_mock() + mock_data.side_effect = SensiboError("info") + freezer.tick(timedelta(minutes=1)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == STATE_UNAVAILABLE + mock_data.reset_mock() - monkeypatch.setattr(get_data.parsed["ABC999111"], "hvac_mode", "heat") - monkeypatch.setattr(get_data.parsed["ABC999111"], "device_on", True) + mock_data.return_value = SensiboData(raw={}, parsed={}) + mock_data.side_effect = None + freezer.tick(timedelta(minutes=1)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == STATE_UNAVAILABLE + mock_data.reset_mock() - mock_data.return_value = get_data - mock_data.side_effect = None - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=5)) - await hass.async_block_till_done() - mock_data.assert_called_once() - state = hass.states.get("climate.hallway") - assert state.state == "heat" - mock_data.reset_mock() + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "hvac_mode", "heat") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "device_on", True) - mock_data.side_effect = AuthenticationError("info") - async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=7)) - await hass.async_block_till_done() - mock_data.assert_called_once() - state = hass.states.get("climate.hallway") - assert state.state == STATE_UNAVAILABLE + mock_data.return_value = get_data[0] + mock_data.side_effect = None + freezer.tick(timedelta(minutes=1)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == HVACMode.HEAT + mock_data.reset_mock() + + mock_data.side_effect = AuthenticationError("info") + freezer.tick(timedelta(minutes=1)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + mock_data.assert_called_once() + state = hass.states.get("climate.hallway") + assert state.state == STATE_UNAVAILABLE diff --git a/tests/components/sensibo/test_entity.py b/tests/components/sensibo/test_entity.py index e17877b63b1..b8818149510 100644 --- a/tests/components/sensibo/test_entity.py +++ b/tests/components/sensibo/test_entity.py @@ -2,10 +2,10 @@ from __future__ import annotations -from unittest.mock import patch +from unittest.mock import MagicMock -from pysensibo.model import SensiboData import pytest +from syrupy.assertion import SnapshotAssertion from homeassistant.components.climate import ( ATTR_FAN_MODE, @@ -17,35 +17,24 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers import device_registry as dr -async def test_entity( +async def test_device( hass: HomeAssistant, device_registry: dr.DeviceRegistry, - entity_registry: er.EntityRegistry, load_int: ConfigEntry, - get_data: SensiboData, + snapshot: SnapshotAssertion, ) -> None: - """Test the Sensibo climate.""" + """Test the Sensibo device.""" state1 = hass.states.get("climate.hallway") assert state1 - dr_entries = dr.async_entries_for_config_entry(device_registry, load_int.entry_id) - dr_entry: dr.DeviceEntry - for dr_entry in dr_entries: - if dr_entry.name == "Hallway": - assert dr_entry.identifiers == {("sensibo", "ABC999111")} - device_id = dr_entry.id - - er_entries = er.async_entries_for_device( - entity_registry, device_id, include_disabled_entities=True + assert ( + dr.async_entries_for_config_entry(device_registry, load_int.entry_id) + == snapshot ) - er_entry: er.RegistryEntry - for er_entry in er_entries: - if er_entry.name == "Hallway": - assert er_entry.unique_id == "Hallway" @pytest.mark.parametrize("p_error", SENSIBO_ERRORS) @@ -53,35 +42,30 @@ async def test_entity_failed_service_calls( hass: HomeAssistant, p_error: Exception, load_int: ConfigEntry, - get_data: SensiboData, + mock_client: MagicMock, ) -> None: """Test the Sensibo send command with error.""" state = hass.states.get("climate.hallway") assert state - with patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ): - await hass.services.async_call( - CLIMATE_DOMAIN, - SERVICE_SET_FAN_MODE, - {ATTR_ENTITY_ID: state.entity_id, ATTR_FAN_MODE: "low"}, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Success"} + } + + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_FAN_MODE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_FAN_MODE: "low"}, + blocking=True, + ) state = hass.states.get("climate.hallway") assert state.attributes["fan_mode"] == "low" - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - side_effect=p_error, - ), - pytest.raises(HomeAssistantError), - ): + mock_client.async_set_ac_state_property.side_effect = p_error + + with pytest.raises(HomeAssistantError): await hass.services.async_call( CLIMATE_DOMAIN, SERVICE_SET_FAN_MODE, diff --git a/tests/components/sensibo/test_init.py b/tests/components/sensibo/test_init.py index 2938d4ede0e..b472272d1cf 100644 --- a/tests/components/sensibo/test_init.py +++ b/tests/components/sensibo/test_init.py @@ -1,10 +1,8 @@ -"""Test for Sensibo component Init.""" +"""Test for Sensibo integration setup.""" from __future__ import annotations -from unittest.mock import patch - -from pysensibo.model import SensiboData +from unittest.mock import MagicMock, patch from homeassistant.components.sensibo.const import DOMAIN from homeassistant.components.sensibo.util import NoUsernameError @@ -19,137 +17,70 @@ from tests.common import MockConfigEntry from tests.typing import WebSocketGenerator -async def test_setup_entry(hass: HomeAssistant, get_data: SensiboData) -> None: - """Test setup entry.""" +async def test_load_unload_entry(hass: HomeAssistant, mock_client: MagicMock) -> None: + """Test setup and unload config entry.""" entry = MockConfigEntry( domain=DOMAIN, - source=SOURCE_USER, data=ENTRY_CONFIG, entry_id="1", - unique_id="12", + unique_id="firstnamelastname", version=2, ) entry.add_to_hass(hass) - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ), - ): - await hass.config_entries.async_setup(entry.entry_id) + await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() assert entry.state is ConfigEntryState.LOADED + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() -async def test_migrate_entry(hass: HomeAssistant, get_data: SensiboData) -> None: + assert entry.state is ConfigEntryState.NOT_LOADED + + +async def test_migrate_entry(hass: HomeAssistant, mock_client: MagicMock) -> None: """Test migrate entry unique id.""" entry = MockConfigEntry( domain=DOMAIN, source=SOURCE_USER, data=ENTRY_CONFIG, entry_id="1", - unique_id="12", + unique_id="someother", version=1, ) entry.add_to_hass(hass) - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ), - ): - await hass.config_entries.async_setup(entry.entry_id) + await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() assert entry.state is ConfigEntryState.LOADED assert entry.version == 2 - assert entry.unique_id == "username" + assert entry.unique_id == "firstnamelastname" -async def test_migrate_entry_fails(hass: HomeAssistant, get_data: SensiboData) -> None: - """Test migrate entry unique id.""" +async def test_migrate_entry_fails(hass: HomeAssistant, mock_client: MagicMock) -> None: + """Test migrate entry fails.""" entry = MockConfigEntry( domain=DOMAIN, source=SOURCE_USER, data=ENTRY_CONFIG, entry_id="1", - unique_id="12", + unique_id="someother", version=1, ) entry.add_to_hass(hass) - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - side_effect=NoUsernameError("No username returned"), - ), + with patch( + "homeassistant.components.sensibo.util.SensiboClient.async_get_me", + side_effect=NoUsernameError("No username returned"), ): await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + await hass.async_block_till_done() assert entry.state is ConfigEntryState.MIGRATION_ERROR assert entry.version == 1 - assert entry.unique_id == "12" - - -async def test_unload_entry(hass: HomeAssistant, get_data: SensiboData) -> None: - """Test unload an entry.""" - entry = MockConfigEntry( - domain=DOMAIN, - source=SOURCE_USER, - data=ENTRY_CONFIG, - entry_id="1", - unique_id="12", - version="2", - ) - entry.add_to_hass(hass) - - with ( - patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices", - return_value={"result": [{"id": "xyzxyz"}, {"id": "abcabc"}]}, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_me", - return_value={"result": {"username": "username"}}, - ), - ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - assert entry.state is ConfigEntryState.LOADED - assert await hass.config_entries.async_unload(entry.entry_id) - await hass.async_block_till_done() - assert entry.state is ConfigEntryState.NOT_LOADED + assert entry.unique_id == "someother" async def test_device_remove_devices( diff --git a/tests/components/sensibo/test_number.py b/tests/components/sensibo/test_number.py index 95836ba023c..9abb636abfb 100644 --- a/tests/components/sensibo/test_number.py +++ b/tests/components/sensibo/test_number.py @@ -3,8 +3,10 @@ from __future__ import annotations from datetime import timedelta -from unittest.mock import patch +from typing import Any +from unittest.mock import MagicMock +from freezegun.api import FrozenDateTimeFactory from pysensibo.model import SensiboData import pytest from syrupy.assertion import SnapshotAssertion @@ -19,7 +21,6 @@ from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er -from homeassistant.util import dt as dt_util from tests.common import async_fire_time_changed, snapshot_platform @@ -33,80 +34,55 @@ async def test_number( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + get_data: tuple[SensiboData, dict[str, Any]], entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion, + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo number.""" await snapshot_platform(hass, entity_registry, snapshot, load_int.entry_id) - monkeypatch.setattr(get_data.parsed["ABC999111"], "calibration_temp", 0.2) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "calibration_temp", 0.2) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state1 = hass.states.get("number.hallway_temperature_calibration") - assert state1.state == "0.2" + state = hass.states.get("number.hallway_temperature_calibration") + assert state.state == "0.2" @pytest.mark.usefixtures("entity_registry_enabled_by_default") async def test_number_set_value( - hass: HomeAssistant, - load_int: ConfigEntry, - get_data: SensiboData, + hass: HomeAssistant, load_int: ConfigEntry, mock_client: MagicMock ) -> None: """Test the Sensibo number service.""" - state1 = hass.states.get("number.hallway_temperature_calibration") - assert state1.state == "0.1" + state = hass.states.get("number.hallway_temperature_calibration") + assert state.state == "0.1" - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_calibration", - return_value={"status": "failure"}, - ), - ): - with pytest.raises(HomeAssistantError): - await hass.services.async_call( - NUMBER_DOMAIN, - SERVICE_SET_VALUE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_VALUE: "0.2"}, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_calibration.return_value = {"status": "failure"} - state2 = hass.states.get("number.hallway_temperature_calibration") - assert state2.state == "0.1" - - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_calibration", - return_value={"status": "success"}, - ), - ): + with pytest.raises(HomeAssistantError): await hass.services.async_call( NUMBER_DOMAIN, SERVICE_SET_VALUE, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_VALUE: "0.2"}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_VALUE: "0.2"}, blocking=True, ) - await hass.async_block_till_done() - state2 = hass.states.get("number.hallway_temperature_calibration") - assert state2.state == "0.2" + state = hass.states.get("number.hallway_temperature_calibration") + assert state.state == "0.1" + + mock_client.async_set_calibration.return_value = {"status": "success"} + + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: state.entity_id, ATTR_VALUE: "0.2"}, + blocking=True, + ) + + state = hass.states.get("number.hallway_temperature_calibration") + assert state.state == "0.2" diff --git a/tests/components/sensibo/test_select.py b/tests/components/sensibo/test_select.py index 2e4a1cb507c..002e2a62540 100644 --- a/tests/components/sensibo/test_select.py +++ b/tests/components/sensibo/test_select.py @@ -3,8 +3,10 @@ from __future__ import annotations from datetime import timedelta -from unittest.mock import patch +from typing import Any +from unittest.mock import MagicMock +from freezegun.api import FrozenDateTimeFactory from pysensibo.model import SensiboData import pytest from syrupy.assertion import SnapshotAssertion @@ -19,7 +21,6 @@ from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er -from homeassistant.util import dt as dt_util from tests.common import async_fire_time_changed, snapshot_platform @@ -32,42 +33,39 @@ async def test_select( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + get_data: tuple[SensiboData, dict[str, Any]], entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion, + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo select.""" await snapshot_platform(hass, entity_registry, snapshot, load_int.entry_id) monkeypatch.setattr( - get_data.parsed["ABC999111"], "horizontal_swing_mode", "fixedleft" + get_data[0].parsed["ABC999111"], "horizontal_swing_mode", "fixedleft" ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state1 = hass.states.get("select.hallway_horizontal_swing") - assert state1.state == "fixedleft" + state = hass.states.get("select.hallway_horizontal_swing") + assert state.state == "fixedleft" async def test_select_set_option( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo select service.""" monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "active_features", [ "timestamp", @@ -78,45 +76,32 @@ async def test_select_set_option( ], ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state1 = hass.states.get("select.hallway_horizontal_swing") - assert state1.state == "stopped" + state = hass.states.get("select.hallway_horizontal_swing") + assert state.state == "stopped" - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "failed"}}, - ), - pytest.raises( - HomeAssistantError, - ), + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "failed"} + } + + with pytest.raises( + HomeAssistantError, ): await hass.services.async_call( SELECT_DOMAIN, SERVICE_SELECT_OPTION, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_OPTION: "fixedleft"}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_OPTION: "fixedleft"}, blocking=True, ) - await hass.async_block_till_done() - state2 = hass.states.get("select.hallway_horizontal_swing") - assert state2.state == "stopped" + state = hass.states.get("select.hallway_horizontal_swing") + assert state.state == "stopped" monkeypatch.setattr( - get_data.parsed["ABC999111"], + get_data[0].parsed["ABC999111"], "active_features", [ "timestamp", @@ -128,58 +113,37 @@ async def test_select_set_option( ], ) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={ - "result": {"status": "Failed", "failureReason": "No connection"} - }, - ), - pytest.raises( - HomeAssistantError, - ), + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Failed", "failureReason": "No connection"} + } + + with pytest.raises( + HomeAssistantError, ): await hass.services.async_call( SELECT_DOMAIN, SERVICE_SELECT_OPTION, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_OPTION: "fixedleft"}, + {ATTR_ENTITY_ID: state.entity_id, ATTR_OPTION: "fixedleft"}, blocking=True, ) - await hass.async_block_till_done() - state2 = hass.states.get("select.hallway_horizontal_swing") - assert state2.state == "stopped" + state = hass.states.get("select.hallway_horizontal_swing") + assert state.state == "stopped" - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_ac_state_property", - return_value={"result": {"status": "Success"}}, - ), - ): - await hass.services.async_call( - SELECT_DOMAIN, - SERVICE_SELECT_OPTION, - {ATTR_ENTITY_ID: state1.entity_id, ATTR_OPTION: "fixedleft"}, - blocking=True, - ) - await hass.async_block_till_done() + mock_client.async_set_ac_state_property.return_value = { + "result": {"status": "Success"} + } - state2 = hass.states.get("select.hallway_horizontal_swing") - assert state2.state == "fixedleft" + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + {ATTR_ENTITY_ID: state.entity_id, ATTR_OPTION: "fixedleft"}, + blocking=True, + ) + + state = hass.states.get("select.hallway_horizontal_swing") + assert state.state == "fixedleft" diff --git a/tests/components/sensibo/test_sensor.py b/tests/components/sensibo/test_sensor.py index 32794e266b0..57bb3f93a04 100644 --- a/tests/components/sensibo/test_sensor.py +++ b/tests/components/sensibo/test_sensor.py @@ -1,9 +1,9 @@ -"""The test for the sensibo select platform.""" +"""The test for the sensibo sensor platform.""" from __future__ import annotations from datetime import timedelta -from unittest.mock import patch +from typing import Any from freezegun.api import FrozenDateTimeFactory from pysensibo.model import PureAQI, SensiboData @@ -14,7 +14,6 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er -from homeassistant.util import dt as dt_util from tests.common import async_fire_time_changed, snapshot_platform @@ -28,7 +27,7 @@ async def test_sensor( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + get_data: tuple[SensiboData, dict[str, Any]], entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion, freezer: FrozenDateTimeFactory, @@ -37,17 +36,11 @@ async def test_sensor( await snapshot_platform(hass, entity_registry, snapshot, load_int.entry_id) - monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pm25_pure", PureAQI(2)) + monkeypatch.setattr(get_data[0].parsed["AAZZAAZZ"], "pm25_pure", PureAQI(2)) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state1 = hass.states.get("sensor.kitchen_pure_aqi") - assert state1.state == "moderate" + state = hass.states.get("sensor.kitchen_pure_aqi") + assert state.state == "moderate" diff --git a/tests/components/sensibo/test_switch.py b/tests/components/sensibo/test_switch.py index f260af7baaa..6f77c60a63d 100644 --- a/tests/components/sensibo/test_switch.py +++ b/tests/components/sensibo/test_switch.py @@ -3,8 +3,10 @@ from __future__ import annotations from datetime import timedelta -from unittest.mock import patch +from typing import Any +from unittest.mock import MagicMock +from freezegun.api import FrozenDateTimeFactory from pysensibo.model import SensiboData import pytest from syrupy.assertion import SnapshotAssertion @@ -22,7 +24,6 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er -from homeassistant.util import dt as dt_util from tests.common import async_fire_time_changed, snapshot_platform @@ -46,220 +47,154 @@ async def test_switch_timer( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: - """Test the Sensibo switch.""" + """Test the Sensibo switch timer.""" - state1 = hass.states.get("switch.hallway_timer") - assert state1.state == STATE_OFF - assert state1.attributes["id"] is None - assert state1.attributes["turn_on"] is None + state = hass.states.get("switch.hallway_timer") + assert state.state == STATE_OFF + assert state.attributes["id"] is None + assert state.attributes["turn_on"] is None - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_timer", - return_value={"status": "success", "result": {"id": "SzTGE4oZ4D"}}, - ), - ): - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_ON, - { - ATTR_ENTITY_ID: state1.entity_id, - }, - blocking=True, - ) + mock_client.async_set_timer.return_value = { + "status": "success", + "result": {"id": "SzTGE4oZ4D"}, + } + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + { + ATTR_ENTITY_ID: state.entity_id, + }, + blocking=True, + ) + + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "timer_on", True) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "timer_id", "SzTGE4oZ4D") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "timer_state_on", False) + + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_on", True) - monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_id", "SzTGE4oZ4D") - monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_state_on", False) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - state1 = hass.states.get("switch.hallway_timer") - assert state1.state == STATE_ON - assert state1.attributes["id"] == "SzTGE4oZ4D" - assert state1.attributes["turn_on"] is False + state = hass.states.get("switch.hallway_timer") + assert state.state == STATE_ON + assert state.attributes["id"] == "SzTGE4oZ4D" + assert state.attributes["turn_on"] is False - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_del_timer", - return_value={"status": "success", "result": {"id": "SzTGE4oZ4D"}}, - ), - ): - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_OFF, - { - ATTR_ENTITY_ID: state1.entity_id, - }, - blocking=True, - ) + mock_client.async_del_timer.return_value = { + "status": "success", + "result": {"id": "SzTGE4oZ4D"}, + } + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + { + ATTR_ENTITY_ID: state.entity_id, + }, + blocking=True, + ) + + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "timer_on", False) + + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_on", False) - - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - - state1 = hass.states.get("switch.hallway_timer") - assert state1.state == STATE_OFF + state = hass.states.get("switch.hallway_timer") + assert state.state == STATE_OFF async def test_switch_pure_boost( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: - """Test the Sensibo switch.""" + """Test the Sensibo switch pure boost.""" - state1 = hass.states.get("switch.kitchen_pure_boost") - assert state1.state == STATE_OFF + state = hass.states.get("switch.kitchen_pure_boost") + assert state.state == STATE_OFF - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_pureboost", - return_value={"status": "success"}, - ), - ): - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_ON, - { - ATTR_ENTITY_ID: state1.entity_id, - }, - blocking=True, - ) + mock_client.async_set_pureboost.return_value = {"status": "success"} + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + { + ATTR_ENTITY_ID: state.entity_id, + }, + blocking=True, + ) + + monkeypatch.setattr(get_data[0].parsed["AAZZAAZZ"], "pure_boost_enabled", True) + monkeypatch.setattr( + get_data[0].parsed["AAZZAAZZ"], "pure_measure_integration", None + ) + + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_boost_enabled", True) - monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_measure_integration", None) + state = hass.states.get("switch.kitchen_pure_boost") + assert state.state == STATE_ON - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - state1 = hass.states.get("switch.kitchen_pure_boost") - assert state1.state == STATE_ON + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + { + ATTR_ENTITY_ID: state.entity_id, + }, + blocking=True, + ) - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_pureboost", - return_value={"status": "success"}, - ), - ): - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_OFF, - { - ATTR_ENTITY_ID: state1.entity_id, - }, - blocking=True, - ) + monkeypatch.setattr(get_data[0].parsed["AAZZAAZZ"], "pure_boost_enabled", False) + + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - monkeypatch.setattr(get_data.parsed["AAZZAAZZ"], "pure_boost_enabled", False) - - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - - state1 = hass.states.get("switch.kitchen_pure_boost") - assert state1.state == STATE_OFF + state = hass.states.get("switch.kitchen_pure_boost") + assert state.state == STATE_OFF async def test_switch_command_failure( - hass: HomeAssistant, - load_int: ConfigEntry, - monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + hass: HomeAssistant, load_int: ConfigEntry, mock_client: MagicMock ) -> None: """Test the Sensibo switch fails commands.""" - state1 = hass.states.get("switch.hallway_timer") + state = hass.states.get("switch.hallway_timer") - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_set_timer", - return_value={"status": "failure"}, - ), - pytest.raises( - HomeAssistantError, - ), + mock_client.async_set_timer.return_value = {"status": "failure"} + + with pytest.raises( + HomeAssistantError, ): await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: state1.entity_id, + ATTR_ENTITY_ID: state.entity_id, }, blocking=True, ) - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_del_timer", - return_value={"status": "failure"}, - ), - pytest.raises( - HomeAssistantError, - ), + mock_client.async_del_timer.return_value = {"status": "failure"} + + with pytest.raises( + HomeAssistantError, ): await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_OFF, { - ATTR_ENTITY_ID: state1.entity_id, + ATTR_ENTITY_ID: state.entity_id, }, blocking=True, ) @@ -269,113 +204,78 @@ async def test_switch_climate_react( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + mock_client: MagicMock, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: """Test the Sensibo switch for climate react.""" - state1 = hass.states.get("switch.hallway_climate_react") - assert state1.state == STATE_OFF + state = hass.states.get("switch.hallway_climate_react") + assert state.state == STATE_OFF - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_enable_climate_react", - return_value={"status": "success"}, - ), - ): - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_ON, - { - ATTR_ENTITY_ID: state1.entity_id, - }, - blocking=True, - ) + mock_client.async_enable_climate_react.return_value = {"status": "success"} + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + { + ATTR_ENTITY_ID: state.entity_id, + }, + blocking=True, + ) + + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "smart_on", True) + + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_on", True) + state = hass.states.get("switch.hallway_climate_react") + assert state.state == STATE_ON - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - state1 = hass.states.get("switch.hallway_climate_react") - assert state1.state == STATE_ON + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + { + ATTR_ENTITY_ID: state.entity_id, + }, + blocking=True, + ) - with ( - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data", - return_value=get_data, - ), - patch( - "homeassistant.components.sensibo.util.SensiboClient.async_enable_climate_react", - return_value={"status": "success"}, - ), - ): - await hass.services.async_call( - SWITCH_DOMAIN, - SERVICE_TURN_OFF, - { - ATTR_ENTITY_ID: state1.entity_id, - }, - blocking=True, - ) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "smart_on", False) + + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) await hass.async_block_till_done() - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_on", False) - - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() - - state1 = hass.states.get("switch.hallway_climate_react") - assert state1.state == STATE_OFF + state = hass.states.get("switch.hallway_climate_react") + assert state.state == STATE_OFF async def test_switch_climate_react_no_data( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + get_data: tuple[SensiboData, dict[str, Any]], + freezer: FrozenDateTimeFactory, ) -> None: - """Test the Sensibo switch for climate react.""" + """Test the Sensibo switch for climate react with no data.""" - monkeypatch.setattr(get_data.parsed["ABC999111"], "smart_type", None) + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "smart_type", None) - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - async_fire_time_changed( - hass, - dt_util.utcnow() + timedelta(minutes=5), - ) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state1 = hass.states.get("switch.hallway_climate_react") - assert state1.state == STATE_OFF + state = hass.states.get("switch.hallway_climate_react") + assert state.state == STATE_OFF with pytest.raises(HomeAssistantError): await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: state1.entity_id, + ATTR_ENTITY_ID: state.entity_id, }, blocking=True, ) - await hass.async_block_till_done() diff --git a/tests/components/sensibo/test_update.py b/tests/components/sensibo/test_update.py index a4eb9751243..1a6dd9b2fc5 100644 --- a/tests/components/sensibo/test_update.py +++ b/tests/components/sensibo/test_update.py @@ -3,7 +3,7 @@ from __future__ import annotations from datetime import timedelta -from unittest.mock import patch +from typing import Any from freezegun.api import FrozenDateTimeFactory from pysensibo.model import SensiboData @@ -27,7 +27,7 @@ async def test_update( hass: HomeAssistant, load_int: ConfigEntry, monkeypatch: pytest.MonkeyPatch, - get_data: SensiboData, + get_data: tuple[SensiboData, dict[str, Any]], entity_registry: er.EntityRegistry, snapshot: SnapshotAssertion, freezer: FrozenDateTimeFactory, @@ -36,15 +36,11 @@ async def test_update( await snapshot_platform(hass, entity_registry, snapshot, load_int.entry_id) - monkeypatch.setattr(get_data.parsed["ABC999111"], "fw_ver", "SKY30048") + monkeypatch.setattr(get_data[0].parsed["ABC999111"], "fw_ver", "SKY30048") - with patch( - "homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data", - return_value=get_data, - ): - freezer.tick(timedelta(minutes=5)) - async_fire_time_changed(hass) - await hass.async_block_till_done() + freezer.tick(timedelta(minutes=5)) + async_fire_time_changed(hass) + await hass.async_block_till_done() - state1 = hass.states.get("update.hallway_firmware") - assert state1.state == STATE_OFF + state = hass.states.get("update.hallway_firmware") + assert state.state == STATE_OFF