mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 09:17:10 +00:00
Add ISY994 variables as number entities (#85511)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
7af23698bc
commit
d3249432c9
@ -607,6 +607,7 @@ omit =
|
|||||||
homeassistant/components/isy994/helpers.py
|
homeassistant/components/isy994/helpers.py
|
||||||
homeassistant/components/isy994/light.py
|
homeassistant/components/isy994/light.py
|
||||||
homeassistant/components/isy994/lock.py
|
homeassistant/components/isy994/lock.py
|
||||||
|
homeassistant/components/isy994/number.py
|
||||||
homeassistant/components/isy994/sensor.py
|
homeassistant/components/isy994/sensor.py
|
||||||
homeassistant/components/isy994/services.py
|
homeassistant/components/isy994/services.py
|
||||||
homeassistant/components/isy994/switch.py
|
homeassistant/components/isy994/switch.py
|
||||||
|
@ -16,6 +16,7 @@ from homeassistant.const import (
|
|||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
|
Platform,
|
||||||
)
|
)
|
||||||
from homeassistant.core import Event, HomeAssistant, callback
|
from homeassistant.core import Event, HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||||
@ -141,7 +142,9 @@ async def async_setup_entry(
|
|||||||
for platform in PROGRAM_PLATFORMS:
|
for platform in PROGRAM_PLATFORMS:
|
||||||
hass_isy_data[ISY994_PROGRAMS][platform] = []
|
hass_isy_data[ISY994_PROGRAMS][platform] = []
|
||||||
|
|
||||||
hass_isy_data[ISY994_VARIABLES] = []
|
hass_isy_data[ISY994_VARIABLES] = {}
|
||||||
|
hass_isy_data[ISY994_VARIABLES][Platform.NUMBER] = []
|
||||||
|
hass_isy_data[ISY994_VARIABLES][Platform.SENSOR] = []
|
||||||
|
|
||||||
isy_config = entry.data
|
isy_config = entry.data
|
||||||
isy_options = entry.options
|
isy_options = entry.options
|
||||||
@ -212,7 +215,12 @@ async def async_setup_entry(
|
|||||||
|
|
||||||
_categorize_nodes(hass_isy_data, isy.nodes, ignore_identifier, sensor_identifier)
|
_categorize_nodes(hass_isy_data, isy.nodes, ignore_identifier, sensor_identifier)
|
||||||
_categorize_programs(hass_isy_data, isy.programs)
|
_categorize_programs(hass_isy_data, isy.programs)
|
||||||
|
# Categorize variables call to be removed with variable sensors in 2023.5.0
|
||||||
_categorize_variables(hass_isy_data, isy.variables, variable_identifier)
|
_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 isy.configuration[ISY_CONF_NETWORKING]:
|
||||||
for resource in isy.networking.nobjs:
|
for resource in isy.networking.nobjs:
|
||||||
hass_isy_data[ISY994_NODES][PROTO_NETWORK_RESOURCE].append(resource)
|
hass_isy_data[ISY994_NODES][PROTO_NETWORK_RESOURCE].append(resource)
|
||||||
|
@ -82,6 +82,7 @@ PLATFORMS = [
|
|||||||
Platform.FAN,
|
Platform.FAN,
|
||||||
Platform.LIGHT,
|
Platform.LIGHT,
|
||||||
Platform.LOCK,
|
Platform.LOCK,
|
||||||
|
Platform.NUMBER,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
Platform.SWITCH,
|
Platform.SWITCH,
|
||||||
]
|
]
|
||||||
@ -307,6 +308,14 @@ NODE_FILTERS: dict[Platform, dict[str, list[str]]] = {
|
|||||||
FILTER_INSTEON_TYPE: ["4.8", TYPE_CATEGORY_CLIMATE],
|
FILTER_INSTEON_TYPE: ["4.8", TYPE_CATEGORY_CLIMATE],
|
||||||
FILTER_ZWAVE_CAT: ["140"],
|
FILTER_ZWAVE_CAT: ["140"],
|
||||||
},
|
},
|
||||||
|
Platform.NUMBER: {
|
||||||
|
# No devices automatically sorted as numbers at this time.
|
||||||
|
FILTER_UOM: [],
|
||||||
|
FILTER_STATES: [],
|
||||||
|
FILTER_NODE_DEF_ID: [],
|
||||||
|
FILTER_INSTEON_TYPE: [],
|
||||||
|
FILTER_ZWAVE_CAT: [],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
UOM_FRIENDLY_NAME = {
|
UOM_FRIENDLY_NAME = {
|
||||||
|
@ -376,8 +376,9 @@ def _categorize_variables(
|
|||||||
except KeyError as err:
|
except KeyError as err:
|
||||||
_LOGGER.error("Error adding ISY Variables: %s", err)
|
_LOGGER.error("Error adding ISY Variables: %s", err)
|
||||||
return
|
return
|
||||||
|
variable_entities = hass_isy_data[ISY994_VARIABLES]
|
||||||
for vtype, vname, vid in var_to_add:
|
for vtype, vname, vid in var_to_add:
|
||||||
hass_isy_data[ISY994_VARIABLES].append((vname, variables[vtype][vid]))
|
variable_entities[Platform.SENSOR].append((vname, variables[vtype][vid]))
|
||||||
|
|
||||||
|
|
||||||
async def migrate_old_unique_ids(
|
async def migrate_old_unique_ids(
|
||||||
|
162
homeassistant/components/isy994/number.py
Normal file
162
homeassistant/components/isy994/number.py
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
"""Support for ISY number entities."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pyisy import ISY
|
||||||
|
from pyisy.helpers import EventListener, NodeProperty
|
||||||
|
from pyisy.variables import Variable
|
||||||
|
|
||||||
|
from homeassistant.components.number import NumberEntity, NumberEntityDescription
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import Platform
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.device_registry import DeviceEntryType
|
||||||
|
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import _async_isy_to_configuration_url
|
||||||
|
from .const import (
|
||||||
|
DOMAIN as ISY994_DOMAIN,
|
||||||
|
ISY994_ISY,
|
||||||
|
ISY994_VARIABLES,
|
||||||
|
ISY_CONF_FIRMWARE,
|
||||||
|
ISY_CONF_MODEL,
|
||||||
|
ISY_CONF_NAME,
|
||||||
|
ISY_CONF_UUID,
|
||||||
|
MANUFACTURER,
|
||||||
|
)
|
||||||
|
from .helpers import convert_isy_value_to_hass
|
||||||
|
|
||||||
|
ISY_MAX_SIZE = (2**32) / 2
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up ISY/IoX number entities from config entry."""
|
||||||
|
hass_isy_data = hass.data[ISY994_DOMAIN][config_entry.entry_id]
|
||||||
|
isy: ISY = hass_isy_data[ISY994_ISY]
|
||||||
|
uuid = isy.configuration[ISY_CONF_UUID]
|
||||||
|
entities: list[ISYVariableNumberEntity] = []
|
||||||
|
|
||||||
|
for node, enable_by_default in hass_isy_data[ISY994_VARIABLES][Platform.NUMBER]:
|
||||||
|
step = 10 ** (-1 * node.prec)
|
||||||
|
min_max = ISY_MAX_SIZE / (10**node.prec)
|
||||||
|
description = NumberEntityDescription(
|
||||||
|
key=node.address,
|
||||||
|
name=node.name,
|
||||||
|
icon="mdi:counter",
|
||||||
|
entity_registry_enabled_default=enable_by_default,
|
||||||
|
native_unit_of_measurement=None,
|
||||||
|
native_step=step,
|
||||||
|
native_min_value=-min_max,
|
||||||
|
native_max_value=min_max,
|
||||||
|
)
|
||||||
|
description_init = NumberEntityDescription(
|
||||||
|
key=f"{node.address}_init",
|
||||||
|
name=f"{node.name} Initial Value",
|
||||||
|
icon="mdi:counter",
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
native_unit_of_measurement=None,
|
||||||
|
native_step=step,
|
||||||
|
native_min_value=-min_max,
|
||||||
|
native_max_value=min_max,
|
||||||
|
entity_category=EntityCategory.CONFIG,
|
||||||
|
)
|
||||||
|
|
||||||
|
entities.append(
|
||||||
|
ISYVariableNumberEntity(
|
||||||
|
node,
|
||||||
|
unique_id=f"{uuid}_{node.address}",
|
||||||
|
description=description,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
entities.append(
|
||||||
|
ISYVariableNumberEntity(
|
||||||
|
node=node,
|
||||||
|
unique_id=f"{uuid}_{node.address}_init",
|
||||||
|
description=description_init,
|
||||||
|
init_entity=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
|
class ISYVariableNumberEntity(NumberEntity):
|
||||||
|
"""Representation of an ISY variable as a number entity device."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
_attr_should_poll = False
|
||||||
|
_init_entity: bool
|
||||||
|
_node: Variable
|
||||||
|
entity_description: NumberEntityDescription
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
node: Variable,
|
||||||
|
unique_id: str,
|
||||||
|
description: NumberEntityDescription,
|
||||||
|
init_entity: bool = False,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the ISY variable number."""
|
||||||
|
self._node = node
|
||||||
|
self._name = description.name
|
||||||
|
self.entity_description = description
|
||||||
|
self._change_handler: EventListener | None = None
|
||||||
|
|
||||||
|
# Two entities are created for each variable, one for current value and one for initial.
|
||||||
|
# Initial value entities are disabled by default
|
||||||
|
self._init_entity = init_entity
|
||||||
|
|
||||||
|
self._attr_unique_id = unique_id
|
||||||
|
|
||||||
|
url = _async_isy_to_configuration_url(node.isy)
|
||||||
|
config = node.isy.configuration
|
||||||
|
self._attr_device_info = DeviceInfo(
|
||||||
|
identifiers={
|
||||||
|
(
|
||||||
|
ISY994_DOMAIN,
|
||||||
|
f"{config[ISY_CONF_UUID]}_variables",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
manufacturer=MANUFACTURER,
|
||||||
|
name=f"{config[ISY_CONF_NAME]} Variables",
|
||||||
|
model=config[ISY_CONF_MODEL],
|
||||||
|
sw_version=config[ISY_CONF_FIRMWARE],
|
||||||
|
configuration_url=url,
|
||||||
|
via_device=(ISY994_DOMAIN, config[ISY_CONF_UUID]),
|
||||||
|
entry_type=DeviceEntryType.SERVICE,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Subscribe to the node change events."""
|
||||||
|
self._change_handler = self._node.status_events.subscribe(self.async_on_update)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_on_update(self, event: NodeProperty) -> None:
|
||||||
|
"""Handle the update event from the ISY Node."""
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_value(self) -> float | int | None:
|
||||||
|
"""Return the state of the variable."""
|
||||||
|
return convert_isy_value_to_hass(
|
||||||
|
self._node.init if self._init_entity else self._node.status,
|
||||||
|
"",
|
||||||
|
self._node.prec,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extra_state_attributes(self) -> dict[str, Any]:
|
||||||
|
"""Get the state attributes for the device."""
|
||||||
|
return {
|
||||||
|
"last_edited": self._node.last_edited,
|
||||||
|
}
|
||||||
|
|
||||||
|
async def async_set_native_value(self, value: float) -> None:
|
||||||
|
"""Set new value."""
|
||||||
|
await self._node.set_value(value, init=self._init_entity)
|
@ -132,7 +132,7 @@ async def async_setup_entry(
|
|||||||
# Any node in SENSOR_AUX can potentially have communication errors
|
# Any node in SENSOR_AUX can potentially have communication errors
|
||||||
entities.append(ISYAuxSensorEntity(node, PROP_COMMS_ERROR, False))
|
entities.append(ISYAuxSensorEntity(node, PROP_COMMS_ERROR, False))
|
||||||
|
|
||||||
for vname, vobj in hass_isy_data[ISY994_VARIABLES]:
|
for vname, vobj in hass_isy_data[ISY994_VARIABLES][Platform.SENSOR]:
|
||||||
entities.append(ISYSensorVariableEntity(vname, vobj))
|
entities.append(ISYSensorVariableEntity(vname, vobj))
|
||||||
|
|
||||||
await migrate_old_unique_ids(hass, Platform.SENSOR, entities)
|
await migrate_old_unique_ids(hass, Platform.SENSOR, entities)
|
||||||
@ -269,6 +269,9 @@ class ISYAuxSensorEntity(ISYSensorEntity):
|
|||||||
class ISYSensorVariableEntity(ISYEntity, SensorEntity):
|
class ISYSensorVariableEntity(ISYEntity, SensorEntity):
|
||||||
"""Representation of an ISY variable as a sensor device."""
|
"""Representation of an ISY variable as a sensor device."""
|
||||||
|
|
||||||
|
# Depreceted sensors, will be removed in 2023.5.0
|
||||||
|
_attr_entity_registry_enabled_default = False
|
||||||
|
|
||||||
def __init__(self, vname: str, vobj: object) -> None:
|
def __init__(self, vname: str, vobj: object) -> None:
|
||||||
"""Initialize the ISY binary sensor program."""
|
"""Initialize the ISY binary sensor program."""
|
||||||
super().__init__(vobj)
|
super().__init__(vobj)
|
||||||
|
@ -307,6 +307,18 @@ def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
|
|||||||
variable = isy.variables.vobjs[vtype].get(address)
|
variable = isy.variables.vobjs[vtype].get(address)
|
||||||
if variable is not None:
|
if variable is not None:
|
||||||
await variable.set_value(value, init)
|
await variable.set_value(value, init)
|
||||||
|
entity_registry = er.async_get(hass)
|
||||||
|
async_log_deprecated_service_call(
|
||||||
|
hass,
|
||||||
|
call=service,
|
||||||
|
alternate_service="number.set_value",
|
||||||
|
alternate_target=entity_registry.async_get_entity_id(
|
||||||
|
Platform.NUMBER,
|
||||||
|
DOMAIN,
|
||||||
|
f"{isy.configuration[ISY_CONF_UUID]}_{address}{'_init' if init else ''}",
|
||||||
|
),
|
||||||
|
breaks_in_ha_version="2023.5.0",
|
||||||
|
)
|
||||||
return
|
return
|
||||||
_LOGGER.error("Could not set variable value; not found or enabled on the ISY")
|
_LOGGER.error("Could not set variable value; not found or enabled on the ISY")
|
||||||
|
|
||||||
|
@ -184,8 +184,8 @@ system_query:
|
|||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
set_variable:
|
set_variable:
|
||||||
name: Set variable
|
name: Set variable (Deprecated)
|
||||||
description: Set an ISY variable's current or initial value. Variables can be set by either type/address or by name.
|
description: "Set an ISY variable's current or initial value. Variables can be set by either type/address or by name. Deprecated: Use number entities instead."
|
||||||
fields:
|
fields:
|
||||||
address:
|
address:
|
||||||
name: Address
|
name: Address
|
||||||
|
Loading…
x
Reference in New Issue
Block a user