diff --git a/homeassistant/components/homekit_controller/config_flow.py b/homeassistant/components/homekit_controller/config_flow.py index 9ca247382c7..9881ef15dcb 100644 --- a/homeassistant/components/homekit_controller/config_flow.py +++ b/homeassistant/components/homekit_controller/config_flow.py @@ -23,11 +23,29 @@ HOMEKIT_BRIDGE_MODEL = "Home Assistant HomeKit Bridge" PAIRING_FILE = "pairing.json" +MDNS_SUFFIX = "._hap._tcp.local." + PIN_FORMAT = re.compile(r"^(\d{3})-{0,1}(\d{2})-{0,1}(\d{3})$") _LOGGER = logging.getLogger(__name__) +DISALLOWED_CODES = { + "00000000", + "11111111", + "22222222", + "33333333", + "44444444", + "55555555", + "66666666", + "77777777", + "88888888", + "99999999", + "12345678", + "87654321", +} + + def normalize_hkid(hkid): """Normalize a hkid so that it is safe to compare with other normalized hkids.""" return hkid.lower() @@ -49,9 +67,12 @@ def ensure_pin_format(pin): If incorrect code is entered, an exception is raised. """ - match = PIN_FORMAT.search(pin) + match = PIN_FORMAT.search(pin.strip()) if not match: raise aiohomekit.exceptions.MalformedPinError(f"Invalid PIN code f{pin}") + pin_without_dashes = "".join(match.groups()) + if pin_without_dashes in DISALLOWED_CODES: + raise aiohomekit.exceptions.MalformedPinError(f"Invalid PIN code f{pin}") return "-".join(match.groups()) @@ -66,6 +87,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow): """Initialize the homekit_controller flow.""" self.model = None self.hkid = None + self.name = None self.devices = {} self.controller = None self.finish_pairing = None @@ -83,9 +105,11 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow): key = user_input["device"] self.hkid = self.devices[key].device_id self.model = self.devices[key].info["md"] + self.name = key[: -len(MDNS_SUFFIX)] if key.endswith(MDNS_SUFFIX) else key await self.async_set_unique_id( normalize_hkid(self.hkid), raise_on_progress=False ) + return await self.async_step_pair() if self.controller is None: @@ -222,7 +246,6 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow): # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 self.context["hkid"] = hkid - self.context["title_placeholders"] = {"name": name} if paired: # Device is paired but not to us - ignore it @@ -235,6 +258,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow): if await self._hkid_is_homekit_bridge(hkid): return self.async_abort(reason="ignored_model") + self.name = name self.model = model self.hkid = hkid @@ -355,9 +379,14 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow): @callback def _async_step_pair_show_form(self, errors=None): + # pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167 + placeholders = {"name": self.name} + self.context["title_placeholders"] = {"name": self.name} + return self.async_show_form( step_id="pair", errors=errors or {}, + description_placeholders=placeholders, data_schema=vol.Schema( {vol.Required("pairing_code"): vol.All(str, vol.Strip)} ), diff --git a/homeassistant/components/homekit_controller/strings.json b/homeassistant/components/homekit_controller/strings.json index 3f9f93450e0..62fb51709bc 100644 --- a/homeassistant/components/homekit_controller/strings.json +++ b/homeassistant/components/homekit_controller/strings.json @@ -1,18 +1,18 @@ { "title": "HomeKit Controller", "config": { - "flow_title": "HomeKit Accessory: {name}", + "flow_title": "{name} via HomeKit Accessory Protocol", "step": { "user": { - "title": "Pair with HomeKit Accessory", - "description": "Select the device you want to pair with", + "title": "Device selection", + "description": "HomeKit Controller communicates over the local area network using a secure encrypted connection without a separate HomeKit controller or iCloud. Select the device you want to pair with:", "data": { "device": "Device" } }, "pair": { - "title": "Pair with HomeKit Accessory", - "description": "Enter your HomeKit pairing code (in the format XXX-XX-XXX) to use this accessory", + "title": "Pair with a device via HomeKit Accessory Protocol", + "description": "HomeKit Controller communicates with {name} over the local area network using a secure encrypted connection without a separate HomeKit controller or iCloud. Enter your HomeKit pairing code (in the format XXX-XX-XXX) to use this accessory. This code is usually found on the device itself or in the packaging.", "data": { "pairing_code": "Pairing Code" } diff --git a/homeassistant/components/homekit_controller/translations/en.json b/homeassistant/components/homekit_controller/translations/en.json index eb0a5cbf6b6..62fb51709bc 100644 --- a/homeassistant/components/homekit_controller/translations/en.json +++ b/homeassistant/components/homekit_controller/translations/en.json @@ -1,77 +1,71 @@ { - "config": { - "abort": { - "accessory_not_found_error": "Cannot add pairing as device can no longer be found.", - "already_configured": "Accessory is already configured with this controller.", - "already_in_progress": "Config flow for device is already in progress.", - "already_paired": "This accessory is already paired to another device. Please reset the accessory and try again.", - "ignored_model": "HomeKit support for this model is blocked as a more feature complete native integration is available.", - "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.", - "no_devices": "No unpaired devices could be found" - }, - "error": { - "authentication_error": "Incorrect HomeKit code. Please check it and try again.", - "busy_error": "Device refused to add pairing as it is already pairing with another controller.", - "max_peers_error": "Device refused to add pairing as it has no free pairing storage.", - "max_tries_error": "Device refused to add pairing as it has received more than 100 unsuccessful authentication attempts.", - "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.", - "protocol_error": "Error communicating with the accessory. Device may not be in pairing mode and may require a physical or virtual button press.", - "unable_to_pair": "Unable to pair, please try again.", - "unknown_error": "Device reported an unknown error. Pairing failed." - }, - "flow_title": "HomeKit Accessory: {name}", - "step": { - "busy_error": { - "description": "Abort pairing on all controllers, or try restarting the device, then continue to resume pairing.", - "title": "The device is already pairing with another controller" - }, - "max_tries_error": { - "description": "The device has received more than 100 unsuccessful authentication attempts. Try restarting the device, then continue to resume pairing.", - "title": "Maximum authentication attempts exceeded" - }, - "pair": { - "data": { - "pairing_code": "Pairing Code" - }, - "description": "Enter your HomeKit pairing code (in the format XXX-XX-XXX) to use this accessory", - "title": "Pair with HomeKit Accessory" - }, - "protocol_error": { - "description": "The device may not be in pairing mode and may require a physical or virtual button press. Ensure the device is in pairing mode or try restarting the device, then continue to resume pairing.", - "title": "Error communicating with the accessory" - }, - "try_pair_later": { - "description": "Ensure the device is in pairing mode or try restarting the device, then continue to re-start pairing.", - "title": "Pairing Unavailable" - }, - "user": { - "data": { - "device": "Device" - }, - "description": "Select the device you want to pair with", - "title": "Pair with HomeKit Accessory" - } + "title": "HomeKit Controller", + "config": { + "flow_title": "{name} via HomeKit Accessory Protocol", + "step": { + "user": { + "title": "Device selection", + "description": "HomeKit Controller communicates over the local area network using a secure encrypted connection without a separate HomeKit controller or iCloud. Select the device you want to pair with:", + "data": { + "device": "Device" } - }, - "device_automation": { - "trigger_subtype": { - "button1": "Button 1", - "button10": "Button 10", - "button2": "Button 2", - "button3": "Button 3", - "button4": "Button 4", - "button5": "Button 5", - "button6": "Button 6", - "button7": "Button 7", - "button8": "Button 8", - "button9": "Button 9", - "doorbell": "Doorbell" - }, - "trigger_type": { - "double_press": "\"{subtype}\" pressed twice", - "long_press": "\"{subtype}\" pressed and held", - "single_press": "\"{subtype}\" pressed" + }, + "pair": { + "title": "Pair with a device via HomeKit Accessory Protocol", + "description": "HomeKit Controller communicates with {name} over the local area network using a secure encrypted connection without a separate HomeKit controller or iCloud. Enter your HomeKit pairing code (in the format XXX-XX-XXX) to use this accessory. This code is usually found on the device itself or in the packaging.", + "data": { + "pairing_code": "Pairing Code" } + }, + "protocol_error": { + "title": "Error communicating with the accessory", + "description": "The device may not be in pairing mode and may require a physical or virtual button press. Ensure the device is in pairing mode or try restarting the device, then continue to resume pairing." + }, + "busy_error": { + "title": "The device is already pairing with another controller", + "description": "Abort pairing on all controllers, or try restarting the device, then continue to resume pairing." + }, + "max_tries_error": { + "title": "Maximum authentication attempts exceeded", + "description": "The device has received more than 100 unsuccessful authentication attempts. Try restarting the device, then continue to resume pairing." + } }, - "title": "HomeKit Controller" -} \ No newline at end of file + "error": { + "unable_to_pair": "Unable to pair, please try again.", + "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." + }, + "abort": { + "no_devices": "No unpaired devices could be found", + "already_paired": "This accessory is already paired to another device. Please reset the accessory and try again.", + "ignored_model": "HomeKit support for this model is blocked as a more feature complete native integration is available.", + "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." + } + }, + "device_automation": { + "trigger_type": { + "single_press": "\"{subtype}\" pressed", + "double_press": "\"{subtype}\" pressed twice", + "long_press": "\"{subtype}\" pressed and held" + }, + "trigger_subtype": { + "doorbell": "Doorbell", + "button1": "Button 1", + "button2": "Button 2", + "button3": "Button 3", + "button4": "Button 4", + "button5": "Button 5", + "button6": "Button 6", + "button7": "Button 7", + "button8": "Button 8", + "button9": "Button 9", + "button10": "Button 10" + } + } +} diff --git a/tests/components/homekit_controller/test_config_flow.py b/tests/components/homekit_controller/test_config_flow.py index e5c8e381a5f..a8eb869abf4 100644 --- a/tests/components/homekit_controller/test_config_flow.py +++ b/tests/components/homekit_controller/test_config_flow.py @@ -52,14 +52,17 @@ INVALID_PAIRING_CODES = [ "111-11-111 ", "111-11-111a", "1111111", + "22222222", ] VALID_PAIRING_CODES = [ - "111-11-111", - "123-45-678", - "11111111", + "114-11-111", + "123-45-679", + "123-45-679 ", + "11121111", "98765432", + " 98765432 ", ] @@ -548,6 +551,7 @@ async def test_user_works(hass, controller): assert get_flow_context(hass, result) == { "source": "user", "unique_id": "00:00:00:00:00:00", + "title_placeholders": {"name": "TestDevice"}, } result = await hass.config_entries.flow.async_configure(