Migrate ISY994 to PyISY v2 (#35338)
* Remove unnecessary pylint exceptions * Move up some change to binary_sensors and switch. Fix program d.s.a's. * ISY994 Basic support for PyISYv2 - Bare minimum changes to be able to support PyISYv2. - Renaming imports and functions to new names. - Use necessary constants from module. - **BREAKING CHANGE** Remove ISY Climate Module support. - Climate module was retired on 3/30/2020: [UDI Annoucement](https://www.universal-devices.com/byebyeclimatemodule/) - **BREAKING CHANGE** Device State Attributes use NodeProperty - Some attributes names and types will have changed as part of the changes to PyISY. If a user relied on a device state attribute for a given entity, they should check that it is still there and formatted the same. In general, *more* state attributes should be getting picked up now that the underlying changes have been made. - **BREAKING CHANGE** `isy994_control` event changes (using NodeProperty) - Control events now return an object with additional information. Control events are now parsed to the friendly names and will need to be updated in automations. Remove cast * PyISY v2.0.2, add extra UOMs, omit EMPTY_TIME attributes * Fix typo in function doc string. Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
7ac547a6e0
commit
4ec88b41dc
13 changed files with 268 additions and 245 deletions
|
@ -1,5 +1,14 @@
|
|||
"""Representation of ISYEntity Types."""
|
||||
|
||||
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
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.typing import Dict
|
||||
|
||||
|
@ -7,39 +16,48 @@ 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._attrs = {}
|
||||
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)
|
||||
self._change_handler = self._node.status_events.subscribe(self.on_update)
|
||||
|
||||
if hasattr(self._node, "controlEvents"):
|
||||
self._control_handler = self._node.controlEvents.subscribe(self.on_control)
|
||||
if hasattr(self._node, "control_events"):
|
||||
self._control_handler = self._node.control_events.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:
|
||||
def on_control(self, event: NodeProperty) -> None:
|
||||
"""Handle a control event from the ISY994 Node."""
|
||||
self.hass.bus.fire(
|
||||
"isy994_control", {"entity_id": self.entity_id, "control": event}
|
||||
)
|
||||
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)
|
||||
|
||||
@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
|
||||
|
||||
if hasattr(self._node, "address"):
|
||||
return self._node.address
|
||||
return None
|
||||
|
||||
@property
|
||||
|
@ -55,21 +73,13 @@ class ISYEntity(Entity):
|
|||
@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")
|
||||
return self._node.status
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the ISY device."""
|
||||
if self.is_unknown():
|
||||
return None
|
||||
if self.value == ISY_VALUE_UNKNOWN:
|
||||
return STATE_UNKNOWN
|
||||
return super().state
|
||||
|
||||
|
||||
|
@ -78,12 +88,25 @@ class ISYNodeEntity(ISYEntity):
|
|||
|
||||
@property
|
||||
def device_state_attributes(self) -> Dict:
|
||||
"""Get the state attributes for the device."""
|
||||
"""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.
|
||||
"""
|
||||
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
|
||||
# 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
|
||||
|
||||
|
||||
class ISYProgramEntity(ISYEntity):
|
||||
|
@ -94,3 +117,28 @@ class ISYProgramEntity(ISYEntity):
|
|||
super().__init__(status)
|
||||
self._name = name
|
||||
self._actions = actions
|
||||
|
||||
@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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue