Add enable/disable config switch for ISY994 devices (#85975)
Co-authored-by: J. Nick Koston <nick@koston.org>pull/86013/head^2
parent
b087c1e734
commit
3aad153913
|
@ -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,
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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}")
|
||||||
|
|
Loading…
Reference in New Issue