From fa0c544bf572303e3d16b4dad9fac0d2ae29bce4 Mon Sep 17 00:00:00 2001 From: MatsNl <37705266+MatsNl@users.noreply.github.com> Date: Fri, 12 Mar 2021 07:15:45 +0100 Subject: [PATCH] Improve Atag integration and bump version to 0.3.5.3 (#47778) Co-authored-by: Paulus Schoutsen --- homeassistant/components/atag/__init__.py | 57 +++++++--------- homeassistant/components/atag/climate.py | 46 ++++++------- homeassistant/components/atag/config_flow.py | 16 ++--- homeassistant/components/atag/manifest.json | 2 +- homeassistant/components/atag/sensor.py | 17 ++--- homeassistant/components/atag/strings.json | 1 - homeassistant/components/atag/water_heater.py | 12 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/atag/__init__.py | 68 ++++++++++++------- tests/components/atag/test_climate.py | 49 ++++++------- tests/components/atag/test_config_flow.py | 53 ++++++--------- tests/components/atag/test_init.py | 17 +---- 13 files changed, 160 insertions(+), 182 deletions(-) diff --git a/homeassistant/components/atag/__init__.py b/homeassistant/components/atag/__init__.py index c4cffe1c41a..02aab5036b3 100644 --- a/homeassistant/components/atag/__init__.py +++ b/homeassistant/components/atag/__init__.py @@ -31,17 +31,34 @@ async def async_setup(hass: HomeAssistant, config): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Atag integration from a config entry.""" - session = async_get_clientsession(hass) - coordinator = AtagDataUpdateCoordinator(hass, session, entry) + async def _async_update_data(): + """Update data via library.""" + with async_timeout.timeout(20): + try: + await atag.update() + except AtagException as err: + raise UpdateFailed(err) from err + return atag + + atag = AtagOne( + session=async_get_clientsession(hass), **entry.data, device=entry.unique_id + ) + coordinator = DataUpdateCoordinator( + hass, + _LOGGER, + name=DOMAIN.title(), + update_method=_async_update_data, + update_interval=timedelta(seconds=60), + ) + await coordinator.async_refresh() if not coordinator.last_update_success: raise ConfigEntryNotReady - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN][entry.entry_id] = coordinator + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator if entry.unique_id is None: - hass.config_entries.async_update_entry(entry, unique_id=coordinator.atag.id) + hass.config_entries.async_update_entry(entry, unique_id=atag.id) for platform in PLATFORMS: hass.async_create_task( @@ -51,28 +68,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): return True -class AtagDataUpdateCoordinator(DataUpdateCoordinator): - """Define an object to hold Atag data.""" - - def __init__(self, hass, session, entry): - """Initialize.""" - self.atag = AtagOne(session=session, **entry.data) - - super().__init__( - hass, _LOGGER, name=DOMAIN, update_interval=timedelta(seconds=30) - ) - - async def _async_update_data(self): - """Update data via library.""" - with async_timeout.timeout(20): - try: - if not await self.atag.update(): - raise UpdateFailed("No data received") - except AtagException as error: - raise UpdateFailed(error) from error - return self.atag.report - - async def async_unload_entry(hass, entry): """Unload Atag config entry.""" unload_ok = all( @@ -91,7 +86,7 @@ async def async_unload_entry(hass, entry): class AtagEntity(CoordinatorEntity): """Defines a base Atag entity.""" - def __init__(self, coordinator: AtagDataUpdateCoordinator, atag_id: str) -> None: + def __init__(self, coordinator: DataUpdateCoordinator, atag_id: str) -> None: """Initialize the Atag entity.""" super().__init__(coordinator) @@ -101,8 +96,8 @@ class AtagEntity(CoordinatorEntity): @property def device_info(self) -> dict: """Return info for device registry.""" - device = self.coordinator.atag.id - version = self.coordinator.atag.apiversion + device = self.coordinator.data.id + version = self.coordinator.data.apiversion return { "identifiers": {(DOMAIN, device)}, "name": "Atag Thermostat", @@ -119,4 +114,4 @@ class AtagEntity(CoordinatorEntity): @property def unique_id(self): """Return a unique ID to use for this entity.""" - return f"{self.coordinator.atag.id}-{self._id}" + return f"{self.coordinator.data.id}-{self._id}" diff --git a/homeassistant/components/atag/climate.py b/homeassistant/components/atag/climate.py index ad46fefe8c2..a2aa5cf16e4 100644 --- a/homeassistant/components/atag/climate.py +++ b/homeassistant/components/atag/climate.py @@ -16,16 +16,14 @@ from homeassistant.const import ATTR_TEMPERATURE from . import CLIMATE, DOMAIN, AtagEntity -PRESET_SCHEDULE = "Auto" -PRESET_MANUAL = "Manual" -PRESET_EXTEND = "Extend" -SUPPORT_PRESET = [ - PRESET_MANUAL, - PRESET_SCHEDULE, - PRESET_EXTEND, - PRESET_AWAY, - PRESET_BOOST, -] +PRESET_MAP = { + "Manual": "manual", + "Auto": "automatic", + "Extend": "extend", + PRESET_AWAY: "vacation", + PRESET_BOOST: "fireplace", +} +PRESET_INVERTED = {v: k for k, v in PRESET_MAP.items()} SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE HVAC_MODES = [HVAC_MODE_AUTO, HVAC_MODE_HEAT] @@ -47,8 +45,8 @@ class AtagThermostat(AtagEntity, ClimateEntity): @property def hvac_mode(self) -> Optional[str]: """Return hvac operation ie. heat, cool mode.""" - if self.coordinator.atag.climate.hvac_mode in HVAC_MODES: - return self.coordinator.atag.climate.hvac_mode + if self.coordinator.data.climate.hvac_mode in HVAC_MODES: + return self.coordinator.data.climate.hvac_mode return None @property @@ -59,46 +57,46 @@ class AtagThermostat(AtagEntity, ClimateEntity): @property def hvac_action(self) -> Optional[str]: """Return the current running hvac operation.""" - if self.coordinator.atag.climate.status: - return CURRENT_HVAC_HEAT - return CURRENT_HVAC_IDLE + is_active = self.coordinator.data.climate.status + return CURRENT_HVAC_HEAT if is_active else CURRENT_HVAC_IDLE @property - def temperature_unit(self): + def temperature_unit(self) -> Optional[str]: """Return the unit of measurement.""" - return self.coordinator.atag.climate.temp_unit + return self.coordinator.data.climate.temp_unit @property def current_temperature(self) -> Optional[float]: """Return the current temperature.""" - return self.coordinator.atag.climate.temperature + return self.coordinator.data.climate.temperature @property def target_temperature(self) -> Optional[float]: """Return the temperature we try to reach.""" - return self.coordinator.atag.climate.target_temperature + return self.coordinator.data.climate.target_temperature @property def preset_mode(self) -> Optional[str]: """Return the current preset mode, e.g., auto, manual, fireplace, extend, etc.""" - return self.coordinator.atag.climate.preset_mode + preset = self.coordinator.data.climate.preset_mode + return PRESET_INVERTED.get(preset) @property def preset_modes(self) -> Optional[List[str]]: """Return a list of available preset modes.""" - return SUPPORT_PRESET + return list(PRESET_MAP.keys()) async def async_set_temperature(self, **kwargs) -> None: """Set new target temperature.""" - await self.coordinator.atag.climate.set_temp(kwargs.get(ATTR_TEMPERATURE)) + await self.coordinator.data.climate.set_temp(kwargs.get(ATTR_TEMPERATURE)) self.async_write_ha_state() async def async_set_hvac_mode(self, hvac_mode: str) -> None: """Set new target hvac mode.""" - await self.coordinator.atag.climate.set_hvac_mode(hvac_mode) + await self.coordinator.data.climate.set_hvac_mode(hvac_mode) self.async_write_ha_state() async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new preset mode.""" - await self.coordinator.atag.climate.set_preset_mode(preset_mode) + await self.coordinator.data.climate.set_preset_mode(PRESET_MAP[preset_mode]) self.async_write_ha_state() diff --git a/homeassistant/components/atag/config_flow.py b/homeassistant/components/atag/config_flow.py index 865159aa658..b1dcedd58dc 100644 --- a/homeassistant/components/atag/config_flow.py +++ b/homeassistant/components/atag/config_flow.py @@ -3,14 +3,13 @@ import pyatag import voluptuous as vol from homeassistant import config_entries -from homeassistant.const import CONF_EMAIL, CONF_HOST, CONF_PORT +from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.helpers.aiohttp_client import async_get_clientsession from . import DOMAIN # pylint: disable=unused-import DATA_SCHEMA = { vol.Required(CONF_HOST): str, - vol.Optional(CONF_EMAIL): str, vol.Required(CONF_PORT, default=pyatag.const.DEFAULT_PORT): vol.Coerce(int), } @@ -26,15 +25,14 @@ class AtagConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if not user_input: return await self._show_form() - session = async_get_clientsession(self.hass) - try: - atag = pyatag.AtagOne(session=session, **user_input) - await atag.authorize() - await atag.update(force=True) - except pyatag.errors.Unauthorized: + atag = pyatag.AtagOne(session=async_get_clientsession(self.hass), **user_input) + try: + await atag.update() + + except pyatag.Unauthorized: return await self._show_form({"base": "unauthorized"}) - except pyatag.errors.AtagException: + except pyatag.AtagException: return await self._show_form({"base": "cannot_connect"}) await self.async_set_unique_id(atag.id) diff --git a/homeassistant/components/atag/manifest.json b/homeassistant/components/atag/manifest.json index 5e94afb06d3..1154a120f91 100644 --- a/homeassistant/components/atag/manifest.json +++ b/homeassistant/components/atag/manifest.json @@ -3,6 +3,6 @@ "name": "Atag", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/atag/", - "requirements": ["pyatag==0.3.4.4"], + "requirements": ["pyatag==0.3.5.3"], "codeowners": ["@MatsNL"] } diff --git a/homeassistant/components/atag/sensor.py b/homeassistant/components/atag/sensor.py index d6abe16ffdb..3123e245711 100644 --- a/homeassistant/components/atag/sensor.py +++ b/homeassistant/components/atag/sensor.py @@ -26,10 +26,7 @@ SENSORS = { async def async_setup_entry(hass, config_entry, async_add_entities): """Initialize sensor platform from config entry.""" coordinator = hass.data[DOMAIN][config_entry.entry_id] - entities = [] - for sensor in SENSORS: - entities.append(AtagSensor(coordinator, sensor)) - async_add_entities(entities) + async_add_entities([AtagSensor(coordinator, sensor) for sensor in SENSORS]) class AtagSensor(AtagEntity): @@ -43,32 +40,32 @@ class AtagSensor(AtagEntity): @property def state(self): """Return the state of the sensor.""" - return self.coordinator.data[self._id].state + return self.coordinator.data.report[self._id].state @property def icon(self): """Return icon.""" - return self.coordinator.data[self._id].icon + return self.coordinator.data.report[self._id].icon @property def device_class(self): """Return deviceclass.""" - if self.coordinator.data[self._id].sensorclass in [ + if self.coordinator.data.report[self._id].sensorclass in [ DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TEMPERATURE, ]: - return self.coordinator.data[self._id].sensorclass + return self.coordinator.data.report[self._id].sensorclass return None @property def unit_of_measurement(self): """Return measure.""" - if self.coordinator.data[self._id].measure in [ + if self.coordinator.data.report[self._id].measure in [ PRESSURE_BAR, TEMP_CELSIUS, TEMP_FAHRENHEIT, PERCENTAGE, TIME_HOURS, ]: - return self.coordinator.data[self._id].measure + return self.coordinator.data.report[self._id].measure return None diff --git a/homeassistant/components/atag/strings.json b/homeassistant/components/atag/strings.json index b06e9188b5b..39ed972524d 100644 --- a/homeassistant/components/atag/strings.json +++ b/homeassistant/components/atag/strings.json @@ -5,7 +5,6 @@ "title": "Connect to the device", "data": { "host": "[%key:common::config_flow::data::host%]", - "email": "[%key:common::config_flow::data::email%]", "port": "[%key:common::config_flow::data::port%]" } } diff --git a/homeassistant/components/atag/water_heater.py b/homeassistant/components/atag/water_heater.py index f9c2a4625bb..dac56edf89d 100644 --- a/homeassistant/components/atag/water_heater.py +++ b/homeassistant/components/atag/water_heater.py @@ -35,12 +35,12 @@ class AtagWaterHeater(AtagEntity, WaterHeaterEntity): @property def current_temperature(self): """Return the current temperature.""" - return self.coordinator.atag.dhw.temperature + return self.coordinator.data.dhw.temperature @property def current_operation(self): """Return current operation.""" - operation = self.coordinator.atag.dhw.current_operation + operation = self.coordinator.data.dhw.current_operation return operation if operation in self.operation_list else STATE_OFF @property @@ -50,20 +50,20 @@ class AtagWaterHeater(AtagEntity, WaterHeaterEntity): async def async_set_temperature(self, **kwargs): """Set new target temperature.""" - if await self.coordinator.atag.dhw.set_temp(kwargs.get(ATTR_TEMPERATURE)): + if await self.coordinator.data.dhw.set_temp(kwargs.get(ATTR_TEMPERATURE)): self.async_write_ha_state() @property def target_temperature(self): """Return the setpoint if water demand, otherwise return base temp (comfort level).""" - return self.coordinator.atag.dhw.target_temperature + return self.coordinator.data.dhw.target_temperature @property def max_temp(self): """Return the maximum temperature.""" - return self.coordinator.atag.dhw.max_temp + return self.coordinator.data.dhw.max_temp @property def min_temp(self): """Return the minimum temperature.""" - return self.coordinator.atag.dhw.min_temp + return self.coordinator.data.dhw.min_temp diff --git a/requirements_all.txt b/requirements_all.txt index 5f4e3a67e5b..2a532ee9ae8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1269,7 +1269,7 @@ pyalmond==0.0.2 pyarlo==0.2.4 # homeassistant.components.atag -pyatag==0.3.4.4 +pyatag==0.3.5.3 # homeassistant.components.netatmo pyatmo==4.2.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 96f6d6bb4ef..1dab1e8837b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -667,7 +667,7 @@ pyalmond==0.0.2 pyarlo==0.2.4 # homeassistant.components.atag -pyatag==0.3.4.4 +pyatag==0.3.5.3 # homeassistant.components.netatmo pyatmo==4.2.2 diff --git a/tests/components/atag/__init__.py b/tests/components/atag/__init__.py index 52d53ee9948..c41632b9715 100644 --- a/tests/components/atag/__init__.py +++ b/tests/components/atag/__init__.py @@ -1,7 +1,7 @@ """Tests for the Atag integration.""" -from homeassistant.components.atag import DOMAIN -from homeassistant.const import CONF_EMAIL, CONF_HOST, CONF_PORT, CONTENT_TYPE_JSON +from homeassistant.components.atag import DOMAIN, AtagException +from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry @@ -9,12 +9,15 @@ from tests.test_util.aiohttp import AiohttpClientMocker USER_INPUT = { CONF_HOST: "127.0.0.1", - CONF_EMAIL: "atag@domain.com", CONF_PORT: 10000, } UID = "xxxx-xxxx-xxxx_xx-xx-xxx-xxx" -PAIR_REPLY = {"pair_reply": {"status": {"device_id": UID}, "acc_status": 2}} -UPDATE_REPLY = {"update_reply": {"status": {"device_id": UID}, "acc_status": 2}} +AUTHORIZED = 2 +UNAUTHORIZED = 3 +PAIR_REPLY = {"pair_reply": {"status": {"device_id": UID}, "acc_status": AUTHORIZED}} +UPDATE_REPLY = { + "update_reply": {"status": {"device_id": UID}, "acc_status": AUTHORIZED} +} RECEIVE_REPLY = { "retrieve_reply": { "status": {"device_id": UID}, @@ -46,35 +49,52 @@ RECEIVE_REPLY = { "dhw_max_set": 65, "dhw_min_set": 40, }, - "acc_status": 2, + "acc_status": AUTHORIZED, } } +def mock_connection( + aioclient_mock: AiohttpClientMocker, authorized=True, conn_error=False +) -> None: + """Mock the requests to Atag endpoint.""" + if conn_error: + aioclient_mock.post( + "http://127.0.0.1:10000/pair", + exc=AtagException, + ) + aioclient_mock.post( + "http://127.0.0.1:10000/retrieve", + exc=AtagException, + ) + return + PAIR_REPLY["pair_reply"].update( + {"acc_status": AUTHORIZED if authorized else UNAUTHORIZED} + ) + RECEIVE_REPLY["retrieve_reply"].update( + {"acc_status": AUTHORIZED if authorized else UNAUTHORIZED} + ) + aioclient_mock.post( + "http://127.0.0.1:10000/retrieve", + json=RECEIVE_REPLY, + ) + aioclient_mock.post( + "http://127.0.0.1:10000/update", + json=UPDATE_REPLY, + ) + aioclient_mock.post( + "http://127.0.0.1:10000/pair", + json=PAIR_REPLY, + ) + + async def init_integration( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, - rgbw: bool = False, skip_setup: bool = False, ) -> MockConfigEntry: """Set up the Atag integration in Home Assistant.""" - - aioclient_mock.post( - "http://127.0.0.1:10000/retrieve", - json=RECEIVE_REPLY, - headers={"Content-Type": CONTENT_TYPE_JSON}, - ) - aioclient_mock.post( - "http://127.0.0.1:10000/update", - json=UPDATE_REPLY, - headers={"Content-Type": CONTENT_TYPE_JSON}, - ) - aioclient_mock.post( - "http://127.0.0.1:10000/pair", - json=PAIR_REPLY, - headers={"Content-Type": CONTENT_TYPE_JSON}, - ) - + mock_connection(aioclient_mock) entry = MockConfigEntry(domain=DOMAIN, data=USER_INPUT) entry.add_to_hass(hass) diff --git a/tests/components/atag/test_climate.py b/tests/components/atag/test_climate.py index e263ade7c75..3c9a9c3f820 100644 --- a/tests/components/atag/test_climate.py +++ b/tests/components/atag/test_climate.py @@ -1,7 +1,7 @@ """Tests for the Atag climate platform.""" from unittest.mock import PropertyMock, patch -from homeassistant.components.atag import CLIMATE, DOMAIN +from homeassistant.components.atag.climate import CLIMATE, DOMAIN, PRESET_MAP from homeassistant.components.climate import ( ATTR_HVAC_ACTION, ATTR_HVAC_MODE, @@ -11,11 +11,8 @@ from homeassistant.components.climate import ( SERVICE_SET_PRESET_MODE, SERVICE_SET_TEMPERATURE, ) -from homeassistant.components.climate.const import CURRENT_HVAC_HEAT, PRESET_AWAY -from homeassistant.components.homeassistant import ( - DOMAIN as HA_DOMAIN, - SERVICE_UPDATE_ENTITY, -) +from homeassistant.components.climate.const import CURRENT_HVAC_IDLE, PRESET_AWAY +from homeassistant.components.homeassistant import DOMAIN as HA_DOMAIN from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNKNOWN from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er @@ -31,17 +28,13 @@ async def test_climate( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test the creation and values of Atag climate device.""" - with patch("pyatag.entities.Climate.status"): - entry = await init_integration(hass, aioclient_mock) - registry = er.async_get(hass) + await init_integration(hass, aioclient_mock) + entity_registry = er.async_get(hass) - assert registry.async_is_registered(CLIMATE_ID) - entry = registry.async_get(CLIMATE_ID) - assert entry.unique_id == f"{UID}-{CLIMATE}" - assert ( - hass.states.get(CLIMATE_ID).attributes[ATTR_HVAC_ACTION] - == CURRENT_HVAC_HEAT - ) + assert entity_registry.async_is_registered(CLIMATE_ID) + entity = entity_registry.async_get(CLIMATE_ID) + assert entity.unique_id == f"{UID}-{CLIMATE}" + assert hass.states.get(CLIMATE_ID).attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE async def test_setting_climate( @@ -67,7 +60,7 @@ async def test_setting_climate( blocking=True, ) await hass.async_block_till_done() - mock_set_preset.assert_called_once_with(PRESET_AWAY) + mock_set_preset.assert_called_once_with(PRESET_MAP[PRESET_AWAY]) with patch("pyatag.entities.Climate.set_hvac_mode") as mock_set_hvac: await hass.services.async_call( @@ -93,18 +86,18 @@ async def test_incorrect_modes( assert hass.states.get(CLIMATE_ID).state == STATE_UNKNOWN -async def test_update_service( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +async def test_update_failed( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, ) -> None: - """Test the updater service is called.""" - await init_integration(hass, aioclient_mock) + """Test data is not destroyed on update failure.""" + entry = await init_integration(hass, aioclient_mock) await async_setup_component(hass, HA_DOMAIN, {}) - with patch("pyatag.AtagOne.update") as updater: - await hass.services.async_call( - HA_DOMAIN, - SERVICE_UPDATE_ENTITY, - {ATTR_ENTITY_ID: CLIMATE_ID}, - blocking=True, - ) + assert hass.states.get(CLIMATE_ID).state == HVAC_MODE_HEAT + coordinator = hass.data[DOMAIN][entry.entry_id] + with patch("pyatag.AtagOne.update", side_effect=TimeoutError) as updater: + await coordinator.async_refresh() await hass.async_block_till_done() updater.assert_called_once() + assert not coordinator.last_update_success + assert coordinator.data.id == UID diff --git a/tests/components/atag/test_config_flow.py b/tests/components/atag/test_config_flow.py index 81375792c71..a92e73ae18e 100644 --- a/tests/components/atag/test_config_flow.py +++ b/tests/components/atag/test_config_flow.py @@ -1,24 +1,18 @@ """Tests for the Atag config flow.""" from unittest.mock import PropertyMock, patch -from pyatag import errors - from homeassistant import config_entries, data_entry_flow from homeassistant.components.atag import DOMAIN from homeassistant.core import HomeAssistant -from tests.components.atag import ( - PAIR_REPLY, - RECEIVE_REPLY, - UID, - USER_INPUT, - init_integration, -) +from . import UID, USER_INPUT, init_integration, mock_connection + from tests.test_util.aiohttp import AiohttpClientMocker -async def test_show_form(hass): +async def test_show_form(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker): """Test that the form is served with no input.""" + mock_connection(aioclient_mock) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -48,28 +42,30 @@ async def test_adding_second_device( assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY -async def test_connection_error(hass): +async def test_connection_error( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +): """Test we show user form on Atag connection error.""" - with patch("pyatag.AtagOne.authorize", side_effect=errors.AtagException()): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_USER}, - data=USER_INPUT, - ) + mock_connection(aioclient_mock, conn_error=True) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + data=USER_INPUT, + ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" assert result["errors"] == {"base": "cannot_connect"} -async def test_unauthorized(hass): +async def test_unauthorized(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker): """Test we show correct form when Unauthorized error is raised.""" - with patch("pyatag.AtagOne.authorize", side_effect=errors.Unauthorized()): - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_USER}, - data=USER_INPUT, - ) + mock_connection(aioclient_mock, authorized=False) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + data=USER_INPUT, + ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" assert result["errors"] == {"base": "unauthorized"} @@ -79,14 +75,7 @@ async def test_full_flow_implementation( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test registering an integration and finishing flow works.""" - aioclient_mock.post( - "http://127.0.0.1:10000/pair", - json=PAIR_REPLY, - ) - aioclient_mock.post( - "http://127.0.0.1:10000/retrieve", - json=RECEIVE_REPLY, - ) + mock_connection(aioclient_mock) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER}, diff --git a/tests/components/atag/test_init.py b/tests/components/atag/test_init.py index b86de8a8be5..7b7f3c1e33a 100644 --- a/tests/components/atag/test_init.py +++ b/tests/components/atag/test_init.py @@ -1,13 +1,11 @@ """Tests for the ATAG integration.""" -from unittest.mock import patch - -import aiohttp from homeassistant.components.atag import DOMAIN from homeassistant.config_entries import ENTRY_STATE_SETUP_RETRY from homeassistant.core import HomeAssistant -from tests.components.atag import init_integration +from . import init_integration, mock_connection + from tests.test_util.aiohttp import AiohttpClientMocker @@ -15,20 +13,11 @@ async def test_config_entry_not_ready( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test configuration entry not ready on library error.""" - aioclient_mock.post("http://127.0.0.1:10000/retrieve", exc=aiohttp.ClientError) + mock_connection(aioclient_mock, conn_error=True) entry = await init_integration(hass, aioclient_mock) assert entry.state == ENTRY_STATE_SETUP_RETRY -async def test_config_entry_empty_reply( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker -) -> None: - """Test configuration entry not ready when library returns False.""" - with patch("pyatag.AtagOne.update", return_value=False): - entry = await init_integration(hass, aioclient_mock) - assert entry.state == ENTRY_STATE_SETUP_RETRY - - async def test_unload_config_entry( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: