Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ omit =
homeassistant/components/isy994/helpers.py
homeassistant/components/isy994/light.py
homeassistant/components/isy994/lock.py
homeassistant/components/isy994/number.py
homeassistant/components/isy994/sensor.py
homeassistant/components/isy994/services.py
homeassistant/components/isy994/switch.py
Expand Down
10 changes: 9 additions & 1 deletion homeassistant/components/isy994/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
CONF_PASSWORD,
CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP,
Platform,
)
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
Expand Down Expand Up @@ -141,7 +142,9 @@ async def async_setup_entry(
for platform in PROGRAM_PLATFORMS:
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_options = entry.options
Expand Down Expand Up @@ -212,7 +215,12 @@ async def async_setup_entry(

_categorize_nodes(hass_isy_data, isy.nodes, ignore_identifier, sensor_identifier)
_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)
# 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]:
for resource in isy.networking.nobjs:
hass_isy_data[ISY994_NODES][PROTO_NETWORK_RESOURCE].append(resource)
Expand Down
9 changes: 9 additions & 0 deletions homeassistant/components/isy994/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
Platform.FAN,
Platform.LIGHT,
Platform.LOCK,
Platform.NUMBER,
Platform.SENSOR,
Platform.SWITCH,
]
Expand Down Expand Up @@ -307,6 +308,14 @@
FILTER_INSTEON_TYPE: ["4.8", TYPE_CATEGORY_CLIMATE],
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 = {
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/isy994/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,9 @@ def _categorize_variables(
except KeyError as err:
_LOGGER.error("Error adding ISY Variables: %s", err)
return
variable_entities = hass_isy_data[ISY994_VARIABLES]
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(
Expand Down
162 changes: 162 additions & 0 deletions homeassistant/components/isy994/number.py
Original file line number Diff line number Diff line change
@@ -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]),
Comment thread
bdraco marked this conversation as resolved.
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)
5 changes: 4 additions & 1 deletion homeassistant/components/isy994/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ async def async_setup_entry(
# Any node in SENSOR_AUX can potentially have communication errors
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))

await migrate_old_unique_ids(hass, Platform.SENSOR, entities)
Expand Down Expand Up @@ -269,6 +269,9 @@ def name(self) -> str:
class ISYSensorVariableEntity(ISYEntity, SensorEntity):
"""Representation of an ISY variable as a sensor device."""

# Depreceted sensors, will be removed in 2023.5.0
Comment thread
shbatm marked this conversation as resolved.
_attr_entity_registry_enabled_default = False

def __init__(self, vname: str, vobj: object) -> None:
"""Initialize the ISY binary sensor program."""
super().__init__(vobj)
Expand Down
12 changes: 12 additions & 0 deletions homeassistant/components/isy994/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,18 @@ async def async_set_variable_service_handler(service: ServiceCall) -> None:
variable = isy.variables.vobjs[vtype].get(address)
if variable is not None:
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
_LOGGER.error("Could not set variable value; not found or enabled on the ISY")

Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/isy994/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ system_query:
selector:
text:
set_variable:
name: Set variable
description: Set an ISY variable's current or initial value. Variables can be set by either type/address or by name.
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. Deprecated: Use number entities instead."
fields:
address:
name: Address
Expand Down