Add enable/disable config switch for ISY994 devices (#85975)

Co-authored-by: J. Nick Koston <nick@koston.org>
pull/86013/head^2
shbatm 2023-01-16 13:33:55 -06:00 committed by GitHub
parent b087c1e734
commit 3aad153913
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 11 deletions

View File

@ -87,7 +87,12 @@ NODE_PLATFORMS = [
Platform.SENSOR, Platform.SENSOR,
Platform.SWITCH, Platform.SWITCH,
] ]
NODE_AUX_PROP_PLATFORMS = [Platform.SELECT, Platform.SENSOR, Platform.NUMBER] NODE_AUX_PROP_PLATFORMS = [
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
]
PROGRAM_PLATFORMS = [ PROGRAM_PLATFORMS = [
Platform.BINARY_SENSOR, Platform.BINARY_SENSOR,
Platform.COVER, Platform.COVER,

View File

@ -16,6 +16,7 @@ from pyisy.constants import (
PROTO_INSTEON, PROTO_INSTEON,
PROTO_PROGRAM, PROTO_PROGRAM,
PROTO_ZWAVE, PROTO_ZWAVE,
TAG_ENABLED,
TAG_FOLDER, TAG_FOLDER,
UOM_INDEX, UOM_INDEX,
) )
@ -349,6 +350,8 @@ def _categorize_nodes(
isy_data.aux_properties[Platform.SENSOR].append((node, control)) isy_data.aux_properties[Platform.SENSOR].append((node, control))
platform = NODE_AUX_FILTERS[control] platform = NODE_AUX_FILTERS[control]
isy_data.aux_properties[platform].append((node, control)) isy_data.aux_properties[platform].append((node, control))
if hasattr(node, TAG_ENABLED):
isy_data.aux_properties[Platform.SWITCH].append((node, TAG_ENABLED))
_add_backlight_if_supported(isy_data, node) _add_backlight_if_supported(isy_data, node)
if node.protocol == PROTO_GROUP: if node.protocol == PROTO_GROUP:

View File

@ -3,16 +3,30 @@ from __future__ import annotations
from typing import Any from typing import Any
from pyisy.constants import ISY_VALUE_UNKNOWN, PROTO_GROUP from pyisy.constants import (
ATTR_ACTION,
ISY_VALUE_UNKNOWN,
NC_NODE_ENABLED,
PROTO_GROUP,
TAG_ADDRESS,
)
from pyisy.helpers import EventListener
from pyisy.nodes import Node, NodeChangedEvent
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import (
SwitchDeviceClass,
SwitchEntity,
SwitchEntityDescription,
)
from homeassistant.config_entries import ConfigEntry 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, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import DeviceInfo, EntityCategory, EntityDescription
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import _LOGGER, DOMAIN from .const import DOMAIN
from .entity import ISYNodeEntity, ISYProgramEntity from .entity import ISYAuxControlEntity, ISYNodeEntity, ISYProgramEntity
from .models import IsyData from .models import IsyData
@ -21,7 +35,9 @@ async def async_setup_entry(
) -> 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: IsyData = hass.data[DOMAIN][entry.entry_id]
entities: list[ISYSwitchProgramEntity | ISYSwitchEntity] = [] entities: list[
ISYSwitchProgramEntity | ISYSwitchEntity | ISYEnableSwitchEntity
] = []
device_info = isy_data.devices device_info = isy_data.devices
for node in isy_data.nodes[Platform.SWITCH]: for node in isy_data.nodes[Platform.SWITCH]:
primary = node.primary_node primary = node.primary_node
@ -34,6 +50,24 @@ async def async_setup_entry(
for name, status, actions in isy_data.programs[Platform.SWITCH]: for name, status, actions in isy_data.programs[Platform.SWITCH]:
entities.append(ISYSwitchProgramEntity(name, status, actions)) entities.append(ISYSwitchProgramEntity(name, status, actions))
for node, control in isy_data.aux_properties[Platform.SWITCH]:
# Currently only used for enable switches, will need to be updated for NS support
# by making sure control == TAG_ENABLED
description = SwitchEntityDescription(
key=control,
device_class=SwitchDeviceClass.SWITCH,
name=control.title(),
entity_category=EntityCategory.CONFIG,
)
entities.append(
ISYEnableSwitchEntity(
node=node,
control=control,
unique_id=f"{isy_data.uid_base(node)}_{control}",
description=description,
device_info=device_info.get(node.primary_node),
)
)
async_add_entities(entities) async_add_entities(entities)
@ -50,12 +84,12 @@ class ISYSwitchEntity(ISYNodeEntity, SwitchEntity):
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Send the turn off command to the ISY switch.""" """Send the turn off command to the ISY switch."""
if not await self._node.turn_off(): if not await self._node.turn_off():
_LOGGER.debug("Unable to turn off switch") HomeAssistantError(f"Unable to turn off switch {self._node.address}")
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Send the turn on command to the ISY switch.""" """Send the turn on command to the ISY switch."""
if not await self._node.turn_on(): if not await self._node.turn_on():
_LOGGER.debug("Unable to turn on switch") HomeAssistantError(f"Unable to turn on switch {self._node.address}")
@property @property
def icon(self) -> str | None: def icon(self) -> str | None:
@ -76,14 +110,77 @@ class ISYSwitchProgramEntity(ISYProgramEntity, SwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
"""Send the turn on command to the ISY switch program.""" """Send the turn on command to the ISY switch program."""
if not await self._actions.run_then(): if not await self._actions.run_then():
_LOGGER.error("Unable to turn on switch") HomeAssistantError(
f"Unable to run 'then' clause on program switch {self._actions.address}"
)
async def async_turn_off(self, **kwargs: Any) -> None: async def async_turn_off(self, **kwargs: Any) -> None:
"""Send the turn off command to the ISY switch program.""" """Send the turn off command to the ISY switch program."""
if not await self._actions.run_else(): if not await self._actions.run_else():
_LOGGER.error("Unable to turn off switch") HomeAssistantError(
f"Unable to run 'else' clause on program switch {self._actions.address}"
)
@property @property
def icon(self) -> str: def icon(self) -> str:
"""Get the icon for programs.""" """Get the icon for programs."""
return "mdi:script-text-outline" # Matches isy program icon return "mdi:script-text-outline" # Matches isy program icon
class ISYEnableSwitchEntity(ISYAuxControlEntity, SwitchEntity):
"""A representation of an ISY enable/disable switch."""
def __init__(
self,
node: Node,
control: str,
unique_id: str,
description: EntityDescription,
device_info: DeviceInfo | None,
) -> None:
"""Initialize the ISY Aux Control Number entity."""
super().__init__(
node=node,
control=control,
unique_id=unique_id,
description=description,
device_info=device_info,
)
self._attr_name = description.name # Override super
self._change_handler: EventListener = None
async def async_added_to_hass(self) -> None:
"""Subscribe to the node control change events."""
self._change_handler = self._node.isy.nodes.status_events.subscribe(
self.async_on_update,
event_filter={
TAG_ADDRESS: self._node.address,
ATTR_ACTION: NC_NODE_ENABLED,
},
key=self.unique_id,
)
@callback
def async_on_update(self, event: NodeChangedEvent, key: str) -> None:
"""Handle a control event from the ISY Node."""
self.async_write_ha_state()
@property
def available(self) -> bool:
"""Return entity availability."""
return True # Enable switch is always available
@property
def is_on(self) -> bool | None:
"""Get whether the ISY device is in the on state."""
return bool(self._node.enabled)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Send the turn off command to the ISY switch."""
if not await self._node.disable():
raise HomeAssistantError(f"Unable to disable device {self._node.address}")
async def async_turn_on(self, **kwargs: Any) -> None:
"""Send the turn on command to the ISY switch."""
if not await self._node.enable():
raise HomeAssistantError(f"Unable to enable device {self._node.address}")