Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump ZHA dependencies #104335

Merged
merged 13 commits into from
Nov 29, 2023
Merged
108 changes: 77 additions & 31 deletions homeassistant/components/zha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
import voluptuous as vol
from zhaquirks import setup as setup_quirks
from zigpy.config import CONF_DATABASE, CONF_DEVICE, CONF_DEVICE_PATH
from zigpy.exceptions import NetworkSettingsInconsistent
from zigpy.exceptions import NetworkSettingsInconsistent, TransientConnectionError

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_TYPE, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import Event, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send
Expand All @@ -29,13 +29,16 @@
CONF_CUSTOM_QUIRKS_PATH,
CONF_DEVICE_CONFIG,
CONF_ENABLE_QUIRKS,
CONF_FLOW_CONTROL,
CONF_RADIO_TYPE,
CONF_USB_PATH,
CONF_ZIGPY,
DATA_ZHA,
DOMAIN,
PLATFORMS,
SIGNAL_ADD_ENTITIES,
STARTUP_FAILURE_DELAY_S,
STARTUP_RETRIES,
RadioType,
)
from .core.device import get_device_automation_triggers
Expand Down Expand Up @@ -158,42 +161,67 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b

_LOGGER.debug("Trigger cache: %s", zha_data.device_trigger_cache)

zha_gateway = ZHAGateway(hass, zha_data.yaml_config, config_entry)
# Retry setup a few times before giving up to deal with missing serial ports in VMs
for attempt in range(STARTUP_RETRIES):
try:
zha_gateway = await ZHAGateway.async_from_config(
hass=hass,
config=zha_data.yaml_config,
config_entry=config_entry,
)
break
except NetworkSettingsInconsistent as exc:
await warn_on_inconsistent_network_settings(
hass,
config_entry=config_entry,
old_state=exc.old_state,
new_state=exc.new_state,
)
raise ConfigEntryError(
"Network settings do not match most recent backup"
) from exc
except TransientConnectionError as exc:
raise ConfigEntryNotReady from exc
except Exception as exc: # pylint: disable=broad-except
_LOGGER.debug(
"Couldn't start coordinator (attempt %s of %s)",
attempt + 1,
STARTUP_RETRIES,
exc_info=exc,
)

try:
await zha_gateway.async_initialize()
except NetworkSettingsInconsistent as exc:
await warn_on_inconsistent_network_settings(
hass,
config_entry=config_entry,
old_state=exc.old_state,
new_state=exc.new_state,
)
raise HomeAssistantError(
"Network settings do not match most recent backup"
) from exc
except Exception:
if RadioType[config_entry.data[CONF_RADIO_TYPE]] == RadioType.ezsp:
try:
await warn_on_wrong_silabs_firmware(
hass, config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH]
)
except AlreadyRunningEZSP as exc:
# If connecting fails but we somehow probe EZSP (e.g. stuck in the
# bootloader), reconnect, it should work
raise ConfigEntryNotReady from exc

raise
if attempt < STARTUP_RETRIES - 1:
await asyncio.sleep(STARTUP_FAILURE_DELAY_S)
continue

if RadioType[config_entry.data[CONF_RADIO_TYPE]] == RadioType.ezsp:
try:
# Ignore all exceptions during probing, they shouldn't halt setup
await warn_on_wrong_silabs_firmware(
hass, config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH]
)
except AlreadyRunningEZSP as ezsp_exc:
raise ConfigEntryNotReady from ezsp_exc

raise

repairs.async_delete_blocking_issues(hass)

manufacturer = zha_gateway.state.node_info.manufacturer
model = zha_gateway.state.node_info.model

if manufacturer is None and model is None:
manufacturer = "Unknown"
model = "Unknown"

device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
connections={(dr.CONNECTION_ZIGBEE, str(zha_gateway.coordinator_ieee))},
identifiers={(DOMAIN, str(zha_gateway.coordinator_ieee))},
connections={(dr.CONNECTION_ZIGBEE, str(zha_gateway.state.node_info.ieee))},
identifiers={(DOMAIN, str(zha_gateway.state.node_info.ieee))},
name="Zigbee Coordinator",
manufacturer="ZHA",
model=zha_gateway.radio_description,
manufacturer=manufacturer,
model=model,
sw_version=zha_gateway.state.node_info.version,
)

websocket_api.async_load_api(hass)
Expand Down Expand Up @@ -267,5 +295,23 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
config_entry.version = 3
hass.config_entries.async_update_entry(config_entry, data=data)

if config_entry.version == 3:
data = {**config_entry.data}

if not data[CONF_DEVICE].get(CONF_BAUDRATE):
data[CONF_DEVICE][CONF_BAUDRATE] = {
"deconz": 38400,
"xbee": 57600,
"ezsp": 57600,
"znp": 115200,
"zigate": 115200,
}[data[CONF_RADIO_TYPE]]

if not data[CONF_DEVICE].get(CONF_FLOW_CONTROL):
data[CONF_DEVICE][CONF_FLOW_CONTROL] = None

config_entry.version = 4
hass.config_entries.async_update_entry(config_entry, data=data)

_LOGGER.info("Migration to version %s successful", config_entry.version)
return True
26 changes: 11 additions & 15 deletions homeassistant/components/zha/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@

from .core.const import (
CONF_BAUDRATE,
CONF_FLOWCONTROL,
CONF_FLOW_CONTROL,
CONF_RADIO_TYPE,
DOMAIN,
RadioType,
)
from .radio_manager import (
DEVICE_SCHEMA,
HARDWARE_DISCOVERY_SCHEMA,
RECOMMENDED_RADIOS,
ProbeResult,
Expand All @@ -42,7 +43,7 @@
CONF_MANUAL_PATH = "Enter Manually"
SUPPORTED_PORT_SETTINGS = (
CONF_BAUDRATE,
CONF_FLOWCONTROL,
CONF_FLOW_CONTROL,
)
DECONZ_DOMAIN = "deconz"

Expand Down Expand Up @@ -160,7 +161,7 @@ async def _async_create_radio_entry(self) -> FlowResult:
return self.async_create_entry(
title=self._title,
data={
CONF_DEVICE: device_settings,
CONF_DEVICE: DEVICE_SCHEMA(device_settings),
CONF_RADIO_TYPE: self._radio_mgr.radio_type.name,
},
)
Expand Down Expand Up @@ -281,7 +282,7 @@ async def async_step_manual_port_config(
for (
param,
value,
) in self._radio_mgr.radio_type.controller.SCHEMA_DEVICE.schema.items():
) in DEVICE_SCHEMA.schema.items():
if param not in SUPPORTED_PORT_SETTINGS:
continue

Expand Down Expand Up @@ -488,7 +489,7 @@ async def async_step_maybe_confirm_ezsp_restore(
class ZhaConfigFlowHandler(BaseZhaFlow, config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""

VERSION = 3
VERSION = 4

async def _set_unique_id_or_update_path(
self, unique_id: str, device_path: str
Expand Down Expand Up @@ -646,22 +647,17 @@ async def async_step_hardware(

name = discovery_data["name"]
radio_type = self._radio_mgr.parse_radio_type(discovery_data["radio_type"])

try:
device_settings = radio_type.controller.SCHEMA_DEVICE(
discovery_data["port"]
)
except vol.Invalid:
return self.async_abort(reason="invalid_hardware_data")
device_settings = discovery_data["port"]
device_path = device_settings[CONF_DEVICE_PATH]

await self._set_unique_id_or_update_path(
unique_id=f"{name}_{radio_type.name}_{device_settings[CONF_DEVICE_PATH]}",
device_path=device_settings[CONF_DEVICE_PATH],
unique_id=f"{name}_{radio_type.name}_{device_path}",
device_path=device_path,
)

self._title = name
self._radio_mgr.radio_type = radio_type
self._radio_mgr.device_path = device_settings[CONF_DEVICE_PATH]
self._radio_mgr.device_path = device_path
self._radio_mgr.device_settings = device_settings
self.context["title_placeholders"] = {CONF_NAME: name}

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/zha/core/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
CONF_ALARM_ARM_REQUIRES_CODE = "alarm_arm_requires_code"

CONF_BAUDRATE = "baudrate"
CONF_FLOW_CONTROL = "flow_control"
CONF_CUSTOM_QUIRKS_PATH = "custom_quirks_path"
CONF_DEFAULT_LIGHT_TRANSITION = "default_light_transition"
CONF_DEVICE_CONFIG = "device_config"
Expand All @@ -136,7 +137,6 @@
CONF_GROUP_MEMBERS_ASSUME_STATE = "group_members_assume_state"
CONF_ENABLE_IDENTIFY_ON_JOIN = "enable_identify_on_join"
CONF_ENABLE_QUIRKS = "enable_quirks"
CONF_FLOWCONTROL = "flow_control"
CONF_RADIO_TYPE = "radio_type"
CONF_USB_PATH = "usb_path"
CONF_USE_THREAD = "use_thread"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/zha/core/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def is_active_coordinator(self) -> bool:
if not self.is_coordinator:
return False

return self.ieee == self.gateway.coordinator_ieee
return self.ieee == self.gateway.state.node_info.ieee

@property
def is_end_device(self) -> bool | None:
Expand Down
Loading
Loading