359 lines
9.8 KiB
Python
359 lines
9.8 KiB
Python
"""
|
|
Support for ISY994 sensors.
|
|
|
|
For more details about this platform, please refer to the documentation at
|
|
https://home-assistant.io/components/sensor.isy994/
|
|
"""
|
|
import logging
|
|
from typing import Callable # noqa
|
|
|
|
import homeassistant.components.isy994 as isy
|
|
from homeassistant.const import (
|
|
TEMP_CELSIUS, TEMP_FAHRENHEIT, STATE_OFF, STATE_ON)
|
|
from homeassistant.helpers.typing import ConfigType
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
UOM_FRIENDLY_NAME = {
|
|
'1': 'amp',
|
|
'3': 'btu/h',
|
|
'4': TEMP_CELSIUS,
|
|
'5': 'cm',
|
|
'6': 'ft³',
|
|
'7': 'ft³/min',
|
|
'8': 'm³',
|
|
'9': 'day',
|
|
'10': 'days',
|
|
'12': 'dB',
|
|
'13': 'dB A',
|
|
'14': '°',
|
|
'16': 'macroseismic',
|
|
'17': TEMP_FAHRENHEIT,
|
|
'18': 'ft',
|
|
'19': 'hour',
|
|
'20': 'hours',
|
|
'21': 'abs. humidity (%)',
|
|
'22': 'rel. humidity (%)',
|
|
'23': 'inHg',
|
|
'24': 'in/hr',
|
|
'25': 'index',
|
|
'26': 'K',
|
|
'27': 'keyword',
|
|
'28': 'kg',
|
|
'29': 'kV',
|
|
'30': 'kW',
|
|
'31': 'kPa',
|
|
'32': 'KPH',
|
|
'33': 'kWH',
|
|
'34': 'liedu',
|
|
'35': 'l',
|
|
'36': 'lux',
|
|
'37': 'mercalli',
|
|
'38': 'm',
|
|
'39': 'm³/hr',
|
|
'40': 'm/s',
|
|
'41': 'mA',
|
|
'42': 'ms',
|
|
'43': 'mV',
|
|
'44': 'min',
|
|
'45': 'min',
|
|
'46': 'mm/hr',
|
|
'47': 'month',
|
|
'48': 'MPH',
|
|
'49': 'm/s',
|
|
'50': 'ohm',
|
|
'51': '%',
|
|
'52': 'lb',
|
|
'53': 'power factor',
|
|
'54': 'ppm',
|
|
'55': 'pulse count',
|
|
'57': 's',
|
|
'58': 's',
|
|
'59': 'seimens/m',
|
|
'60': 'body wave magnitude scale',
|
|
'61': 'Ricter scale',
|
|
'62': 'moment magnitude scale',
|
|
'63': 'surface wave magnitude scale',
|
|
'64': 'shindo',
|
|
'65': 'SML',
|
|
'69': 'gal',
|
|
'71': 'UV index',
|
|
'72': 'V',
|
|
'73': 'W',
|
|
'74': 'W/m²',
|
|
'75': 'weekday',
|
|
'76': 'Wind Direction (°)',
|
|
'77': 'year',
|
|
'82': 'mm',
|
|
'83': 'km',
|
|
'85': 'ohm',
|
|
'86': 'kOhm',
|
|
'87': 'm³/m³',
|
|
'88': 'Water activity',
|
|
'89': 'RPM',
|
|
'90': 'Hz',
|
|
'91': '° (Relative to North)',
|
|
'92': '° (Relative to South)',
|
|
}
|
|
|
|
UOM_TO_STATES = {
|
|
'11': {
|
|
'0': 'unlocked',
|
|
'100': 'locked',
|
|
'102': 'jammed',
|
|
},
|
|
'15': {
|
|
'1': 'master code changed',
|
|
'2': 'tamper code entry limit',
|
|
'3': 'escutcheon removed',
|
|
'4': 'key/manually locked',
|
|
'5': 'locked by touch',
|
|
'6': 'key/manually unlocked',
|
|
'7': 'remote locking jammed bolt',
|
|
'8': 'remotely locked',
|
|
'9': 'remotely unlocked',
|
|
'10': 'deadbolt jammed',
|
|
'11': 'battery too low to operate',
|
|
'12': 'critical low battery',
|
|
'13': 'low battery',
|
|
'14': 'automatically locked',
|
|
'15': 'automatic locking jammed bolt',
|
|
'16': 'remotely power cycled',
|
|
'17': 'lock handling complete',
|
|
'19': 'user deleted',
|
|
'20': 'user added',
|
|
'21': 'duplicate pin',
|
|
'22': 'jammed bolt by locking with keypad',
|
|
'23': 'locked by keypad',
|
|
'24': 'unlocked by keypad',
|
|
'25': 'keypad attempt outside schedule',
|
|
'26': 'hardware failure',
|
|
'27': 'factory reset'
|
|
},
|
|
'66': {
|
|
'0': 'idle',
|
|
'1': 'heating',
|
|
'2': 'cooling',
|
|
'3': 'fan only',
|
|
'4': 'pending heat',
|
|
'5': 'pending cool',
|
|
'6': 'vent',
|
|
'7': 'aux heat',
|
|
'8': '2nd stage heating',
|
|
'9': '2nd stage cooling',
|
|
'10': '2nd stage aux heat',
|
|
'11': '3rd stage aux heat'
|
|
},
|
|
'67': {
|
|
'0': 'off',
|
|
'1': 'heat',
|
|
'2': 'cool',
|
|
'3': 'auto',
|
|
'4': 'aux/emergency heat',
|
|
'5': 'resume',
|
|
'6': 'fan only',
|
|
'7': 'furnace',
|
|
'8': 'dry air',
|
|
'9': 'moist air',
|
|
'10': 'auto changeover',
|
|
'11': 'energy save heat',
|
|
'12': 'energy save cool',
|
|
'13': 'away'
|
|
},
|
|
'68': {
|
|
'0': 'auto',
|
|
'1': 'on',
|
|
'2': 'auto high',
|
|
'3': 'high',
|
|
'4': 'auto medium',
|
|
'5': 'medium',
|
|
'6': 'circulation',
|
|
'7': 'humidity circulation'
|
|
},
|
|
'93': {
|
|
'1': 'power applied',
|
|
'2': 'ac mains disconnected',
|
|
'3': 'ac mains reconnected',
|
|
'4': 'surge detection',
|
|
'5': 'volt drop or drift',
|
|
'6': 'over current detected',
|
|
'7': 'over voltage detected',
|
|
'8': 'over load detected',
|
|
'9': 'load error',
|
|
'10': 'replace battery soon',
|
|
'11': 'replace battery now',
|
|
'12': 'battery is charging',
|
|
'13': 'battery is fully charged',
|
|
'14': 'charge battery soon',
|
|
'15': 'charge battery now'
|
|
},
|
|
'94': {
|
|
'1': 'program started',
|
|
'2': 'program in progress',
|
|
'3': 'program completed',
|
|
'4': 'replace main filter',
|
|
'5': 'failure to set target temperature',
|
|
'6': 'supplying water',
|
|
'7': 'water supply failure',
|
|
'8': 'boiling',
|
|
'9': 'boiling failure',
|
|
'10': 'washing',
|
|
'11': 'washing failure',
|
|
'12': 'rinsing',
|
|
'13': 'rinsing failure',
|
|
'14': 'draining',
|
|
'15': 'draining failure',
|
|
'16': 'spinning',
|
|
'17': 'spinning failure',
|
|
'18': 'drying',
|
|
'19': 'drying failure',
|
|
'20': 'fan failure',
|
|
'21': 'compressor failure'
|
|
},
|
|
'95': {
|
|
'1': 'leaving bed',
|
|
'2': 'sitting on bed',
|
|
'3': 'lying on bed',
|
|
'4': 'posture changed',
|
|
'5': 'sitting on edge of bed'
|
|
},
|
|
'96': {
|
|
'1': 'clean',
|
|
'2': 'slightly polluted',
|
|
'3': 'moderately polluted',
|
|
'4': 'highly polluted'
|
|
},
|
|
'97': {
|
|
'0': 'closed',
|
|
'100': 'open',
|
|
'102': 'stopped',
|
|
'103': 'closing',
|
|
'104': 'opening'
|
|
}
|
|
}
|
|
|
|
BINARY_UOM = ['2', '78']
|
|
|
|
|
|
# pylint: disable=unused-argument
|
|
def setup_platform(hass, config: ConfigType,
|
|
add_devices: Callable[[list], None], discovery_info=None):
|
|
"""Set up the ISY994 sensor platform."""
|
|
if isy.ISY is None or not isy.ISY.connected:
|
|
_LOGGER.error("A connection has not been made to the ISY controller")
|
|
return False
|
|
|
|
devices = []
|
|
|
|
for node in isy.SENSOR_NODES:
|
|
if (not node.uom or node.uom[0] not in BINARY_UOM) and \
|
|
STATE_OFF not in node.uom and STATE_ON not in node.uom:
|
|
_LOGGER.debug("Loading %s", node.name)
|
|
devices.append(ISYSensorDevice(node))
|
|
|
|
for node in isy.WEATHER_NODES:
|
|
devices.append(ISYWeatherDevice(node))
|
|
|
|
add_devices(devices)
|
|
|
|
|
|
class ISYSensorDevice(isy.ISYDevice):
|
|
"""Representation of an ISY994 sensor device."""
|
|
|
|
def __init__(self, node) -> None:
|
|
"""Initialize the ISY994 sensor device."""
|
|
isy.ISYDevice.__init__(self, node)
|
|
|
|
@property
|
|
def raw_unit_of_measurement(self) -> str:
|
|
"""Get the raw unit of measurement for the ISY994 sensor device."""
|
|
if len(self._node.uom) == 1:
|
|
if self._node.uom[0] in UOM_FRIENDLY_NAME:
|
|
friendly_name = UOM_FRIENDLY_NAME.get(self._node.uom[0])
|
|
if friendly_name == TEMP_CELSIUS or \
|
|
friendly_name == TEMP_FAHRENHEIT:
|
|
friendly_name = self.hass.config.units.temperature_unit
|
|
return friendly_name
|
|
else:
|
|
return self._node.uom[0]
|
|
else:
|
|
return None
|
|
|
|
@property
|
|
def state(self) -> str:
|
|
"""Get the state of the ISY994 sensor device."""
|
|
if len(self._node.uom) == 1:
|
|
if self._node.uom[0] in UOM_TO_STATES:
|
|
states = UOM_TO_STATES.get(self._node.uom[0])
|
|
if self.value in states:
|
|
return states.get(self.value)
|
|
elif self._node.prec and self._node.prec != [0]:
|
|
str_val = str(self.value)
|
|
int_prec = int(self._node.prec)
|
|
decimal_part = str_val[-int_prec:]
|
|
whole_part = str_val[:len(str_val) - int_prec]
|
|
val = float('{}.{}'.format(whole_part, decimal_part))
|
|
raw_units = self.raw_unit_of_measurement
|
|
if raw_units in (
|
|
TEMP_CELSIUS, TEMP_FAHRENHEIT):
|
|
val = self.hass.config.units.temperature(val, raw_units)
|
|
|
|
return str(val)
|
|
else:
|
|
return self.value
|
|
|
|
return None
|
|
|
|
@property
|
|
def unit_of_measurement(self) -> str:
|
|
"""Get the unit of measurement for the ISY994 sensor device."""
|
|
raw_units = self.raw_unit_of_measurement
|
|
if raw_units in (TEMP_FAHRENHEIT, TEMP_CELSIUS):
|
|
return self.hass.config.units.temperature_unit
|
|
else:
|
|
return raw_units
|
|
|
|
|
|
class ISYWeatherDevice(isy.ISYDevice):
|
|
"""Representation of an ISY994 weather device."""
|
|
|
|
_domain = 'sensor'
|
|
|
|
def __init__(self, node) -> None:
|
|
"""Initialize the ISY994 weather device."""
|
|
isy.ISYDevice.__init__(self, node)
|
|
|
|
@property
|
|
def unique_id(self) -> str:
|
|
"""Return the unique identifier for the node."""
|
|
return self._node.name
|
|
|
|
@property
|
|
def raw_units(self) -> str:
|
|
"""Return the raw unit of measurement."""
|
|
if self._node.uom == 'F':
|
|
return TEMP_FAHRENHEIT
|
|
if self._node.uom == 'C':
|
|
return TEMP_CELSIUS
|
|
return self._node.uom
|
|
|
|
@property
|
|
def state(self) -> object:
|
|
"""Return the value of the node."""
|
|
# pylint: disable=protected-access
|
|
val = self._node.status._val
|
|
raw_units = self._node.uom
|
|
|
|
if raw_units in [TEMP_CELSIUS, TEMP_FAHRENHEIT]:
|
|
return self.hass.config.units.temperature(val, raw_units)
|
|
return val
|
|
|
|
@property
|
|
def unit_of_measurement(self) -> str:
|
|
"""Return the unit of measurement for the node."""
|
|
raw_units = self.raw_units
|
|
|
|
if raw_units in [TEMP_CELSIUS, TEMP_FAHRENHEIT]:
|
|
return self.hass.config.units.temperature_unit
|
|
return raw_units
|