Handle homekit accessories where the pairing flag is wrong (#53385)

This commit is contained in:
Jc2k 2021-07-24 07:03:44 +01:00 committed by GitHub
parent bf3a16eed4
commit 0db160e372
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 6 deletions

View File

@ -3,6 +3,7 @@ import logging
import re import re
import aiohomekit import aiohomekit
from aiohomekit.exceptions import AuthenticationError
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
@ -270,6 +271,28 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
# invalid. Remove it automatically. # invalid. Remove it automatically.
existing = find_existing_host(self.hass, hkid) existing = find_existing_host(self.hass, hkid)
if not paired and existing: if not paired and existing:
if self.controller is None:
await self._async_setup_controller()
pairing = self.controller.load_pairing(
existing.data["AccessoryPairingID"], dict(existing.data)
)
try:
await pairing.list_accessories_and_characteristics()
_LOGGER.debug(
"%s (%s - %s) claims to be unpaired but isn't. It's implementation of HomeKit is defective or a zeroconf relay is broadcasting stale data",
name,
model,
hkid,
)
return self.async_abort(reason="already_paired")
except AuthenticationError:
_LOGGER.debug(
"%s (%s - %s) is unpaired. Removing invalid pairing for this device",
name,
model,
hkid,
)
await self.hass.config_entries.async_remove(existing.entry_id) await self.hass.config_entries.async_remove(existing.entry_id)
# Set unique-id and error out if it's already configured # Set unique-id and error out if it's already configured

View File

@ -4,6 +4,7 @@ import unittest.mock
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
import aiohomekit import aiohomekit
from aiohomekit.exceptions import AuthenticationError
from aiohomekit.model import Accessories, Accessory from aiohomekit.model import Accessories, Accessory
from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.characteristics import CharacteristicsTypes
from aiohomekit.model.services import ServicesTypes from aiohomekit.model.services import ServicesTypes
@ -351,8 +352,48 @@ async def test_discovery_does_not_ignore_non_homekit(hass, controller):
assert result["type"] == "form" assert result["type"] == "form"
async def test_discovery_broken_pairing_flag(hass, controller):
"""
There is already a config entry for the pairing and its pairing flag is wrong in zeroconf.
We have seen this particular implementation error in 2 different devices.
"""
await controller.add_paired_device(Accessories(), "00:00:00:00:00:00")
MockConfigEntry(
domain="homekit_controller",
data={"AccessoryPairingID": "00:00:00:00:00:00"},
unique_id="00:00:00:00:00:00",
).add_to_hass(hass)
# We just added a mock config entry so it must be visible in hass
assert len(hass.config_entries.async_entries()) == 1
device = setup_mock_accessory(controller)
discovery_info = get_device_discovery_info(device)
# Make sure that we are pairable
assert discovery_info["properties"]["sf"] != 0x0
# Device is discovered
result = await hass.config_entries.flow.async_init(
"homekit_controller",
context={"source": config_entries.SOURCE_ZEROCONF},
data=discovery_info,
)
# Should still be paired.
config_entry_count = len(hass.config_entries.async_entries())
assert config_entry_count == 1
# Even though discovered as pairable, we bail out as already paired.
assert result["reason"] == "already_paired"
async def test_discovery_invalid_config_entry(hass, controller): async def test_discovery_invalid_config_entry(hass, controller):
"""There is already a config entry for the pairing id but it's invalid.""" """There is already a config entry for the pairing id but it's invalid."""
pairing = await controller.add_paired_device(Accessories(), "00:00:00:00:00:00")
MockConfigEntry( MockConfigEntry(
domain="homekit_controller", domain="homekit_controller",
data={"AccessoryPairingID": "00:00:00:00:00:00"}, data={"AccessoryPairingID": "00:00:00:00:00:00"},
@ -366,6 +407,11 @@ async def test_discovery_invalid_config_entry(hass, controller):
discovery_info = get_device_discovery_info(device) discovery_info = get_device_discovery_info(device)
# Device is discovered # Device is discovered
with patch.object(
pairing,
"list_accessories_and_characteristics",
side_effect=AuthenticationError("Invalid pairing keys"),
):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
"homekit_controller", "homekit_controller",
context={"source": config_entries.SOURCE_ZEROCONF}, context={"source": config_entries.SOURCE_ZEROCONF},