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

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

View File

@ -37,7 +37,7 @@
"unknown_error": "Device reported an unknown error. Pairing failed.", "unknown_error": "Device reported an unknown error. Pairing failed.",
"authentication_error": "Incorrect HomeKit code. Please check it and try again.", "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.", "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": { "abort": {
"no_devices": "No unpaired devices could be found", "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.", "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.", "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.", "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.", "unable_to_pair": "Unable to pair, please try again.",
"unknown_error": "Device reported an unknown error. Pairing failed." "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 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
from bleak.exc import BleakError
import pytest import pytest
from homeassistant import config_entries 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): async def test_user_works(hass, controller):
"""Test user initiated disovers devices.""" """Test user initiated disovers devices."""
setup_mock_accessory(controller) setup_mock_accessory(controller)