diff --git a/homeassistant/components/homematicip_cloud/__init__.py b/homeassistant/components/homematicip_cloud/__init__.py index f3e1fc9fbec..46bf300753f 100644 --- a/homeassistant/components/homematicip_cloud/__init__.py +++ b/homeassistant/components/homematicip_cloud/__init__.py @@ -17,7 +17,6 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.config_validation import comp_entity_ids from homeassistant.helpers.typing import ConfigType, HomeAssistantType -from .config_flow import configured_haps from .const import ( CONF_ACCESSPOINT, CONF_AUTHTOKEN, @@ -130,7 +129,10 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool: accesspoints = config.get(DOMAIN, []) for conf in accesspoints: - if conf[CONF_ACCESSPOINT] not in configured_haps(hass): + if conf[CONF_ACCESSPOINT] not in set( + entry.data[HMIPC_HAPID] + for entry in hass.config_entries.async_entries(DOMAIN) + ): hass.async_add_job( hass.config_entries.flow.async_init( DOMAIN, @@ -274,7 +276,7 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool: anonymize = service.data[ATTR_ANONYMIZE] for hap in hass.data[DOMAIN].values(): - hap_sgtin = hap.config_entry.title + hap_sgtin = hap.config_entry.unique_id if anonymize: hap_sgtin = hap_sgtin[-4:] @@ -331,9 +333,17 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool: """Set up an access point from a config entry.""" + + # 0.104 introduced config entry unique id, this makes upgrading possible + if entry.unique_id is None: + new_data = dict(entry.data) + + hass.config_entries.async_update_entry( + entry, unique_id=new_data[HMIPC_HAPID], data=new_data + ) + hap = HomematicipHAP(hass, entry) - hapid = entry.data[HMIPC_HAPID].replace("-", "").upper() - hass.data[DOMAIN][hapid] = hap + hass.data[DOMAIN][entry.unique_id] = hap if not await hap.async_setup(): return False @@ -356,5 +366,5 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool: """Unload a config entry.""" - hap = hass.data[DOMAIN].pop(entry.data[HMIPC_HAPID]) + hap = hass.data[DOMAIN].pop(entry.unique_id) return await hap.async_reset() diff --git a/homeassistant/components/homematicip_cloud/alarm_control_panel.py b/homeassistant/components/homematicip_cloud/alarm_control_panel.py index dea84b90bd6..c2f4d833a35 100644 --- a/homeassistant/components/homematicip_cloud/alarm_control_panel.py +++ b/homeassistant/components/homematicip_cloud/alarm_control_panel.py @@ -18,7 +18,7 @@ from homeassistant.const import ( ) from homeassistant.helpers.typing import HomeAssistantType -from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID +from . import DOMAIN as HMIPC_DOMAIN from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -30,7 +30,7 @@ async def async_setup_entry( hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP alrm control panel from a config entry.""" - hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] + hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id] async_add_entities([HomematicipAlarmControlPanel(hap)]) diff --git a/homeassistant/components/homematicip_cloud/binary_sensor.py b/homeassistant/components/homematicip_cloud/binary_sensor.py index 5a679626679..f16dfc986f0 100644 --- a/homeassistant/components/homematicip_cloud/binary_sensor.py +++ b/homeassistant/components/homematicip_cloud/binary_sensor.py @@ -41,7 +41,7 @@ from homeassistant.components.binary_sensor import ( from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType -from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericDevice from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -79,7 +79,7 @@ async def async_setup_entry( hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP Cloud binary sensor from a config entry.""" - hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] + hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id] entities = [] for device in hap.home.devices: if isinstance(device, AsyncAccelerationSensor): diff --git a/homeassistant/components/homematicip_cloud/climate.py b/homeassistant/components/homematicip_cloud/climate.py index d932d5c3f0a..c5fb978e690 100644 --- a/homeassistant/components/homematicip_cloud/climate.py +++ b/homeassistant/components/homematicip_cloud/climate.py @@ -27,7 +27,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from homeassistant.helpers.typing import HomeAssistantType -from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericDevice from .hap import HomematicipHAP HEATING_PROFILES = {"PROFILE_1": 0, "PROFILE_2": 1, "PROFILE_3": 2} @@ -47,7 +47,7 @@ async def async_setup_entry( hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP climate from a config entry.""" - hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] + hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id] entities = [] for device in hap.home.groups: if isinstance(device, AsyncHeatingGroup): diff --git a/homeassistant/components/homematicip_cloud/config_flow.py b/homeassistant/components/homematicip_cloud/config_flow.py index 8d85dfda328..547289f871a 100644 --- a/homeassistant/components/homematicip_cloud/config_flow.py +++ b/homeassistant/components/homematicip_cloud/config_flow.py @@ -1,11 +1,9 @@ """Config flow to configure the HomematicIP Cloud component.""" -from typing import Any, Dict, Set +from typing import Any, Dict import voluptuous as vol from homeassistant import config_entries -from homeassistant.core import callback -from homeassistant.helpers.typing import HomeAssistantType from .const import ( _LOGGER, @@ -18,15 +16,6 @@ from .const import ( from .hap import HomematicipAuth -@callback -def configured_haps(hass: HomeAssistantType) -> Set[str]: - """Return a set of the configured access points.""" - return set( - entry.data[HMIPC_HAPID] - for entry in hass.config_entries.async_entries(HMIPC_DOMAIN) - ) - - @config_entries.HANDLERS.register(HMIPC_DOMAIN) class HomematicipCloudFlowHandler(config_entries.ConfigFlow): """Config flow for the HomematicIP Cloud component.""" @@ -48,8 +37,9 @@ class HomematicipCloudFlowHandler(config_entries.ConfigFlow): if user_input is not None: user_input[HMIPC_HAPID] = user_input[HMIPC_HAPID].replace("-", "").upper() - if user_input[HMIPC_HAPID] in configured_haps(self.hass): - return self.async_abort(reason="already_configured") + + await self.async_set_unique_id(user_input[HMIPC_HAPID]) + self._abort_if_unique_id_configured() self.auth = HomematicipAuth(self.hass, user_input) connected = await self.auth.async_setup() @@ -93,16 +83,14 @@ class HomematicipCloudFlowHandler(config_entries.ConfigFlow): async def async_step_import(self, import_info) -> Dict[str, Any]: """Import a new access point as a config entry.""" - hapid = import_info[HMIPC_HAPID] + hapid = import_info[HMIPC_HAPID].replace("-", "").upper() authtoken = import_info[HMIPC_AUTHTOKEN] name = import_info[HMIPC_NAME] - hapid = hapid.replace("-", "").upper() - if hapid in configured_haps(self.hass): - return self.async_abort(reason="already_configured") + await self.async_set_unique_id(hapid) + self._abort_if_unique_id_configured() _LOGGER.info("Imported authentication for %s", hapid) - return self.async_create_entry( title=hapid, data={HMIPC_AUTHTOKEN: authtoken, HMIPC_HAPID: hapid, HMIPC_NAME: name}, diff --git a/homeassistant/components/homematicip_cloud/cover.py b/homeassistant/components/homematicip_cloud/cover.py index 2e6d8b546bc..0d2131f9cb3 100644 --- a/homeassistant/components/homematicip_cloud/cover.py +++ b/homeassistant/components/homematicip_cloud/cover.py @@ -17,7 +17,7 @@ from homeassistant.components.cover import ( from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType -from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericDevice _LOGGER = logging.getLogger(__name__) @@ -31,7 +31,7 @@ async def async_setup_entry( hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP cover from a config entry.""" - hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] + hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id] entities = [] for device in hap.home.devices: if isinstance(device, AsyncFullFlushBlind): diff --git a/homeassistant/components/homematicip_cloud/hap.py b/homeassistant/components/homematicip_cloud/hap.py index 63bdf3166eb..3c97cc1af9f 100644 --- a/homeassistant/components/homematicip_cloud/hap.py +++ b/homeassistant/components/homematicip_cloud/hap.py @@ -95,8 +95,7 @@ class HomematicipHAP: raise ConfigEntryNotReady _LOGGER.info( - "Connected to HomematicIP with HAP %s", - self.config_entry.data.get(HMIPC_HAPID), + "Connected to HomematicIP with HAP %s", self.config_entry.unique_id ) for component in COMPONENTS: @@ -193,7 +192,7 @@ class HomematicipHAP: _LOGGER.error( "Error connecting to HomematicIP with HAP %s. " "Retrying in %d seconds", - self.config_entry.data.get(HMIPC_HAPID), + self.config_entry.unique_id, retry_delay, ) diff --git a/homeassistant/components/homematicip_cloud/light.py b/homeassistant/components/homematicip_cloud/light.py index f35118d0d84..4e081f4d8fa 100644 --- a/homeassistant/components/homematicip_cloud/light.py +++ b/homeassistant/components/homematicip_cloud/light.py @@ -25,7 +25,7 @@ from homeassistant.components.light import ( from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType -from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericDevice from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -38,7 +38,7 @@ async def async_setup_entry( hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP Cloud lights from a config entry.""" - hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] + hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id] entities = [] for device in hap.home.devices: if isinstance(device, AsyncBrandSwitchMeasuring): diff --git a/homeassistant/components/homematicip_cloud/manifest.json b/homeassistant/components/homematicip_cloud/manifest.json index e920a847292..d823621f6cb 100644 --- a/homeassistant/components/homematicip_cloud/manifest.json +++ b/homeassistant/components/homematicip_cloud/manifest.json @@ -5,5 +5,6 @@ "documentation": "https://www.home-assistant.io/integrations/homematicip_cloud", "requirements": ["homematicip==0.10.15"], "dependencies": [], - "codeowners": ["@SukramJ"] + "codeowners": ["@SukramJ"], + "quality_scale": "platinum" } diff --git a/homeassistant/components/homematicip_cloud/sensor.py b/homeassistant/components/homematicip_cloud/sensor.py index 50a1c4ae34a..ebbee1abc44 100644 --- a/homeassistant/components/homematicip_cloud/sensor.py +++ b/homeassistant/components/homematicip_cloud/sensor.py @@ -34,7 +34,7 @@ from homeassistant.const import ( ) from homeassistant.helpers.typing import HomeAssistantType -from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericDevice from .device import ATTR_IS_GROUP, ATTR_MODEL_TYPE from .hap import HomematicipHAP @@ -60,7 +60,7 @@ async def async_setup_entry( hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP Cloud sensors from a config entry.""" - hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] + hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id] entities = [HomematicipAccesspointStatus(hap)] for device in hap.home.devices: if isinstance(device, (AsyncHeatingThermostat, AsyncHeatingThermostatCompact)): diff --git a/homeassistant/components/homematicip_cloud/switch.py b/homeassistant/components/homematicip_cloud/switch.py index bce85592891..45adf54df2b 100644 --- a/homeassistant/components/homematicip_cloud/switch.py +++ b/homeassistant/components/homematicip_cloud/switch.py @@ -19,7 +19,7 @@ from homeassistant.components.switch import SwitchDevice from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import HomeAssistantType -from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericDevice from .device import ATTR_GROUP_MEMBER_UNREACHABLE from .hap import HomematicipHAP @@ -30,7 +30,7 @@ async def async_setup_entry( hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP switch from a config entry.""" - hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] + hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id] entities = [] for device in hap.home.devices: if isinstance(device, AsyncBrandSwitchMeasuring): diff --git a/homeassistant/components/homematicip_cloud/weather.py b/homeassistant/components/homematicip_cloud/weather.py index f6ea95ab117..04f3b06cbb0 100644 --- a/homeassistant/components/homematicip_cloud/weather.py +++ b/homeassistant/components/homematicip_cloud/weather.py @@ -13,7 +13,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.typing import HomeAssistantType -from . import DOMAIN as HMIPC_DOMAIN, HMIPC_HAPID, HomematicipGenericDevice +from . import DOMAIN as HMIPC_DOMAIN, HomematicipGenericDevice from .hap import HomematicipHAP _LOGGER = logging.getLogger(__name__) @@ -41,7 +41,7 @@ async def async_setup_entry( hass: HomeAssistantType, config_entry: ConfigEntry, async_add_entities ) -> None: """Set up the HomematicIP weather sensor from a config entry.""" - hap = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]] + hap = hass.data[HMIPC_DOMAIN][config_entry.unique_id] entities = [] for device in hap.home.devices: if isinstance(device, AsyncWeatherSensorPro): diff --git a/tests/components/homematicip_cloud/conftest.py b/tests/components/homematicip_cloud/conftest.py index fa19f573c7c..a37583cc139 100644 --- a/tests/components/homematicip_cloud/conftest.py +++ b/tests/components/homematicip_cloud/conftest.py @@ -49,6 +49,7 @@ def hmip_config_entry_fixture() -> config_entries.ConfigEntry: version=1, domain=HMIPC_DOMAIN, title=HAPID, + unique_id=HAPID, data=entry_data, source="import", connection_class=config_entries.CONN_CLASS_CLOUD_PUSH, diff --git a/tests/components/homematicip_cloud/test_config_flow.py b/tests/components/homematicip_cloud/test_config_flow.py index afaf71c67b5..bf1d628d9c2 100644 --- a/tests/components/homematicip_cloud/test_config_flow.py +++ b/tests/components/homematicip_cloud/test_config_flow.py @@ -1,34 +1,58 @@ """Tests for HomematicIP Cloud config flow.""" -from unittest.mock import patch +from asynctest import patch -from homeassistant.components.homematicip_cloud import config_flow, const, hap as hmipc +from homeassistant.components.homematicip_cloud import const -from tests.common import MockConfigEntry, mock_coro +from tests.common import MockConfigEntry async def test_flow_works(hass): - """Test config flow works.""" + """Test config flow.""" config = { const.HMIPC_HAPID: "ABC123", const.HMIPC_PIN: "123", const.HMIPC_NAME: "hmip", } - flow = config_flow.HomematicipCloudFlowHandler() - flow.hass = hass - hap = hmipc.HomematicipAuth(hass, config) - with patch.object(hap, "get_auth", return_value=mock_coro()), patch.object( - hmipc.HomematicipAuth, "async_checkbutton", return_value=mock_coro(True) - ), patch.object( - hmipc.HomematicipAuth, "async_setup", return_value=mock_coro(True) - ), patch.object( - hmipc.HomematicipAuth, "async_register", return_value=mock_coro(True) + with patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_checkbutton", + return_value=False, ): - hap.authtoken = "ABC" - result = await flow.async_step_init(user_input=config) + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": "user"}, data=config + ) - assert hap.authtoken == "ABC" - assert result["type"] == "create_entry" + assert result["type"] == "form" + assert result["step_id"] == "link" + assert result["errors"] == {"base": "press_the_button"} + + flow = next( + ( + flow + for flow in hass.config_entries.flow.async_progress() + if flow["flow_id"] == result["flow_id"] + ) + ) + assert flow["context"]["unique_id"] == "ABC123" + + with patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_checkbutton", + return_value=True, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_setup", + return_value=True, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_register", + return_value=True, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={} + ) + + assert result["type"] == "create_entry" + assert result["title"] == "ABC123" + assert result["data"] == {"hapid": "ABC123", "authtoken": True, "name": "hmip"} + assert result["result"].unique_id == "ABC123" async def test_flow_init_connection_error(hass): @@ -38,14 +62,17 @@ async def test_flow_init_connection_error(hass): const.HMIPC_PIN: "123", const.HMIPC_NAME: "hmip", } - flow = config_flow.HomematicipCloudFlowHandler() - flow.hass = hass - with patch.object( - hmipc.HomematicipAuth, "async_setup", return_value=mock_coro(False) + with patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_setup", + return_value=False, ): - result = await flow.async_step_init(user_input=config) - assert result["type"] == "form" + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": "user"}, data=config + ) + + assert result["type"] == "form" + assert result["step_id"] == "init" async def test_flow_link_connection_error(hass): @@ -55,18 +82,23 @@ async def test_flow_link_connection_error(hass): const.HMIPC_PIN: "123", const.HMIPC_NAME: "hmip", } - flow = config_flow.HomematicipCloudFlowHandler() - flow.hass = hass - with patch.object( - hmipc.HomematicipAuth, "async_setup", return_value=mock_coro(True) - ), patch.object( - hmipc.HomematicipAuth, "async_checkbutton", return_value=mock_coro(True) - ), patch.object( - hmipc.HomematicipAuth, "async_register", return_value=mock_coro(False) + with patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_checkbutton", + return_value=True, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_setup", + return_value=True, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_register", + return_value=False, ): - result = await flow.async_step_init(user_input=config) - assert result["type"] == "abort" + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": "user"}, data=config + ) + + assert result["type"] == "abort" + assert result["reason"] == "connection_aborted" async def test_flow_link_press_button(hass): @@ -76,92 +108,104 @@ async def test_flow_link_press_button(hass): const.HMIPC_PIN: "123", const.HMIPC_NAME: "hmip", } - flow = config_flow.HomematicipCloudFlowHandler() - flow.hass = hass - with patch.object( - hmipc.HomematicipAuth, "async_setup", return_value=mock_coro(True) - ), patch.object( - hmipc.HomematicipAuth, "async_checkbutton", return_value=mock_coro(False) + with patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_checkbutton", + return_value=False, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_setup", + return_value=True, ): - result = await flow.async_step_init(user_input=config) - assert result["type"] == "form" - assert result["errors"] == {"base": "press_the_button"} + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": "user"}, data=config + ) + + assert result["type"] == "form" + assert result["step_id"] == "link" + assert result["errors"] == {"base": "press_the_button"} async def test_init_flow_show_form(hass): """Test config flow shows up with a form.""" - flow = config_flow.HomematicipCloudFlowHandler() - flow.hass = hass - result = await flow.async_step_init(user_input=None) - assert result["type"] == "form" - - -async def test_init_flow_user_show_form(hass): - """Test config flow shows up with a form.""" - flow = config_flow.HomematicipCloudFlowHandler() - flow.hass = hass - - result = await flow.async_step_user(user_input=None) + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": "user"} + ) assert result["type"] == "form" + assert result["step_id"] == "init" async def test_init_already_configured(hass): """Test accesspoint is already configured.""" - MockConfigEntry( - domain=const.DOMAIN, data={const.HMIPC_HAPID: "ABC123"} - ).add_to_hass(hass) + MockConfigEntry(domain=const.DOMAIN, unique_id="ABC123").add_to_hass(hass) config = { const.HMIPC_HAPID: "ABC123", const.HMIPC_PIN: "123", const.HMIPC_NAME: "hmip", } - flow = config_flow.HomematicipCloudFlowHandler() - flow.hass = hass + with patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_checkbutton", + return_value=True, + ): + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": "user"}, data=config + ) - result = await flow.async_step_init(user_input=config) assert result["type"] == "abort" + assert result["reason"] == "already_configured" async def test_import_config(hass): """Test importing a host with an existing config file.""" - flow = config_flow.HomematicipCloudFlowHandler() - flow.hass = hass + config = { + const.HMIPC_HAPID: "ABC123", + const.HMIPC_AUTHTOKEN: "123", + const.HMIPC_NAME: "hmip", + } - result = await flow.async_step_import( - { - hmipc.HMIPC_HAPID: "ABC123", - hmipc.HMIPC_AUTHTOKEN: "123", - hmipc.HMIPC_NAME: "hmip", - } - ) + with patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_checkbutton", + return_value=True, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_setup", + return_value=True, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_register", + return_value=True, + ): + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": "import"}, data=config + ) assert result["type"] == "create_entry" assert result["title"] == "ABC123" - assert result["data"] == { - hmipc.HMIPC_HAPID: "ABC123", - hmipc.HMIPC_AUTHTOKEN: "123", - hmipc.HMIPC_NAME: "hmip", - } + assert result["data"] == {"authtoken": "123", "hapid": "ABC123", "name": "hmip"} + assert result["result"].unique_id == "ABC123" async def test_import_existing_config(hass): """Test abort of an existing accesspoint from config.""" - flow = config_flow.HomematicipCloudFlowHandler() - flow.hass = hass + MockConfigEntry(domain=const.DOMAIN, unique_id="ABC123").add_to_hass(hass) + config = { + const.HMIPC_HAPID: "ABC123", + const.HMIPC_AUTHTOKEN: "123", + const.HMIPC_NAME: "hmip", + } - MockConfigEntry( - domain=const.DOMAIN, data={hmipc.HMIPC_HAPID: "ABC123"} - ).add_to_hass(hass) - - result = await flow.async_step_import( - { - hmipc.HMIPC_HAPID: "ABC123", - hmipc.HMIPC_AUTHTOKEN: "123", - hmipc.HMIPC_NAME: "hmip", - } - ) + with patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_checkbutton", + return_value=True, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_setup", + return_value=True, + ), patch( + "homeassistant.components.homematicip_cloud.hap.HomematicipAuth.async_register", + return_value=True, + ): + result = await hass.config_entries.flow.async_init( + const.DOMAIN, context={"source": "import"}, data=config + ) assert result["type"] == "abort" + assert result["reason"] == "already_configured" diff --git a/tests/components/homematicip_cloud/test_init.py b/tests/components/homematicip_cloud/test_init.py index eb51c3ece38..c1ce12d4bfc 100644 --- a/tests/components/homematicip_cloud/test_init.py +++ b/tests/components/homematicip_cloud/test_init.py @@ -1,154 +1,115 @@ """Test HomematicIP Cloud setup process.""" -from unittest.mock import patch +from asynctest import CoroutineMock, patch from homeassistant.components import homematicip_cloud as hmipc from homeassistant.setup import async_setup_component -from tests.common import Mock, MockConfigEntry, mock_coro +from tests.common import Mock, MockConfigEntry async def test_config_with_accesspoint_passed_to_config_entry(hass): """Test that config for a accesspoint are loaded via config entry.""" - with patch.object(hass, "config_entries") as mock_config_entries, patch.object( - hmipc, "configured_haps", return_value=[] - ): - assert ( - await async_setup_component( - hass, - hmipc.DOMAIN, - { - hmipc.DOMAIN: { - hmipc.CONF_ACCESSPOINT: "ABC123", - hmipc.CONF_AUTHTOKEN: "123", - hmipc.CONF_NAME: "name", - } - }, - ) - is True - ) - # Flow started for the access point - assert len(mock_config_entries.flow.mock_calls) >= 2 + entry_config = { + hmipc.CONF_ACCESSPOINT: "ABC123", + hmipc.CONF_AUTHTOKEN: "123", + hmipc.CONF_NAME: "name", + } + # no config_entry exists + assert len(hass.config_entries.async_entries(hmipc.DOMAIN)) == 0 + # no acccesspoint exists + assert not hass.data.get(hmipc.DOMAIN) + + assert ( + await async_setup_component(hass, hmipc.DOMAIN, {hmipc.DOMAIN: entry_config}) + is True + ) + + # config_entry created for access point + config_entries = hass.config_entries.async_entries(hmipc.DOMAIN) + assert len(config_entries) == 1 + assert config_entries[0].data == { + "authtoken": "123", + "hapid": "ABC123", + "name": "name", + } + # defined access_point created for config_entry + assert isinstance(hass.data[hmipc.DOMAIN]["ABC123"], hmipc.HomematicipHAP) async def test_config_already_registered_not_passed_to_config_entry(hass): """Test that an already registered accesspoint does not get imported.""" - with patch.object(hass, "config_entries") as mock_config_entries, patch.object( - hmipc, "configured_haps", return_value=["ABC123"] - ): - assert ( - await async_setup_component( - hass, - hmipc.DOMAIN, - { - hmipc.DOMAIN: { - hmipc.CONF_ACCESSPOINT: "ABC123", - hmipc.CONF_AUTHTOKEN: "123", - hmipc.CONF_NAME: "name", - } - }, - ) - is True - ) - # No flow started - assert not mock_config_entries.flow.mock_calls - - -async def test_setup_entry_successful(hass): - """Test setup entry is successful.""" - entry = MockConfigEntry( - domain=hmipc.DOMAIN, - data={ - hmipc.HMIPC_HAPID: "ABC123", - hmipc.HMIPC_AUTHTOKEN: "123", - hmipc.HMIPC_NAME: "hmip", - }, - ) - entry.add_to_hass(hass) - with patch.object(hmipc, "HomematicipHAP") as mock_hap: - instance = mock_hap.return_value - instance.async_setup.return_value = mock_coro(True) - instance.home.id = "1" - instance.home.modelType = "mock-type" - instance.home.name = "mock-name" - instance.home.currentAPVersion = "mock-ap-version" - - assert ( - await async_setup_component( - hass, - hmipc.DOMAIN, - { - hmipc.DOMAIN: { - hmipc.CONF_ACCESSPOINT: "ABC123", - hmipc.CONF_AUTHTOKEN: "123", - hmipc.CONF_NAME: "hmip", - } - }, - ) - is True - ) - - assert len(mock_hap.mock_calls) >= 2 - - -async def test_setup_defined_accesspoint(hass): - """Test we initiate config entry for the accesspoint.""" - with patch.object(hass, "config_entries") as mock_config_entries, patch.object( - hmipc, "configured_haps", return_value=[] - ): - mock_config_entries.flow.async_init.return_value = mock_coro() - assert ( - await async_setup_component( - hass, - hmipc.DOMAIN, - { - hmipc.DOMAIN: { - hmipc.CONF_ACCESSPOINT: "ABC123", - hmipc.CONF_AUTHTOKEN: "123", - hmipc.CONF_NAME: "hmip", - } - }, - ) - is True - ) - - assert len(mock_config_entries.flow.mock_calls) == 1 - assert mock_config_entries.flow.mock_calls[0][2]["data"] == { - hmipc.HMIPC_HAPID: "ABC123", + mock_config = { hmipc.HMIPC_AUTHTOKEN: "123", - hmipc.HMIPC_NAME: "hmip", + hmipc.HMIPC_HAPID: "ABC123", + hmipc.HMIPC_NAME: "name", } + MockConfigEntry(domain=hmipc.DOMAIN, data=mock_config).add_to_hass(hass) + + # one config_entry exists + config_entries = hass.config_entries.async_entries(hmipc.DOMAIN) + assert len(config_entries) == 1 + assert config_entries[0].data == { + "authtoken": "123", + "hapid": "ABC123", + "name": "name", + } + # config_enty has no unique_id + assert not config_entries[0].unique_id + + entry_config = { + hmipc.CONF_ACCESSPOINT: "ABC123", + hmipc.CONF_AUTHTOKEN: "123", + hmipc.CONF_NAME: "name", + } + assert ( + await async_setup_component(hass, hmipc.DOMAIN, {hmipc.DOMAIN: entry_config}) + is True + ) + + # no new config_entry created / still one config_entry + config_entries = hass.config_entries.async_entries(hmipc.DOMAIN) + assert len(config_entries) == 1 + assert config_entries[0].data == { + "authtoken": "123", + "hapid": "ABC123", + "name": "name", + } + # config_enty updated with unique_id + assert config_entries[0].unique_id == "ABC123" async def test_unload_entry(hass): """Test being able to unload an entry.""" - entry = MockConfigEntry( - domain=hmipc.DOMAIN, - data={ - hmipc.HMIPC_HAPID: "ABC123", - hmipc.HMIPC_AUTHTOKEN: "123", - hmipc.HMIPC_NAME: "hmip", - }, - ) - entry.add_to_hass(hass) + mock_config = { + hmipc.HMIPC_AUTHTOKEN: "123", + hmipc.HMIPC_HAPID: "ABC123", + hmipc.HMIPC_NAME: "name", + } + MockConfigEntry(domain=hmipc.DOMAIN, data=mock_config).add_to_hass(hass) with patch.object(hmipc, "HomematicipHAP") as mock_hap: instance = mock_hap.return_value - instance.async_setup.return_value = mock_coro(True) + instance.async_setup = CoroutineMock(return_value=True) instance.home.id = "1" instance.home.modelType = "mock-type" instance.home.name = "mock-name" instance.home.currentAPVersion = "mock-ap-version" + instance.async_reset = CoroutineMock(return_value=True) assert await async_setup_component(hass, hmipc.DOMAIN, {}) is True - assert len(mock_hap.return_value.mock_calls) >= 1 + assert mock_hap.return_value.mock_calls[0][0] == "async_setup" - mock_hap.return_value.async_reset.return_value = mock_coro(True) - assert await hmipc.async_unload_entry(hass, entry) - assert len(mock_hap.return_value.async_reset.mock_calls) == 1 + assert hass.data[hmipc.DOMAIN]["ABC123"] + config_entries = hass.config_entries.async_entries(hmipc.DOMAIN) + assert len(config_entries) == 1 + + await hass.config_entries.async_unload(config_entries[0].entry_id) + + # entry is unloaded assert hass.data[hmipc.DOMAIN] == {}