2019-02-14 15:01:46 +00:00
|
|
|
"""Support the ISY-994 controllers."""
|
2017-01-05 22:33:52 +00:00
|
|
|
from collections import namedtuple
|
2015-04-04 08:33:03 +00:00
|
|
|
from urllib.parse import urlparse
|
2017-04-30 05:04:49 +00:00
|
|
|
|
2019-10-22 05:40:49 +00:00
|
|
|
import PyISY
|
|
|
|
from PyISY.Nodes import Group
|
2016-09-11 18:18:53 +00:00
|
|
|
import voluptuous as vol
|
2015-04-04 08:33:03 +00:00
|
|
|
|
2020-05-05 00:21:40 +00:00
|
|
|
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
|
2016-02-19 05:27:50 +00:00
|
|
|
from homeassistant.const import (
|
2019-07-31 19:25:30 +00:00
|
|
|
CONF_HOST,
|
|
|
|
CONF_PASSWORD,
|
|
|
|
CONF_USERNAME,
|
|
|
|
EVENT_HOMEASSISTANT_STOP,
|
|
|
|
)
|
2019-02-14 15:01:46 +00:00
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from homeassistant.helpers import config_validation as cv, discovery
|
2016-09-11 18:18:53 +00:00
|
|
|
from homeassistant.helpers.entity import Entity
|
2018-06-25 17:05:07 +00:00
|
|
|
from homeassistant.helpers.typing import ConfigType, Dict
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2020-05-05 00:21:40 +00:00
|
|
|
from .const import (
|
|
|
|
_LOGGER,
|
|
|
|
CONF_ENABLE_CLIMATE,
|
|
|
|
CONF_IGNORE_STRING,
|
|
|
|
CONF_SENSOR_STRING,
|
|
|
|
CONF_TLS_VER,
|
|
|
|
DEFAULT_IGNORE_STRING,
|
|
|
|
DEFAULT_SENSOR_STRING,
|
|
|
|
DOMAIN,
|
|
|
|
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,
|
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
|
|
|
|
CONFIG_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
DOMAIN: vol.Schema(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_HOST): cv.url,
|
|
|
|
vol.Required(CONF_USERNAME): cv.string,
|
|
|
|
vol.Required(CONF_PASSWORD): cv.string,
|
|
|
|
vol.Optional(CONF_TLS_VER): vol.Coerce(float),
|
|
|
|
vol.Optional(
|
|
|
|
CONF_IGNORE_STRING, default=DEFAULT_IGNORE_STRING
|
|
|
|
): cv.string,
|
|
|
|
vol.Optional(
|
|
|
|
CONF_SENSOR_STRING, default=DEFAULT_SENSOR_STRING
|
|
|
|
): cv.string,
|
|
|
|
vol.Optional(CONF_ENABLE_CLIMATE, default=True): cv.boolean,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
extra=vol.ALLOW_EXTRA,
|
|
|
|
)
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
WeatherNode = namedtuple("WeatherNode", ("status", "name", "uom"))
|
2016-09-11 18:18:53 +00:00
|
|
|
|
|
|
|
|
2020-05-05 00:21:40 +00:00
|
|
|
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.
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
This is only present on the 5.0 ISY firmware, and is the most reliable
|
|
|
|
way to determine a device's type.
|
|
|
|
"""
|
2019-07-31 19:25:30 +00:00
|
|
|
if not hasattr(node, "node_def_id") or node.node_def_id is None:
|
2017-12-26 08:26:37 +00:00
|
|
|
# Node doesn't have a node_def (pre 5.0 firmware most likely)
|
|
|
|
return False
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
node_def_id = node.node_def_id
|
2017-01-05 22:33:52 +00:00
|
|
|
|
2020-05-05 00:21:40 +00:00
|
|
|
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)
|
2017-12-26 08:26:37 +00:00
|
|
|
return True
|
2017-01-05 22:33:52 +00:00
|
|
|
|
2019-09-04 05:00:20 +00:00
|
|
|
_LOGGER.warning("Unsupported node: %s, type: %s", node.name, node.type)
|
2017-12-26 08:26:37 +00:00
|
|
|
return False
|
2016-09-11 18:18:53 +00:00
|
|
|
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
def _check_for_insteon_type(
|
2020-05-05 00:21:40 +00:00
|
|
|
hass: HomeAssistant, node, single_platform: str = None
|
2019-07-31 19:25:30 +00:00
|
|
|
) -> bool:
|
2020-05-05 00:21:40 +00:00
|
|
|
"""Check if the node matches the Insteon type for any platforms.
|
2017-12-26 08:26:37 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
"""
|
2019-07-31 19:25:30 +00:00
|
|
|
if not hasattr(node, "type") or node.type is None:
|
2017-12-26 08:26:37 +00:00
|
|
|
# Node doesn't have a type (non-Insteon device most likely)
|
|
|
|
return False
|
|
|
|
|
|
|
|
device_type = node.type
|
2020-05-05 00:21:40 +00:00
|
|
|
platforms = SUPPORTED_PLATFORMS if not single_platform else [single_platform]
|
|
|
|
for platform in platforms:
|
2019-07-31 19:25:30 +00:00
|
|
|
if any(
|
|
|
|
[
|
|
|
|
device_type.startswith(t)
|
2020-05-05 00:21:40 +00:00
|
|
|
for t in set(NODE_FILTERS[platform]["insteon_type"])
|
2019-07-31 19:25:30 +00:00
|
|
|
]
|
|
|
|
):
|
2018-02-22 06:20:40 +00:00
|
|
|
|
|
|
|
# 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
|
2020-05-05 00:21:40 +00:00
|
|
|
if platform == FAN and int(node.nid[-1]) == 1:
|
|
|
|
hass.data[ISY994_NODES][LIGHT].append(node)
|
2018-02-22 06:20:40 +00:00
|
|
|
return True
|
|
|
|
|
2020-05-05 00:21:40 +00:00
|
|
|
hass.data[ISY994_NODES][platform].append(node)
|
2017-12-26 08:26:37 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
2016-09-11 18:18:53 +00:00
|
|
|
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
def _check_for_uom_id(
|
2020-05-05 00:21:40 +00:00
|
|
|
hass: HomeAssistant, node, single_platform: str = None, uom_list: list = None
|
2019-07-31 19:25:30 +00:00
|
|
|
) -> bool:
|
2020-05-05 00:21:40 +00:00
|
|
|
"""Check if a node's uom matches any of the platforms uom filter.
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
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.
|
|
|
|
"""
|
2019-07-31 19:25:30 +00:00
|
|
|
if not hasattr(node, "uom") or node.uom is None:
|
2017-12-26 08:26:37 +00:00
|
|
|
# Node doesn't have a uom (Scenes for example)
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
return False
|
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
node_uom = set(map(str.lower, node.uom))
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
if uom_list:
|
2018-05-18 15:42:09 +00:00
|
|
|
if node_uom.intersection(uom_list):
|
2020-05-05 00:21:40 +00:00
|
|
|
hass.data[ISY994_NODES][single_platform].append(node)
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
return True
|
|
|
|
else:
|
2020-05-05 00:21:40 +00:00
|
|
|
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)
|
2017-12-26 08:26:37 +00:00
|
|
|
return True
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
def _check_for_states_in_uom(
|
2020-05-05 00:21:40 +00:00
|
|
|
hass: HomeAssistant, node, single_platform: str = None, states_list: list = None
|
2019-07-31 19:25:30 +00:00
|
|
|
) -> bool:
|
2017-12-26 08:26:37 +00:00
|
|
|
"""Check if a list of uoms matches two possible filters.
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
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.
|
|
|
|
"""
|
2019-07-31 19:25:30 +00:00
|
|
|
if not hasattr(node, "uom") or node.uom is None:
|
2017-12-26 08:26:37 +00:00
|
|
|
# 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):
|
2020-05-05 00:21:40 +00:00
|
|
|
hass.data[ISY994_NODES][single_platform].append(node)
|
2017-12-26 08:26:37 +00:00
|
|
|
return True
|
|
|
|
else:
|
2020-05-05 00:21:40 +00:00
|
|
|
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)
|
2017-12-26 08:26:37 +00:00
|
|
|
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."""
|
2020-05-05 00:21:40 +00:00
|
|
|
if _check_for_node_def(hass, node, single_platform=BINARY_SENSOR):
|
2017-12-26 08:26:37 +00:00
|
|
|
return True
|
2020-05-05 00:21:40 +00:00
|
|
|
if _check_for_insteon_type(hass, node, single_platform=BINARY_SENSOR):
|
2017-12-26 08:26:37 +00:00
|
|
|
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.
|
2019-07-31 19:25:30 +00:00
|
|
|
if _check_for_uom_id(
|
2020-05-05 00:21:40 +00:00
|
|
|
hass, node, single_platform=BINARY_SENSOR, uom_list=["2", "78"]
|
2019-07-31 19:25:30 +00:00
|
|
|
):
|
2017-12-26 08:26:37 +00:00
|
|
|
return True
|
2019-07-31 19:25:30 +00:00
|
|
|
if _check_for_states_in_uom(
|
2020-05-05 00:21:40 +00:00
|
|
|
hass, node, single_platform=BINARY_SENSOR, states_list=["on", "off"]
|
2019-07-31 19:25:30 +00:00
|
|
|
):
|
2017-12-26 08:26:37 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
def _categorize_nodes(
|
|
|
|
hass: HomeAssistant, nodes, ignore_identifier: str, sensor_identifier: str
|
|
|
|
) -> None:
|
2020-05-05 00:21:40 +00:00
|
|
|
"""Sort the nodes to their proper platforms."""
|
2017-12-26 08:26:37 +00:00
|
|
|
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):
|
2020-05-05 00:21:40 +00:00
|
|
|
hass.data[ISY994_NODES][ISY_GROUP_PLATFORM].append(node)
|
2017-12-26 08:26:37 +00:00
|
|
|
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
|
2019-09-24 20:53:03 +00:00
|
|
|
|
2020-05-05 00:21:40 +00:00
|
|
|
hass.data[ISY994_NODES][SENSOR].append(node)
|
2019-09-24 20:53:03 +00:00
|
|
|
continue
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
# 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
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
|
|
|
|
def _categorize_programs(hass: HomeAssistant, programs: dict) -> None:
|
|
|
|
"""Categorize the ISY994 programs."""
|
2020-05-05 00:21:40 +00:00
|
|
|
for platform in SUPPORTED_PROGRAM_PLATFORMS:
|
2016-09-11 18:18:53 +00:00
|
|
|
try:
|
2020-05-05 00:21:40 +00:00
|
|
|
folder = programs[KEY_MY_PROGRAMS][f"HA.{platform}"]
|
2016-09-11 18:18:53 +00:00
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
for dtype, _, node_id in folder.children:
|
2018-02-11 17:20:28 +00:00
|
|
|
if dtype != KEY_FOLDER:
|
|
|
|
continue
|
|
|
|
entity_folder = folder[node_id]
|
|
|
|
try:
|
|
|
|
status = entity_folder[KEY_STATUS]
|
2019-07-31 19:25:30 +00:00
|
|
|
assert status.dtype == "program", "Not a program"
|
2020-05-05 00:21:40 +00:00
|
|
|
if platform != BINARY_SENSOR:
|
2018-02-11 17:20:28 +00:00
|
|
|
actions = entity_folder[KEY_ACTIONS]
|
2019-07-31 19:25:30 +00:00
|
|
|
assert actions.dtype == "program", "Not a program"
|
2018-02-11 17:20:28 +00:00
|
|
|
else:
|
|
|
|
actions = None
|
|
|
|
except (AttributeError, KeyError, AssertionError):
|
2019-07-31 19:25:30 +00:00
|
|
|
_LOGGER.warning(
|
|
|
|
"Program entity '%s' not loaded due "
|
|
|
|
"to invalid folder structure.",
|
|
|
|
entity_folder.name,
|
|
|
|
)
|
2018-02-11 17:20:28 +00:00
|
|
|
continue
|
|
|
|
|
|
|
|
entity = (entity_folder.name, status, actions)
|
2020-05-05 00:21:40 +00:00
|
|
|
hass.data[ISY994_PROGRAMS][platform].append(entity)
|
2017-12-26 08:26:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _categorize_weather(hass: HomeAssistant, climate) -> None:
|
2017-01-05 22:33:52 +00:00
|
|
|
"""Categorize the ISY994 weather data."""
|
2017-12-26 08:26:37 +00:00
|
|
|
climate_attrs = dir(climate)
|
2019-07-31 19:25:30 +00:00
|
|
|
weather_nodes = [
|
|
|
|
WeatherNode(
|
|
|
|
getattr(climate, attr),
|
|
|
|
attr.replace("_", " "),
|
2019-09-03 15:27:14 +00:00
|
|
|
getattr(climate, f"{attr}_units"),
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
for attr in climate_attrs
|
2019-09-03 15:27:14 +00:00
|
|
|
if f"{attr}_units" in climate_attrs
|
2019-07-31 19:25:30 +00:00
|
|
|
]
|
2017-12-26 08:26:37 +00:00
|
|
|
hass.data[ISY994_WEATHER].extend(weather_nodes)
|
2017-01-05 22:33:52 +00:00
|
|
|
|
|
|
|
|
2016-09-11 18:18:53 +00:00
|
|
|
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|
|
|
"""Set up the ISY 994 platform."""
|
2017-12-26 08:26:37 +00:00
|
|
|
hass.data[ISY994_NODES] = {}
|
2020-05-05 00:21:40 +00:00
|
|
|
for platform in SUPPORTED_PLATFORMS:
|
|
|
|
hass.data[ISY994_NODES][platform] = []
|
2017-12-26 08:26:37 +00:00
|
|
|
|
|
|
|
hass.data[ISY994_WEATHER] = []
|
|
|
|
|
|
|
|
hass.data[ISY994_PROGRAMS] = {}
|
2020-05-05 00:21:40 +00:00
|
|
|
for platform in SUPPORTED_PROGRAM_PLATFORMS:
|
|
|
|
hass.data[ISY994_PROGRAMS][platform] = []
|
2017-12-26 08:26:37 +00:00
|
|
|
|
2016-09-11 18:18:53 +00:00
|
|
|
isy_config = config.get(DOMAIN)
|
|
|
|
|
|
|
|
user = isy_config.get(CONF_USERNAME)
|
|
|
|
password = isy_config.get(CONF_PASSWORD)
|
|
|
|
tls_version = isy_config.get(CONF_TLS_VER)
|
|
|
|
host = urlparse(isy_config.get(CONF_HOST))
|
2017-12-26 08:26:37 +00:00
|
|
|
ignore_identifier = isy_config.get(CONF_IGNORE_STRING)
|
|
|
|
sensor_identifier = isy_config.get(CONF_SENSOR_STRING)
|
|
|
|
enable_climate = isy_config.get(CONF_ENABLE_CLIMATE)
|
2015-04-13 16:56:37 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
if host.scheme == "http":
|
2015-04-13 16:56:37 +00:00
|
|
|
https = False
|
2017-12-26 08:26:37 +00:00
|
|
|
port = host.port or 80
|
2019-07-31 19:25:30 +00:00
|
|
|
elif host.scheme == "https":
|
2015-04-13 16:56:37 +00:00
|
|
|
https = True
|
2017-12-26 08:26:37 +00:00
|
|
|
port = host.port or 443
|
2015-04-04 08:33:03 +00:00
|
|
|
else:
|
2017-04-30 05:04:49 +00:00
|
|
|
_LOGGER.error("isy994 host value in configuration is invalid")
|
2015-04-13 16:56:37 +00:00
|
|
|
return False
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2016-03-07 17:49:31 +00:00
|
|
|
# Connect to ISY controller.
|
2019-07-31 19:25:30 +00:00
|
|
|
isy = PyISY.ISY(
|
|
|
|
host.hostname,
|
|
|
|
port,
|
|
|
|
username=user,
|
|
|
|
password=password,
|
|
|
|
use_https=https,
|
|
|
|
tls_ver=tls_version,
|
|
|
|
log=_LOGGER,
|
|
|
|
)
|
2017-12-26 08:26:37 +00:00
|
|
|
if not isy.connected:
|
2015-04-04 08:33:03 +00:00
|
|
|
return False
|
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
_categorize_nodes(hass, isy.nodes, ignore_identifier, sensor_identifier)
|
|
|
|
_categorize_programs(hass, isy.programs)
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
if enable_climate and isy.configuration.get("Weather Information"):
|
2017-12-26 08:26:37 +00:00
|
|
|
_categorize_weather(hass, isy.climate)
|
2016-09-11 18:18:53 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
def stop(event: object) -> None:
|
|
|
|
"""Stop ISY auto updates."""
|
|
|
|
isy.auto_update = False
|
2017-01-05 22:33:52 +00:00
|
|
|
|
2016-03-07 17:49:31 +00:00
|
|
|
# Listen for HA stop to disconnect.
|
2015-04-25 05:10:41 +00:00
|
|
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop)
|
|
|
|
|
2016-06-12 00:43:13 +00:00
|
|
|
# Load platforms for the devices in the ISY controller that we support.
|
2020-05-05 00:21:40 +00:00
|
|
|
for platform in SUPPORTED_PLATFORMS:
|
|
|
|
discovery.load_platform(hass, platform, DOMAIN, {}, config)
|
2015-04-04 08:33:03 +00:00
|
|
|
|
2017-12-26 08:26:37 +00:00
|
|
|
isy.auto_update = True
|
2015-04-04 08:33:03 +00:00
|
|
|
return True
|
2015-04-12 20:45:23 +00:00
|
|
|
|
|
|
|
|
2016-09-11 18:18:53 +00:00
|
|
|
class ISYDevice(Entity):
|
|
|
|
"""Representation of an ISY994 device."""
|
2016-03-08 16:55:57 +00:00
|
|
|
|
2015-04-12 20:45:23 +00:00
|
|
|
_attrs = {}
|
2019-09-07 06:48:58 +00:00
|
|
|
_name: str = None
|
2015-04-12 20:45:23 +00:00
|
|
|
|
2016-09-11 18:18:53 +00:00
|
|
|
def __init__(self, node) -> None:
|
|
|
|
"""Initialize the insteon device."""
|
|
|
|
self._node = node
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
self._change_handler = None
|
|
|
|
self._control_handler = None
|
2015-04-12 20:45:23 +00:00
|
|
|
|
2018-10-01 06:52:42 +00:00
|
|
|
async def async_added_to_hass(self) -> None:
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
"""Subscribe to the node change events."""
|
2019-07-31 19:25:30 +00:00
|
|
|
self._change_handler = self._node.status.subscribe("changed", self.on_update)
|
2015-04-12 20:45:23 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
if hasattr(self._node, "controlEvents"):
|
|
|
|
self._control_handler = self._node.controlEvents.subscribe(self.on_control)
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
|
2016-09-11 18:18:53 +00:00
|
|
|
def on_update(self, event: object) -> None:
|
|
|
|
"""Handle the update event from the ISY994 Node."""
|
2017-03-04 23:10:36 +00:00
|
|
|
self.schedule_update_ha_state()
|
2015-04-12 20:45:23 +00:00
|
|
|
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
def on_control(self, event: object) -> None:
|
|
|
|
"""Handle a control event from the ISY994 Node."""
|
2019-07-31 19:25:30 +00:00
|
|
|
self.hass.bus.fire(
|
|
|
|
"isy994_control", {"entity_id": self.entity_id, "control": event}
|
|
|
|
)
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
|
2015-04-12 20:45:23 +00:00
|
|
|
@property
|
2016-09-11 18:18:53 +00:00
|
|
|
def unique_id(self) -> str:
|
|
|
|
"""Get the unique identifier of the device."""
|
2015-04-15 06:05:34 +00:00
|
|
|
# pylint: disable=protected-access
|
2019-07-31 19:25:30 +00:00
|
|
|
if hasattr(self._node, "_id"):
|
2018-02-15 05:58:49 +00:00
|
|
|
return self._node._id
|
|
|
|
|
|
|
|
return None
|
2015-04-12 20:45:23 +00:00
|
|
|
|
2015-04-15 02:57:32 +00:00
|
|
|
@property
|
2016-09-11 18:18:53 +00:00
|
|
|
def name(self) -> str:
|
|
|
|
"""Get the name of the device."""
|
2017-12-26 08:26:37 +00:00
|
|
|
return self._name or str(self._node.name)
|
2015-04-12 20:45:23 +00:00
|
|
|
|
2015-12-02 07:32:06 +00:00
|
|
|
@property
|
2016-09-11 18:18:53 +00:00
|
|
|
def should_poll(self) -> bool:
|
|
|
|
"""No polling required since we're using the subscription."""
|
|
|
|
return False
|
2015-04-12 20:45:23 +00:00
|
|
|
|
2016-09-11 18:18:53 +00:00
|
|
|
@property
|
2017-12-26 08:26:37 +00:00
|
|
|
def value(self) -> int:
|
2016-09-11 18:18:53 +00:00
|
|
|
"""Get the current value of the device."""
|
|
|
|
# pylint: disable=protected-access
|
|
|
|
return self._node.status._val
|
2015-04-12 20:45:23 +00:00
|
|
|
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
def is_unknown(self) -> bool:
|
|
|
|
"""Get whether or not the value of this Entity's node is unknown.
|
|
|
|
|
|
|
|
PyISY reports unknown values as -inf
|
|
|
|
"""
|
2019-07-31 19:25:30 +00:00
|
|
|
return self.value == -1 * float("inf")
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def state(self):
|
|
|
|
"""Return the state of the ISY device."""
|
|
|
|
if self.is_unknown():
|
|
|
|
return None
|
2018-02-11 17:20:28 +00:00
|
|
|
return super().state
|
ISY994 sensor improvements (#10805)
* Fire events for ISY994 control events
This allows hass to react directly to Insteon button presses (on switches and remotes), including presses, double-presses, and long holds
* Move change event subscription to after entity is added to hass
The event handler method requires `self.hass` to exist, which doesn't have a value until the async_added_to_hass method is called. Should eliminate a race condition.
* Overhaul binary sensors in ISY994 to be functional "out of the box"
We now smash all of the subnodes from the ISY994 in to one Hass binary_sensor, and automatically support both paradigms of state reporting that Insteon sensors can do. Sometimes a single node's state represents the sensor's state, other times two nodes are used and only "ON" events are sent from each. The logic between the two forunately do not conflict so we can support both without knowing which mode the device is in.
This also allows us to handle the heartbeat functionality that certain sensors have - we simply store the timestamp of the heartbeat as an attribute on the sensor device. It defaults to Unknown on bootup if and only if the device supports heartbeats, due to the presence of subnode 4.
* Parse the binary sensor device class from the ISY's device "type"
Now we automatically know which sensors are moisture, motion, and openings! (We also reverse the moisture sensor state, because Insteon reports ON for dry on the primary node.)
* Code review tweaks
The one material change here is that the event subscribers were moved to the `async_added_to_hass` method, as the handlers depend on things that only exist after the entity has been added.
* Handle cases where a sensor's state is unknown
When the ISY first boots up, if a battery-powered sensor has not reported in yet (due to heartbeat or a change in state), the state is unknown until it does.
* Clean up from code review
Fix coroutine await, remove unnecessary exception check, and return None when state is unknown
* Unknown value from PyISY is now -inf rather than -1
* Move heartbeat handling to a separate sensor
Now all heartbeat-compatible sensors will have a separate `binary_sensor` device that represents the battery state (on = dead)
* Add support for Unknown state, which is being added in next PyISY
PyISY will report unknown states as the number "-inf". This is implemented in the base ISY994 component, but subcomponents that override the `state` method needed some extra logic to handle it as well.
* Change a couple try blocks to explicit None checks
* Bump PyISY to 1.1.0, now that it has been published!
* Remove -inf checking from base component
The implementation of the -inf checking was improved in another branch which has been merged in to this branch already.
* Restrict negative-node and heartbeat support to known compatible types
Not all Insteon sensors use the same subnode IDs for the same things, so we need to use different logic depending on device type. Negative node and heartbeat support is now only used for leak sensors and open/close sensors.
* Use new style string formatting
* Add binary sensor detection for pre-5.x firmware
Meant to do this originally; writing documentation revealed that this requirement was missed!
2017-12-14 04:14:56 +00:00
|
|
|
|
2015-04-12 20:45:23 +00:00
|
|
|
@property
|
2017-01-20 06:22:33 +00:00
|
|
|
def device_state_attributes(self) -> Dict:
|
2016-09-11 18:18:53 +00:00
|
|
|
"""Get the state attributes for the device."""
|
|
|
|
attr = {}
|
2019-07-31 19:25:30 +00:00
|
|
|
if hasattr(self._node, "aux_properties"):
|
2016-09-11 18:18:53 +00:00
|
|
|
for name, val in self._node.aux_properties.items():
|
2020-02-28 11:39:29 +00:00
|
|
|
attr[name] = f"{val.get('value')} {val.get('uom')}"
|
2016-09-11 18:18:53 +00:00
|
|
|
return attr
|