Suppress homekit bridge discovery by homekit controller (#39990)

pull/40024/head
J. Nick Koston 2020-09-13 09:12:10 -05:00 committed by GitHub
parent 11d74124cd
commit 84578f515d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 4 deletions

View File

@ -8,12 +8,19 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import zeroconf
from homeassistant.core import callback
from homeassistant.helpers.device_registry import (
CONNECTION_NETWORK_MAC,
async_get_registry as async_get_device_registry,
)
from .connection import get_accessory_name, get_bridge_information
from .const import DOMAIN, KNOWN_DEVICES
HOMEKIT_IGNORE = ["Home Assistant Bridge"]
HOMEKIT_DIR = ".homekit"
HOMEKIT_BRIDGE_DOMAIN = "homekit"
HOMEKIT_BRIDGE_SERIAL_NUMBER = "homekit.bridge"
HOMEKIT_BRIDGE_MODEL = "Home Assistant HomeKit Bridge"
PAIRING_FILE = "pairing.json"
PIN_FORMAT = re.compile(r"^(\d{3})-{0,1}(\d{2})-{0,1}(\d{3})$")
@ -141,6 +148,17 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow):
return self.async_abort(reason="no_devices")
async def _hkid_is_homekit_bridge(self, hkid):
"""Determine if the device is a homekit bridge."""
dev_reg = await async_get_device_registry(self.hass)
device = dev_reg.async_get_device(
identifiers=set(), connections={(CONNECTION_NETWORK_MAC, hkid)}
)
if device is None:
return False
return device.model == HOMEKIT_BRIDGE_MODEL
async def async_step_zeroconf(self, discovery_info):
"""Handle a discovered HomeKit accessory.
@ -153,6 +171,12 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow):
key.lower(): value for (key, value) in discovery_info["properties"].items()
}
if "id" not in properties:
_LOGGER.warning(
"HomeKit device %s: id not exposed, in violation of spec", properties
)
return self.async_abort(reason="invalid_properties")
# The hkid is a unique random number that looks like a pairing code.
# It changes if a device is factory reset.
hkid = properties["id"]
@ -208,7 +232,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow):
# Devices in HOMEKIT_IGNORE have native local integrations - users
# should be encouraged to use native integration and not confused
# by alternative HK API.
if model in HOMEKIT_IGNORE:
if await self._hkid_is_homekit_bridge(hkid):
return self.async_abort(reason="ignored_model")
self.model = model

View File

@ -44,6 +44,7 @@
"already_configured": "Accessory is already configured with this controller.",
"invalid_config_entry": "This device is showing as ready to pair but there is already a conflicting configuration entry for it in Home Assistant that must first be removed.",
"accessory_not_found_error": "Cannot add pairing as device can no longer be found.",
"invalid_properties": "Invalid properties announced by device.",
"already_in_progress": "Config flow for device is already in progress."
}
},

View File

@ -8,10 +8,11 @@ from aiohomekit.model.services import ServicesTypes
import pytest
from homeassistant.components.homekit_controller import config_flow
from homeassistant.helpers import device_registry
import tests.async_mock
from tests.async_mock import patch
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, mock_device_registry
PAIRING_START_FORM_ERRORS = [
(KeyError, "pairing_failed"),
@ -233,11 +234,45 @@ async def test_pair_already_paired_1(hass, controller):
assert result["reason"] == "already_paired"
async def test_id_missing(hass, controller):
"""Test id is missing."""
device = setup_mock_accessory(controller)
discovery_info = get_device_discovery_info(device)
# Remove id from device
del discovery_info["properties"]["id"]
# Device is discovered
result = await hass.config_entries.flow.async_init(
"homekit_controller", context={"source": "zeroconf"}, data=discovery_info
)
assert result["type"] == "abort"
assert result["reason"] == "invalid_properties"
async def test_discovery_ignored_model(hass, controller):
"""Already paired."""
device = setup_mock_accessory(controller)
discovery_info = get_device_discovery_info(device)
discovery_info["properties"]["md"] = config_flow.HOMEKIT_IGNORE[0]
config_entry = MockConfigEntry(domain=config_flow.HOMEKIT_BRIDGE_DOMAIN, data={})
formatted_mac = device_registry.format_mac("AA:BB:CC:DD:EE:FF")
dev_reg = mock_device_registry(hass)
dev_reg.async_get_or_create(
config_entry_id=config_entry.entry_id,
identifiers={
(
config_flow.HOMEKIT_BRIDGE_DOMAIN,
config_entry.entry_id,
config_flow.HOMEKIT_BRIDGE_SERIAL_NUMBER,
)
},
connections={(device_registry.CONNECTION_NETWORK_MAC, formatted_mac)},
model=config_flow.HOMEKIT_BRIDGE_MODEL,
)
discovery_info["properties"]["id"] = "AA:BB:CC:DD:EE:FF"
# Device is discovered
result = await hass.config_entries.flow.async_init(