diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 8a81648b580..51357e28f02 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -18,7 +18,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.storage import STORAGE_DIR from homeassistant.helpers.typing import ConfigType -from . import websocket_api +from . import repairs, websocket_api from .core import ZHAGateway from .core.const import ( BAUD_RATES, @@ -135,7 +135,19 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b _LOGGER.debug("ZHA storage file does not exist or was already removed") zha_gateway = ZHAGateway(hass, config, config_entry) - await zha_gateway.async_initialize() + + try: + await zha_gateway.async_initialize() + except Exception: # pylint: disable=broad-except + radio_type = RadioType[config_entry.data[CONF_RADIO_TYPE]] + device = config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH] + + if radio_type == RadioType.ezsp and not device.startswith("socket://"): + await repairs.warn_on_wrong_silabs_firmware(hass, device) + + raise + + repairs.async_delete_blocking_issues(hass) device_registry = dr.async_get(hass) device_registry.async_get_or_create( diff --git a/homeassistant/components/zha/repairs.py b/homeassistant/components/zha/repairs.py new file mode 100644 index 00000000000..be568a40fcf --- /dev/null +++ b/homeassistant/components/zha/repairs.py @@ -0,0 +1,67 @@ +"""ZHA repairs for common environmental and device problems.""" +from __future__ import annotations + +from universal_silabs_flasher.const import ApplicationType +from universal_silabs_flasher.flasher import Flasher + +from homeassistant.components.homeassistant_yellow import hardware as yellow_hardware +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError +from homeassistant.helpers import issue_registry as ir + +from .core.const import DOMAIN + +DISABLE_MULTIPAN_URL_YELLOW = ( + "https://yellow.home-assistant.io/guides/disable-multiprotocol/" +) +DISABLE_MULTIPAN_URL_SKYCONNECT = ( + "https://skyconnect.home-assistant.io/procedures/disable-multiprotocol/" +) + + +ISSUE_WRONG_SILABS_FIRMWARE_INSTALLED = "wrong_silabs_firmware_installed" + + +async def probe_silabs_firmware_type(device: str) -> ApplicationType | None: + """Probe the running firmware on a Silabs device.""" + flasher = Flasher(device=device) + await flasher.probe_app_type() + + return flasher.app_type + + +async def warn_on_wrong_silabs_firmware(hass: HomeAssistant, device: str) -> None: + """Create a repair issue if the wrong type of SiLabs firmware is detected.""" + app_type = await probe_silabs_firmware_type(device) + + if app_type is None: + # Failed to probe, we can't tell if the wrong firmware is installed + return + + if app_type == ApplicationType.EZSP: + # If connecting fails but we somehow probe EZSP (e.g. stuck in bootloader), + # reconnect, it should work + raise ConfigEntryNotReady() + + try: + yellow_hardware.async_info(hass) + learn_more_url = DISABLE_MULTIPAN_URL_YELLOW + except HomeAssistantError: + learn_more_url = DISABLE_MULTIPAN_URL_SKYCONNECT + + ir.async_create_issue( + hass, + DOMAIN, + ISSUE_WRONG_SILABS_FIRMWARE_INSTALLED, + is_fixable=False, + is_persistent=False, + learn_more_url=learn_more_url, + severity=ir.IssueSeverity.CRITICAL, + translation_key=ISSUE_WRONG_SILABS_FIRMWARE_INSTALLED, + translation_placeholders={"firmware_type": app_type.name}, + ) + + +def async_delete_blocking_issues(hass: HomeAssistant) -> None: + """Delete repair issues that should disappear on a successful startup.""" + ir.async_delete_issue(hass, DOMAIN, ISSUE_WRONG_SILABS_FIRMWARE_INSTALLED) diff --git a/homeassistant/components/zha/strings.json b/homeassistant/components/zha/strings.json index 3829ee68bb5..e15ba9c999d 100644 --- a/homeassistant/components/zha/strings.json +++ b/homeassistant/components/zha/strings.json @@ -502,5 +502,11 @@ } } } + }, + "issues": { + "wrong_silabs_firmware_installed": { + "title": "Zigbee radio has the wrong firmware installed", + "description": "Your Zigbee radio was previously used with multi-PAN and has has non-Zigbee firmware installed ({firmware_type}). It cannot be used with ZHA directly until Zigbee firmware is installed again. Click the \"Learn More\" link below and follow the instructions to re-install Zigbee firmware and reload the integration to try again." + } } }