ISY994 Structure updates in prep for PyISYv2 (Part 2) (#35225)
- Move core Entity Class definitions to separate file. - New ISYNodeEntity and ISYProgramEntity base classes. - Update inheriances on other classes. - Move helper functions to separate file.pull/35260/head
parent
710deb8316
commit
e45f2cf5a2
|
@ -1,15 +1,9 @@
|
|||
"""Support the ISY-994 controllers."""
|
||||
from collections import namedtuple
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import PyISY
|
||||
from PyISY.Nodes import Group
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR
|
||||
from homeassistant.components.fan import DOMAIN as FAN
|
||||
from homeassistant.components.light import DOMAIN as LIGHT
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
|
@ -18,8 +12,7 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv, discovery
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.typing import ConfigType, Dict
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import (
|
||||
_LOGGER,
|
||||
|
@ -33,15 +26,10 @@ from .const import (
|
|||
ISY994_NODES,
|
||||
ISY994_PROGRAMS,
|
||||
ISY994_WEATHER,
|
||||
ISY_GROUP_PLATFORM,
|
||||
KEY_ACTIONS,
|
||||
KEY_FOLDER,
|
||||
KEY_MY_PROGRAMS,
|
||||
KEY_STATUS,
|
||||
NODE_FILTERS,
|
||||
SUPPORTED_PLATFORMS,
|
||||
SUPPORTED_PROGRAM_PLATFORMS,
|
||||
)
|
||||
from .helpers import _categorize_nodes, _categorize_programs, _categorize_weather
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
|
@ -64,229 +52,6 @@ CONFIG_SCHEMA = vol.Schema(
|
|||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
WeatherNode = namedtuple("WeatherNode", ("status", "name", "uom"))
|
||||
|
||||
|
||||
def _check_for_node_def(hass: HomeAssistant, node, single_platform: str = None) -> bool:
|
||||
"""Check if the node matches the node_def_id for any platforms.
|
||||
|
||||
This is only present on the 5.0 ISY firmware, and is the most reliable
|
||||
way to determine a device's type.
|
||||
"""
|
||||
if not hasattr(node, "node_def_id") or node.node_def_id is None:
|
||||
# Node doesn't have a node_def (pre 5.0 firmware most likely)
|
||||
return False
|
||||
|
||||
node_def_id = node.node_def_id
|
||||
|
||||
platforms = SUPPORTED_PLATFORMS if not single_platform else [single_platform]
|
||||
for platform in platforms:
|
||||
if node_def_id in NODE_FILTERS[platform]["node_def_id"]:
|
||||
hass.data[ISY994_NODES][platform].append(node)
|
||||
return True
|
||||
|
||||
_LOGGER.warning("Unsupported node: %s, type: %s", node.name, node.type)
|
||||
return False
|
||||
|
||||
|
||||
def _check_for_insteon_type(
|
||||
hass: HomeAssistant, node, single_platform: str = None
|
||||
) -> bool:
|
||||
"""Check if the node matches the Insteon type for any platforms.
|
||||
|
||||
This is for (presumably) every version of the ISY firmware, but only
|
||||
works for Insteon device. "Node Server" (v5+) and Z-Wave and others will
|
||||
not have a type.
|
||||
"""
|
||||
if not hasattr(node, "type") or node.type is None:
|
||||
# Node doesn't have a type (non-Insteon device most likely)
|
||||
return False
|
||||
|
||||
device_type = node.type
|
||||
platforms = SUPPORTED_PLATFORMS if not single_platform else [single_platform]
|
||||
for platform in platforms:
|
||||
if any(
|
||||
[
|
||||
device_type.startswith(t)
|
||||
for t in set(NODE_FILTERS[platform]["insteon_type"])
|
||||
]
|
||||
):
|
||||
|
||||
# Hacky special-case just for FanLinc, which has a light module
|
||||
# as one of its nodes. Note that this special-case is not necessary
|
||||
# on ISY 5.x firmware as it uses the superior NodeDefs method
|
||||
if platform == FAN and int(node.nid[-1]) == 1:
|
||||
hass.data[ISY994_NODES][LIGHT].append(node)
|
||||
return True
|
||||
|
||||
hass.data[ISY994_NODES][platform].append(node)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _check_for_uom_id(
|
||||
hass: HomeAssistant, node, single_platform: str = None, uom_list: list = None
|
||||
) -> bool:
|
||||
"""Check if a node's uom matches any of the platforms uom filter.
|
||||
|
||||
This is used for versions of the ISY firmware that report uoms as a single
|
||||
ID. We can often infer what type of device it is by that ID.
|
||||
"""
|
||||
if not hasattr(node, "uom") or node.uom is None:
|
||||
# Node doesn't have a uom (Scenes for example)
|
||||
return False
|
||||
|
||||
node_uom = set(map(str.lower, node.uom))
|
||||
|
||||
if uom_list:
|
||||
if node_uom.intersection(uom_list):
|
||||
hass.data[ISY994_NODES][single_platform].append(node)
|
||||
return True
|
||||
else:
|
||||
platforms = SUPPORTED_PLATFORMS if not single_platform else [single_platform]
|
||||
for platform in platforms:
|
||||
if node_uom.intersection(NODE_FILTERS[platform]["uom"]):
|
||||
hass.data[ISY994_NODES][platform].append(node)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _check_for_states_in_uom(
|
||||
hass: HomeAssistant, node, single_platform: str = None, states_list: list = None
|
||||
) -> bool:
|
||||
"""Check if a list of uoms matches two possible filters.
|
||||
|
||||
This is for versions of the ISY firmware that report uoms as a list of all
|
||||
possible "human readable" states. This filter passes if all of the possible
|
||||
states fit inside the given filter.
|
||||
"""
|
||||
if not hasattr(node, "uom") or node.uom is None:
|
||||
# Node doesn't have a uom (Scenes for example)
|
||||
return False
|
||||
|
||||
node_uom = set(map(str.lower, node.uom))
|
||||
|
||||
if states_list:
|
||||
if node_uom == set(states_list):
|
||||
hass.data[ISY994_NODES][single_platform].append(node)
|
||||
return True
|
||||
else:
|
||||
platforms = SUPPORTED_PLATFORMS if not single_platform else [single_platform]
|
||||
for platform in platforms:
|
||||
if node_uom == set(NODE_FILTERS[platform]["states"]):
|
||||
hass.data[ISY994_NODES][platform].append(node)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _is_sensor_a_binary_sensor(hass: HomeAssistant, node) -> bool:
|
||||
"""Determine if the given sensor node should be a binary_sensor."""
|
||||
if _check_for_node_def(hass, node, single_platform=BINARY_SENSOR):
|
||||
return True
|
||||
if _check_for_insteon_type(hass, node, single_platform=BINARY_SENSOR):
|
||||
return True
|
||||
|
||||
# For the next two checks, we're providing our own set of uoms that
|
||||
# represent on/off devices. This is because we can only depend on these
|
||||
# checks in the context of already knowing that this is definitely a
|
||||
# sensor device.
|
||||
if _check_for_uom_id(
|
||||
hass, node, single_platform=BINARY_SENSOR, uom_list=["2", "78"]
|
||||
):
|
||||
return True
|
||||
if _check_for_states_in_uom(
|
||||
hass, node, single_platform=BINARY_SENSOR, states_list=["on", "off"]
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _categorize_nodes(
|
||||
hass: HomeAssistant, nodes, ignore_identifier: str, sensor_identifier: str
|
||||
) -> None:
|
||||
"""Sort the nodes to their proper platforms."""
|
||||
for (path, node) in nodes:
|
||||
ignored = ignore_identifier in path or ignore_identifier in node.name
|
||||
if ignored:
|
||||
# Don't import this node as a device at all
|
||||
continue
|
||||
|
||||
if isinstance(node, Group):
|
||||
hass.data[ISY994_NODES][ISY_GROUP_PLATFORM].append(node)
|
||||
continue
|
||||
|
||||
if sensor_identifier in path or sensor_identifier in node.name:
|
||||
# User has specified to treat this as a sensor. First we need to
|
||||
# determine if it should be a binary_sensor.
|
||||
if _is_sensor_a_binary_sensor(hass, node):
|
||||
continue
|
||||
|
||||
hass.data[ISY994_NODES][SENSOR].append(node)
|
||||
continue
|
||||
|
||||
# We have a bunch of different methods for determining the device type,
|
||||
# each of which works with different ISY firmware versions or device
|
||||
# family. The order here is important, from most reliable to least.
|
||||
if _check_for_node_def(hass, node):
|
||||
continue
|
||||
if _check_for_insteon_type(hass, node):
|
||||
continue
|
||||
if _check_for_uom_id(hass, node):
|
||||
continue
|
||||
if _check_for_states_in_uom(hass, node):
|
||||
continue
|
||||
|
||||
|
||||
def _categorize_programs(hass: HomeAssistant, programs: dict) -> None:
|
||||
"""Categorize the ISY994 programs."""
|
||||
for platform in SUPPORTED_PROGRAM_PLATFORMS:
|
||||
try:
|
||||
folder = programs[KEY_MY_PROGRAMS][f"HA.{platform}"]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
for dtype, _, node_id in folder.children:
|
||||
if dtype != KEY_FOLDER:
|
||||
continue
|
||||
entity_folder = folder[node_id]
|
||||
try:
|
||||
status = entity_folder[KEY_STATUS]
|
||||
assert status.dtype == "program", "Not a program"
|
||||
if platform != BINARY_SENSOR:
|
||||
actions = entity_folder[KEY_ACTIONS]
|
||||
assert actions.dtype == "program", "Not a program"
|
||||
else:
|
||||
actions = None
|
||||
except (AttributeError, KeyError, AssertionError):
|
||||
_LOGGER.warning(
|
||||
"Program entity '%s' not loaded due "
|
||||
"to invalid folder structure.",
|
||||
entity_folder.name,
|
||||
)
|
||||
continue
|
||||
|
||||
entity = (entity_folder.name, status, actions)
|
||||
hass.data[ISY994_PROGRAMS][platform].append(entity)
|
||||
|
||||
|
||||
def _categorize_weather(hass: HomeAssistant, climate) -> None:
|
||||
"""Categorize the ISY994 weather data."""
|
||||
climate_attrs = dir(climate)
|
||||
weather_nodes = [
|
||||
WeatherNode(
|
||||
getattr(climate, attr),
|
||||
attr.replace("_", " "),
|
||||
getattr(climate, f"{attr}_units"),
|
||||
)
|
||||
for attr in climate_attrs
|
||||
if f"{attr}_units" in climate_attrs
|
||||
]
|
||||
hass.data[ISY994_WEATHER].extend(weather_nodes)
|
||||
|
||||
|
||||
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the ISY 994 platform."""
|
||||
|
@ -352,81 +117,3 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
|
||||
isy.auto_update = True
|
||||
return True
|
||||
|
||||
|
||||
class ISYDevice(Entity):
|
||||
"""Representation of an ISY994 device."""
|
||||
|
||||
_attrs = {}
|
||||
_name: str = None
|
||||
|
||||
def __init__(self, node) -> None:
|
||||
"""Initialize the insteon device."""
|
||||
self._node = node
|
||||
self._change_handler = None
|
||||
self._control_handler = None
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to the node change events."""
|
||||
self._change_handler = self._node.status.subscribe("changed", self.on_update)
|
||||
|
||||
if hasattr(self._node, "controlEvents"):
|
||||
self._control_handler = self._node.controlEvents.subscribe(self.on_control)
|
||||
|
||||
def on_update(self, event: object) -> None:
|
||||
"""Handle the update event from the ISY994 Node."""
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def on_control(self, event: object) -> None:
|
||||
"""Handle a control event from the ISY994 Node."""
|
||||
self.hass.bus.fire(
|
||||
"isy994_control", {"entity_id": self.entity_id, "control": event}
|
||||
)
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Get the unique identifier of the device."""
|
||||
# pylint: disable=protected-access
|
||||
if hasattr(self._node, "_id"):
|
||||
return self._node._id
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Get the name of the device."""
|
||||
return self._name or str(self._node.name)
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""No polling required since we're using the subscription."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def value(self) -> int:
|
||||
"""Get the current value of the device."""
|
||||
# pylint: disable=protected-access
|
||||
return self._node.status._val
|
||||
|
||||
def is_unknown(self) -> bool:
|
||||
"""Get whether or not the value of this Entity's node is unknown.
|
||||
|
||||
PyISY reports unknown values as -inf
|
||||
"""
|
||||
return self.value == -1 * float("inf")
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the ISY device."""
|
||||
if self.is_unknown():
|
||||
return None
|
||||
return super().state
|
||||
|
||||
@property
|
||||
def device_state_attributes(self) -> Dict:
|
||||
"""Get the state attributes for the device."""
|
||||
attr = {}
|
||||
if hasattr(self._node, "aux_properties"):
|
||||
for name, val in self._node.aux_properties.items():
|
||||
attr[name] = f"{val.get('value')} {val.get('uom')}"
|
||||
return attr
|
||||
|
|
|
@ -12,8 +12,9 @@ from homeassistant.helpers.event import async_track_point_in_utc_time
|
|||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS
|
||||
from .const import _LOGGER, ISY_BIN_SENS_DEVICE_TYPES
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
|
||||
|
||||
def setup_platform(
|
||||
|
@ -65,7 +66,7 @@ def setup_platform(
|
|||
devices.append(device)
|
||||
|
||||
for name, status, _ in hass.data[ISY994_PROGRAMS][BINARY_SENSOR]:
|
||||
devices.append(ISYBinarySensorProgram(name, status))
|
||||
devices.append(ISYBinarySensorProgramEntity(name, status))
|
||||
|
||||
add_entities(devices)
|
||||
|
||||
|
@ -90,7 +91,7 @@ def _is_val_unknown(val):
|
|||
return val == -1 * float("inf")
|
||||
|
||||
|
||||
class ISYBinarySensorEntity(ISYDevice, BinarySensorEntity):
|
||||
class ISYBinarySensorEntity(ISYNodeEntity, BinarySensorEntity):
|
||||
"""Representation of an ISY994 binary sensor device.
|
||||
|
||||
Often times, a single device is represented by multiple nodes in the ISY,
|
||||
|
@ -248,7 +249,7 @@ class ISYBinarySensorEntity(ISYDevice, BinarySensorEntity):
|
|||
return self._device_class_from_type
|
||||
|
||||
|
||||
class ISYBinarySensorHeartbeat(ISYDevice, BinarySensorEntity):
|
||||
class ISYBinarySensorHeartbeat(ISYNodeEntity, BinarySensorEntity):
|
||||
"""Representation of the battery state of an ISY994 sensor."""
|
||||
|
||||
def __init__(self, node, parent_device) -> None:
|
||||
|
@ -348,18 +349,13 @@ class ISYBinarySensorHeartbeat(ISYDevice, BinarySensorEntity):
|
|||
return attr
|
||||
|
||||
|
||||
class ISYBinarySensorProgram(ISYDevice, BinarySensorEntity):
|
||||
class ISYBinarySensorProgramEntity(ISYProgramEntity, BinarySensorEntity):
|
||||
"""Representation of an ISY994 binary sensor program.
|
||||
|
||||
This does not need all of the subnode logic in the device version of binary
|
||||
sensors.
|
||||
"""
|
||||
|
||||
def __init__(self, name, node) -> None:
|
||||
"""Initialize the ISY994 binary sensor program."""
|
||||
super().__init__(node)
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Get whether the ISY994 binary sensor device is on."""
|
||||
|
|
|
@ -5,8 +5,9 @@ from homeassistant.components.cover import DOMAIN as COVER, CoverEntity
|
|||
from homeassistant.const import STATE_CLOSED, STATE_OPEN
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS
|
||||
from .const import _LOGGER, UOM_TO_STATES
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
|
||||
|
||||
def setup_platform(
|
||||
|
@ -18,12 +19,12 @@ def setup_platform(
|
|||
devices.append(ISYCoverEntity(node))
|
||||
|
||||
for name, status, actions in hass.data[ISY994_PROGRAMS][COVER]:
|
||||
devices.append(ISYCoverProgram(name, status, actions))
|
||||
devices.append(ISYCoverProgramEntity(name, status, actions))
|
||||
|
||||
add_entities(devices)
|
||||
|
||||
|
||||
class ISYCoverEntity(ISYDevice, CoverEntity):
|
||||
class ISYCoverEntity(ISYNodeEntity, CoverEntity):
|
||||
"""Representation of an ISY994 cover device."""
|
||||
|
||||
@property
|
||||
|
@ -57,15 +58,9 @@ class ISYCoverEntity(ISYDevice, CoverEntity):
|
|||
_LOGGER.error("Unable to close the cover")
|
||||
|
||||
|
||||
class ISYCoverProgram(ISYCoverEntity):
|
||||
class ISYCoverProgramEntity(ISYProgramEntity, CoverEntity):
|
||||
"""Representation of an ISY994 cover program."""
|
||||
|
||||
def __init__(self, name: str, node: object, actions: object) -> None:
|
||||
"""Initialize the ISY994 cover program."""
|
||||
super().__init__(node)
|
||||
self._name = name
|
||||
self._actions = actions
|
||||
|
||||
@property
|
||||
def state(self) -> str:
|
||||
"""Get the state of the ISY994 cover program."""
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
"""Representation of ISYEntity Types."""
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.typing import Dict
|
||||
|
||||
|
||||
class ISYEntity(Entity):
|
||||
"""Representation of an ISY994 device."""
|
||||
|
||||
_attrs = {}
|
||||
_name: str = None
|
||||
|
||||
def __init__(self, node) -> None:
|
||||
"""Initialize the insteon device."""
|
||||
self._node = node
|
||||
self._change_handler = None
|
||||
self._control_handler = None
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to the node change events."""
|
||||
self._change_handler = self._node.status.subscribe("changed", self.on_update)
|
||||
|
||||
if hasattr(self._node, "controlEvents"):
|
||||
self._control_handler = self._node.controlEvents.subscribe(self.on_control)
|
||||
|
||||
def on_update(self, event: object) -> None:
|
||||
"""Handle the update event from the ISY994 Node."""
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def on_control(self, event: object) -> None:
|
||||
"""Handle a control event from the ISY994 Node."""
|
||||
self.hass.bus.fire(
|
||||
"isy994_control", {"entity_id": self.entity_id, "control": event}
|
||||
)
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Get the unique identifier of the device."""
|
||||
# pylint: disable=protected-access
|
||||
if hasattr(self._node, "_id"):
|
||||
return self._node._id
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Get the name of the device."""
|
||||
return self._name or str(self._node.name)
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""No polling required since we're using the subscription."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def value(self) -> int:
|
||||
"""Get the current value of the device."""
|
||||
# pylint: disable=protected-access
|
||||
return self._node.status._val
|
||||
|
||||
def is_unknown(self) -> bool:
|
||||
"""Get whether or not the value of this Entity's node is unknown.
|
||||
|
||||
PyISY reports unknown values as -inf
|
||||
"""
|
||||
return self.value == -1 * float("inf")
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the ISY device."""
|
||||
if self.is_unknown():
|
||||
return None
|
||||
return super().state
|
||||
|
||||
|
||||
class ISYNodeEntity(ISYEntity):
|
||||
"""Representation of a ISY Nodebase (Node/Group) entity."""
|
||||
|
||||
@property
|
||||
def device_state_attributes(self) -> Dict:
|
||||
"""Get the state attributes for the device."""
|
||||
attr = {}
|
||||
if hasattr(self._node, "aux_properties"):
|
||||
for name, val in self._node.aux_properties.items():
|
||||
attr[name] = f"{val.get('value')} {val.get('uom')}"
|
||||
return attr
|
||||
|
||||
|
||||
class ISYProgramEntity(ISYEntity):
|
||||
"""Representation of an ISY994 program base."""
|
||||
|
||||
def __init__(self, name: str, status, actions=None) -> None:
|
||||
"""Initialize the ISY994 program-based entity."""
|
||||
super().__init__(status)
|
||||
self._name = name
|
||||
self._actions = actions
|
|
@ -12,8 +12,9 @@ from homeassistant.components.fan import (
|
|||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS
|
||||
from .const import _LOGGER
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
|
||||
VALUE_TO_STATE = {
|
||||
0: SPEED_OFF,
|
||||
|
@ -36,15 +37,15 @@ def setup_platform(
|
|||
devices = []
|
||||
|
||||
for node in hass.data[ISY994_NODES][FAN]:
|
||||
devices.append(ISYFanDevice(node))
|
||||
devices.append(ISYFanEntity(node))
|
||||
|
||||
for name, status, actions in hass.data[ISY994_PROGRAMS][FAN]:
|
||||
devices.append(ISYFanProgram(name, status, actions))
|
||||
devices.append(ISYFanProgramEntity(name, status, actions))
|
||||
|
||||
add_entities(devices)
|
||||
|
||||
|
||||
class ISYFanDevice(ISYDevice, FanEntity):
|
||||
class ISYFanEntity(ISYNodeEntity, FanEntity):
|
||||
"""Representation of an ISY994 fan device."""
|
||||
|
||||
@property
|
||||
|
@ -80,14 +81,20 @@ class ISYFanDevice(ISYDevice, FanEntity):
|
|||
return SUPPORT_SET_SPEED
|
||||
|
||||
|
||||
class ISYFanProgram(ISYFanDevice):
|
||||
class ISYFanProgramEntity(ISYProgramEntity, FanEntity):
|
||||
"""Representation of an ISY994 fan program."""
|
||||
|
||||
def __init__(self, name: str, node, actions) -> None:
|
||||
"""Initialize the ISY994 fan program."""
|
||||
super().__init__(node)
|
||||
self._name = name
|
||||
self._actions = actions
|
||||
@property
|
||||
def speed(self) -> str:
|
||||
"""Return the current speed."""
|
||||
# TEMPORARY: Cast value to int until PyISYv2.
|
||||
return VALUE_TO_STATE.get(int(self.value))
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Get if the fan is on."""
|
||||
# TEMPORARY: Cast value to int until PyISYv2.
|
||||
return int(self.value) != 0
|
||||
|
||||
def turn_off(self, **kwargs) -> None:
|
||||
"""Send the turn on command to ISY994 fan program."""
|
||||
|
@ -98,8 +105,3 @@ class ISYFanProgram(ISYFanDevice):
|
|||
"""Send the turn off command to ISY994 fan program."""
|
||||
if not self._actions.runElse():
|
||||
_LOGGER.error("Unable to turn on the fan")
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Flag supported features."""
|
||||
return 0
|
||||
|
|
|
@ -9,8 +9,9 @@ from homeassistant.components.light import (
|
|||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import ISY994_NODES, ISYDevice
|
||||
from . import ISY994_NODES
|
||||
from .const import _LOGGER
|
||||
from .entity import ISYNodeEntity
|
||||
|
||||
ATTR_LAST_BRIGHTNESS = "last_brightness"
|
||||
|
||||
|
@ -21,12 +22,12 @@ def setup_platform(
|
|||
"""Set up the ISY994 light platform."""
|
||||
devices = []
|
||||
for node in hass.data[ISY994_NODES][LIGHT]:
|
||||
devices.append(ISYLightDevice(node))
|
||||
devices.append(ISYLightEntity(node))
|
||||
|
||||
add_entities(devices)
|
||||
|
||||
|
||||
class ISYLightDevice(ISYDevice, LightEntity, RestoreEntity):
|
||||
class ISYLightEntity(ISYNodeEntity, LightEntity, RestoreEntity):
|
||||
"""Representation of an ISY994 light device."""
|
||||
|
||||
def __init__(self, node) -> None:
|
||||
|
|
|
@ -5,8 +5,9 @@ from homeassistant.components.lock import DOMAIN as LOCK, LockEntity
|
|||
from homeassistant.const import STATE_LOCKED, STATE_UNKNOWN, STATE_UNLOCKED
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS
|
||||
from .const import _LOGGER
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
|
||||
VALUE_TO_STATE = {0: STATE_UNLOCKED, 100: STATE_LOCKED}
|
||||
|
||||
|
@ -17,15 +18,15 @@ def setup_platform(
|
|||
"""Set up the ISY994 lock platform."""
|
||||
devices = []
|
||||
for node in hass.data[ISY994_NODES][LOCK]:
|
||||
devices.append(ISYLockDevice(node))
|
||||
devices.append(ISYLockEntity(node))
|
||||
|
||||
for name, status, actions in hass.data[ISY994_PROGRAMS][LOCK]:
|
||||
devices.append(ISYLockProgram(name, status, actions))
|
||||
devices.append(ISYLockProgramEntity(name, status, actions))
|
||||
|
||||
add_entities(devices)
|
||||
|
||||
|
||||
class ISYLockDevice(ISYDevice, LockEntity):
|
||||
class ISYLockEntity(ISYNodeEntity, LockEntity):
|
||||
"""Representation of an ISY994 lock device."""
|
||||
|
||||
def __init__(self, node) -> None:
|
||||
|
@ -68,15 +69,9 @@ class ISYLockDevice(ISYDevice, LockEntity):
|
|||
self._node.update(0.5)
|
||||
|
||||
|
||||
class ISYLockProgram(ISYLockDevice):
|
||||
class ISYLockProgramEntity(ISYProgramEntity, LockEntity):
|
||||
"""Representation of a ISY lock program."""
|
||||
|
||||
def __init__(self, name: str, node, actions) -> None:
|
||||
"""Initialize the lock."""
|
||||
super().__init__(node)
|
||||
self._name = name
|
||||
self._actions = actions
|
||||
|
||||
@property
|
||||
def is_locked(self) -> bool:
|
||||
"""Return true if the device is locked."""
|
||||
|
|
|
@ -5,8 +5,9 @@ from homeassistant.components.sensor import DOMAIN as SENSOR
|
|||
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import ISY994_NODES, ISY994_WEATHER, ISYDevice
|
||||
from . import ISY994_NODES, ISY994_WEATHER
|
||||
from .const import _LOGGER, UOM_FRIENDLY_NAME, UOM_TO_STATES
|
||||
from .entity import ISYEntity, ISYNodeEntity
|
||||
|
||||
|
||||
def setup_platform(
|
||||
|
@ -17,7 +18,7 @@ def setup_platform(
|
|||
|
||||
for node in hass.data[ISY994_NODES][SENSOR]:
|
||||
_LOGGER.debug("Loading %s", node.name)
|
||||
devices.append(ISYSensorDevice(node))
|
||||
devices.append(ISYSensorEntity(node))
|
||||
|
||||
for node in hass.data[ISY994_WEATHER]:
|
||||
devices.append(ISYWeatherDevice(node))
|
||||
|
@ -25,7 +26,7 @@ def setup_platform(
|
|||
add_entities(devices)
|
||||
|
||||
|
||||
class ISYSensorDevice(ISYDevice):
|
||||
class ISYSensorEntity(ISYNodeEntity):
|
||||
"""Representation of an ISY994 sensor device."""
|
||||
|
||||
@property
|
||||
|
@ -77,7 +78,8 @@ class ISYSensorDevice(ISYDevice):
|
|||
return raw_units
|
||||
|
||||
|
||||
class ISYWeatherDevice(ISYDevice):
|
||||
# Depreciated, not renaming. Will be removed in next PR.
|
||||
class ISYWeatherDevice(ISYEntity):
|
||||
"""Representation of an ISY994 weather device."""
|
||||
|
||||
@property
|
||||
|
|
|
@ -4,8 +4,9 @@ from typing import Callable
|
|||
from homeassistant.components.switch import DOMAIN as SWITCH, SwitchEntity
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice
|
||||
from . import ISY994_NODES, ISY994_PROGRAMS
|
||||
from .const import _LOGGER
|
||||
from .entity import ISYNodeEntity, ISYProgramEntity
|
||||
|
||||
|
||||
def setup_platform(
|
||||
|
@ -15,15 +16,15 @@ def setup_platform(
|
|||
devices = []
|
||||
for node in hass.data[ISY994_NODES][SWITCH]:
|
||||
if not node.dimmable:
|
||||
devices.append(ISYSwitchDevice(node))
|
||||
devices.append(ISYSwitchEntity(node))
|
||||
|
||||
for name, status, actions in hass.data[ISY994_PROGRAMS][SWITCH]:
|
||||
devices.append(ISYSwitchProgram(name, status, actions))
|
||||
devices.append(ISYSwitchProgramEntity(name, status, actions))
|
||||
|
||||
add_entities(devices)
|
||||
|
||||
|
||||
class ISYSwitchDevice(ISYDevice, SwitchEntity):
|
||||
class ISYSwitchEntity(ISYNodeEntity, SwitchEntity):
|
||||
"""Representation of an ISY994 switch device."""
|
||||
|
||||
@property
|
||||
|
@ -42,15 +43,9 @@ class ISYSwitchDevice(ISYDevice, SwitchEntity):
|
|||
_LOGGER.debug("Unable to turn on switch.")
|
||||
|
||||
|
||||
class ISYSwitchProgram(ISYSwitchDevice):
|
||||
class ISYSwitchProgramEntity(ISYProgramEntity, SwitchEntity):
|
||||
"""A representation of an ISY994 program switch."""
|
||||
|
||||
def __init__(self, name: str, node, actions) -> None:
|
||||
"""Initialize the ISY994 switch program."""
|
||||
super().__init__(node)
|
||||
self._name = name
|
||||
self._actions = actions
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Get whether the ISY994 switch program is on."""
|
||||
|
@ -65,3 +60,8 @@ class ISYSwitchProgram(ISYSwitchDevice):
|
|||
"""Send the turn off command to the ISY994 switch program."""
|
||||
if not self._actions.runElse():
|
||||
_LOGGER.error("Unable to turn off switch")
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Get the icon for programs."""
|
||||
return "mdi:script-text-outline" # Matches isy program icon
|
||||
|
|
Loading…
Reference in New Issue