diff --git a/homeassistant/components/hue/config_flow.py b/homeassistant/components/hue/config_flow.py index 4a7ebd01fbd..72938ebfe0a 100644 --- a/homeassistant/components/hue/config_flow.py +++ b/homeassistant/components/hue/config_flow.py @@ -207,6 +207,24 @@ class HueFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self.bridge = bridge return await self.async_step_link() + async def async_step_zeroconf(self, discovery_info): + """Handle a discovered Hue bridge. + + This flow is triggered by the Zeroconf component. It will check if the + host is already configured and delegate to the import step if not. + """ + bridge = self._async_get_bridge( + discovery_info["host"], discovery_info["properties"]["bridgeid"] + ) + + await self.async_set_unique_id(bridge.id) + self._abort_if_unique_id_configured( + updates={CONF_HOST: bridge.host}, reload_on_update=False + ) + + self.bridge = bridge + return await self.async_step_link() + async def async_step_homekit(self, discovery_info): """Handle a discovered Hue bridge on HomeKit. diff --git a/homeassistant/components/hue/manifest.json b/homeassistant/components/hue/manifest.json index 32b3cd4ee51..67659b96275 100644 --- a/homeassistant/components/hue/manifest.json +++ b/homeassistant/components/hue/manifest.json @@ -21,6 +21,7 @@ "homekit": { "models": ["BSB002"] }, + "zeroconf": ["_hue._tcp.local."], "codeowners": ["@balloob", "@frenck"], "quality_scale": "platinum", "iot_class": "local_push" diff --git a/homeassistant/generated/zeroconf.py b/homeassistant/generated/zeroconf.py index fd5194bd025..cf94ff03a1c 100644 --- a/homeassistant/generated/zeroconf.py +++ b/homeassistant/generated/zeroconf.py @@ -131,6 +131,11 @@ ZEROCONF = { "name": "shelly*" } ], + "_hue._tcp.local.": [ + { + "domain": "hue" + } + ], "_ipp._tcp.local.": [ { "domain": "ipp" diff --git a/tests/components/hue/test_config_flow.py b/tests/components/hue/test_config_flow.py index 3deec0988fa..2c79795d48b 100644 --- a/tests/components/hue/test_config_flow.py +++ b/tests/components/hue/test_config_flow.py @@ -681,3 +681,57 @@ def _get_schema_default(schema, key_name): if schema_key == key_name: return schema_key.default() raise KeyError(f"{key_name} not found in schema") + + +async def test_bridge_zeroconf(hass): + """Test a bridge being discovered.""" + result = await hass.config_entries.flow.async_init( + const.DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data={ + "host": "192.168.1.217", + "port": 443, + "hostname": "Philips-hue.local.", + "type": "_hue._tcp.local.", + "name": "Philips Hue - ABCABC._hue._tcp.local.", + "properties": { + "_raw": {"bridgeid": b"ecb5fafffeabcabc", "modelid": b"BSB002"}, + "bridgeid": "ecb5fafffeabcabc", + "modelid": "BSB002", + }, + }, + ) + + assert result["type"] == "form" + assert result["step_id"] == "link" + + +async def test_bridge_zeroconf_already_exists(hass): + """Test a bridge being discovered by zeroconf already exists.""" + entry = MockConfigEntry( + domain="hue", + source=config_entries.SOURCE_SSDP, + data={"host": "0.0.0.0"}, + unique_id="ecb5faabcabc", + ) + entry.add_to_hass(hass) + result = await hass.config_entries.flow.async_init( + const.DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data={ + "host": "192.168.1.217", + "port": 443, + "hostname": "Philips-hue.local.", + "type": "_hue._tcp.local.", + "name": "Philips Hue - ABCABC._hue._tcp.local.", + "properties": { + "_raw": {"bridgeid": b"ecb5faabcabc", "modelid": b"BSB002"}, + "bridgeid": "ecb5faabcabc", + "modelid": "BSB002", + }, + }, + ) + + assert result["type"] == "abort" + assert result["reason"] == "already_configured" + assert entry.data["host"] == "192.168.1.217"