"""Support for ISY994 sensors.""" import logging from typing import Callable from homeassistant.components.sensor import DOMAIN from homeassistant.const import POWER_WATT, TEMP_CELSIUS, TEMP_FAHRENHEIT, UNIT_UV_INDEX from homeassistant.helpers.typing import ConfigType from . import ISY994_NODES, ISY994_WEATHER, ISYDevice _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": "lx", "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": UNIT_UV_INDEX, "72": "V", "73": POWER_WATT, "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", }, } def setup_platform( hass, config: ConfigType, add_entities: Callable[[list], None], discovery_info=None ): """Set up the ISY994 sensor platform.""" devices = [] for node in hass.data[ISY994_NODES][DOMAIN]: _LOGGER.debug("Loading %s", node.name) devices.append(ISYSensorDevice(node)) for node in hass.data[ISY994_WEATHER]: devices.append(ISYWeatherDevice(node)) add_entities(devices) class ISYSensorDevice(ISYDevice): """Representation of an ISY994 sensor device.""" @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 in (TEMP_CELSIUS, TEMP_FAHRENHEIT): friendly_name = self.hass.config.units.temperature_unit return friendly_name return self._node.uom[0] return None @property def state(self) -> str: """Get the state of the ISY994 sensor device.""" if self.is_unknown(): return None 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(f"{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 return raw_units class ISYWeatherDevice(ISYDevice): """Representation of an ISY994 weather device.""" @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