Update nexia to use asyncio (#72108)

This commit is contained in:
J. Nick Koston 2022-05-18 18:08:02 -05:00 committed by GitHub
parent bf63d381b2
commit d8a580a90f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 121 additions and 98 deletions

View File

@ -1,15 +1,16 @@
"""Support for Nexia / Trane XL Thermostats.""" """Support for Nexia / Trane XL Thermostats."""
from functools import partial import asyncio
import logging import logging
import aiohttp
from nexia.const import BRAND_NEXIA from nexia.const import BRAND_NEXIA
from nexia.home import NexiaHome from nexia.home import NexiaHome
from requests.exceptions import ConnectTimeout, HTTPError
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from .const import CONF_BRAND, DOMAIN, PLATFORMS from .const import CONF_BRAND, DOMAIN, PLATFORMS
@ -30,31 +31,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
brand = conf.get(CONF_BRAND, BRAND_NEXIA) brand = conf.get(CONF_BRAND, BRAND_NEXIA)
state_file = hass.config.path(f"nexia_config_{username}.conf") state_file = hass.config.path(f"nexia_config_{username}.conf")
session = async_get_clientsession(hass)
nexia_home = NexiaHome(
session,
username=username,
password=password,
device_name=hass.config.location_name,
state_file=state_file,
brand=brand,
)
try: try:
nexia_home = await hass.async_add_executor_job( await nexia_home.login()
partial( except asyncio.TimeoutError as ex:
NexiaHome, raise ConfigEntryNotReady(
username=username, f"Timed out trying to connect to Nexia service: {ex}"
password=password, ) from ex
device_name=hass.config.location_name, except aiohttp.ClientResponseError as http_ex:
state_file=state_file, if is_invalid_auth_code(http_ex.status):
brand=brand,
)
)
except ConnectTimeout as ex:
_LOGGER.error("Unable to connect to Nexia service: %s", ex)
raise ConfigEntryNotReady from ex
except HTTPError as http_ex:
if is_invalid_auth_code(http_ex.response.status_code):
_LOGGER.error( _LOGGER.error(
"Access error from Nexia service, please check credentials: %s", http_ex "Access error from Nexia service, please check credentials: %s", http_ex
) )
return False return False
_LOGGER.error("HTTP error from Nexia service: %s", http_ex) raise ConfigEntryNotReady(f"Error from Nexia service: {http_ex}") from http_ex
raise ConfigEntryNotReady from http_ex
coordinator = NexiaDataUpdateCoordinator(hass, nexia_home) coordinator = NexiaDataUpdateCoordinator(hass, nexia_home)
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
hass.config_entries.async_setup_platforms(entry, PLATFORMS) hass.config_entries.async_setup_platforms(entry, PLATFORMS)

View File

@ -123,13 +123,17 @@ async def async_setup_entry(
platform.async_register_entity_service( platform.async_register_entity_service(
SERVICE_SET_HUMIDIFY_SETPOINT, SERVICE_SET_HUMIDIFY_SETPOINT,
SET_HUMIDITY_SCHEMA, SET_HUMIDITY_SCHEMA,
SERVICE_SET_HUMIDIFY_SETPOINT, f"async_{SERVICE_SET_HUMIDIFY_SETPOINT}",
) )
platform.async_register_entity_service( platform.async_register_entity_service(
SERVICE_SET_AIRCLEANER_MODE, SET_AIRCLEANER_SCHEMA, SERVICE_SET_AIRCLEANER_MODE SERVICE_SET_AIRCLEANER_MODE,
SET_AIRCLEANER_SCHEMA,
f"async_{SERVICE_SET_AIRCLEANER_MODE}",
) )
platform.async_register_entity_service( platform.async_register_entity_service(
SERVICE_SET_HVAC_RUN_MODE, SET_HVAC_RUN_MODE_SCHEMA, SERVICE_SET_HVAC_RUN_MODE SERVICE_SET_HVAC_RUN_MODE,
SET_HVAC_RUN_MODE_SCHEMA,
f"async_{SERVICE_SET_HVAC_RUN_MODE}",
) )
entities: list[NexiaZone] = [] entities: list[NexiaZone] = []
@ -192,20 +196,20 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
"""Return the fan setting.""" """Return the fan setting."""
return self._thermostat.get_fan_mode() return self._thermostat.get_fan_mode()
def set_fan_mode(self, fan_mode): async def async_set_fan_mode(self, fan_mode):
"""Set new target fan mode.""" """Set new target fan mode."""
self._thermostat.set_fan_mode(fan_mode) await self._thermostat.set_fan_mode(fan_mode)
self._signal_thermostat_update() self._signal_thermostat_update()
def set_hvac_run_mode(self, run_mode, hvac_mode): async def async_set_hvac_run_mode(self, run_mode, hvac_mode):
"""Set the hvac run mode.""" """Set the hvac run mode."""
if run_mode is not None: if run_mode is not None:
if run_mode == HOLD_PERMANENT: if run_mode == HOLD_PERMANENT:
self._zone.call_permanent_hold() await self._zone.call_permanent_hold()
else: else:
self._zone.call_return_to_schedule() await self._zone.call_return_to_schedule()
if hvac_mode is not None: if hvac_mode is not None:
self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode]) await self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode])
self._signal_thermostat_update() self._signal_thermostat_update()
@property @property
@ -213,12 +217,12 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
"""Preset that is active.""" """Preset that is active."""
return self._zone.get_preset() return self._zone.get_preset()
def set_humidity(self, humidity): async def async_set_humidity(self, humidity):
"""Dehumidify target.""" """Dehumidify target."""
if self._thermostat.has_dehumidify_support(): if self._thermostat.has_dehumidify_support():
self.set_dehumidify_setpoint(humidity) await self.async_set_dehumidify_setpoint(humidity)
else: else:
self.set_humidify_setpoint(humidity) await self.async_set_humidify_setpoint(humidity)
self._signal_thermostat_update() self._signal_thermostat_update()
@property @property
@ -300,7 +304,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
return NEXIA_TO_HA_HVAC_MODE_MAP[mode] return NEXIA_TO_HA_HVAC_MODE_MAP[mode]
def set_temperature(self, **kwargs): async def async_set_temperature(self, **kwargs):
"""Set target temperature.""" """Set target temperature."""
new_heat_temp = kwargs.get(ATTR_TARGET_TEMP_LOW) new_heat_temp = kwargs.get(ATTR_TARGET_TEMP_LOW)
new_cool_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH) new_cool_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH)
@ -332,7 +336,7 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
): ):
new_heat_temp = new_cool_temp - deadband new_heat_temp = new_cool_temp - deadband
self._zone.set_heat_cool_temp( await self._zone.set_heat_cool_temp(
heat_temperature=new_heat_temp, heat_temperature=new_heat_temp,
cool_temperature=new_cool_temp, cool_temperature=new_cool_temp,
set_temperature=set_temp, set_temperature=set_temp,
@ -366,63 +370,63 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity):
return data return data
def set_preset_mode(self, preset_mode: str): async def async_set_preset_mode(self, preset_mode: str):
"""Set the preset mode.""" """Set the preset mode."""
self._zone.set_preset(preset_mode) await self._zone.set_preset(preset_mode)
self._signal_zone_update() self._signal_zone_update()
def turn_aux_heat_off(self): async def async_turn_aux_heat_off(self):
"""Turn. Aux Heat off.""" """Turn. Aux Heat off."""
self._thermostat.set_emergency_heat(False) await self._thermostat.set_emergency_heat(False)
self._signal_thermostat_update() self._signal_thermostat_update()
def turn_aux_heat_on(self): async def async_turn_aux_heat_on(self):
"""Turn. Aux Heat on.""" """Turn. Aux Heat on."""
self._thermostat.set_emergency_heat(True) self._thermostat.set_emergency_heat(True)
self._signal_thermostat_update() self._signal_thermostat_update()
def turn_off(self): async def async_turn_off(self):
"""Turn. off the zone.""" """Turn. off the zone."""
self.set_hvac_mode(OPERATION_MODE_OFF) await self.set_hvac_mode(OPERATION_MODE_OFF)
self._signal_zone_update() self._signal_zone_update()
def turn_on(self): async def async_turn_on(self):
"""Turn. on the zone.""" """Turn. on the zone."""
self.set_hvac_mode(OPERATION_MODE_AUTO) await self.set_hvac_mode(OPERATION_MODE_AUTO)
self._signal_zone_update() self._signal_zone_update()
def set_hvac_mode(self, hvac_mode: HVACMode) -> None: async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the system mode (Auto, Heat_Cool, Cool, Heat, etc).""" """Set the system mode (Auto, Heat_Cool, Cool, Heat, etc)."""
if hvac_mode == HVACMode.AUTO: if hvac_mode == HVACMode.AUTO:
self._zone.call_return_to_schedule() await self._zone.call_return_to_schedule()
self._zone.set_mode(mode=OPERATION_MODE_AUTO) await self._zone.set_mode(mode=OPERATION_MODE_AUTO)
else: else:
self._zone.call_permanent_hold() await self._zone.call_permanent_hold()
self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode]) await self._zone.set_mode(mode=HA_TO_NEXIA_HVAC_MODE_MAP[hvac_mode])
self._signal_zone_update() self._signal_zone_update()
def set_aircleaner_mode(self, aircleaner_mode): async def async_set_aircleaner_mode(self, aircleaner_mode):
"""Set the aircleaner mode.""" """Set the aircleaner mode."""
self._thermostat.set_air_cleaner(aircleaner_mode) await self._thermostat.set_air_cleaner(aircleaner_mode)
self._signal_thermostat_update() self._signal_thermostat_update()
def set_humidify_setpoint(self, humidity): async def async_set_humidify_setpoint(self, humidity):
"""Set the humidify setpoint.""" """Set the humidify setpoint."""
target_humidity = find_humidity_setpoint(humidity / 100.0) target_humidity = find_humidity_setpoint(humidity / 100.0)
if self._thermostat.get_humidify_setpoint() == target_humidity: if self._thermostat.get_humidify_setpoint() == target_humidity:
# Trying to set the humidify setpoint to the # Trying to set the humidify setpoint to the
# same value will cause the api to timeout # same value will cause the api to timeout
return return
self._thermostat.set_humidify_setpoint(target_humidity) await self._thermostat.set_humidify_setpoint(target_humidity)
self._signal_thermostat_update() self._signal_thermostat_update()
def set_dehumidify_setpoint(self, humidity): async def async_set_dehumidify_setpoint(self, humidity):
"""Set the dehumidify setpoint.""" """Set the dehumidify setpoint."""
target_humidity = find_humidity_setpoint(humidity / 100.0) target_humidity = find_humidity_setpoint(humidity / 100.0)
if self._thermostat.get_dehumidify_setpoint() == target_humidity: if self._thermostat.get_dehumidify_setpoint() == target_humidity:
# Trying to set the dehumidify setpoint to the # Trying to set the dehumidify setpoint to the
# same value will cause the api to timeout # same value will cause the api to timeout
return return
self._thermostat.set_dehumidify_setpoint(target_humidity) await self._thermostat.set_dehumidify_setpoint(target_humidity)
self._signal_thermostat_update() self._signal_thermostat_update()

View File

@ -1,13 +1,15 @@
"""Config flow for Nexia integration.""" """Config flow for Nexia integration."""
import asyncio
import logging import logging
import aiohttp
from nexia.const import BRAND_ASAIR, BRAND_NEXIA, BRAND_TRANE from nexia.const import BRAND_ASAIR, BRAND_NEXIA, BRAND_TRANE
from nexia.home import NexiaHome from nexia.home import NexiaHome
from requests.exceptions import ConnectTimeout, HTTPError
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries, core, exceptions from homeassistant import config_entries, core, exceptions
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import ( from .const import (
BRAND_ASAIR_NAME, BRAND_ASAIR_NAME,
@ -44,23 +46,23 @@ async def validate_input(hass: core.HomeAssistant, data):
state_file = hass.config.path( state_file = hass.config.path(
f"{data[CONF_BRAND]}_config_{data[CONF_USERNAME]}.conf" f"{data[CONF_BRAND]}_config_{data[CONF_USERNAME]}.conf"
) )
session = async_get_clientsession(hass)
nexia_home = NexiaHome(
session,
username=data[CONF_USERNAME],
password=data[CONF_PASSWORD],
brand=data[CONF_BRAND],
device_name=hass.config.location_name,
state_file=state_file,
)
try: try:
nexia_home = NexiaHome( await nexia_home.login()
username=data[CONF_USERNAME], except asyncio.TimeoutError as ex:
password=data[CONF_PASSWORD],
brand=data[CONF_BRAND],
auto_login=False,
auto_update=False,
device_name=hass.config.location_name,
state_file=state_file,
)
await hass.async_add_executor_job(nexia_home.login)
except ConnectTimeout as ex:
_LOGGER.error("Unable to connect to Nexia service: %s", ex) _LOGGER.error("Unable to connect to Nexia service: %s", ex)
raise CannotConnect from ex raise CannotConnect from ex
except HTTPError as http_ex: except aiohttp.ClientResponseError as http_ex:
_LOGGER.error("HTTP error from Nexia service: %s", http_ex) _LOGGER.error("HTTP error from Nexia service: %s", http_ex)
if is_invalid_auth_code(http_ex.response.status_code): if is_invalid_auth_code(http_ex.status):
raise InvalidAuth from http_ex raise InvalidAuth from http_ex
raise CannotConnect from http_ex raise CannotConnect from http_ex

View File

@ -33,4 +33,4 @@ class NexiaDataUpdateCoordinator(DataUpdateCoordinator):
async def _async_update_data(self) -> None: async def _async_update_data(self) -> None:
"""Fetch data from API endpoint.""" """Fetch data from API endpoint."""
return await self.hass.async_add_executor_job(self.nexia_home.update) return await self.nexia_home.update()

View File

@ -3,7 +3,10 @@ from nexia.thermostat import NexiaThermostat
from nexia.zone import NexiaThermostatZone from nexia.zone import NexiaThermostatZone
from homeassistant.const import ATTR_ATTRIBUTION from homeassistant.const import ATTR_ATTRIBUTION
from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -20,7 +23,9 @@ from .coordinator import NexiaDataUpdateCoordinator
class NexiaEntity(CoordinatorEntity): class NexiaEntity(CoordinatorEntity):
"""Base class for nexia entities.""" """Base class for nexia entities."""
def __init__(self, coordinator, name, unique_id): def __init__(
self, coordinator: NexiaDataUpdateCoordinator, name: str, unique_id: str
) -> None:
"""Initialize the entity.""" """Initialize the entity."""
super().__init__(coordinator) super().__init__(coordinator)
self._unique_id = unique_id self._unique_id = unique_id
@ -85,7 +90,7 @@ class NexiaThermostatEntity(NexiaEntity):
Update all the zones on the thermostat. Update all the zones on the thermostat.
""" """
dispatcher_send( async_dispatcher_send(
self.hass, f"{SIGNAL_THERMOSTAT_UPDATE}-{self._thermostat.thermostat_id}" self.hass, f"{SIGNAL_THERMOSTAT_UPDATE}-{self._thermostat.thermostat_id}"
) )
@ -132,4 +137,4 @@ class NexiaThermostatZoneEntity(NexiaThermostatEntity):
Update a single zone. Update a single zone.
""" """
dispatcher_send(self.hass, f"{SIGNAL_ZONE_UPDATE}-{self._zone.zone_id}") async_dispatcher_send(self.hass, f"{SIGNAL_ZONE_UPDATE}-{self._zone.zone_id}")

View File

@ -1,7 +1,7 @@
{ {
"domain": "nexia", "domain": "nexia",
"name": "Nexia/American Standard/Trane", "name": "Nexia/American Standard/Trane",
"requirements": ["nexia==0.9.13"], "requirements": ["nexia==1.0.0"],
"codeowners": ["@bdraco"], "codeowners": ["@bdraco"],
"documentation": "https://www.home-assistant.io/integrations/nexia", "documentation": "https://www.home-assistant.io/integrations/nexia",
"config_flow": true, "config_flow": true,

View File

@ -1,6 +1,8 @@
"""Support for Nexia Automations.""" """Support for Nexia Automations."""
from typing import Any from typing import Any
from nexia.automation import NexiaAutomation
from homeassistant.components.scene import Scene from homeassistant.components.scene import Scene
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -37,7 +39,9 @@ async def async_setup_entry(
class NexiaAutomationScene(NexiaEntity, Scene): class NexiaAutomationScene(NexiaEntity, Scene):
"""Provides Nexia automation support.""" """Provides Nexia automation support."""
def __init__(self, coordinator, automation): def __init__(
self, coordinator: NexiaDataUpdateCoordinator, automation: NexiaAutomation
) -> None:
"""Initialize the automation scene.""" """Initialize the automation scene."""
super().__init__( super().__init__(
coordinator, coordinator,
@ -60,7 +64,7 @@ class NexiaAutomationScene(NexiaEntity, Scene):
async def async_activate(self, **kwargs: Any) -> None: async def async_activate(self, **kwargs: Any) -> None:
"""Activate an automation scene.""" """Activate an automation scene."""
await self.hass.async_add_executor_job(self._automation.activate) await self._automation.activate()
async def refresh_callback(_): async def refresh_callback(_):
await self.coordinator.async_refresh() await self.coordinator.async_refresh()

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
from nexia.const import UNIT_CELSIUS from nexia.const import UNIT_CELSIUS
from nexia.thermostat import NexiaThermostat
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
SensorDeviceClass, SensorDeviceClass,
@ -32,7 +33,7 @@ async def async_setup_entry(
# Thermostat / System Sensors # Thermostat / System Sensors
for thermostat_id in nexia_home.get_thermostat_ids(): for thermostat_id in nexia_home.get_thermostat_ids():
thermostat = nexia_home.get_thermostat_by_id(thermostat_id) thermostat: NexiaThermostat = nexia_home.get_thermostat_by_id(thermostat_id)
entities.append( entities.append(
NexiaThermostatSensor( NexiaThermostatSensor(

View File

@ -56,12 +56,12 @@ class NexiaHoldSwitch(NexiaThermostatZoneEntity, SwitchEntity):
"""Return the icon for the switch.""" """Return the icon for the switch."""
return "mdi:timer-off" if self._zone.is_in_permanent_hold() else "mdi:timer" return "mdi:timer-off" if self._zone.is_in_permanent_hold() else "mdi:timer"
def turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Enable permanent hold.""" """Enable permanent hold."""
self._zone.call_permanent_hold() await self._zone.call_permanent_hold()
self._signal_zone_update() self._signal_zone_update()
def turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Disable permanent hold.""" """Disable permanent hold."""
self._zone.call_return_to_schedule() await self._zone.call_return_to_schedule()
self._signal_zone_update() self._signal_zone_update()

View File

@ -1077,7 +1077,7 @@ nettigo-air-monitor==1.2.4
neurio==0.3.1 neurio==0.3.1
# homeassistant.components.nexia # homeassistant.components.nexia
nexia==0.9.13 nexia==1.0.0
# homeassistant.components.nextcloud # homeassistant.components.nextcloud
nextcloudmonitor==1.1.0 nextcloudmonitor==1.1.0

View File

@ -742,7 +742,7 @@ netmap==0.7.0.2
nettigo-air-monitor==1.2.4 nettigo-air-monitor==1.2.4
# homeassistant.components.nexia # homeassistant.components.nexia
nexia==0.9.13 nexia==1.0.0
# homeassistant.components.discord # homeassistant.components.discord
nextcord==2.0.0a8 nextcord==2.0.0a8

View File

@ -1,9 +1,10 @@
"""Test the nexia config flow.""" """Test the nexia config flow."""
import asyncio
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
import aiohttp
from nexia.const import BRAND_ASAIR, BRAND_NEXIA from nexia.const import BRAND_ASAIR, BRAND_NEXIA
import pytest import pytest
from requests.exceptions import ConnectTimeout, HTTPError
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.nexia.const import CONF_BRAND, DOMAIN from homeassistant.components.nexia.const import CONF_BRAND, DOMAIN
@ -52,7 +53,10 @@ async def test_form_invalid_auth(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
with patch("homeassistant.components.nexia.config_flow.NexiaHome.login"): with patch("homeassistant.components.nexia.config_flow.NexiaHome.login",), patch(
"homeassistant.components.nexia.config_flow.NexiaHome.get_name",
return_value=None,
):
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{ {
@ -74,7 +78,7 @@ async def test_form_cannot_connect(hass):
with patch( with patch(
"homeassistant.components.nexia.config_flow.NexiaHome.login", "homeassistant.components.nexia.config_flow.NexiaHome.login",
side_effect=ConnectTimeout, side_effect=asyncio.TimeoutError,
): ):
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
@ -95,11 +99,11 @@ async def test_form_invalid_auth_http_401(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
response_mock = MagicMock()
type(response_mock).status_code = 401
with patch( with patch(
"homeassistant.components.nexia.config_flow.NexiaHome.login", "homeassistant.components.nexia.config_flow.NexiaHome.login",
side_effect=HTTPError(response=response_mock), side_effect=aiohttp.ClientResponseError(
status=401, request_info=MagicMock(), history=MagicMock()
),
): ):
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
@ -120,11 +124,11 @@ async def test_form_cannot_connect_not_found(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
response_mock = MagicMock()
type(response_mock).status_code = 404
with patch( with patch(
"homeassistant.components.nexia.config_flow.NexiaHome.login", "homeassistant.components.nexia.config_flow.NexiaHome.login",
side_effect=HTTPError(response=response_mock), side_effect=aiohttp.ClientResponseError(
status=404, request_info=MagicMock(), history=MagicMock()
),
): ):
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],

View File

@ -3,13 +3,13 @@ from unittest.mock import patch
import uuid import uuid
from nexia.home import NexiaHome from nexia.home import NexiaHome
import requests_mock
from homeassistant.components.nexia.const import DOMAIN from homeassistant.components.nexia.const import DOMAIN
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry, load_fixture from tests.common import MockConfigEntry, load_fixture
from tests.test_util.aiohttp import mock_aiohttp_client
async def async_init_integration( async def async_init_integration(
@ -21,17 +21,18 @@ async def async_init_integration(
house_fixture = "nexia/mobile_houses_123456.json" house_fixture = "nexia/mobile_houses_123456.json"
session_fixture = "nexia/session_123456.json" session_fixture = "nexia/session_123456.json"
sign_in_fixture = "nexia/sign_in.json" sign_in_fixture = "nexia/sign_in.json"
nexia = NexiaHome(auto_login=False) with mock_aiohttp_client() as mock_session, patch(
with requests_mock.mock() as m, patch(
"nexia.home.load_or_create_uuid", return_value=uuid.uuid4() "nexia.home.load_or_create_uuid", return_value=uuid.uuid4()
): ):
m.post(nexia.API_MOBILE_SESSION_URL, text=load_fixture(session_fixture)) nexia = NexiaHome(mock_session)
m.get( mock_session.post(
nexia.API_MOBILE_SESSION_URL, text=load_fixture(session_fixture)
)
mock_session.get(
nexia.API_MOBILE_HOUSES_URL.format(house_id=123456), nexia.API_MOBILE_HOUSES_URL.format(house_id=123456),
text=load_fixture(house_fixture), text=load_fixture(house_fixture),
) )
m.post( mock_session.post(
nexia.API_MOBILE_ACCOUNTS_SIGN_IN_URL, nexia.API_MOBILE_ACCOUNTS_SIGN_IN_URL,
text=load_fixture(sign_in_fixture), text=load_fixture(sign_in_fixture),
) )