Use runtime_data in isy994 (#144961)

This commit is contained in:
epenet 2025-05-15 20:50:48 +02:00 committed by GitHub
parent 50e6c83dd8
commit d195726ed2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 90 additions and 131 deletions

View File

@ -10,7 +10,6 @@ from pyisy import ISY, ISYConnectionError, ISYInvalidAuthError, ISYResponseParse
from pyisy.constants import CONFIG_NETWORKING, CONFIG_PORTAL from pyisy.constants import CONFIG_NETWORKING, CONFIG_PORTAL
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_HOST,
CONF_PASSWORD, CONF_PASSWORD,
@ -46,7 +45,7 @@ from .const import (
SCHEME_HTTPS, SCHEME_HTTPS,
) )
from .helpers import _categorize_nodes, _categorize_programs from .helpers import _categorize_nodes, _categorize_programs
from .models import IsyData from .models import IsyConfigEntry, IsyData
from .services import async_setup_services, async_unload_services from .services import async_setup_services, async_unload_services
from .util import _async_cleanup_registry_entries from .util import _async_cleanup_registry_entries
@ -56,13 +55,8 @@ CONFIG_SCHEMA = vol.Schema(
) )
async def async_setup_entry( async def async_setup_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
hass: HomeAssistant, entry: config_entries.ConfigEntry
) -> bool:
"""Set up the ISY 994 integration.""" """Set up the ISY 994 integration."""
hass.data.setdefault(DOMAIN, {})
isy_data = hass.data[DOMAIN][entry.entry_id] = IsyData()
isy_config = entry.data isy_config = entry.data
isy_options = entry.options isy_options = entry.options
@ -127,6 +121,7 @@ async def async_setup_entry(
f"Invalid response ISY, device is likely still starting: {err}" f"Invalid response ISY, device is likely still starting: {err}"
) from err ) from err
isy_data = entry.runtime_data = IsyData()
_categorize_nodes(isy_data, isy.nodes, ignore_identifier, sensor_identifier) _categorize_nodes(isy_data, isy.nodes, ignore_identifier, sensor_identifier)
_categorize_programs(isy_data, isy.programs) _categorize_programs(isy_data, isy.programs)
# Gather ISY Variables to be added. # Gather ISY Variables to be added.
@ -156,7 +151,7 @@ async def async_setup_entry(
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
# Clean-up any old entities that we no longer provide. # Clean-up any old entities that we no longer provide.
_async_cleanup_registry_entries(hass, entry.entry_id) _async_cleanup_registry_entries(hass, entry)
@callback @callback
def _async_stop_auto_update(event: Event) -> None: def _async_stop_auto_update(event: Event) -> None:
@ -178,16 +173,14 @@ async def async_setup_entry(
return True return True
async def _async_update_listener( async def _async_update_listener(hass: HomeAssistant, entry: IsyConfigEntry) -> None:
hass: HomeAssistant, entry: config_entries.ConfigEntry
) -> None:
"""Handle options update.""" """Handle options update."""
await hass.config_entries.async_reload(entry.entry_id) await hass.config_entries.async_reload(entry.entry_id)
@callback @callback
def _async_get_or_create_isy_device_in_registry( def _async_get_or_create_isy_device_in_registry(
hass: HomeAssistant, entry: config_entries.ConfigEntry, isy: ISY hass: HomeAssistant, entry: IsyConfigEntry, isy: ISY
) -> None: ) -> None:
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)
device_registry.async_get_or_create( device_registry.async_get_or_create(
@ -221,22 +214,14 @@ def _create_service_device_info(isy: ISY, name: str, unique_id: str) -> DeviceIn
) )
async def async_unload_entry( async def async_unload_entry(hass: HomeAssistant, entry: IsyConfigEntry) -> bool:
hass: HomeAssistant, entry: config_entries.ConfigEntry
) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
isy_data: IsyData = hass.data[DOMAIN][entry.entry_id]
isy = isy_data.root
_LOGGER.debug("ISY Stopping Event Stream and automatic updates") _LOGGER.debug("ISY Stopping Event Stream and automatic updates")
isy.websocket.stop() entry.runtime_data.root.websocket.stop()
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
if not hass.config_entries.async_loaded_entries(DOMAIN):
async_unload_services(hass) async_unload_services(hass)
return unload_ok return unload_ok
@ -244,11 +229,10 @@ async def async_unload_entry(
async def async_remove_config_entry_device( async def async_remove_config_entry_device(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: config_entries.ConfigEntry, config_entry: IsyConfigEntry,
device_entry: dr.DeviceEntry, device_entry: dr.DeviceEntry,
) -> bool: ) -> bool:
"""Remove ISY config entry from a device.""" """Remove ISY config entry from a device."""
isy_data = hass.data[DOMAIN][config_entry.entry_id]
return not device_entry.identifiers.intersection( return not device_entry.identifiers.intersection(
(DOMAIN, unique_id) for unique_id in isy_data.devices (DOMAIN, unique_id) for unique_id in config_entry.runtime_data.devices
) )

View File

@ -19,7 +19,6 @@ from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass, BinarySensorDeviceClass,
BinarySensorEntity, BinarySensorEntity,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_ON, Platform from homeassistant.const import STATE_ON, Platform
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
@ -31,7 +30,6 @@ from .const import (
_LOGGER, _LOGGER,
BINARY_SENSOR_DEVICE_TYPES_ISY, BINARY_SENSOR_DEVICE_TYPES_ISY,
BINARY_SENSOR_DEVICE_TYPES_ZWAVE, BINARY_SENSOR_DEVICE_TYPES_ZWAVE,
DOMAIN,
SUBNODE_CLIMATE_COOL, SUBNODE_CLIMATE_COOL,
SUBNODE_CLIMATE_HEAT, SUBNODE_CLIMATE_HEAT,
SUBNODE_DUSK_DAWN, SUBNODE_DUSK_DAWN,
@ -44,7 +42,7 @@ from .const import (
TYPE_INSTEON_MOTION, TYPE_INSTEON_MOTION,
) )
from .entity import ISYNodeEntity, ISYProgramEntity from .entity import ISYNodeEntity, ISYProgramEntity
from .models import IsyData from .models import IsyConfigEntry
DEVICE_PARENT_REQUIRED = [ DEVICE_PARENT_REQUIRED = [
BinarySensorDeviceClass.OPENING, BinarySensorDeviceClass.OPENING,
@ -55,7 +53,7 @@ DEVICE_PARENT_REQUIRED = [
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the ISY binary sensor platform.""" """Set up the ISY binary sensor platform."""
@ -82,8 +80,8 @@ async def async_setup_entry(
| ISYBinarySensorProgramEntity | ISYBinarySensorProgramEntity
) )
isy_data: IsyData = hass.data[DOMAIN][entry.entry_id] isy_data = entry.runtime_data
devices: dict[str, DeviceInfo] = isy_data.devices devices = isy_data.devices
for node in isy_data.nodes[Platform.BINARY_SENSOR]: for node in isy_data.nodes[Platform.BINARY_SENSOR]:
assert isinstance(node, Node) assert isinstance(node, Node)
device_info = devices.get(node.primary_node) device_info = devices.get(node.primary_node)

View File

@ -15,24 +15,23 @@ from pyisy.networking import NetworkCommand
from pyisy.nodes import Node from pyisy.nodes import Node
from homeassistant.components.button import ButtonEntity from homeassistant.components.button import ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, Platform from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import CONF_NETWORK, DOMAIN from .const import CONF_NETWORK, DOMAIN
from .models import IsyData from .models import IsyConfigEntry
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up ISY/IoX button from config entry.""" """Set up ISY/IoX button from config entry."""
isy_data: IsyData = hass.data[DOMAIN][config_entry.entry_id] isy_data = config_entry.runtime_data
isy: ISY = isy_data.root isy = isy_data.root
device_info = isy_data.devices device_info = isy_data.devices
entities: list[ entities: list[
ISYNodeQueryButtonEntity ISYNodeQueryButtonEntity

View File

@ -28,7 +28,6 @@ from homeassistant.components.climate import (
HVACAction, HVACAction,
HVACMode, HVACMode,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_TEMPERATURE, ATTR_TEMPERATURE,
PRECISION_TENTHS, PRECISION_TENTHS,
@ -42,7 +41,6 @@ from homeassistant.util.enum import try_parse_enum
from .const import ( from .const import (
_LOGGER, _LOGGER,
DOMAIN,
HA_FAN_TO_ISY, HA_FAN_TO_ISY,
HA_HVAC_TO_ISY, HA_HVAC_TO_ISY,
ISY_HVAC_MODES, ISY_HVAC_MODES,
@ -57,18 +55,18 @@ from .const import (
) )
from .entity import ISYNodeEntity from .entity import ISYNodeEntity
from .helpers import convert_isy_value_to_hass from .helpers import convert_isy_value_to_hass
from .models import IsyData from .models import IsyConfigEntry
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the ISY thermostat platform.""" """Set up the ISY thermostat platform."""
isy_data: IsyData = hass.data[DOMAIN][entry.entry_id] isy_data = entry.runtime_data
devices: dict[str, DeviceInfo] = isy_data.devices devices = isy_data.devices
async_add_entities( async_add_entities(
ISYThermostatEntity(node, devices.get(node.primary_node)) ISYThermostatEntity(node, devices.get(node.primary_node))

View File

@ -16,7 +16,6 @@ import voluptuous as vol
from homeassistant.config_entries import ( from homeassistant.config_entries import (
SOURCE_IGNORE, SOURCE_IGNORE,
ConfigEntry,
ConfigFlow, ConfigFlow,
ConfigFlowResult, ConfigFlowResult,
OptionsFlow, OptionsFlow,
@ -54,6 +53,7 @@ from .const import (
SCHEME_HTTPS, SCHEME_HTTPS,
UDN_UUID_PREFIX, UDN_UUID_PREFIX,
) )
from .models import IsyConfigEntry
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -137,12 +137,12 @@ class Isy994ConfigFlow(ConfigFlow, domain=DOMAIN):
def __init__(self) -> None: def __init__(self) -> None:
"""Initialize the ISY/IoX config flow.""" """Initialize the ISY/IoX config flow."""
self.discovered_conf: dict[str, str] = {} self.discovered_conf: dict[str, str] = {}
self._existing_entry: ConfigEntry | None = None self._existing_entry: IsyConfigEntry | None = None
@staticmethod @staticmethod
@callback @callback
def async_get_options_flow( def async_get_options_flow(
config_entry: ConfigEntry, config_entry: IsyConfigEntry,
) -> OptionsFlow: ) -> OptionsFlow:
"""Get the options flow for this handler.""" """Get the options flow for this handler."""
return OptionsFlowHandler() return OptionsFlowHandler()

View File

@ -11,25 +11,23 @@ from homeassistant.components.cover import (
CoverEntity, CoverEntity,
CoverEntityFeature, CoverEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import _LOGGER, DOMAIN, UOM_8_BIT_RANGE from .const import _LOGGER, UOM_8_BIT_RANGE
from .entity import ISYNodeEntity, ISYProgramEntity from .entity import ISYNodeEntity, ISYProgramEntity
from .models import IsyData from .models import IsyConfigEntry
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the ISY cover platform.""" """Set up the ISY cover platform."""
isy_data: IsyData = hass.data[DOMAIN][entry.entry_id] isy_data = entry.runtime_data
devices: dict[str, DeviceInfo] = isy_data.devices devices = isy_data.devices
entities: list[ISYCoverEntity | ISYCoverProgramEntity] = [ entities: list[ISYCoverEntity | ISYCoverProgramEntity] = [
ISYCoverEntity(node, devices.get(node.primary_node)) ISYCoverEntity(node, devices.get(node.primary_node))
for node in isy_data.nodes[Platform.COVER] for node in isy_data.nodes[Platform.COVER]

View File

@ -8,10 +8,8 @@ from typing import Any
from pyisy.constants import ISY_VALUE_UNKNOWN, PROTO_INSTEON from pyisy.constants import ISY_VALUE_UNKNOWN, PROTO_INSTEON
from homeassistant.components.fan import FanEntity, FanEntityFeature from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util.percentage import ( from homeassistant.util.percentage import (
percentage_to_ranged_value, percentage_to_ranged_value,
@ -19,21 +17,21 @@ from homeassistant.util.percentage import (
) )
from homeassistant.util.scaling import int_states_in_range from homeassistant.util.scaling import int_states_in_range
from .const import _LOGGER, DOMAIN from .const import _LOGGER
from .entity import ISYNodeEntity, ISYProgramEntity from .entity import ISYNodeEntity, ISYProgramEntity
from .models import IsyData from .models import IsyConfigEntry
SPEED_RANGE = (1, 255) # off is not included SPEED_RANGE = (1, 255) # off is not included
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the ISY fan platform.""" """Set up the ISY fan platform."""
isy_data: IsyData = hass.data[DOMAIN][entry.entry_id] isy_data = entry.runtime_data
devices: dict[str, DeviceInfo] = isy_data.devices devices = isy_data.devices
entities: list[ISYFanEntity | ISYFanProgramEntity] = [ entities: list[ISYFanEntity | ISYFanProgramEntity] = [
ISYFanEntity(node, devices.get(node.primary_node)) ISYFanEntity(node, devices.get(node.primary_node))
for node in isy_data.nodes[Platform.FAN] for node in isy_data.nodes[Platform.FAN]

View File

@ -9,28 +9,27 @@ from pyisy.helpers import NodeProperty
from pyisy.nodes import Node from pyisy.nodes import Node
from homeassistant.components.light import ColorMode, LightEntity from homeassistant.components.light import ColorMode, LightEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.restore_state import RestoreEntity
from .const import _LOGGER, CONF_RESTORE_LIGHT_STATE, DOMAIN, UOM_PERCENTAGE from .const import _LOGGER, CONF_RESTORE_LIGHT_STATE, UOM_PERCENTAGE
from .entity import ISYNodeEntity from .entity import ISYNodeEntity
from .models import IsyData from .models import IsyConfigEntry
ATTR_LAST_BRIGHTNESS = "last_brightness" ATTR_LAST_BRIGHTNESS = "last_brightness"
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the ISY light platform.""" """Set up the ISY light platform."""
isy_data: IsyData = hass.data[DOMAIN][entry.entry_id] isy_data = entry.runtime_data
devices: dict[str, DeviceInfo] = isy_data.devices devices = isy_data.devices
isy_options = entry.options isy_options = entry.options
restore_light_state = isy_options.get(CONF_RESTORE_LIGHT_STATE, False) restore_light_state = isy_options.get(CONF_RESTORE_LIGHT_STATE, False)

View File

@ -7,19 +7,16 @@ from typing import Any
from pyisy.constants import ISY_VALUE_UNKNOWN from pyisy.constants import ISY_VALUE_UNKNOWN
from homeassistant.components.lock import LockEntity from homeassistant.components.lock import LockEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import ( from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback, AddConfigEntryEntitiesCallback,
async_get_current_platform, async_get_current_platform,
) )
from .const import DOMAIN
from .entity import ISYNodeEntity, ISYProgramEntity from .entity import ISYNodeEntity, ISYProgramEntity
from .models import IsyData from .models import IsyConfigEntry
from .services import ( from .services import (
SERVICE_DELETE_USER_CODE_SCHEMA, SERVICE_DELETE_USER_CODE_SCHEMA,
SERVICE_DELETE_ZWAVE_LOCK_USER_CODE, SERVICE_DELETE_ZWAVE_LOCK_USER_CODE,
@ -49,12 +46,12 @@ def async_setup_lock_services(hass: HomeAssistant) -> None:
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the ISY lock platform.""" """Set up the ISY lock platform."""
isy_data: IsyData = hass.data[DOMAIN][entry.entry_id] isy_data = entry.runtime_data
devices: dict[str, DeviceInfo] = isy_data.devices devices = isy_data.devices
entities: list[ISYLockEntity | ISYLockProgramEntity] = [ entities: list[ISYLockEntity | ISYLockProgramEntity] = [
ISYLockEntity(node, devices.get(node.primary_node)) ISYLockEntity(node, devices.get(node.primary_node))
for node in isy_data.nodes[Platform.LOCK] for node in isy_data.nodes[Platform.LOCK]

View File

@ -12,6 +12,7 @@ from pyisy.nodes import Group, Node
from pyisy.programs import Program from pyisy.programs import Program
from pyisy.variables import Variable from pyisy.variables import Variable
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
@ -24,6 +25,8 @@ from .const import (
VARIABLE_PLATFORMS, VARIABLE_PLATFORMS,
) )
type IsyConfigEntry = ConfigEntry[IsyData]
@dataclass @dataclass
class IsyData: class IsyData:

View File

@ -26,7 +26,6 @@ from homeassistant.components.number import (
NumberMode, NumberMode,
RestoreNumber, RestoreNumber,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_VARIABLES, CONF_VARIABLES,
PERCENTAGE, PERCENTAGE,
@ -44,15 +43,10 @@ from homeassistant.util.percentage import (
ranged_value_to_percentage, ranged_value_to_percentage,
) )
from .const import ( from .const import CONF_VAR_SENSOR_STRING, DEFAULT_VAR_SENSOR_STRING, UOM_8_BIT_RANGE
CONF_VAR_SENSOR_STRING,
DEFAULT_VAR_SENSOR_STRING,
DOMAIN,
UOM_8_BIT_RANGE,
)
from .entity import ISYAuxControlEntity from .entity import ISYAuxControlEntity
from .helpers import convert_isy_value_to_hass from .helpers import convert_isy_value_to_hass
from .models import IsyData from .models import IsyConfigEntry
ISY_MAX_SIZE = (2**32) / 2 ISY_MAX_SIZE = (2**32) / 2
ON_RANGE = (1, 255) # Off is not included ON_RANGE = (1, 255) # Off is not included
@ -79,11 +73,11 @@ BACKLIGHT_MEMORY_FILTER = {"memory": DEV_BL_ADDR, "cmd1": DEV_CMD_MEMORY_WRITE}
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up ISY/IoX number entities from config entry.""" """Set up ISY/IoX number entities from config entry."""
isy_data: IsyData = hass.data[DOMAIN][config_entry.entry_id] isy_data = config_entry.runtime_data
device_info = isy_data.devices device_info = isy_data.devices
entities: list[ entities: list[
ISYVariableNumberEntity | ISYAuxControlNumberEntity | ISYBacklightNumberEntity ISYVariableNumberEntity | ISYAuxControlNumberEntity | ISYBacklightNumberEntity

View File

@ -23,7 +23,6 @@ from pyisy.helpers import EventListener, NodeProperty
from pyisy.nodes import Node, NodeChangedEvent from pyisy.nodes import Node, NodeChangedEvent
from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
STATE_UNKNOWN, STATE_UNKNOWN,
@ -37,9 +36,9 @@ from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.restore_state import RestoreEntity
from .const import _LOGGER, DOMAIN, UOM_INDEX from .const import _LOGGER, UOM_INDEX
from .entity import ISYAuxControlEntity from .entity import ISYAuxControlEntity
from .models import IsyData from .models import IsyConfigEntry
def time_string(i: int) -> str: def time_string(i: int) -> str:
@ -55,11 +54,11 @@ BACKLIGHT_MEMORY_FILTER = {"memory": DEV_BL_ADDR, "cmd1": DEV_CMD_MEMORY_WRITE}
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up ISY/IoX select entities from config entry.""" """Set up ISY/IoX select entities from config entry."""
isy_data: IsyData = hass.data[DOMAIN][config_entry.entry_id] isy_data = config_entry.runtime_data
device_info = isy_data.devices device_info = isy_data.devices
entities: list[ entities: list[
ISYAuxControlIndexSelectEntity ISYAuxControlIndexSelectEntity

View File

@ -29,7 +29,6 @@ from homeassistant.components.sensor import (
SensorEntity, SensorEntity,
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, Platform, UnitOfTemperature from homeassistant.const import EntityCategory, Platform, UnitOfTemperature
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
@ -37,7 +36,6 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import ( from .const import (
_LOGGER, _LOGGER,
DOMAIN,
UOM_DOUBLE_TEMP, UOM_DOUBLE_TEMP,
UOM_FRIENDLY_NAME, UOM_FRIENDLY_NAME,
UOM_INDEX, UOM_INDEX,
@ -46,7 +44,7 @@ from .const import (
) )
from .entity import ISYNodeEntity from .entity import ISYNodeEntity
from .helpers import convert_isy_value_to_hass from .helpers import convert_isy_value_to_hass
from .models import IsyData from .models import IsyConfigEntry
# Disable general purpose and redundant sensors by default # Disable general purpose and redundant sensors by default
AUX_DISABLED_BY_DEFAULT_MATCH = ["GV", "DO"] AUX_DISABLED_BY_DEFAULT_MATCH = ["GV", "DO"]
@ -109,13 +107,13 @@ ISY_CONTROL_TO_ENTITY_CATEGORY = {
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the ISY sensor platform.""" """Set up the ISY sensor platform."""
isy_data: IsyData = hass.data[DOMAIN][entry.entry_id] isy_data = entry.runtime_data
entities: list[ISYSensorEntity] = [] entities: list[ISYSensorEntity] = []
devices: dict[str, DeviceInfo] = isy_data.devices devices = isy_data.devices
for node in isy_data.nodes[Platform.SENSOR]: for node in isy_data.nodes[Platform.SENSOR]:
_LOGGER.debug("Loading %s", node.name) _LOGGER.debug("Loading %s", node.name)

View File

@ -21,7 +21,7 @@ from homeassistant.helpers.service import entity_service_call
from homeassistant.helpers.typing import VolDictType from homeassistant.helpers.typing import VolDictType
from .const import _LOGGER, DOMAIN from .const import _LOGGER, DOMAIN
from .models import IsyData from .models import IsyConfigEntry
# Common Services for All Platforms: # Common Services for All Platforms:
SERVICE_SEND_PROGRAM_COMMAND = "send_program_command" SERVICE_SEND_PROGRAM_COMMAND = "send_program_command"
@ -149,9 +149,9 @@ def async_setup_services(hass: HomeAssistant) -> None:
command = service.data[CONF_COMMAND] command = service.data[CONF_COMMAND]
isy_name = service.data.get(CONF_ISY) isy_name = service.data.get(CONF_ISY)
for config_entry_id in hass.data[DOMAIN]: config_entry: IsyConfigEntry
isy_data: IsyData = hass.data[DOMAIN][config_entry_id] for config_entry in hass.config_entries.async_loaded_entries(DOMAIN):
isy = isy_data.root isy = config_entry.runtime_data.root
if isy_name and isy_name != isy.conf["name"]: if isy_name and isy_name != isy.conf["name"]:
continue continue
program = None program = None
@ -235,10 +235,6 @@ def async_setup_services(hass: HomeAssistant) -> None:
@callback @callback
def async_unload_services(hass: HomeAssistant) -> None: def async_unload_services(hass: HomeAssistant) -> None:
"""Unload services for the ISY integration.""" """Unload services for the ISY integration."""
if hass.data[DOMAIN]:
# There is still another config entry for this domain, don't remove services.
return
existing_services = hass.services.async_services_for_domain(DOMAIN) existing_services = hass.services.async_services_for_domain(DOMAIN)
if not existing_services or SERVICE_SEND_PROGRAM_COMMAND not in existing_services: if not existing_services or SERVICE_SEND_PROGRAM_COMMAND not in existing_services:
return return

View File

@ -20,16 +20,14 @@ from homeassistant.components.switch import (
SwitchEntity, SwitchEntity,
SwitchEntityDescription, SwitchEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, Platform from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN
from .entity import ISYAuxControlEntity, ISYNodeEntity, ISYProgramEntity from .entity import ISYAuxControlEntity, ISYNodeEntity, ISYProgramEntity
from .models import IsyData from .models import IsyConfigEntry
@dataclass(frozen=True) @dataclass(frozen=True)
@ -43,11 +41,11 @@ class ISYSwitchEntityDescription(SwitchEntityDescription):
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: IsyConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up the ISY switch platform.""" """Set up the ISY switch platform."""
isy_data: IsyData = hass.data[DOMAIN][entry.entry_id] isy_data = entry.runtime_data
entities: list[ entities: list[
ISYSwitchProgramEntity | ISYSwitchEntity | ISYEnableSwitchEntity ISYSwitchProgramEntity | ISYSwitchEntity | ISYEnableSwitchEntity
] = [] ] = []

View File

@ -4,15 +4,12 @@ from __future__ import annotations
from typing import Any from typing import Any
from pyisy import ISY
from homeassistant.components import system_health from homeassistant.components import system_health
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from .const import DOMAIN, ISY_URL_POSTFIX from .const import DOMAIN, ISY_URL_POSTFIX
from .models import IsyData from .models import IsyConfigEntry
@callback @callback
@ -27,14 +24,9 @@ async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
"""Get info for the info page.""" """Get info for the info page."""
health_info = {} health_info = {}
config_entry_id = next( entry: IsyConfigEntry = hass.config_entries.async_loaded_entries(DOMAIN)[0]
iter(hass.data[DOMAIN]) isy = entry.runtime_data.root
) # Only first ISY is supported for now
isy_data: IsyData = hass.data[DOMAIN][config_entry_id]
isy: ISY = isy_data.root
entry = hass.config_entries.async_get_entry(config_entry_id)
assert isinstance(entry, ConfigEntry)
health_info["host_reachable"] = await system_health.async_check_can_reach_url( health_info["host_reachable"] = await system_health.async_check_can_reach_url(
hass, f"{entry.data[CONF_HOST]}{ISY_URL_POSTFIX}" hass, f"{entry.data[CONF_HOST]}{ISY_URL_POSTFIX}"
) )

View File

@ -5,16 +5,19 @@ from __future__ import annotations
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from .const import _LOGGER, DOMAIN from .const import _LOGGER
from .models import IsyConfigEntry
@callback @callback
def _async_cleanup_registry_entries(hass: HomeAssistant, entry_id: str) -> None: def _async_cleanup_registry_entries(hass: HomeAssistant, entry: IsyConfigEntry) -> None:
"""Remove extra entities that are no longer part of the integration.""" """Remove extra entities that are no longer part of the integration."""
entity_registry = er.async_get(hass) entity_registry = er.async_get(hass)
isy_data = hass.data[DOMAIN][entry_id] isy_data = entry.runtime_data
existing_entries = er.async_entries_for_config_entry(entity_registry, entry_id) existing_entries = er.async_entries_for_config_entry(
entity_registry, entry.entry_id
)
entities = { entities = {
(entity.domain, entity.unique_id): entity.entity_id (entity.domain, entity.unique_id): entity.entity_id
for entity in existing_entries for entity in existing_entries
@ -31,5 +34,5 @@ def _async_cleanup_registry_entries(hass: HomeAssistant, entry_id: str) -> None:
_LOGGER.debug( _LOGGER.debug(
("Cleaning up ISY entities: removed %s extra entities for config entry %s"), ("Cleaning up ISY entities: removed %s extra entities for config entry %s"),
len(extra_entities), len(extra_entities),
entry_id, entry.entry_id,
) )

View File

@ -6,6 +6,7 @@ from unittest.mock import Mock
from aiohttp import ClientError from aiohttp import ClientError
from homeassistant.components.isy994.const import DOMAIN, ISY_URL_POSTFIX from homeassistant.components.isy994.const import DOMAIN, ISY_URL_POSTFIX
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -30,12 +31,14 @@ async def test_system_health(
assert await async_setup_component(hass, "system_health", {}) assert await async_setup_component(hass, "system_health", {})
await hass.async_block_till_done() await hass.async_block_till_done()
MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
entry_id=MOCK_ENTRY_ID, entry_id=MOCK_ENTRY_ID,
data={CONF_HOST: f"http://{MOCK_HOSTNAME}"}, data={CONF_HOST: f"http://{MOCK_HOSTNAME}"},
unique_id=MOCK_UUID, unique_id=MOCK_UUID,
).add_to_hass(hass) state=ConfigEntryState.LOADED,
)
entry.add_to_hass(hass)
isy_data = Mock( isy_data = Mock(
root=Mock( root=Mock(
@ -46,7 +49,7 @@ async def test_system_health(
), ),
) )
) )
hass.data[DOMAIN] = {MOCK_ENTRY_ID: isy_data} entry.runtime_data = isy_data
info = await get_system_health_info(hass, DOMAIN) info = await get_system_health_info(hass, DOMAIN)
@ -70,12 +73,14 @@ async def test_system_health_failed_connect(
assert await async_setup_component(hass, "system_health", {}) assert await async_setup_component(hass, "system_health", {})
await hass.async_block_till_done() await hass.async_block_till_done()
MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
entry_id=MOCK_ENTRY_ID, entry_id=MOCK_ENTRY_ID,
data={CONF_HOST: f"http://{MOCK_HOSTNAME}"}, data={CONF_HOST: f"http://{MOCK_HOSTNAME}"},
unique_id=MOCK_UUID, unique_id=MOCK_UUID,
).add_to_hass(hass) state=ConfigEntryState.LOADED,
)
entry.add_to_hass(hass)
isy_data = Mock( isy_data = Mock(
root=Mock( root=Mock(
@ -86,7 +91,7 @@ async def test_system_health_failed_connect(
), ),
) )
) )
hass.data[DOMAIN] = {MOCK_ENTRY_ID: isy_data} entry.runtime_data = isy_data
info = await get_system_health_info(hass, DOMAIN) info = await get_system_health_info(hass, DOMAIN)