As discussed here ( https://www.domoticz.com/forum/viewtopic.php?f=6&t=5661 ) this components reports two different power consumption values. Unfortunately only one of them is correct. Both of them map to the exactly same object id. This bugfix gets rid of the incorrect one.
214 lines
6.7 KiB
Python
214 lines
6.7 KiB
Python
"""
|
|
homeassistant.components.sensor.zwave
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
Interfaces with Z-Wave sensors.
|
|
|
|
For more details about this platform, please refer to the documentation
|
|
at https://home-assistant.io/components/zwave/
|
|
"""
|
|
# Because we do not compile openzwave on CI
|
|
# pylint: disable=import-error
|
|
import datetime
|
|
|
|
from homeassistant.helpers.event import track_point_in_time
|
|
import homeassistant.util.dt as dt_util
|
|
from homeassistant.components.sensor import DOMAIN
|
|
from homeassistant.helpers.entity import Entity
|
|
from homeassistant.components.zwave import (
|
|
NETWORK, ATTR_NODE_ID, ATTR_VALUE_ID, COMMAND_CLASS_SENSOR_BINARY,
|
|
COMMAND_CLASS_SENSOR_MULTILEVEL, COMMAND_CLASS_METER, TYPE_DECIMAL,
|
|
COMMAND_CLASS_ALARM, ZWaveDeviceEntity, get_config_value)
|
|
|
|
from homeassistant.const import (
|
|
STATE_ON, STATE_OFF, TEMP_CELCIUS, TEMP_FAHRENHEIT)
|
|
|
|
PHILIO = '0x013c'
|
|
PHILIO_SLIM_SENSOR = '0x0002'
|
|
PHILIO_SLIM_SENSOR_MOTION = (PHILIO, PHILIO_SLIM_SENSOR, 0)
|
|
|
|
FIBARO = '0x010f'
|
|
FIBARO_WALL_PLUG = '0x1000'
|
|
FIBARO_WALL_PLUG_SENSOR_METER = (FIBARO, FIBARO_WALL_PLUG, 8)
|
|
|
|
WORKAROUND_NO_OFF_EVENT = 'trigger_no_off_event'
|
|
WORKAROUND_IGNORE = 'ignore'
|
|
|
|
DEVICE_MAPPINGS = {
|
|
PHILIO_SLIM_SENSOR_MOTION: WORKAROUND_NO_OFF_EVENT,
|
|
|
|
# For some reason Fibaro Wall Plug reports 2 power consumptions. One value updates as the power consumption changes
|
|
# and the other does not change
|
|
FIBARO_WALL_PLUG_SENSOR_METER: WORKAROUND_IGNORE,
|
|
}
|
|
|
|
|
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
|
""" Sets up Z-Wave sensors. """
|
|
|
|
# Return on empty `discovery_info`. Given you configure HA with:
|
|
#
|
|
# sensor:
|
|
# platform: zwave
|
|
#
|
|
# `setup_platform` will be called without `discovery_info`.
|
|
if discovery_info is None:
|
|
return
|
|
|
|
node = NETWORK.nodes[discovery_info[ATTR_NODE_ID]]
|
|
value = node.values[discovery_info[ATTR_VALUE_ID]]
|
|
|
|
value.set_change_verified(False)
|
|
|
|
# if 1 in groups and (NETWORK.controller.node_id not in
|
|
# groups[1].associations):
|
|
# node.groups[1].add_association(NETWORK.controller.node_id)
|
|
|
|
specific_sensor_key = (value.node.manufacturer_id,
|
|
value.node.product_id,
|
|
value.index)
|
|
|
|
# Check workaround mappings for specific devices
|
|
if specific_sensor_key in DEVICE_MAPPINGS:
|
|
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_NO_OFF_EVENT:
|
|
# Default the multiplier to 4
|
|
re_arm_multiplier = (get_config_value(value.node, 9) or 4)
|
|
add_devices([
|
|
ZWaveTriggerSensor(value, hass, re_arm_multiplier * 8)
|
|
])
|
|
elif DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_IGNORE:
|
|
return
|
|
|
|
# generic Device mappings
|
|
elif value.command_class == COMMAND_CLASS_SENSOR_BINARY:
|
|
add_devices([ZWaveBinarySensor(value)])
|
|
|
|
elif value.command_class == COMMAND_CLASS_SENSOR_MULTILEVEL:
|
|
add_devices([ZWaveMultilevelSensor(value)])
|
|
|
|
elif (value.command_class == COMMAND_CLASS_METER and
|
|
value.type == TYPE_DECIMAL):
|
|
add_devices([ZWaveMultilevelSensor(value)])
|
|
|
|
elif value.command_class == COMMAND_CLASS_ALARM:
|
|
add_devices([ZWaveAlarmSensor(value)])
|
|
|
|
|
|
class ZWaveSensor(ZWaveDeviceEntity, Entity):
|
|
""" Represents a Z-Wave sensor. """
|
|
|
|
def __init__(self, sensor_value):
|
|
from openzwave.network import ZWaveNetwork
|
|
from pydispatch import dispatcher
|
|
|
|
ZWaveDeviceEntity.__init__(self, sensor_value, DOMAIN)
|
|
|
|
dispatcher.connect(
|
|
self.value_changed, ZWaveNetwork.SIGNAL_VALUE_CHANGED)
|
|
|
|
@property
|
|
def state(self):
|
|
""" Returns the state of the sensor. """
|
|
return self._value.data
|
|
|
|
@property
|
|
def unit_of_measurement(self):
|
|
return self._value.units
|
|
|
|
def value_changed(self, value):
|
|
""" Called when a value has changed on the network. """
|
|
if self._value.value_id == value.value_id:
|
|
self.update_ha_state()
|
|
|
|
|
|
# pylint: disable=too-few-public-methods
|
|
class ZWaveBinarySensor(ZWaveSensor):
|
|
""" Represents a binary sensor within Z-Wave. """
|
|
|
|
@property
|
|
def state(self):
|
|
""" Returns the state of the sensor. """
|
|
return STATE_ON if self._value.data else STATE_OFF
|
|
|
|
|
|
class ZWaveTriggerSensor(ZWaveSensor):
|
|
"""
|
|
Represents a stateless sensor which
|
|
triggers events just 'On' within Z-Wave.
|
|
"""
|
|
|
|
def __init__(self, sensor_value, hass, re_arm_sec=60):
|
|
"""
|
|
:param sensor_value: The z-wave node
|
|
:param hass:
|
|
:param re_arm_sec: Set state to Off re_arm_sec after the last On event
|
|
:return:
|
|
"""
|
|
super(ZWaveTriggerSensor, self).__init__(sensor_value)
|
|
self._hass = hass
|
|
self.invalidate_after = dt_util.utcnow()
|
|
self.re_arm_sec = re_arm_sec
|
|
|
|
def value_changed(self, value):
|
|
""" Called when a value has changed on the network. """
|
|
if self._value.value_id == value.value_id:
|
|
self.update_ha_state()
|
|
if value.data:
|
|
# only allow this value to be true for 60 secs
|
|
self.invalidate_after = dt_util.utcnow() + datetime.timedelta(
|
|
seconds=self.re_arm_sec)
|
|
track_point_in_time(
|
|
self._hass, self.update_ha_state,
|
|
self.invalidate_after)
|
|
|
|
@property
|
|
def state(self):
|
|
""" Returns the state of the sensor. """
|
|
if not self._value.data or \
|
|
(self.invalidate_after is not None and
|
|
self.invalidate_after <= dt_util.utcnow()):
|
|
return STATE_OFF
|
|
|
|
return STATE_ON
|
|
|
|
|
|
class ZWaveMultilevelSensor(ZWaveSensor):
|
|
""" Represents a multi level sensor Z-Wave sensor. """
|
|
|
|
@property
|
|
def state(self):
|
|
""" Returns the state of the sensor. """
|
|
value = self._value.data
|
|
|
|
if self._value.units in ('C', 'F'):
|
|
return round(value, 1)
|
|
elif isinstance(value, float):
|
|
return round(value, 2)
|
|
|
|
return value
|
|
|
|
@property
|
|
def unit_of_measurement(self):
|
|
unit = self._value.units
|
|
|
|
if unit == 'C':
|
|
return TEMP_CELCIUS
|
|
elif unit == 'F':
|
|
return TEMP_FAHRENHEIT
|
|
else:
|
|
return unit
|
|
|
|
|
|
class ZWaveAlarmSensor(ZWaveSensor):
|
|
""" A Z-wave sensor that sends Alarm alerts
|
|
|
|
Examples include certain Multisensors that have motion and
|
|
vibration capabilities. Z-Wave defines various alarm types
|
|
such as Smoke, Flood, Burglar, CarbonMonoxide, etc.
|
|
|
|
This wraps these alarms and allows you to use them to
|
|
trigger things, etc.
|
|
|
|
COMMAND_CLASS_ALARM is what we get here.
|
|
"""
|
|
# Empty subclass for now. Allows for later customizations
|
|
pass
|