Show HomeKit Controller unhandled pairing error reason in the UI (#82505)

pull/82567/head
J. Nick Koston 2022-11-21 20:24:35 -06:00 committed by GitHub
parent a7caa038be
commit 7df711f1f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 7 deletions

View File

@ -420,6 +420,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
# Should never call this step without setting self.hkid
assert self.hkid
description_placeholders = {}
errors = {}
@ -465,10 +466,11 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="accessory_not_found_error")
except InsecureSetupCode:
errors["pairing_code"] = "insecure_setup_code"
except Exception: # pylint: disable=broad-except
except Exception as err: # pylint: disable=broad-except
_LOGGER.exception("Pairing attempt failed with an unhandled exception")
self.finish_pairing = None
errors["pairing_code"] = "pairing_failed"
description_placeholders["error"] = str(err)
if not self.finish_pairing:
# Its possible that the first try may have been busy so
@ -496,11 +498,12 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
# TLV error, usually not in pairing mode
_LOGGER.exception("Pairing communication failed")
return await self.async_step_protocol_error()
except Exception: # pylint: disable=broad-except
except Exception as err: # pylint: disable=broad-except
_LOGGER.exception("Pairing attempt failed with an unhandled exception")
errors["pairing_code"] = "pairing_failed"
description_placeholders["error"] = str(err)
return self._async_step_pair_show_form(errors)
return self._async_step_pair_show_form(errors, description_placeholders)
async def async_step_busy_error(
self, user_input: dict[str, Any] | None = None
@ -531,7 +534,9 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
@callback
def _async_step_pair_show_form(
self, errors: dict[str, str] | None = None
self,
errors: dict[str, str] | None = None,
description_placeholders: dict[str, str] | None = None,
) -> FlowResult:
assert self.category
@ -547,7 +552,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_show_form(
step_id="pair",
errors=errors or {},
description_placeholders=placeholders,
description_placeholders=placeholders | (description_placeholders or {}),
data_schema=vol.Schema(schema),
)

View File

@ -37,7 +37,7 @@
"unknown_error": "Device reported an unknown error. Pairing failed.",
"authentication_error": "Incorrect HomeKit code. Please check it and try again.",
"max_peers_error": "Device refused to add pairing as it has no free pairing storage.",
"pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently."
"pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently: {error}"
},
"abort": {
"no_devices": "No unpaired devices could be found",

View File

@ -14,7 +14,7 @@
"authentication_error": "Incorrect HomeKit code. Please check it and try again.",
"insecure_setup_code": "The requested setup code is insecure because of its trivial nature. This accessory fails to meet basic security requirements.",
"max_peers_error": "Device refused to add pairing as it has no free pairing storage.",
"pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently.",
"pairing_failed": "An unhandled error occurred while attempting to pair with this device. This may be a temporary failure or your device may not be supported currently: {error}",
"unable_to_pair": "Unable to pair, please try again.",
"unknown_error": "Device reported an unknown error. Pairing failed."
},

View File

@ -8,6 +8,7 @@ from aiohomekit.exceptions import AuthenticationError
from aiohomekit.model import Accessories, Accessory
from aiohomekit.model.characteristics import CharacteristicsTypes
from aiohomekit.model.services import ServicesTypes
from bleak.exc import BleakError
import pytest
from homeassistant import config_entries
@ -743,6 +744,57 @@ async def test_pair_form_errors_on_finish(hass, controller, exception, expected)
}
async def test_pair_unknown_errors(hass, controller):
"""Test describing unknown errors."""
device = setup_mock_accessory(controller)
discovery_info = get_device_discovery_info(device)
# Device is discovered
result = await hass.config_entries.flow.async_init(
"homekit_controller",
context={"source": config_entries.SOURCE_ZEROCONF},
data=discovery_info,
)
assert get_flow_context(hass, result) == {
"title_placeholders": {"name": "TestDevice", "category": "Outlet"},
"unique_id": "00:00:00:00:00:00",
"source": config_entries.SOURCE_ZEROCONF,
}
# User initiates pairing - this triggers the device to show a pairing code
# and then HA to show a pairing form
finish_pairing = unittest.mock.AsyncMock(
side_effect=BleakError("The bluetooth connection failed")
)
with patch.object(device, "async_start_pairing", return_value=finish_pairing):
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == "form"
assert get_flow_context(hass, result) == {
"title_placeholders": {"name": "TestDevice", "category": "Outlet"},
"unique_id": "00:00:00:00:00:00",
"source": config_entries.SOURCE_ZEROCONF,
}
# User enters pairing code
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"pairing_code": "111-22-333"}
)
assert result["type"] == "form"
assert result["errors"]["pairing_code"] == "pairing_failed"
assert (
result["description_placeholders"]["error"] == "The bluetooth connection failed"
)
assert get_flow_context(hass, result) == {
"title_placeholders": {"name": "TestDevice", "category": "Outlet"},
"unique_id": "00:00:00:00:00:00",
"source": config_entries.SOURCE_ZEROCONF,
"pairing": True,
}
async def test_user_works(hass, controller):
"""Test user initiated disovers devices."""
setup_mock_accessory(controller)