* Re-enabled Weather Sensors for the ISY component. As the ISY component had been updated to support newer components and have better UOM support, the support for ISY’s weather module was dropped. This adds that support back into Home Assistant. * Cleanup of the ISY Weather support. Cleaned up the for loops used to generate nodes representing weather data from the ISY. Moved the weather_node named tuple to the top of the file so that it can be more easily identified. * Update isy994.py
358 lines
9.9 KiB
Python
358 lines
9.9 KiB
Python
"""
|
|
Support for ISY994 binary sensors.
|
|
|
|
For more details about this platform, please refer to the documentation at
|
|
https://home-assistant.io/components/binary_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):
|
|
"""Setup 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 (len(node.uom) == 0 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
|