Consolidate device info and clean-up ISY994 code base (#85657)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
shbatm
2023-01-11 22:07:44 -06:00
committed by GitHub
parent 43cc8a1ebf
commit 255a8362a1
21 changed files with 481 additions and 446 deletions

View File

@ -21,6 +21,7 @@ from homeassistant.components.binary_sensor import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_ON, Platform
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.restore_state import RestoreEntity
@ -30,9 +31,10 @@ from .const import (
_LOGGER,
BINARY_SENSOR_DEVICE_TYPES_ISY,
BINARY_SENSOR_DEVICE_TYPES_ZWAVE,
DOMAIN as ISY994_DOMAIN,
ISY994_NODES,
ISY994_PROGRAMS,
DOMAIN,
ISY_DEVICES,
ISY_NODES,
ISY_PROGRAMS,
SUBNODE_CLIMATE_COOL,
SUBNODE_CLIMATE_HEAT,
SUBNODE_DUSK_DAWN,
@ -70,27 +72,33 @@ async def async_setup_entry(
| ISYBinarySensorHeartbeat
| ISYBinarySensorProgramEntity,
] = {}
child_nodes: list[tuple[Node, BinarySensorDeviceClass | None, str | None]] = []
child_nodes: list[
tuple[Node, BinarySensorDeviceClass | None, str | None, DeviceInfo | 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]
devices: dict[str, DeviceInfo] = hass_isy_data[ISY_DEVICES]
for node in hass_isy_data[ISY_NODES][Platform.BINARY_SENSOR]:
assert isinstance(node, Node)
device_info = devices.get(node.primary_node)
device_class, device_type = _detect_device_type_and_class(node)
if node.protocol == PROTO_INSTEON:
if node.parent_node is not None:
# We'll process the Insteon child nodes last, to ensure all parent
# nodes have been processed
child_nodes.append((node, device_class, device_type))
child_nodes.append((node, device_class, device_type, device_info))
continue
entity = ISYInsteonBinarySensorEntity(node, device_class)
entity = ISYInsteonBinarySensorEntity(
node, device_class, device_info=device_info
)
else:
entity = ISYBinarySensorEntity(node, device_class)
entity = ISYBinarySensorEntity(node, device_class, device_info=device_info)
entities.append(entity)
entities_by_address[node.address] = entity
# Handle some special child node cases for Insteon Devices
for (node, device_class, device_type) in child_nodes:
for (node, device_class, device_type, device_info) in child_nodes:
subnode_id = int(node.address.split(" ")[-1], 16)
# Handle Insteon Thermostats
if device_type is not None and device_type.startswith(TYPE_CATEGORY_CLIMATE):
@ -101,13 +109,13 @@ async def async_setup_entry(
# As soon as the ISY Event Stream connects if it has a
# valid state, it will be set.
entity = ISYInsteonBinarySensorEntity(
node, BinarySensorDeviceClass.COLD, False
node, BinarySensorDeviceClass.COLD, False, device_info=device_info
)
entities.append(entity)
elif subnode_id == SUBNODE_CLIMATE_HEAT:
# Subnode 3 is the "Heat Control" sensor
entity = ISYInsteonBinarySensorEntity(
node, BinarySensorDeviceClass.HEAT, False
node, BinarySensorDeviceClass.HEAT, False, device_info=device_info
)
entities.append(entity)
continue
@ -138,7 +146,9 @@ async def async_setup_entry(
assert isinstance(parent_entity, ISYInsteonBinarySensorEntity)
# Subnode 4 is the heartbeat node, which we will
# represent as a separate binary_sensor
entity = ISYBinarySensorHeartbeat(node, parent_entity)
entity = ISYBinarySensorHeartbeat(
node, parent_entity, device_info=device_info
)
parent_entity.add_heartbeat_device(entity)
entities.append(entity)
continue
@ -157,14 +167,17 @@ async def async_setup_entry(
if subnode_id == SUBNODE_DUSK_DAWN:
# Subnode 2 is the Dusk/Dawn sensor
entity = ISYInsteonBinarySensorEntity(
node, BinarySensorDeviceClass.LIGHT
node, BinarySensorDeviceClass.LIGHT, device_info=device_info
)
entities.append(entity)
continue
if subnode_id == SUBNODE_LOW_BATTERY:
# Subnode 3 is the low battery node
entity = ISYInsteonBinarySensorEntity(
node, BinarySensorDeviceClass.BATTERY, initial_state
node,
BinarySensorDeviceClass.BATTERY,
initial_state,
device_info=device_info,
)
entities.append(entity)
continue
@ -172,22 +185,27 @@ async def async_setup_entry(
# Tamper Sub-node for MS II. Sometimes reported as "A" sometimes
# reported as "10", which translate from Hex to 10 and 16 resp.
entity = ISYInsteonBinarySensorEntity(
node, BinarySensorDeviceClass.PROBLEM, initial_state
node,
BinarySensorDeviceClass.PROBLEM,
initial_state,
device_info=device_info,
)
entities.append(entity)
continue
if subnode_id in SUBNODE_MOTION_DISABLED:
# Motion Disabled Sub-node for MS II ("D" or "13")
entity = ISYInsteonBinarySensorEntity(node)
entity = ISYInsteonBinarySensorEntity(node, device_info=device_info)
entities.append(entity)
continue
# We don't yet have any special logic for other sensor
# types, so add the nodes as individual devices
entity = ISYBinarySensorEntity(node, device_class)
entity = ISYBinarySensorEntity(
node, force_device_class=device_class, device_info=device_info
)
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)
@ -225,9 +243,10 @@ class ISYBinarySensorEntity(ISYNodeEntity, BinarySensorEntity):
node: Node,
force_device_class: BinarySensorDeviceClass | None = None,
unknown_state: bool | None = None,
device_info: DeviceInfo | None = None,
) -> None:
"""Initialize the ISY binary sensor device."""
super().__init__(node)
super().__init__(node, device_info=device_info)
self._device_class = force_device_class
@property
@ -260,9 +279,10 @@ class ISYInsteonBinarySensorEntity(ISYBinarySensorEntity):
node: Node,
force_device_class: BinarySensorDeviceClass | None = None,
unknown_state: bool | None = None,
device_info: DeviceInfo | None = None,
) -> None:
"""Initialize the ISY binary sensor device."""
super().__init__(node, force_device_class)
super().__init__(node, force_device_class, device_info=device_info)
self._negative_node: Node | None = None
self._heartbeat_device: ISYBinarySensorHeartbeat | None = None
if self._node.status == ISY_VALUE_UNKNOWN:
@ -399,6 +419,7 @@ class ISYBinarySensorHeartbeat(ISYNodeEntity, BinarySensorEntity, RestoreEntity)
| ISYBinarySensorEntity
| ISYBinarySensorHeartbeat
| ISYBinarySensorProgramEntity,
device_info: DeviceInfo | None = None,
) -> None:
"""Initialize the ISY binary sensor device.
@ -409,7 +430,7 @@ class ISYBinarySensorHeartbeat(ISYNodeEntity, BinarySensorEntity, RestoreEntity)
If the heartbeat is not received in 25 hours then the computed state is
set to ON (Low Battery).
"""
super().__init__(node)
super().__init__(node, device_info=device_info)
self._parent_device = parent_device
self._heartbeat_timer: CALLBACK_TYPE | None = None
self._computed_state: bool | None = None