mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add Insteon ramp rate select entities to ISY994 (#85895)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
a653ea30bd
commit
a81045653d
@ -612,6 +612,7 @@ omit =
|
||||
homeassistant/components/isy994/lock.py
|
||||
homeassistant/components/isy994/models.py
|
||||
homeassistant/components/isy994/number.py
|
||||
homeassistant/components/isy994/select.py
|
||||
homeassistant/components/isy994/sensor.py
|
||||
homeassistant/components/isy994/services.py
|
||||
homeassistant/components/isy994/switch.py
|
||||
|
@ -87,7 +87,7 @@ NODE_PLATFORMS = [
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
NODE_AUX_PROP_PLATFORMS = [Platform.SENSOR, Platform.NUMBER]
|
||||
NODE_AUX_PROP_PLATFORMS = [Platform.SELECT, Platform.SENSOR, Platform.NUMBER]
|
||||
PROGRAM_PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.COVER,
|
||||
@ -309,7 +309,7 @@ NODE_FILTERS: dict[Platform, dict[str, list[str]]] = {
|
||||
}
|
||||
NODE_AUX_FILTERS: dict[str, Platform] = {
|
||||
PROP_ON_LEVEL: Platform.NUMBER,
|
||||
PROP_RAMP_RATE: Platform.SENSOR,
|
||||
PROP_RAMP_RATE: Platform.SELECT,
|
||||
}
|
||||
|
||||
UOM_FRIENDLY_NAME = {
|
||||
|
@ -18,7 +18,7 @@ from pyisy.variables import Variable
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
@ -189,3 +189,42 @@ class ISYProgramEntity(ISYEntity):
|
||||
if self._node.last_update != EMPTY_TIME:
|
||||
attr["status_last_update"] = self._node.last_update
|
||||
return attr
|
||||
|
||||
|
||||
class ISYAuxControlEntity(Entity):
|
||||
"""Representation of a ISY/IoX Aux Control base entity."""
|
||||
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
node: Node,
|
||||
control: str,
|
||||
unique_id: str,
|
||||
description: EntityDescription,
|
||||
device_info: DeviceInfo | None,
|
||||
) -> None:
|
||||
"""Initialize the ISY Aux Control Number entity."""
|
||||
self._node = node
|
||||
self._control = control
|
||||
name = COMMAND_FRIENDLY_NAME.get(control, control).replace("_", " ").title()
|
||||
if node.address != node.primary_node:
|
||||
name = f"{node.name} {name}"
|
||||
self._attr_name = name
|
||||
self.entity_description = description
|
||||
self._attr_has_entity_name = node.address == node.primary_node
|
||||
self._attr_unique_id = unique_id
|
||||
self._attr_device_info = device_info
|
||||
self._change_handler: EventListener | None = None
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to the node control change events."""
|
||||
self._change_handler = self._node.control_events.subscribe(self.async_on_update)
|
||||
|
||||
@callback
|
||||
def async_on_update(self, event: NodeProperty) -> None:
|
||||
"""Handle a control event from the ISY Node."""
|
||||
# Only watch for our control changing or the node being enabled/disabled
|
||||
if event.control != self._control:
|
||||
return
|
||||
self.async_write_ha_state()
|
||||
|
@ -146,4 +146,16 @@ class ISYLightEntity(ISYNodeEntity, LightEntity, RestoreEntity):
|
||||
|
||||
async def async_set_ramp_rate(self, value: int) -> None:
|
||||
"""Set the Ramp Rate for a device."""
|
||||
entity_registry = er.async_get(self.hass)
|
||||
async_log_deprecated_service_call(
|
||||
self.hass,
|
||||
call=ServiceCall(domain=DOMAIN, service=SERVICE_SET_ON_LEVEL),
|
||||
alternate_service="select.select_option",
|
||||
alternate_target=entity_registry.async_get_entity_id(
|
||||
Platform.NUMBER,
|
||||
DOMAIN,
|
||||
f"{self._node.isy.uuid}_{self._node.address}_RR",
|
||||
),
|
||||
breaks_in_ha_version="2023.5.0",
|
||||
)
|
||||
await self._node.set_ramp_rate(value)
|
||||
|
@ -4,9 +4,8 @@ from __future__ import annotations
|
||||
from dataclasses import replace
|
||||
from typing import Any
|
||||
|
||||
from pyisy.constants import COMMAND_FRIENDLY_NAME, ISY_VALUE_UNKNOWN, PROP_ON_LEVEL
|
||||
from pyisy.constants import ISY_VALUE_UNKNOWN, PROP_ON_LEVEL
|
||||
from pyisy.helpers import EventListener, NodeProperty
|
||||
from pyisy.nodes import Node
|
||||
from pyisy.variables import Variable
|
||||
|
||||
from homeassistant.components.number import (
|
||||
@ -30,6 +29,7 @@ from .const import (
|
||||
DOMAIN,
|
||||
UOM_8_BIT_RANGE,
|
||||
)
|
||||
from .entity import ISYAuxControlEntity
|
||||
from .helpers import convert_isy_value_to_hass
|
||||
|
||||
ISY_MAX_SIZE = (2**32) / 2
|
||||
@ -58,12 +58,11 @@ async def async_setup_entry(
|
||||
var_id = config_entry.options.get(CONF_VAR_SENSOR_STRING, DEFAULT_VAR_SENSOR_STRING)
|
||||
|
||||
for node in isy_data.variables[Platform.NUMBER]:
|
||||
step = 10 ** (-1 * node.prec)
|
||||
min_max = ISY_MAX_SIZE / (10**node.prec)
|
||||
step = 10 ** (-1 * int(node.prec))
|
||||
min_max = ISY_MAX_SIZE / (10 ** int(node.prec))
|
||||
description = NumberEntityDescription(
|
||||
key=node.address,
|
||||
name=node.name,
|
||||
icon="mdi:counter",
|
||||
entity_registry_enabled_default=var_id in node.name,
|
||||
native_unit_of_measurement=None,
|
||||
native_step=step,
|
||||
@ -108,43 +107,10 @@ async def async_setup_entry(
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class ISYAuxControlNumberEntity(NumberEntity):
|
||||
class ISYAuxControlNumberEntity(ISYAuxControlEntity, NumberEntity):
|
||||
"""Representation of a ISY/IoX Aux Control Number entity."""
|
||||
|
||||
_attr_mode = NumberMode.SLIDER
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
node: Node,
|
||||
control: str,
|
||||
unique_id: str,
|
||||
description: NumberEntityDescription,
|
||||
device_info: DeviceInfo | None,
|
||||
) -> None:
|
||||
"""Initialize the ISY Aux Control Number entity."""
|
||||
self._node = node
|
||||
name = COMMAND_FRIENDLY_NAME.get(control, control).replace("_", " ").title()
|
||||
if node.address != node.primary_node:
|
||||
name = f"{node.name} {name}"
|
||||
self._attr_name = name
|
||||
self._control = control
|
||||
self.entity_description = description
|
||||
self._attr_has_entity_name = node.address == node.primary_node
|
||||
self._attr_unique_id = unique_id
|
||||
self._attr_device_info = device_info
|
||||
self._change_handler: EventListener | None = None
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to the node control change events."""
|
||||
self._change_handler = self._node.control_events.subscribe(self.async_on_update)
|
||||
|
||||
@callback
|
||||
def async_on_update(self, event: NodeProperty) -> None:
|
||||
"""Handle a control event from the ISY Node."""
|
||||
if event.control != self._control:
|
||||
return
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | int | None:
|
||||
|
127
homeassistant/components/isy994/select.py
Normal file
127
homeassistant/components/isy994/select.py
Normal file
@ -0,0 +1,127 @@
|
||||
"""Support for ISY select entities."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import cast
|
||||
|
||||
from pyisy.constants import (
|
||||
COMMAND_FRIENDLY_NAME,
|
||||
INSTEON_RAMP_RATES,
|
||||
ISY_VALUE_UNKNOWN,
|
||||
PROP_RAMP_RATE,
|
||||
UOM_TO_STATES,
|
||||
)
|
||||
from pyisy.helpers import NodeProperty
|
||||
|
||||
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform, UnitOfTime
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import _LOGGER, DOMAIN, UOM_INDEX
|
||||
from .entity import ISYAuxControlEntity
|
||||
from .models import IsyData
|
||||
|
||||
|
||||
def time_string(i: int) -> str:
|
||||
"""Return a formatted ramp rate time string."""
|
||||
if i >= 60:
|
||||
return f"{(float(i)/60):.1f} {UnitOfTime.MINUTES}"
|
||||
return f"{i} {UnitOfTime.SECONDS}"
|
||||
|
||||
|
||||
RAMP_RATE_OPTIONS = [time_string(rate) for rate in INSTEON_RAMP_RATES.values()]
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up ISY/IoX select entities from config entry."""
|
||||
isy_data: IsyData = hass.data[DOMAIN][config_entry.entry_id]
|
||||
isy = isy_data.root
|
||||
device_info = isy_data.devices
|
||||
entities: list[ISYAuxControlIndexSelectEntity | ISYRampRateSelectEntity] = []
|
||||
|
||||
for node, control in isy_data.aux_properties[Platform.SELECT]:
|
||||
name = COMMAND_FRIENDLY_NAME.get(control, control).replace("_", " ").title()
|
||||
if node.address != node.primary_node:
|
||||
name = f"{node.name} {name}"
|
||||
|
||||
node_prop: NodeProperty = node.aux_properties[control]
|
||||
|
||||
options = []
|
||||
if control == PROP_RAMP_RATE:
|
||||
options = RAMP_RATE_OPTIONS
|
||||
if node_prop.uom == UOM_INDEX:
|
||||
if options_dict := UOM_TO_STATES.get(node_prop.uom):
|
||||
options = list(options_dict.values())
|
||||
|
||||
description = SelectEntityDescription(
|
||||
key=f"{node.address}_{control}",
|
||||
name=name,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
options=options,
|
||||
)
|
||||
entity_detail = {
|
||||
"node": node,
|
||||
"control": control,
|
||||
"unique_id": f"{isy.uuid}_{node.address}_{control}",
|
||||
"description": description,
|
||||
"device_info": device_info.get(node.primary_node),
|
||||
}
|
||||
|
||||
if control == PROP_RAMP_RATE:
|
||||
entities.append(ISYRampRateSelectEntity(**entity_detail))
|
||||
continue
|
||||
if node.uom == UOM_INDEX and options:
|
||||
entities.append(ISYAuxControlIndexSelectEntity(**entity_detail))
|
||||
continue
|
||||
# Future: support Node Server custom index UOMs
|
||||
_LOGGER.debug(
|
||||
"ISY missing node index unit definitions for %s: %s", node.name, name
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class ISYRampRateSelectEntity(ISYAuxControlEntity, SelectEntity):
|
||||
"""Representation of a ISY/IoX Aux Control Ramp Rate Select entity."""
|
||||
|
||||
@property
|
||||
def current_option(self) -> str | None:
|
||||
"""Return the selected entity option to represent the entity state."""
|
||||
node_prop: NodeProperty = self._node.aux_properties[self._control]
|
||||
if node_prop.value == ISY_VALUE_UNKNOWN:
|
||||
return None
|
||||
|
||||
return RAMP_RATE_OPTIONS[int(node_prop.value)]
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
|
||||
await self._node.set_ramp_rate(RAMP_RATE_OPTIONS.index(option))
|
||||
|
||||
|
||||
class ISYAuxControlIndexSelectEntity(ISYAuxControlEntity, SelectEntity):
|
||||
"""Representation of a ISY/IoX Aux Control Index Select entity."""
|
||||
|
||||
@property
|
||||
def current_option(self) -> str | None:
|
||||
"""Return the selected entity option to represent the entity state."""
|
||||
node_prop: NodeProperty = self._node.aux_properties[self._control]
|
||||
if node_prop.value == ISY_VALUE_UNKNOWN:
|
||||
return None
|
||||
|
||||
if options_dict := UOM_TO_STATES.get(node_prop.uom):
|
||||
return cast(str, options_dict.get(node_prop.value, node_prop.value))
|
||||
return cast(str, node_prop.formatted)
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
node_prop: NodeProperty = self._node.aux_properties[self._control]
|
||||
|
||||
await self._node.send_cmd(
|
||||
self._control, val=self.options.index(option), uom=node_prop.uom
|
||||
)
|
@ -46,6 +46,7 @@ from .helpers import convert_isy_value_to_hass
|
||||
# Disable general purpose and redundant sensors by default
|
||||
AUX_DISABLED_BY_DEFAULT_MATCH = ["GV", "DO"]
|
||||
AUX_DISABLED_BY_DEFAULT_EXACT = {
|
||||
PROP_COMMS_ERROR,
|
||||
PROP_ENERGY_MODE,
|
||||
PROP_HEAT_COOL_STATE,
|
||||
PROP_ON_LEVEL,
|
||||
|
@ -152,8 +152,8 @@ set_on_level:
|
||||
min: 0
|
||||
max: 255
|
||||
set_ramp_rate:
|
||||
name: Set ramp rate
|
||||
description: Send a ISY set_ramp_rate command to a Node.
|
||||
name: Set ramp rate (Deprecated)
|
||||
description: "Send a ISY set_ramp_rate command to a Node. Deprecated: Use On Level Number entity instead."
|
||||
target:
|
||||
entity:
|
||||
integration: isy994
|
||||
|
Loading…
x
Reference in New Issue
Block a user