2020-05-05 04:03:12 +00:00
|
|
|
"""Representation of ISYEntity Types."""
|
|
|
|
|
2020-05-08 04:15:42 +00:00
|
|
|
from pyisy.constants import (
|
|
|
|
COMMAND_FRIENDLY_NAME,
|
|
|
|
EMPTY_TIME,
|
|
|
|
EVENT_PROPS_IGNORED,
|
|
|
|
ISY_VALUE_UNKNOWN,
|
|
|
|
)
|
|
|
|
from pyisy.helpers import NodeProperty
|
|
|
|
|
|
|
|
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN
|
2020-05-05 04:03:12 +00:00
|
|
|
from homeassistant.helpers.entity import Entity
|
|
|
|
from homeassistant.helpers.typing import Dict
|
|
|
|
|
|
|
|
|
|
|
|
class ISYEntity(Entity):
|
|
|
|
"""Representation of an ISY994 device."""
|
|
|
|
|
|
|
|
_name: str = None
|
|
|
|
|
|
|
|
def __init__(self, node) -> None:
|
|
|
|
"""Initialize the insteon device."""
|
|
|
|
self._node = node
|
2020-05-08 04:15:42 +00:00
|
|
|
self._attrs = {}
|
2020-05-05 04:03:12 +00:00
|
|
|
self._change_handler = None
|
|
|
|
self._control_handler = None
|
|
|
|
|
|
|
|
async def async_added_to_hass(self) -> None:
|
|
|
|
"""Subscribe to the node change events."""
|
2020-05-08 04:15:42 +00:00
|
|
|
self._change_handler = self._node.status_events.subscribe(self.on_update)
|
2020-05-05 04:03:12 +00:00
|
|
|
|
2020-05-08 04:15:42 +00:00
|
|
|
if hasattr(self._node, "control_events"):
|
|
|
|
self._control_handler = self._node.control_events.subscribe(self.on_control)
|
2020-05-05 04:03:12 +00:00
|
|
|
|
|
|
|
def on_update(self, event: object) -> None:
|
|
|
|
"""Handle the update event from the ISY994 Node."""
|
|
|
|
self.schedule_update_ha_state()
|
|
|
|
|
2020-05-08 04:15:42 +00:00
|
|
|
def on_control(self, event: NodeProperty) -> None:
|
2020-05-05 04:03:12 +00:00
|
|
|
"""Handle a control event from the ISY994 Node."""
|
2020-05-08 04:15:42 +00:00
|
|
|
event_data = {
|
|
|
|
"entity_id": self.entity_id,
|
|
|
|
"control": event.control,
|
|
|
|
"value": event.value,
|
|
|
|
"formatted": event.formatted,
|
|
|
|
"uom": event.uom,
|
|
|
|
"precision": event.prec,
|
|
|
|
}
|
|
|
|
|
|
|
|
if event.value is None or event.control not in EVENT_PROPS_IGNORED:
|
|
|
|
# New state attributes may be available, update the state.
|
|
|
|
self.schedule_update_ha_state()
|
|
|
|
|
|
|
|
self.hass.bus.fire("isy994_control", event_data)
|
2020-05-05 04:03:12 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def unique_id(self) -> str:
|
|
|
|
"""Get the unique identifier of the device."""
|
2020-05-08 04:15:42 +00:00
|
|
|
if hasattr(self._node, "address"):
|
|
|
|
return self._node.address
|
2020-05-05 04:03:12 +00:00
|
|
|
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."""
|
2020-05-08 04:15:42 +00:00
|
|
|
return self._node.status
|
2020-05-05 04:03:12 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def state(self):
|
|
|
|
"""Return the state of the ISY device."""
|
2020-05-08 04:15:42 +00:00
|
|
|
if self.value == ISY_VALUE_UNKNOWN:
|
|
|
|
return STATE_UNKNOWN
|
2020-05-05 04:03:12 +00:00
|
|
|
return super().state
|
|
|
|
|
|
|
|
|
|
|
|
class ISYNodeEntity(ISYEntity):
|
|
|
|
"""Representation of a ISY Nodebase (Node/Group) entity."""
|
|
|
|
|
|
|
|
@property
|
|
|
|
def device_state_attributes(self) -> Dict:
|
2020-05-08 04:15:42 +00:00
|
|
|
"""Get the state attributes for the device.
|
|
|
|
|
|
|
|
The 'aux_properties' in the pyisy Node class are combined with the
|
|
|
|
other attributes which have been picked up from the event stream and
|
|
|
|
the combined result are returned as the device state attributes.
|
|
|
|
"""
|
2020-05-05 04:03:12 +00:00
|
|
|
attr = {}
|
|
|
|
if hasattr(self._node, "aux_properties"):
|
2020-05-08 04:15:42 +00:00
|
|
|
# Cast as list due to RuntimeError if a new property is added while running.
|
|
|
|
for name, value in list(self._node.aux_properties.items()):
|
|
|
|
attr_name = COMMAND_FRIENDLY_NAME.get(name, name)
|
|
|
|
attr[attr_name] = str(value.formatted).lower()
|
|
|
|
|
|
|
|
# If a Group/Scene, set a property if the entire scene is on/off
|
|
|
|
if hasattr(self._node, "group_all_on"):
|
|
|
|
attr["group_all_on"] = STATE_ON if self._node.group_all_on else STATE_OFF
|
|
|
|
|
|
|
|
self._attrs.update(attr)
|
|
|
|
return self._attrs
|
2020-05-05 04:03:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
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
|
2020-05-08 04:15:42 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def device_state_attributes(self) -> Dict:
|
|
|
|
"""Get the state attributes for the device."""
|
|
|
|
attr = {}
|
|
|
|
if self._actions:
|
|
|
|
attr["actions_enabled"] = self._actions.enabled
|
|
|
|
if self._actions.last_finished != EMPTY_TIME:
|
|
|
|
attr["actions_last_finished"] = self._actions.last_finished
|
|
|
|
if self._actions.last_run != EMPTY_TIME:
|
|
|
|
attr["actions_last_run"] = self._actions.last_run
|
|
|
|
if self._actions.last_update != EMPTY_TIME:
|
|
|
|
attr["actions_last_update"] = self._actions.last_update
|
|
|
|
attr["ran_else"] = self._actions.ran_else
|
|
|
|
attr["ran_then"] = self._actions.ran_then
|
|
|
|
attr["run_at_startup"] = self._actions.run_at_startup
|
|
|
|
attr["running"] = self._actions.running
|
|
|
|
attr["status_enabled"] = self._node.enabled
|
|
|
|
if self._node.last_finished != EMPTY_TIME:
|
|
|
|
attr["status_last_finished"] = self._node.last_finished
|
|
|
|
if self._node.last_run != EMPTY_TIME:
|
|
|
|
attr["status_last_run"] = self._node.last_run
|
|
|
|
if self._node.last_update != EMPTY_TIME:
|
|
|
|
attr["status_last_update"] = self._node.last_update
|
|
|
|
return attr
|