core/homeassistant/components/isy994/lock.py

101 lines
3.0 KiB
Python
Raw Normal View History

"""Support for ISY994 locks."""
import logging
from typing import Callable
from homeassistant.components.lock import DOMAIN, LockEntity
from homeassistant.const import STATE_LOCKED, STATE_UNKNOWN, STATE_UNLOCKED
from homeassistant.helpers.typing import ConfigType
from . import ISY994_NODES, ISY994_PROGRAMS, ISYDevice
_LOGGER = logging.getLogger(__name__)
2019-07-31 19:25:30 +00:00
VALUE_TO_STATE = {0: STATE_UNLOCKED, 100: STATE_LOCKED}
2019-07-31 19:25:30 +00:00
def setup_platform(
hass, config: ConfigType, add_entities: Callable[[list], None], discovery_info=None
):
"""Set up the ISY994 lock platform."""
devices = []
Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware (#11243) * Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware # * No more globals - store on hass.data # * Parent ISY994 component handles categorizing nodes in to Hass components, rather than each individual domain filtering all nodes themselves # * Remove hidden string, replace with ignore string. Hidden should be done via the customize block; ignore fully prevents the node from getting a Hass entity # * Removed a few unused methods in the ISYDevice class # * Cleaned up the hostname parsing # * Removed broken logic in the fan Program component. It was setting properties that have no setters # * Added the missing SUPPORTED_FEATURES to the fan component to indicate that it can set speed # * Added better error handling and a log warning when an ISY994 program entity fails to initialize # * Cleaned up a few instances of unecessarily complicated logic paths, and other cases of unnecessary logic that is already handled by base classes * Use `super()` instead of explicit base class calls * Move `hass` argument to first position * Use str.format instead of string addition * Move program structure building and validation to component Removes the need for a bunch of duplicate exception handling in each individual platform * Fix climate nodes, fix climate names, add config to disable climate Sensor platform was crashing when the ISY reported climate nodes. Logic has been fixed. Also added a config option to prevent climate sensors from getting imported from the ISY. Also replace the underscore from climate node names with spaces so they default to friendly names. * Space missing in error message * Fix string comparison to use `==` * Explicitly check for attributes rather than catch AttributeError Also removes two stray debug lines * Remove null checks on hass.data, as they are always null at this point
2017-12-26 08:26:37 +00:00
for node in hass.data[ISY994_NODES][DOMAIN]:
devices.append(ISYLockDevice(node))
Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware (#11243) * Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware # * No more globals - store on hass.data # * Parent ISY994 component handles categorizing nodes in to Hass components, rather than each individual domain filtering all nodes themselves # * Remove hidden string, replace with ignore string. Hidden should be done via the customize block; ignore fully prevents the node from getting a Hass entity # * Removed a few unused methods in the ISYDevice class # * Cleaned up the hostname parsing # * Removed broken logic in the fan Program component. It was setting properties that have no setters # * Added the missing SUPPORTED_FEATURES to the fan component to indicate that it can set speed # * Added better error handling and a log warning when an ISY994 program entity fails to initialize # * Cleaned up a few instances of unecessarily complicated logic paths, and other cases of unnecessary logic that is already handled by base classes * Use `super()` instead of explicit base class calls * Move `hass` argument to first position * Use str.format instead of string addition * Move program structure building and validation to component Removes the need for a bunch of duplicate exception handling in each individual platform * Fix climate nodes, fix climate names, add config to disable climate Sensor platform was crashing when the ISY reported climate nodes. Logic has been fixed. Also added a config option to prevent climate sensors from getting imported from the ISY. Also replace the underscore from climate node names with spaces so they default to friendly names. * Space missing in error message * Fix string comparison to use `==` * Explicitly check for attributes rather than catch AttributeError Also removes two stray debug lines * Remove null checks on hass.data, as they are always null at this point
2017-12-26 08:26:37 +00:00
for name, status, actions in hass.data[ISY994_PROGRAMS][DOMAIN]:
devices.append(ISYLockProgram(name, status, actions))
add_entities(devices)
class ISYLockDevice(ISYDevice, LockEntity):
"""Representation of an ISY994 lock device."""
def __init__(self, node) -> None:
"""Initialize the ISY994 lock device."""
Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware (#11243) * Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware # * No more globals - store on hass.data # * Parent ISY994 component handles categorizing nodes in to Hass components, rather than each individual domain filtering all nodes themselves # * Remove hidden string, replace with ignore string. Hidden should be done via the customize block; ignore fully prevents the node from getting a Hass entity # * Removed a few unused methods in the ISYDevice class # * Cleaned up the hostname parsing # * Removed broken logic in the fan Program component. It was setting properties that have no setters # * Added the missing SUPPORTED_FEATURES to the fan component to indicate that it can set speed # * Added better error handling and a log warning when an ISY994 program entity fails to initialize # * Cleaned up a few instances of unecessarily complicated logic paths, and other cases of unnecessary logic that is already handled by base classes * Use `super()` instead of explicit base class calls * Move `hass` argument to first position * Use str.format instead of string addition * Move program structure building and validation to component Removes the need for a bunch of duplicate exception handling in each individual platform * Fix climate nodes, fix climate names, add config to disable climate Sensor platform was crashing when the ISY reported climate nodes. Logic has been fixed. Also added a config option to prevent climate sensors from getting imported from the ISY. Also replace the underscore from climate node names with spaces so they default to friendly names. * Space missing in error message * Fix string comparison to use `==` * Explicitly check for attributes rather than catch AttributeError Also removes two stray debug lines * Remove null checks on hass.data, as they are always null at this point
2017-12-26 08:26:37 +00:00
super().__init__(node)
self._conn = node.parent.parent.conn
@property
def is_locked(self) -> bool:
"""Get whether the lock is in locked state."""
return self.state == STATE_LOCKED
@property
def state(self) -> str:
"""Get the state of the lock."""
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 self.is_unknown():
return None
return VALUE_TO_STATE.get(self.value, STATE_UNKNOWN)
def lock(self, **kwargs) -> None:
"""Send the lock command to the ISY994 device."""
# Hack until PyISY is updated
2019-07-31 19:25:30 +00:00
req_url = self._conn.compileURL(["nodes", self.unique_id, "cmd", "SECMD", "1"])
response = self._conn.request(req_url)
if response is None:
2019-07-31 19:25:30 +00:00
_LOGGER.error("Unable to lock device")
self._node.update(0.5)
def unlock(self, **kwargs) -> None:
"""Send the unlock command to the ISY994 device."""
# Hack until PyISY is updated
2019-07-31 19:25:30 +00:00
req_url = self._conn.compileURL(["nodes", self.unique_id, "cmd", "SECMD", "0"])
response = self._conn.request(req_url)
if response is None:
2019-07-31 19:25:30 +00:00
_LOGGER.error("Unable to lock device")
self._node.update(0.5)
class ISYLockProgram(ISYLockDevice):
"""Representation of a ISY lock program."""
def __init__(self, name: str, node, actions) -> None:
"""Initialize the lock."""
Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware (#11243) * Huge ISY994 platform cleanup, fixes support for 5.0.10 firmware # * No more globals - store on hass.data # * Parent ISY994 component handles categorizing nodes in to Hass components, rather than each individual domain filtering all nodes themselves # * Remove hidden string, replace with ignore string. Hidden should be done via the customize block; ignore fully prevents the node from getting a Hass entity # * Removed a few unused methods in the ISYDevice class # * Cleaned up the hostname parsing # * Removed broken logic in the fan Program component. It was setting properties that have no setters # * Added the missing SUPPORTED_FEATURES to the fan component to indicate that it can set speed # * Added better error handling and a log warning when an ISY994 program entity fails to initialize # * Cleaned up a few instances of unecessarily complicated logic paths, and other cases of unnecessary logic that is already handled by base classes * Use `super()` instead of explicit base class calls * Move `hass` argument to first position * Use str.format instead of string addition * Move program structure building and validation to component Removes the need for a bunch of duplicate exception handling in each individual platform * Fix climate nodes, fix climate names, add config to disable climate Sensor platform was crashing when the ISY reported climate nodes. Logic has been fixed. Also added a config option to prevent climate sensors from getting imported from the ISY. Also replace the underscore from climate node names with spaces so they default to friendly names. * Space missing in error message * Fix string comparison to use `==` * Explicitly check for attributes rather than catch AttributeError Also removes two stray debug lines * Remove null checks on hass.data, as they are always null at this point
2017-12-26 08:26:37 +00:00
super().__init__(node)
self._name = name
self._actions = actions
@property
def is_locked(self) -> bool:
"""Return true if the device is locked."""
return bool(self.value)
@property
def state(self) -> str:
"""Return the state of the lock."""
return STATE_LOCKED if self.is_locked else STATE_UNLOCKED
def lock(self, **kwargs) -> None:
"""Lock the device."""
if not self._actions.runThen():
_LOGGER.error("Unable to lock device")
def unlock(self, **kwargs) -> None:
"""Unlock the device."""
if not self._actions.runElse():
_LOGGER.error("Unable to unlock device")