From d7e471e244a4d71b22081acc2907f9a8cfe19582 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 6 Sep 2020 12:56:38 -0500 Subject: [PATCH] Add HomeKit discovery for iSmartGate (#39702) --- .../components/gogogate2/config_flow.py | 26 +++++- .../components/gogogate2/manifest.json | 7 +- homeassistant/generated/zeroconf.py | 1 + .../components/gogogate2/test_config_flow.py | 83 ++++++++++++++++++- 4 files changed, 112 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/gogogate2/config_flow.py b/homeassistant/components/gogogate2/config_flow.py index 4a70a822023..3642e28ccac 100644 --- a/homeassistant/components/gogogate2/config_flow.py +++ b/homeassistant/components/gogogate2/config_flow.py @@ -29,12 +29,32 @@ class Gogogate2FlowHandler(ConfigFlow, domain=DOMAIN): VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL + def __init__(self): + """Initialize the config flow.""" + self._ip_address = None + self._device_type = None + async def async_step_import(self, config_data: dict = None): """Handle importing of configuration.""" result = await self.async_step_user(config_data) self._abort_if_unique_id_configured() return result + async def async_step_homekit(self, discovery_info): + """Handle homekit discovery.""" + await self.async_set_unique_id(discovery_info["properties"]["id"]) + self._abort_if_unique_id_configured({CONF_IP_ADDRESS: discovery_info["host"]}) + + ip_address = discovery_info["host"] + + for entry in self._async_current_entries(): + if entry.data.get(CONF_IP_ADDRESS) == ip_address: + return self.async_abort(reason="already_configured") + + self._ip_address = ip_address + self._device_type = DEVICE_TYPE_ISMARTGATE + return await self.async_step_user() + async def async_step_user(self, user_input: dict = None): """Handle user initiated flow.""" user_input = user_input or {} @@ -88,10 +108,12 @@ class Gogogate2FlowHandler(ConfigFlow, domain=DOMAIN): { vol.Required( CONF_DEVICE, - default=user_input.get(CONF_DEVICE, DEVICE_TYPE_GOGOGATE2), + default=self._device_type + or user_input.get(CONF_DEVICE, DEVICE_TYPE_GOGOGATE2), ): vol.In((DEVICE_TYPE_GOGOGATE2, DEVICE_TYPE_ISMARTGATE)), vol.Required( - CONF_IP_ADDRESS, default=user_input.get(CONF_IP_ADDRESS, "") + CONF_IP_ADDRESS, + default=user_input.get(CONF_IP_ADDRESS, self._ip_address), ): str, vol.Required( CONF_USERNAME, default=user_input.get(CONF_USERNAME, "") diff --git a/homeassistant/components/gogogate2/manifest.json b/homeassistant/components/gogogate2/manifest.json index 08c8c64b139..b6921991ecc 100644 --- a/homeassistant/components/gogogate2/manifest.json +++ b/homeassistant/components/gogogate2/manifest.json @@ -4,5 +4,10 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/gogogate2", "requirements": ["gogogate2-api==2.0.0"], - "codeowners": ["@vangorra"] + "codeowners": ["@vangorra"], + "homekit": { + "models": [ + "iSmartGate" + ] + } } diff --git a/homeassistant/generated/zeroconf.py b/homeassistant/generated/zeroconf.py index ba12b4ec4de..ea61ccfbaeb 100644 --- a/homeassistant/generated/zeroconf.py +++ b/homeassistant/generated/zeroconf.py @@ -92,5 +92,6 @@ HOMEKIT = { "TRADFRI": "tradfri", "Welcome": "netatmo", "Wemo": "wemo", + "iSmartGate": "gogogate2", "tado": "tado" } diff --git a/tests/components/gogogate2/test_config_flow.py b/tests/components/gogogate2/test_config_flow.py index 8c07a3fc023..667c0330d80 100644 --- a/tests/components/gogogate2/test_config_flow.py +++ b/tests/components/gogogate2/test_config_flow.py @@ -3,7 +3,12 @@ from gogogate2_api import GogoGate2Api from gogogate2_api.common import ApiError from gogogate2_api.const import GogoGate2ApiErrorCode -from homeassistant.components.gogogate2.const import DEVICE_TYPE_GOGOGATE2 +from homeassistant import config_entries, setup +from homeassistant.components.gogogate2.const import ( + DEVICE_TYPE_GOGOGATE2, + DEVICE_TYPE_ISMARTGATE, + DOMAIN, +) from homeassistant.config_entries import SOURCE_USER from homeassistant.const import ( CONF_DEVICE, @@ -12,9 +17,12 @@ from homeassistant.const import ( CONF_USERNAME, ) from homeassistant.core import HomeAssistant -from homeassistant.data_entry_flow import RESULT_TYPE_FORM +from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_FORM from tests.async_mock import MagicMock, patch +from tests.common import MockConfigEntry + +MOCK_MAC_ADDR = "AA:BB:CC:DD:EE:FF" @patch("homeassistant.components.gogogate2.async_setup", return_value=True) @@ -64,3 +72,74 @@ async def test_auth_fail( assert result assert result["type"] == RESULT_TYPE_FORM assert result["errors"] == {"base": "cannot_connect"} + + +async def test_form_homekit_unique_id_already_setup(hass): + """Test that we abort from homekit if gogogate2 is already setup.""" + await setup.async_setup_component(hass, "persistent_notification", {}) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HOMEKIT}, + data={"host": "1.2.3.4", "properties": {"id": MOCK_MAC_ADDR}}, + ) + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {} + flow = next( + flow + for flow in hass.config_entries.flow.async_progress() + if flow["flow_id"] == result["flow_id"] + ) + assert flow["context"]["unique_id"] == MOCK_MAC_ADDR + + entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_IP_ADDRESS: "1.2.3.4", CONF_USERNAME: "mock", CONF_PASSWORD: "mock"}, + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HOMEKIT}, + data={"host": "1.2.3.4", "properties": {"id": MOCK_MAC_ADDR}}, + ) + assert result["type"] == RESULT_TYPE_ABORT + + +async def test_form_homekit_ip_address_already_setup(hass): + """Test that we abort from homekit if gogogate2 is already setup.""" + await setup.async_setup_component(hass, "persistent_notification", {}) + + entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_IP_ADDRESS: "1.2.3.4", CONF_USERNAME: "mock", CONF_PASSWORD: "mock"}, + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HOMEKIT}, + data={"host": "1.2.3.4", "properties": {"id": MOCK_MAC_ADDR}}, + ) + assert result["type"] == RESULT_TYPE_ABORT + + +async def test_form_homekit_ip_address(hass): + """Test homekit includes the defaults ip address.""" + await setup.async_setup_component(hass, "persistent_notification", {}) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HOMEKIT}, + data={"host": "1.2.3.4", "properties": {"id": MOCK_MAC_ADDR}}, + ) + assert result["type"] == RESULT_TYPE_FORM + assert result["errors"] == {} + + data_schema = result["data_schema"] + assert data_schema({CONF_USERNAME: "username", CONF_PASSWORD: "password"}) == { + CONF_DEVICE: DEVICE_TYPE_ISMARTGATE, + CONF_IP_ADDRESS: "1.2.3.4", + CONF_PASSWORD: "password", + CONF_USERNAME: "username", + }