Improve Atag integration and bump version to 0.3.5.3 (#47778)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
f4b775b125
commit
fa0c544bf5
13 changed files with 160 additions and 182 deletions
|
@ -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}"
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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%]"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue