diff --git a/homeassistant/components/nexia/__init__.py b/homeassistant/components/nexia/__init__.py index 9d9299b1ce9..4d0993d3569 100644 --- a/homeassistant/components/nexia/__init__.py +++ b/homeassistant/components/nexia/__init__.py @@ -7,7 +7,6 @@ from nexia.const import BRAND_NEXIA from nexia.home import NexiaHome from nexia.thermostat import NexiaThermostat -from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady @@ -17,6 +16,7 @@ import homeassistant.helpers.config_validation as cv from .const import CONF_BRAND, DOMAIN, PLATFORMS from .coordinator import NexiaDataUpdateCoordinator +from .types import NexiaConfigEntry from .util import is_invalid_auth_code _LOGGER = logging.getLogger(__name__) @@ -24,7 +24,7 @@ _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_setup_entry(hass: HomeAssistant, entry: NexiaConfigEntry) -> bool: """Configure the base Nexia device for Home Assistant.""" conf = entry.data @@ -63,26 +63,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: coordinator = NexiaDataUpdateCoordinator(hass, nexia_home) await coordinator.async_config_entry_first_refresh() - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator - + entry.runtime_data = coordinator await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True -async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_unload_entry(hass: HomeAssistant, entry: NexiaConfigEntry) -> bool: """Unload a config entry.""" - if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): - hass.data[DOMAIN].pop(entry.entry_id) - return unload_ok + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) async def async_remove_config_entry_device( - hass: HomeAssistant, config_entry: ConfigEntry, device_entry: dr.DeviceEntry + hass: HomeAssistant, entry: NexiaConfigEntry, device_entry: dr.DeviceEntry ) -> bool: """Remove a nexia config entry from a device.""" - coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] - nexia_home: NexiaHome = coordinator.nexia_home + coordinator = entry.runtime_data + nexia_home = coordinator.nexia_home dev_ids = {dev_id[1] for dev_id in device_entry.identifiers if dev_id[0] == DOMAIN} for thermostat_id in nexia_home.get_thermostat_ids(): if thermostat_id in dev_ids: diff --git a/homeassistant/components/nexia/binary_sensor.py b/homeassistant/components/nexia/binary_sensor.py index 9b3d9cab986..204d84ed975 100644 --- a/homeassistant/components/nexia/binary_sensor.py +++ b/homeassistant/components/nexia/binary_sensor.py @@ -1,22 +1,20 @@ """Support for Nexia / Trane XL Thermostats.""" from homeassistant.components.binary_sensor import BinarySensorEntity -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN -from .coordinator import NexiaDataUpdateCoordinator from .entity import NexiaThermostatEntity +from .types import NexiaConfigEntry async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: NexiaConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up sensors for a Nexia device.""" - coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + coordinator = config_entry.runtime_data nexia_home = coordinator.nexia_home entities = [] diff --git a/homeassistant/components/nexia/climate.py b/homeassistant/components/nexia/climate.py index 7c28062f4b8..a4bcc03c210 100644 --- a/homeassistant/components/nexia/climate.py +++ b/homeassistant/components/nexia/climate.py @@ -15,7 +15,6 @@ from nexia.const import ( SYSTEM_STATUS_HEAT, SYSTEM_STATUS_IDLE, ) -from nexia.home import NexiaHome from nexia.thermostat import NexiaThermostat from nexia.util import find_humidity_setpoint from nexia.zone import NexiaThermostatZone @@ -31,7 +30,6 @@ from homeassistant.components.climate import ( HVACAction, HVACMode, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_platform @@ -44,10 +42,10 @@ from .const import ( ATTR_DEHUMIDIFY_SETPOINT, ATTR_HUMIDIFY_SETPOINT, ATTR_RUN_MODE, - DOMAIN, ) from .coordinator import NexiaDataUpdateCoordinator from .entity import NexiaThermostatZoneEntity +from .types import NexiaConfigEntry from .util import percent_conv PARALLEL_UPDATES = 1 # keep data in sync with only one connection at a time @@ -116,12 +114,12 @@ NEXIA_SUPPORTED = ( async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: NexiaConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up climate for a Nexia device.""" - coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] - nexia_home: NexiaHome = coordinator.nexia_home + coordinator = config_entry.runtime_data + nexia_home = coordinator.nexia_home platform = entity_platform.async_get_current_platform() @@ -162,22 +160,23 @@ class NexiaZone(NexiaThermostatZoneEntity, ClimateEntity): ) -> None: """Initialize the thermostat.""" super().__init__(coordinator, zone, zone.zone_id) - unit = self._thermostat.get_unit() - min_humidity, max_humidity = self._thermostat.get_humidity_setpoint_limits() - min_setpoint, max_setpoint = self._thermostat.get_setpoint_limits() + thermostat = self._thermostat + unit = thermostat.get_unit() + min_humidity, max_humidity = thermostat.get_humidity_setpoint_limits() + min_setpoint, max_setpoint = thermostat.get_setpoint_limits() # The has_* calls are stable for the life of the device # and do not do I/O - self._has_relative_humidity = self._thermostat.has_relative_humidity() - self._has_emergency_heat = self._thermostat.has_emergency_heat() - self._has_humidify_support = self._thermostat.has_humidify_support() - self._has_dehumidify_support = self._thermostat.has_dehumidify_support() + self._has_relative_humidity = thermostat.has_relative_humidity() + self._has_emergency_heat = thermostat.has_emergency_heat() + self._has_humidify_support = thermostat.has_humidify_support() + self._has_dehumidify_support = thermostat.has_dehumidify_support() self._attr_supported_features = NEXIA_SUPPORTED if self._has_humidify_support or self._has_dehumidify_support: self._attr_supported_features |= ClimateEntityFeature.TARGET_HUMIDITY if self._has_emergency_heat: self._attr_supported_features |= ClimateEntityFeature.AUX_HEAT - self._attr_preset_modes = self._zone.get_presets() - self._attr_fan_modes = self._thermostat.get_fan_modes() + self._attr_preset_modes = zone.get_presets() + self._attr_fan_modes = thermostat.get_fan_modes() self._attr_hvac_modes = HVAC_MODES self._attr_min_humidity = percent_conv(min_humidity) self._attr_max_humidity = percent_conv(max_humidity) diff --git a/homeassistant/components/nexia/diagnostics.py b/homeassistant/components/nexia/diagnostics.py index e03cf23b83b..877aad30cb0 100644 --- a/homeassistant/components/nexia/diagnostics.py +++ b/homeassistant/components/nexia/diagnostics.py @@ -5,11 +5,10 @@ from __future__ import annotations from typing import Any from homeassistant.components.diagnostics import async_redact_data -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from .const import CONF_BRAND, DOMAIN -from .coordinator import NexiaDataUpdateCoordinator +from .const import CONF_BRAND +from .types import NexiaConfigEntry TO_REDACT = { "dealer_contact_info", @@ -17,10 +16,10 @@ TO_REDACT = { async def async_get_config_entry_diagnostics( - hass: HomeAssistant, entry: ConfigEntry + hass: HomeAssistant, entry: NexiaConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" - coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + coordinator = entry.runtime_data nexia_home = coordinator.nexia_home return { diff --git a/homeassistant/components/nexia/entity.py b/homeassistant/components/nexia/entity.py index dfb2366d34a..affe693d2c8 100644 --- a/homeassistant/components/nexia/entity.py +++ b/homeassistant/components/nexia/entity.py @@ -1,5 +1,7 @@ """The nexia integration base entity.""" +from typing import TYPE_CHECKING + from nexia.thermostat import NexiaThermostat from nexia.zone import NexiaThermostatZone @@ -42,31 +44,38 @@ class NexiaThermostatEntity(NexiaEntity): _attr_has_entity_name = True - def __init__(self, coordinator, thermostat, unique_id): + def __init__( + self, + coordinator: NexiaDataUpdateCoordinator, + thermostat: NexiaThermostat, + unique_id: str, + ) -> None: """Initialize the entity.""" super().__init__(coordinator, unique_id) - self._thermostat: NexiaThermostat = thermostat + self._thermostat = thermostat + thermostat_id = thermostat.thermostat_id self._attr_device_info = DeviceInfo( configuration_url=self.coordinator.nexia_home.root_url, - identifiers={(DOMAIN, self._thermostat.thermostat_id)}, + identifiers={(DOMAIN, thermostat_id)}, manufacturer=MANUFACTURER, - model=self._thermostat.get_model(), - name=self._thermostat.get_name(), - sw_version=self._thermostat.get_firmware(), + model=thermostat.get_model(), + name=thermostat.get_name(), + sw_version=thermostat.get_firmware(), ) + self._thermostat_signal = f"{SIGNAL_THERMOSTAT_UPDATE}-{thermostat_id}" - async def async_added_to_hass(self): + async def async_added_to_hass(self) -> None: """Listen for signals for services.""" await super().async_added_to_hass() self.async_on_remove( async_dispatcher_connect( self.hass, - f"{SIGNAL_THERMOSTAT_UPDATE}-{self._thermostat.thermostat_id}", + self._thermostat_signal, self.async_write_ha_state, ) ) - def _signal_thermostat_update(self): + def _signal_thermostat_update(self) -> None: """Signal a thermostat update. Whenever the underlying library does an action against @@ -75,9 +84,7 @@ class NexiaThermostatEntity(NexiaEntity): Update all the zones on the thermostat. """ - async_dispatcher_send( - self.hass, f"{SIGNAL_THERMOSTAT_UPDATE}-{self._thermostat.thermostat_id}" - ) + async_dispatcher_send(self.hass, self._thermostat_signal) @property def available(self) -> bool: @@ -88,30 +95,38 @@ class NexiaThermostatEntity(NexiaEntity): class NexiaThermostatZoneEntity(NexiaThermostatEntity): """Base class for nexia devices attached to a thermostat.""" - def __init__(self, coordinator, zone, unique_id): + def __init__( + self, + coordinator: NexiaDataUpdateCoordinator, + zone: NexiaThermostatZone, + unique_id: str, + ) -> None: """Initialize the entity.""" super().__init__(coordinator, zone.thermostat, unique_id) - self._zone: NexiaThermostatZone = zone + self._zone = zone zone_name = self._zone.get_name() + if TYPE_CHECKING: + assert self._attr_device_info is not None self._attr_device_info |= { - ATTR_IDENTIFIERS: {(DOMAIN, self._zone.zone_id)}, + ATTR_IDENTIFIERS: {(DOMAIN, zone.zone_id)}, ATTR_NAME: zone_name, ATTR_SUGGESTED_AREA: zone_name, - ATTR_VIA_DEVICE: (DOMAIN, self._zone.thermostat.thermostat_id), + ATTR_VIA_DEVICE: (DOMAIN, zone.thermostat.thermostat_id), } + self._zone_signal = f"{SIGNAL_ZONE_UPDATE}-{zone.zone_id}" - async def async_added_to_hass(self): + async def async_added_to_hass(self) -> None: """Listen for signals for services.""" await super().async_added_to_hass() self.async_on_remove( async_dispatcher_connect( self.hass, - f"{SIGNAL_ZONE_UPDATE}-{self._zone.zone_id}", + self._zone_signal, self.async_write_ha_state, ) ) - def _signal_zone_update(self): + def _signal_zone_update(self) -> None: """Signal a zone update. Whenever the underlying library does an action against @@ -119,4 +134,4 @@ class NexiaThermostatZoneEntity(NexiaThermostatEntity): Update a single zone. """ - async_dispatcher_send(self.hass, f"{SIGNAL_ZONE_UPDATE}-{self._zone.zone_id}") + async_dispatcher_send(self.hass, self._zone_signal) diff --git a/homeassistant/components/nexia/number.py b/homeassistant/components/nexia/number.py index a4117584720..46cc4d094a3 100644 --- a/homeassistant/components/nexia/number.py +++ b/homeassistant/components/nexia/number.py @@ -2,29 +2,27 @@ from __future__ import annotations -from nexia.home import NexiaHome from nexia.thermostat import NexiaThermostat from homeassistant.components.number import NumberEntity -from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN from .coordinator import NexiaDataUpdateCoordinator from .entity import NexiaThermostatEntity +from .types import NexiaConfigEntry from .util import percent_conv async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: NexiaConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up sensors for a Nexia device.""" - coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] - nexia_home: NexiaHome = coordinator.nexia_home + coordinator = config_entry.runtime_data + nexia_home = coordinator.nexia_home entities: list[NexiaThermostatEntity] = [] for thermostat_id in nexia_home.get_thermostat_ids(): diff --git a/homeassistant/components/nexia/scene.py b/homeassistant/components/nexia/scene.py index 337068e44e9..60078fab822 100644 --- a/homeassistant/components/nexia/scene.py +++ b/homeassistant/components/nexia/scene.py @@ -5,25 +5,25 @@ from typing import Any from nexia.automation import NexiaAutomation from homeassistant.components.scene import Scene -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_call_later -from .const import ATTR_DESCRIPTION, DOMAIN +from .const import ATTR_DESCRIPTION from .coordinator import NexiaDataUpdateCoordinator from .entity import NexiaEntity +from .types import NexiaConfigEntry SCENE_ACTIVATION_TIME = 5 async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: NexiaConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up automations for a Nexia device.""" - coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + coordinator = config_entry.runtime_data nexia_home = coordinator.nexia_home async_add_entities( NexiaAutomationScene( @@ -42,12 +42,9 @@ class NexiaAutomationScene(NexiaEntity, Scene): self, coordinator: NexiaDataUpdateCoordinator, automation: NexiaAutomation ) -> None: """Initialize the automation scene.""" - super().__init__( - coordinator, - automation.automation_id, - ) + super().__init__(coordinator, automation.automation_id) self._attr_name = automation.name - self._automation: NexiaAutomation = automation + self._automation = automation self._attr_extra_state_attributes = {ATTR_DESCRIPTION: automation.description} async def async_activate(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/nexia/sensor.py b/homeassistant/components/nexia/sensor.py index a77920630f8..e50bd750c2f 100644 --- a/homeassistant/components/nexia/sensor.py +++ b/homeassistant/components/nexia/sensor.py @@ -10,25 +10,23 @@ from homeassistant.components.sensor import ( SensorEntity, SensorStateClass, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN -from .coordinator import NexiaDataUpdateCoordinator from .entity import NexiaThermostatEntity, NexiaThermostatZoneEntity +from .types import NexiaConfigEntry from .util import percent_conv async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: NexiaConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up sensors for a Nexia device.""" - coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] + coordinator = config_entry.runtime_data nexia_home = coordinator.nexia_home entities: list[NexiaThermostatEntity] = [] diff --git a/homeassistant/components/nexia/switch.py b/homeassistant/components/nexia/switch.py index 5e19136d55e..0a874ba1817 100644 --- a/homeassistant/components/nexia/switch.py +++ b/homeassistant/components/nexia/switch.py @@ -5,28 +5,26 @@ from __future__ import annotations from typing import Any from nexia.const import OPERATION_MODE_OFF -from nexia.home import NexiaHome from nexia.thermostat import NexiaThermostat from nexia.zone import NexiaThermostatZone from homeassistant.components.switch import SwitchEntity -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .const import DOMAIN from .coordinator import NexiaDataUpdateCoordinator from .entity import NexiaThermostatZoneEntity +from .types import NexiaConfigEntry async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: NexiaConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up switches for a Nexia device.""" - coordinator: NexiaDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id] - nexia_home: NexiaHome = coordinator.nexia_home + coordinator = config_entry.runtime_data + nexia_home = coordinator.nexia_home entities: list[NexiaHoldSwitch] = [] for thermostat_id in nexia_home.get_thermostat_ids(): thermostat: NexiaThermostat = nexia_home.get_thermostat_by_id(thermostat_id) diff --git a/homeassistant/components/nexia/types.py b/homeassistant/components/nexia/types.py new file mode 100644 index 00000000000..4ba4ff89f3e --- /dev/null +++ b/homeassistant/components/nexia/types.py @@ -0,0 +1,7 @@ +"""Support for Nexia / Trane XL Thermostats.""" + +from homeassistant.config_entries import ConfigEntry + +from .coordinator import NexiaDataUpdateCoordinator + +type NexiaConfigEntry = ConfigEntry[NexiaDataUpdateCoordinator]