Convert nexia to use entry.runtime_data (#121640)

pull/121651/head
J. Nick Koston 2024-07-09 23:35:13 -07:00 committed by GitHub
parent bf09cee66f
commit 6f15352eda
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 88 additions and 82 deletions

View File

@ -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:

View File

@ -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 = []

View File

@ -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)

View File

@ -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 {

View File

@ -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)

View File

@ -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():

View File

@ -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:

View File

@ -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] = []

View File

@ -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)

View File

@ -0,0 +1,7 @@
"""Support for Nexia / Trane XL Thermostats."""
from homeassistant.config_entries import ConfigEntry
from .coordinator import NexiaDataUpdateCoordinator
type NexiaConfigEntry = ConfigEntry[NexiaDataUpdateCoordinator]