From 0b53f73fe26af8ecf34ce91bd767426bacc9d6ba Mon Sep 17 00:00:00 2001 From: Milan Meulemans Date: Thu, 23 Sep 2021 22:37:37 +0200 Subject: [PATCH] Convert Nanoleaf integration to use Async library aionanoleaf (#56548) --- .coveragerc | 1 - homeassistant/components/nanoleaf/__init__.py | 15 +-- .../components/nanoleaf/config_flow.py | 43 +++---- homeassistant/components/nanoleaf/light.py | 77 +++++++------ .../components/nanoleaf/manifest.json | 2 +- homeassistant/components/nanoleaf/util.py | 7 -- requirements_all.txt | 6 +- requirements_test_all.txt | 6 +- tests/components/nanoleaf/test_config_flow.py | 106 ++++++++---------- 9 files changed, 127 insertions(+), 136 deletions(-) delete mode 100644 homeassistant/components/nanoleaf/util.py diff --git a/.coveragerc b/.coveragerc index 3525c99f960..44f66468821 100644 --- a/.coveragerc +++ b/.coveragerc @@ -695,7 +695,6 @@ omit = homeassistant/components/nad/media_player.py homeassistant/components/nanoleaf/__init__.py homeassistant/components/nanoleaf/light.py - homeassistant/components/nanoleaf/util.py homeassistant/components/neato/__init__.py homeassistant/components/neato/api.py homeassistant/components/neato/camera.py diff --git a/homeassistant/components/nanoleaf/__init__.py b/homeassistant/components/nanoleaf/__init__.py index be61bbc65a3..313af5b0ae3 100644 --- a/homeassistant/components/nanoleaf/__init__.py +++ b/homeassistant/components/nanoleaf/__init__.py @@ -1,21 +1,22 @@ """The Nanoleaf integration.""" -from pynanoleaf.pynanoleaf import InvalidToken, Nanoleaf, Unavailable +from aionanoleaf import InvalidToken, Nanoleaf, Unavailable from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_TOKEN from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady +from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import DEVICE, DOMAIN, NAME, SERIAL_NO -from .util import pynanoleaf_get_info async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Nanoleaf from a config entry.""" - nanoleaf = Nanoleaf(entry.data[CONF_HOST]) - nanoleaf.token = entry.data[CONF_TOKEN] + nanoleaf = Nanoleaf( + async_get_clientsession(hass), entry.data[CONF_HOST], entry.data[CONF_TOKEN] + ) try: - info = await hass.async_add_executor_job(pynanoleaf_get_info, nanoleaf) + await nanoleaf.get_info() except Unavailable as err: raise ConfigEntryNotReady from err except InvalidToken as err: @@ -23,8 +24,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { DEVICE: nanoleaf, - NAME: info["name"], - SERIAL_NO: info["serialNo"], + NAME: nanoleaf.name, + SERIAL_NO: nanoleaf.serial_no, } hass.async_create_task( diff --git a/homeassistant/components/nanoleaf/config_flow.py b/homeassistant/components/nanoleaf/config_flow.py index 9edfd23e6a9..d5fc023d3a1 100644 --- a/homeassistant/components/nanoleaf/config_flow.py +++ b/homeassistant/components/nanoleaf/config_flow.py @@ -5,17 +5,17 @@ import logging import os from typing import Any, Final, cast -from pynanoleaf import InvalidToken, Nanoleaf, NotAuthorizingNewTokens, Unavailable +from aionanoleaf import InvalidToken, Nanoleaf, Unauthorized, Unavailable import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_HOST, CONF_TOKEN from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.typing import DiscoveryInfoType from homeassistant.util.json import load_json, save_json from .const import DOMAIN -from .util import pynanoleaf_get_info _LOGGER = logging.getLogger(__name__) @@ -53,9 +53,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): step_id="user", data_schema=USER_SCHEMA, last_step=False ) self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]}) - self.nanoleaf = Nanoleaf(user_input[CONF_HOST]) + self.nanoleaf = Nanoleaf( + async_get_clientsession(self.hass), user_input[CONF_HOST] + ) try: - await self.hass.async_add_executor_job(self.nanoleaf.authorize) + await self.nanoleaf.authorize() except Unavailable: return self.async_show_form( step_id="user", @@ -63,7 +65,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors={"base": "cannot_connect"}, last_step=False, ) - except NotAuthorizingNewTokens: + except Unauthorized: pass except Exception: # pylint: disable=broad-except _LOGGER.exception("Unknown error connecting to Nanoleaf") @@ -81,7 +83,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): config_entries.ConfigEntry, self.hass.config_entries.async_get_entry(self.context["entry_id"]), ) - self.nanoleaf = Nanoleaf(data[CONF_HOST]) + self.nanoleaf = Nanoleaf(async_get_clientsession(self.hass), data[CONF_HOST]) self.context["title_placeholders"] = {"name": self.reauth_entry.title} return await self.async_step_link() @@ -106,7 +108,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): name = discovery_info["name"].replace(f".{discovery_info['type']}", "") await self.async_set_unique_id(name) self._abort_if_unique_id_configured({CONF_HOST: host}) - self.nanoleaf = Nanoleaf(host) # Import from discovery integration self.device_id = discovery_info["properties"]["id"] @@ -116,16 +117,19 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): load_json, self.hass.config.path(CONFIG_FILE) ), ) - self.nanoleaf.token = self.discovery_conf.get(self.device_id, {}).get( + auth_token: str | None = self.discovery_conf.get(self.device_id, {}).get( "token", # >= 2021.4 self.discovery_conf.get(host, {}).get("token"), # < 2021.4 ) - if self.nanoleaf.token is not None: + if auth_token is not None: + self.nanoleaf = Nanoleaf( + async_get_clientsession(self.hass), host, auth_token + ) _LOGGER.warning( "Importing Nanoleaf %s from the discovery integration", name ) return await self.async_setup_finish(discovery_integration_import=True) - + self.nanoleaf = Nanoleaf(async_get_clientsession(self.hass), host) self.context["title_placeholders"] = {"name": name} return await self.async_step_link() @@ -137,8 +141,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_show_form(step_id="link") try: - await self.hass.async_add_executor_job(self.nanoleaf.authorize) - except NotAuthorizingNewTokens: + await self.nanoleaf.authorize() + except Unauthorized: return self.async_show_form( step_id="link", errors={"base": "not_allowing_new_tokens"} ) @@ -153,7 +157,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self.reauth_entry, data={ **self.reauth_entry.data, - CONF_TOKEN: self.nanoleaf.token, + CONF_TOKEN: self.nanoleaf.auth_token, }, ) await self.hass.config_entries.async_reload(self.reauth_entry.entry_id) @@ -167,8 +171,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): _LOGGER.debug( "Importing Nanoleaf on %s from your configuration.yaml", config[CONF_HOST] ) - self.nanoleaf = Nanoleaf(config[CONF_HOST]) - self.nanoleaf.token = config[CONF_TOKEN] + self.nanoleaf = Nanoleaf( + async_get_clientsession(self.hass), config[CONF_HOST], config[CONF_TOKEN] + ) return await self.async_setup_finish() async def async_setup_finish( @@ -176,9 +181,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) -> FlowResult: """Finish Nanoleaf config flow.""" try: - info = await self.hass.async_add_executor_job( - pynanoleaf_get_info, self.nanoleaf - ) + await self.nanoleaf.get_info() except Unavailable: return self.async_abort(reason="cannot_connect") except InvalidToken: @@ -188,7 +191,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): "Unknown error connecting with Nanoleaf at %s", self.nanoleaf.host ) return self.async_abort(reason="unknown") - name = info["name"] + name = self.nanoleaf.name await self.async_set_unique_id(name) self._abort_if_unique_id_configured({CONF_HOST: self.nanoleaf.host}) @@ -215,6 +218,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): title=name, data={ CONF_HOST: self.nanoleaf.host, - CONF_TOKEN: self.nanoleaf.token, + CONF_TOKEN: self.nanoleaf.auth_token, }, ) diff --git a/homeassistant/components/nanoleaf/light.py b/homeassistant/components/nanoleaf/light.py index b50edf82179..0a80a3f7d60 100644 --- a/homeassistant/components/nanoleaf/light.py +++ b/homeassistant/components/nanoleaf/light.py @@ -3,7 +3,8 @@ from __future__ import annotations import logging -from pynanoleaf import Unavailable +from aiohttp import ServerDisconnectedError +from aionanoleaf import Unavailable import voluptuous as vol from homeassistant.components.light import ( @@ -153,7 +154,7 @@ class NanoleafLight(LightEntity): @property def is_on(self): """Return true if light is on.""" - return self._state + return self._light.is_on @property def hs_color(self): @@ -165,7 +166,7 @@ class NanoleafLight(LightEntity): """Flag supported features.""" return SUPPORT_NANOLEAF - def turn_on(self, **kwargs): + async def async_turn_on(self, **kwargs): """Instruct the light to turn on.""" brightness = kwargs.get(ATTR_BRIGHTNESS) hs_color = kwargs.get(ATTR_HS_COLOR) @@ -175,57 +176,61 @@ class NanoleafLight(LightEntity): if hs_color: hue, saturation = hs_color - self._light.hue = int(hue) - self._light.saturation = int(saturation) + await self._light.set_hue(int(hue)) + await self._light.set_saturation(int(saturation)) if color_temp_mired: - self._light.color_temperature = mired_to_kelvin(color_temp_mired) - + await self._light.set_color_temperature(mired_to_kelvin(color_temp_mired)) if transition: if brightness: # tune to the required brightness in n seconds - self._light.brightness_transition( - int(brightness / 2.55), int(transition) + await self._light.set_brightness( + int(brightness / 2.55), transition=int(kwargs[ATTR_TRANSITION]) ) else: # If brightness is not specified, assume full brightness - self._light.brightness_transition(100, int(transition)) + await self._light.set_brightness( + 100, transition=int(kwargs[ATTR_TRANSITION]) + ) else: # If no transition is occurring, turn on the light - self._light.on = True + await self._light.turn_on() if brightness: - self._light.brightness = int(brightness / 2.55) - + await self._light.set_brightness(int(brightness / 2.55)) if effect: if effect not in self._effects_list: raise ValueError( f"Attempting to apply effect not in the effect list: '{effect}'" ) - self._light.effect = effect + await self._light.set_effect(effect) - def turn_off(self, **kwargs): + async def async_turn_off(self, **kwargs): """Instruct the light to turn off.""" transition = kwargs.get(ATTR_TRANSITION) if transition: - self._light.brightness_transition(0, int(transition)) + await self._light.set_brightness(0, transition=int(transition)) else: - self._light.on = False + await self._light.turn_off() - def update(self): + async def async_update(self) -> None: """Fetch new state data for this light.""" try: - self._available = self._light.available - self._brightness = self._light.brightness - self._effects_list = self._light.effects - # Nanoleaf api returns non-existent effect named "*Solid*" when light set to solid color. - # This causes various issues with scening (see https://github.com/home-assistant/core/issues/36359). - # Until fixed at the library level, we should ensure the effect exists before saving to light properties - self._effect = ( - self._light.effect if self._light.effect in self._effects_list else None - ) - if self._effect is None: - self._color_temp = self._light.color_temperature - self._hs_color = self._light.hue, self._light.saturation - else: - self._color_temp = None - self._hs_color = None - self._state = self._light.on - except Unavailable as err: - _LOGGER.error("Could not update status for %s (%s)", self.name, err) + await self._light.get_info() + except ServerDisconnectedError: + # Retry the request once if the device disconnected + await self._light.get_info() + except Unavailable: self._available = False + return + self._available = True + self._brightness = self._light.brightness + self._effects_list = self._light.effects_list + # Nanoleaf api returns non-existent effect named "*Solid*" when light set to solid color. + # This causes various issues with scening (see https://github.com/home-assistant/core/issues/36359). + # Until fixed at the library level, we should ensure the effect exists before saving to light properties + self._effect = ( + self._light.effect if self._light.effect in self._effects_list else None + ) + if self._effect is None: + self._color_temp = self._light.color_temperature + self._hs_color = self._light.hue, self._light.saturation + else: + self._color_temp = None + self._hs_color = None + self._state = self._light.is_on diff --git a/homeassistant/components/nanoleaf/manifest.json b/homeassistant/components/nanoleaf/manifest.json index 42a9f512d3d..31576fd73a7 100644 --- a/homeassistant/components/nanoleaf/manifest.json +++ b/homeassistant/components/nanoleaf/manifest.json @@ -3,7 +3,7 @@ "name": "Nanoleaf", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/nanoleaf", - "requirements": ["pynanoleaf==0.1.0"], + "requirements": ["aionanoleaf==0.0.1"], "zeroconf": ["_nanoleafms._tcp.local.", "_nanoleafapi._tcp.local."], "homekit" : { "models": [ diff --git a/homeassistant/components/nanoleaf/util.py b/homeassistant/components/nanoleaf/util.py deleted file mode 100644 index 0031622e90b..00000000000 --- a/homeassistant/components/nanoleaf/util.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Nanoleaf integration util.""" -from pynanoleaf.pynanoleaf import Nanoleaf - - -def pynanoleaf_get_info(nanoleaf_light: Nanoleaf) -> dict: - """Get Nanoleaf light info.""" - return nanoleaf_light.info diff --git a/requirements_all.txt b/requirements_all.txt index be626ba7ba0..d9ba931e894 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -218,6 +218,9 @@ aiomodernforms==0.1.8 # homeassistant.components.yamaha_musiccast aiomusiccast==0.9.2 +# homeassistant.components.nanoleaf +aionanoleaf==0.0.1 + # homeassistant.components.keyboard_remote aionotify==0.2.0 @@ -1649,9 +1652,6 @@ pymyq==3.1.4 # homeassistant.components.mysensors pymysensors==0.21.0 -# homeassistant.components.nanoleaf -pynanoleaf==0.1.0 - # homeassistant.components.nello pynello==2.0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 32badcb7b66..2352fed82e0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -145,6 +145,9 @@ aiomodernforms==0.1.8 # homeassistant.components.yamaha_musiccast aiomusiccast==0.9.2 +# homeassistant.components.nanoleaf +aionanoleaf==0.0.1 + # homeassistant.components.notion aionotion==3.0.2 @@ -968,9 +971,6 @@ pymyq==3.1.4 # homeassistant.components.mysensors pymysensors==0.21.0 -# homeassistant.components.nanoleaf -pynanoleaf==0.1.0 - # homeassistant.components.netgear pynetgear==0.7.0 diff --git a/tests/components/nanoleaf/test_config_flow.py b/tests/components/nanoleaf/test_config_flow.py index 93db43e40c9..8f62830b219 100644 --- a/tests/components/nanoleaf/test_config_flow.py +++ b/tests/components/nanoleaf/test_config_flow.py @@ -1,10 +1,9 @@ """Test the Nanoleaf config flow.""" from __future__ import annotations -from unittest.mock import MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, patch -from pynanoleaf import InvalidToken, NotAuthorizingNewTokens, Unavailable -from pynanoleaf.pynanoleaf import NanoleafError +from aionanoleaf import InvalidToken, NanoleafException, Unauthorized, Unavailable import pytest from homeassistant import config_entries @@ -23,6 +22,21 @@ TEST_DEVICE_ID = "5E:2E:EA:XX:XX:XX" TEST_OTHER_DEVICE_ID = "5E:2E:EA:YY:YY:YY" +def _mock_nanoleaf( + host: str = TEST_HOST, + auth_token: str = TEST_TOKEN, + authorize_error: Exception | None = None, + get_info_error: Exception | None = None, +): + nanoleaf = MagicMock() + nanoleaf.name = TEST_NAME + nanoleaf.host = host + nanoleaf.auth_token = auth_token + nanoleaf.authorize = AsyncMock(side_effect=authorize_error) + nanoleaf.get_info = AsyncMock(side_effect=get_info_error) + return nanoleaf + + async def test_user_unavailable_user_step_link_step(hass: HomeAssistant) -> None: """Test we handle Unavailable in user and link step.""" result = await hass.config_entries.flow.async_init( @@ -30,7 +44,7 @@ async def test_user_unavailable_user_step_link_step(hass: HomeAssistant) -> None ) with patch( "homeassistant.components.nanoleaf.config_flow.Nanoleaf.authorize", - side_effect=Unavailable("message"), + side_effect=Unavailable, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -58,7 +72,7 @@ async def test_user_unavailable_user_step_link_step(hass: HomeAssistant) -> None with patch( "homeassistant.components.nanoleaf.config_flow.Nanoleaf.authorize", - side_effect=Unavailable("message"), + side_effect=Unavailable, ): result3 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -71,8 +85,8 @@ async def test_user_unavailable_user_step_link_step(hass: HomeAssistant) -> None @pytest.mark.parametrize( "error, reason", [ - (Unavailable("message"), "cannot_connect"), - (InvalidToken("message"), "invalid_token"), + (Unavailable, "cannot_connect"), + (InvalidToken, "invalid_token"), (Exception, "unknown"), ], ) @@ -85,7 +99,6 @@ async def test_user_error_setup_finish( ) with patch( "homeassistant.components.nanoleaf.config_flow.Nanoleaf.authorize", - return_value=None, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -98,9 +111,8 @@ async def test_user_error_setup_finish( with patch( "homeassistant.components.nanoleaf.config_flow.Nanoleaf.authorize", - return_value=None, ), patch( - "homeassistant.components.nanoleaf.config_flow.pynanoleaf_get_info", + "homeassistant.components.nanoleaf.config_flow.Nanoleaf.get_info", side_effect=error, ): result3 = await hass.config_entries.flow.async_configure( @@ -117,19 +129,10 @@ async def test_user_not_authorizing_new_tokens_user_step_link_step( """Test we handle NotAuthorizingNewTokens in user step and link step.""" with patch( "homeassistant.components.nanoleaf.config_flow.Nanoleaf", + return_value=_mock_nanoleaf(authorize_error=Unauthorized()), ) as mock_nanoleaf, patch( - "homeassistant.components.nanoleaf.config_flow.pynanoleaf_get_info", - return_value={"name": TEST_NAME}, - ), patch( "homeassistant.components.nanoleaf.async_setup_entry", return_value=True ) as mock_setup_entry: - nanoleaf = mock_nanoleaf.return_value - nanoleaf.authorize.side_effect = NotAuthorizingNewTokens( - "Not authorizing new tokens" - ) - nanoleaf.host = TEST_HOST - nanoleaf.token = TEST_TOKEN - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -160,8 +163,7 @@ async def test_user_not_authorizing_new_tokens_user_step_link_step( assert result4["errors"] == {"base": "not_allowing_new_tokens"} assert result4["step_id"] == "link" - nanoleaf.authorize.side_effect = None - nanoleaf.authorize.return_value = None + mock_nanoleaf.return_value.authorize.side_effect = None result5 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -183,8 +185,8 @@ async def test_user_exception_user_step(hass: HomeAssistant) -> None: DOMAIN, context={"source": config_entries.SOURCE_USER} ) with patch( - "homeassistant.components.nanoleaf.config_flow.Nanoleaf.authorize", - side_effect=Exception, + "homeassistant.components.nanoleaf.config_flow.Nanoleaf", + return_value=_mock_nanoleaf(authorize_error=Exception()), ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -198,36 +200,29 @@ async def test_user_exception_user_step(hass: HomeAssistant) -> None: assert not result2["last_step"] with patch( - "homeassistant.components.nanoleaf.config_flow.Nanoleaf.authorize", - return_value=None, - ): + "homeassistant.components.nanoleaf.config_flow.Nanoleaf", + return_value=_mock_nanoleaf(), + ) as mock_nanoleaf: result3 = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_HOST: TEST_HOST, }, ) - assert result3["step_id"] == "link" + assert result3["step_id"] == "link" + + mock_nanoleaf.return_value.authorize.side_effect = Exception() - with patch( - "homeassistant.components.nanoleaf.config_flow.Nanoleaf.authorize", - side_effect=Exception, - ): result4 = await hass.config_entries.flow.async_configure( result["flow_id"], {}, ) - assert result4["type"] == "form" - assert result4["step_id"] == "link" - assert result4["errors"] == {"base": "unknown"} + assert result4["type"] == "form" + assert result4["step_id"] == "link" + assert result4["errors"] == {"base": "unknown"} - with patch( - "homeassistant.components.nanoleaf.config_flow.Nanoleaf.authorize", - return_value=None, - ), patch( - "homeassistant.components.nanoleaf.config_flow.pynanoleaf_get_info", - side_effect=Exception, - ): + mock_nanoleaf.return_value.authorize.side_effect = None + mock_nanoleaf.return_value.get_info.side_effect = Exception() result5 = await hass.config_entries.flow.async_configure( result["flow_id"], {}, @@ -249,8 +244,7 @@ async def test_discovery_link_unavailable( ) -> None: """Test discovery and abort if device is unavailable.""" with patch( - "homeassistant.components.nanoleaf.config_flow.pynanoleaf_get_info", - return_value={"name": TEST_NAME}, + "homeassistant.components.nanoleaf.config_flow.Nanoleaf.get_info", ), patch( "homeassistant.components.nanoleaf.config_flow.load_json", return_value={}, @@ -278,7 +272,7 @@ async def test_discovery_link_unavailable( with patch( "homeassistant.components.nanoleaf.config_flow.Nanoleaf.authorize", - side_effect=Unavailable("message"), + side_effect=Unavailable, ): result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == "abort" @@ -287,10 +281,6 @@ async def test_discovery_link_unavailable( async def test_reauth(hass: HomeAssistant) -> None: """Test Nanoleaf reauth flow.""" - nanoleaf = MagicMock() - nanoleaf.host = TEST_HOST - nanoleaf.token = TEST_TOKEN - entry = MockConfigEntry( domain=DOMAIN, unique_id=TEST_NAME, @@ -300,7 +290,7 @@ async def test_reauth(hass: HomeAssistant) -> None: with patch( "homeassistant.components.nanoleaf.config_flow.Nanoleaf", - return_value=nanoleaf, + return_value=_mock_nanoleaf(), ), patch( "homeassistant.components.nanoleaf.async_setup_entry", return_value=True, @@ -331,8 +321,8 @@ async def test_reauth(hass: HomeAssistant) -> None: async def test_import_config(hass: HomeAssistant) -> None: """Test configuration import.""" with patch( - "homeassistant.components.nanoleaf.config_flow.pynanoleaf_get_info", - return_value={"name": TEST_NAME}, + "homeassistant.components.nanoleaf.config_flow.Nanoleaf", + return_value=_mock_nanoleaf(TEST_HOST, TEST_TOKEN), ), patch( "homeassistant.components.nanoleaf.async_setup_entry", return_value=True, @@ -355,17 +345,17 @@ async def test_import_config(hass: HomeAssistant) -> None: @pytest.mark.parametrize( "error, reason", [ - (Unavailable("message"), "cannot_connect"), - (InvalidToken("message"), "invalid_token"), + (Unavailable, "cannot_connect"), + (InvalidToken, "invalid_token"), (Exception, "unknown"), ], ) async def test_import_config_error( - hass: HomeAssistant, error: NanoleafError, reason: str + hass: HomeAssistant, error: NanoleafException, reason: str ) -> None: """Test configuration import with errors in setup_finish.""" with patch( - "homeassistant.components.nanoleaf.config_flow.pynanoleaf_get_info", + "homeassistant.components.nanoleaf.config_flow.Nanoleaf.get_info", side_effect=error, ): result = await hass.config_entries.flow.async_init( @@ -432,8 +422,8 @@ async def test_import_discovery_integration( "homeassistant.components.nanoleaf.config_flow.load_json", return_value=dict(nanoleaf_conf_file), ), patch( - "homeassistant.components.nanoleaf.config_flow.pynanoleaf_get_info", - return_value={"name": TEST_NAME}, + "homeassistant.components.nanoleaf.config_flow.Nanoleaf", + return_value=_mock_nanoleaf(TEST_HOST, TEST_TOKEN), ), patch( "homeassistant.components.nanoleaf.config_flow.save_json", return_value=None,