Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 61 additions & 45 deletions homeassistant/components/isy994/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,29 @@
from aiohttp import CookieJar
import async_timeout
from pyisy import ISY, ISYConnectionError, ISYInvalidAuthError, ISYResponseParseError
from pyisy.constants import PROTO_NETWORK_RESOURCE
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_USERNAME,
CONF_VARIABLES,
EVENT_HOMEASSISTANT_STOP,
Platform,
)
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client, config_validation as cv
import homeassistant.helpers.device_registry as dr
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.typing import ConfigType

from .const import (
_LOGGER,
CONF_IGNORE_STRING,
CONF_NETWORK,
CONF_RESTORE_LIGHT_STATE,
CONF_SENSOR_STRING,
CONF_TLS_VER,
Expand All @@ -36,28 +39,29 @@
DEFAULT_SENSOR_STRING,
DEFAULT_VAR_SENSOR_STRING,
DOMAIN,
ISY994_ISY,
ISY994_NODES,
ISY994_PROGRAMS,
ISY994_VARIABLES,
ISY_CONF_FIRMWARE,
ISY_CONF_MODEL,
ISY_CONF_NAME,
ISY_CONF_NETWORKING,
ISY_CONF_UUID,
ISY_CONN_ADDRESS,
ISY_CONN_PORT,
ISY_CONN_TLS,
ISY_DEVICES,
ISY_NET_RES,
ISY_NODES,
ISY_PROGRAMS,
ISY_ROOT,
ISY_ROOT_NODES,
ISY_VARIABLES,
MANUFACTURER,
NODE_PLATFORMS,
PLATFORMS,
PROGRAM_PLATFORMS,
ROOT_NODE_PLATFORMS,
SCHEME_HTTP,
SCHEME_HTTPS,
SENSOR_AUX,
VARIABLE_PLATFORMS,
)
from .helpers import _categorize_nodes, _categorize_programs, _categorize_variables
from .services import async_setup_services, async_unload_services
from .util import unique_ids_for_config_entry_id

CONFIG_SCHEMA = vol.Schema(
{
Expand Down Expand Up @@ -134,17 +138,12 @@ async def async_setup_entry(
hass.data[DOMAIN][entry.entry_id] = {}
hass_isy_data = hass.data[DOMAIN][entry.entry_id]

hass_isy_data[ISY994_NODES] = {SENSOR_AUX: [], PROTO_NETWORK_RESOURCE: []}
for platform in PLATFORMS:
hass_isy_data[ISY994_NODES][platform] = []

hass_isy_data[ISY994_PROGRAMS] = {}
for platform in PROGRAM_PLATFORMS:
hass_isy_data[ISY994_PROGRAMS][platform] = []

hass_isy_data[ISY994_VARIABLES] = {}
hass_isy_data[ISY994_VARIABLES][Platform.NUMBER] = []
hass_isy_data[ISY994_VARIABLES][Platform.SENSOR] = []
hass_isy_data[ISY_NODES] = {p: [] for p in (NODE_PLATFORMS + [SENSOR_AUX])}
hass_isy_data[ISY_ROOT_NODES] = {p: [] for p in ROOT_NODE_PLATFORMS}
hass_isy_data[ISY_PROGRAMS] = {p: [] for p in PROGRAM_PLATFORMS}
hass_isy_data[ISY_VARIABLES] = {p: [] for p in VARIABLE_PLATFORMS}
hass_isy_data[ISY_NET_RES] = []
hass_isy_data[ISY_DEVICES] = {}
Comment on lines +141 to +146

@bdraco bdraco Jan 12, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a future PR: This might be a bit easier to manage as a top level dataclass instead of a dict so you can use named attributes instead. lookin has an example of this in models

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bdraco Good call. I may go ahead and do that before I push the next PR for on level sensors.

Thanks for reviewing all these. I have a few more on deck coming your way while I have time.


isy_config = entry.data
isy_options = entry.options
Expand Down Expand Up @@ -218,17 +217,24 @@ async def async_setup_entry(
# Categorize variables call to be removed with variable sensors in 2023.5.0
_categorize_variables(hass_isy_data, isy.variables, variable_identifier)
# Gather ISY Variables to be added. Identifier used to enable by default.
numbers = hass_isy_data[ISY994_VARIABLES][Platform.NUMBER]
for vtype, vname, vid in isy.variables.children:
numbers.append((isy.variables[vtype][vid], variable_identifier in vname))
if isy.configuration[ISY_CONF_NETWORKING]:
if len(isy.variables.children) > 0:
Comment thread
shbatm marked this conversation as resolved.
hass_isy_data[ISY_DEVICES][CONF_VARIABLES] = _create_service_device_info(
isy, name=CONF_VARIABLES.title(), unique_id=CONF_VARIABLES
)
numbers = hass_isy_data[ISY_VARIABLES][Platform.NUMBER]
for vtype, vname, vid in isy.variables.children:
numbers.append((isy.variables[vtype][vid], variable_identifier in vname))
if isy.conf[ISY_CONF_NETWORKING]:
hass_isy_data[ISY_DEVICES][CONF_NETWORK] = _create_service_device_info(
isy, name=ISY_CONF_NETWORKING, unique_id=CONF_NETWORK
)
for resource in isy.networking.nobjs:
hass_isy_data[ISY994_NODES][PROTO_NETWORK_RESOURCE].append(resource)
hass_isy_data[ISY_NET_RES].append(resource)

# Dump ISY Clock Information. Future: Add ISY as sensor to Hass with attrs
_LOGGER.info(repr(isy.clock))

hass_isy_data[ISY994_ISY] = isy
hass_isy_data[ISY_ROOT] = isy
_async_get_or_create_isy_device_in_registry(hass, entry, isy)

# Load platforms for the devices in the ISY controller that we support.
Expand Down Expand Up @@ -280,29 +286,39 @@ def _async_import_options_from_data_if_missing(
hass.config_entries.async_update_entry(entry, options=options)


@callback
def _async_isy_to_configuration_url(isy: ISY) -> str:
Comment thread
shbatm marked this conversation as resolved.
"""Extract the configuration url from the isy."""
connection_info = isy.conn.connection_info
proto = SCHEME_HTTPS if ISY_CONN_TLS in connection_info else SCHEME_HTTP
return f"{proto}://{connection_info[ISY_CONN_ADDRESS]}:{connection_info[ISY_CONN_PORT]}"


@callback
def _async_get_or_create_isy_device_in_registry(
hass: HomeAssistant, entry: config_entries.ConfigEntry, isy: ISY
) -> None:
device_registry = dr.async_get(hass)
url = _async_isy_to_configuration_url(isy)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, isy.configuration[ISY_CONF_UUID])},
identifiers={(DOMAIN, isy.configuration[ISY_CONF_UUID])},
connections={(dr.CONNECTION_NETWORK_MAC, isy.uuid)},
identifiers={(DOMAIN, isy.uuid)},
manufacturer=MANUFACTURER,
name=isy.conf[ISY_CONF_NAME],
model=isy.conf[ISY_CONF_MODEL],
sw_version=isy.conf[ISY_CONF_FIRMWARE],
configuration_url=isy.conn.url,
)


def _create_service_device_info(isy: ISY, name: str, unique_id: str) -> DeviceInfo:
"""Create device info for ISY service devices."""
return DeviceInfo(
identifiers={
(
DOMAIN,
f"{isy.uuid}_{unique_id}",
)
},
manufacturer=MANUFACTURER,
name=isy.configuration[ISY_CONF_NAME],
model=isy.configuration[ISY_CONF_MODEL],
sw_version=isy.configuration[ISY_CONF_FIRMWARE],
configuration_url=url,
name=f"{isy.conf[ISY_CONF_NAME]} {name}",
model=isy.conf[ISY_CONF_MODEL],
sw_version=isy.conf[ISY_CONF_FIRMWARE],
configuration_url=isy.conn.url,
via_device=(DOMAIN, isy.uuid),
entry_type=DeviceEntryType.SERVICE,
)


Expand All @@ -314,7 +330,7 @@ async def async_unload_entry(

hass_isy_data = hass.data[DOMAIN][entry.entry_id]

isy: ISY = hass_isy_data[ISY994_ISY]
isy: ISY = hass_isy_data[ISY_ROOT]

_LOGGER.debug("ISY Stopping Event Stream and automatic updates")
isy.websocket.stop()
Expand All @@ -333,7 +349,7 @@ async def async_remove_config_entry_device(
device_entry: dr.DeviceEntry,
) -> bool:
"""Remove ISY config entry from a device."""
hass_isy_devices = hass.data[DOMAIN][config_entry.entry_id][ISY_DEVICES]
return not device_entry.identifiers.intersection(
(DOMAIN, unique_id)
for unique_id in unique_ids_for_config_entry_id(hass, config_entry.entry_id)
(DOMAIN, unique_id) for unique_id in hass_isy_devices
)
12 changes: 6 additions & 6 deletions homeassistant/components/isy994/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
_LOGGER,
BINARY_SENSOR_DEVICE_TYPES_ISY,
BINARY_SENSOR_DEVICE_TYPES_ZWAVE,
DOMAIN as ISY994_DOMAIN,
ISY994_NODES,
ISY994_PROGRAMS,
DOMAIN,
ISY_NODES,
ISY_PROGRAMS,
SUBNODE_CLIMATE_COOL,
SUBNODE_CLIMATE_HEAT,
SUBNODE_DUSK_DAWN,
Expand Down Expand Up @@ -73,8 +73,8 @@ async def async_setup_entry(
child_nodes: list[tuple[Node, BinarySensorDeviceClass | None, str | None]] = []
entity: ISYInsteonBinarySensorEntity | ISYBinarySensorEntity | ISYBinarySensorHeartbeat | ISYBinarySensorProgramEntity

hass_isy_data = hass.data[ISY994_DOMAIN][entry.entry_id]
for node in hass_isy_data[ISY994_NODES][Platform.BINARY_SENSOR]:
hass_isy_data = hass.data[DOMAIN][entry.entry_id]
for node in hass_isy_data[ISY_NODES][Platform.BINARY_SENSOR]:
assert isinstance(node, Node)
device_class, device_type = _detect_device_type_and_class(node)
if node.protocol == PROTO_INSTEON:
Expand Down Expand Up @@ -187,7 +187,7 @@ async def async_setup_entry(
entity = ISYBinarySensorEntity(node, device_class)
entities.append(entity)

for name, status, _ in hass_isy_data[ISY994_PROGRAMS][Platform.BINARY_SENSOR]:
for name, status, _ in hass_isy_data[ISY_PROGRAMS][Platform.BINARY_SENSOR]:
entities.append(ISYBinarySensorProgramEntity(name, status))

async_add_entities(entities)
Expand Down
Loading