core/homeassistant/components/isy994.py

360 lines
10 KiB
Python
Raw Normal View History

2015-04-04 08:33:03 +00:00
"""
2016-03-07 17:49:31 +00:00
Support the ISY-994 controllers.
2015-08-08 17:16:15 +00:00
For configuration details please visit the documentation for this component at
2015-11-09 12:12:18 +00:00
https://home-assistant.io/components/isy994/
2015-04-04 08:33:03 +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
import asyncio
from collections import namedtuple
2015-04-04 08:33:03 +00:00
import logging
from urllib.parse import urlparse
import voluptuous as vol
2015-04-04 08:33:03 +00:00
from homeassistant.core import HomeAssistant # noqa
2016-02-19 05:27:50 +00:00
from homeassistant.const import (
CONF_HOST, CONF_PASSWORD, CONF_USERNAME, EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import discovery, config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType, Dict # noqa
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
REQUIREMENTS = ['PyISY==1.1.0']
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'isy994'
CONF_HIDDEN_STRING = 'hidden_string'
CONF_SENSOR_STRING = 'sensor_string'
CONF_TLS_VER = 'tls'
DEFAULT_HIDDEN_STRING = '{HIDE ME}'
DEFAULT_SENSOR_STRING = 'sensor'
ISY = None
KEY_ACTIONS = 'actions'
KEY_FOLDER = 'folder'
KEY_MY_PROGRAMS = 'My Programs'
KEY_STATUS = 'status'
2015-04-04 08:33:03 +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_HIDDEN_STRING,
default=DEFAULT_HIDDEN_STRING): cv.string,
vol.Optional(CONF_SENSOR_STRING,
default=DEFAULT_SENSOR_STRING): cv.string
})
}, extra=vol.ALLOW_EXTRA)
SENSOR_NODES = []
WEATHER_NODES = []
NODES = []
GROUPS = []
PROGRAMS = {}
PYISY = None
HIDDEN_STRING = DEFAULT_HIDDEN_STRING
SUPPORTED_DOMAINS = ['binary_sensor', 'cover', 'fan', 'light', 'lock',
'sensor', 'switch']
WeatherNode = namedtuple('WeatherNode', ('status', 'name', 'uom'))
def filter_nodes(nodes: list, units: list=None, states: list=None) -> list:
"""Filter a list of ISY nodes based on the units and states provided."""
filtered_nodes = []
units = units if units else []
states = states if states else []
for node in nodes:
match_unit = False
match_state = True
for uom in node.uom:
if uom in units:
match_unit = True
continue
elif uom not in states:
match_state = False
if match_unit:
continue
if match_unit or match_state:
filtered_nodes.append(node)
return filtered_nodes
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_node_a_sensor(node, path: str, sensor_identifier: str) -> bool:
"""Determine if the given node is a sensor."""
if not isinstance(node, PYISY.Nodes.Node):
return False
if sensor_identifier in path or sensor_identifier in node.name:
return True
# This method is most reliable but only works on 5.x firmware
try:
if node.node_def_id == 'BinaryAlarm':
return True
except AttributeError:
pass
# This method works on all firmwares, but only for Insteon devices
try:
device_type = node.type
except AttributeError:
# Node has no type; most likely not an Insteon device
pass
else:
split_type = device_type.split('.')
return split_type[0] == '16' # 16 represents Insteon binary sensors
return False
def _categorize_nodes(hidden_identifier: str, sensor_identifier: str) -> None:
"""Categorize the ISY994 nodes."""
global SENSOR_NODES
global NODES
global GROUPS
SENSOR_NODES = []
NODES = []
GROUPS = []
# pylint: disable=no-member
for (path, node) in ISY.nodes:
hidden = hidden_identifier in path or hidden_identifier in node.name
if hidden:
node.name += hidden_identifier
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
if _is_node_a_sensor(node, path, sensor_identifier):
SENSOR_NODES.append(node)
elif isinstance(node, PYISY.Nodes.Node):
NODES.append(node)
elif isinstance(node, PYISY.Nodes.Group):
GROUPS.append(node)
def _categorize_programs() -> None:
"""Categorize the ISY994 programs."""
global PROGRAMS
PROGRAMS = {}
for component in SUPPORTED_DOMAINS:
try:
folder = ISY.programs[KEY_MY_PROGRAMS]['HA.' + component]
except KeyError:
pass
else:
for dtype, _, node_id in folder.children:
if dtype is KEY_FOLDER:
program = folder[node_id]
try:
node = program[KEY_STATUS].leaf
assert node.dtype == 'program', 'Not a program'
except (KeyError, AssertionError):
pass
else:
if component not in PROGRAMS:
PROGRAMS[component] = []
PROGRAMS[component].append(program)
def _categorize_weather() -> None:
"""Categorize the ISY994 weather data."""
global WEATHER_NODES
climate_attrs = dir(ISY.climate)
WEATHER_NODES = [WeatherNode(getattr(ISY.climate, attr), attr,
getattr(ISY.climate, attr + '_units'))
for attr in climate_attrs
if attr + '_units' in climate_attrs]
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the ISY 994 platform."""
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))
port = host.port
addr = host.geturl()
hidden_identifier = isy_config.get(
CONF_HIDDEN_STRING, DEFAULT_HIDDEN_STRING)
sensor_identifier = isy_config.get(
CONF_SENSOR_STRING, DEFAULT_SENSOR_STRING)
2015-04-04 08:33:03 +00:00
global HIDDEN_STRING
HIDDEN_STRING = hidden_identifier
if host.scheme == 'http':
addr = addr.replace('http://', '')
https = False
elif host.scheme == 'https':
addr = addr.replace('https://', '')
https = True
2015-04-04 08:33:03 +00:00
else:
_LOGGER.error("isy994 host value in configuration is invalid")
return False
addr = addr.replace(':{}'.format(port), '')
import PyISY
global PYISY
PYISY = PyISY
2015-04-04 08:33:03 +00:00
2016-03-07 17:49:31 +00:00
# Connect to ISY controller.
2015-04-04 08:33:03 +00:00
global ISY
ISY = PyISY.ISY(addr, port, username=user, password=password,
use_https=https, tls_ver=tls_version, log=_LOGGER)
2015-04-04 08:33:03 +00:00
if not ISY.connected:
return False
_categorize_nodes(hidden_identifier, sensor_identifier)
_categorize_programs()
if ISY.configuration.get('Weather Information'):
_categorize_weather()
2016-03-07 17:49:31 +00:00
# Listen for HA stop to disconnect.
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop)
# Load platforms for the devices in the ISY controller that we support.
for component in SUPPORTED_DOMAINS:
2016-06-15 05:51:46 +00:00
discovery.load_platform(hass, component, DOMAIN, {}, config)
2015-04-04 08:33:03 +00:00
ISY.auto_update = True
return True
# pylint: disable=unused-argument
def stop(event: object) -> None:
"""Stop ISY auto updates."""
ISY.auto_update = False
class ISYDevice(Entity):
"""Representation of an ISY994 device."""
2016-03-08 16:55:57 +00:00
_attrs = {}
_domain = None # type: str
_name = None # type: str
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
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
@asyncio.coroutine
def async_added_to_hass(self) -> None:
"""Subscribe to the node change events."""
self._change_handler = self._node.status.subscribe(
'changed', self.on_update)
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
if hasattr(self._node, 'controlEvents'):
self._control_handler = self._node.controlEvents.subscribe(
self.on_control)
# pylint: disable=unused-argument
def on_update(self, event: object) -> None:
"""Handle the update event from the ISY994 Node."""
self.schedule_update_ha_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
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 domain(self) -> str:
"""Get the domain of the device."""
return self._domain
@property
def unique_id(self) -> str:
"""Get the unique identifier of the device."""
# pylint: disable=protected-access
return self._node._id
@property
def raw_name(self) -> str:
"""Get the raw name of the device."""
return str(self._name) \
if self._name is not None else str(self._node.name)
@property
def name(self) -> str:
"""Get the name of the device."""
return self.raw_name.replace(HIDDEN_STRING, '').strip() \
.replace('_', ' ')
2015-12-02 07:32:06 +00:00
@property
def should_poll(self) -> bool:
"""No polling required since we're using the subscription."""
return False
@property
def value(self) -> object:
"""Get the current value of the device."""
# pylint: disable=protected-access
return self._node.status._val
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
"""
return self.value == -1 * float('inf')
@property
def state(self):
"""Return the state of the ISY device."""
if self.is_unknown():
return None
else:
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] = '{} {}'.format(val.get('value'), val.get('uom'))
return attr
@property
def hidden(self) -> bool:
"""Get whether the device should be hidden from the UI."""
return HIDDEN_STRING in self.raw_name
@property
def unit_of_measurement(self) -> str:
"""Get the device unit of measure."""
return None
def _attr_filter(self, attr: str) -> str:
"""Filter the attribute."""
# pylint: disable=no-self-use
return attr
def update(self) -> None:
"""Perform an update for the device."""
pass