text: """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,
      TIME_DAYS,
      TIME_HOURS,
      TIME_MILLISECONDS,
      TIME_MINUTES,
      TIME_MONTHS,
      TIME_SECONDS,
      TIME_YEARS,
      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": f"btu/{TIME_HOURS}",
      "4": TEMP_CELSIUS,
      "5": "cm",
      "6": "ft³",
      "7": f"ft³/{TIME_MINUTES}",
      "8": "m³",
      "9": TIME_DAYS,
      "10": TIME_DAYS,
      "12": "dB",
      "13": "dB A",
      "14": "°",
      "16": "macroseismic",
      "17": TEMP_FAHRENHEIT,
      "18": "ft",
      "19": TIME_HOURS,
      "20": TIME_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": f"m/{TIME_SECONDS}",
      "41": "mA",
      "42": TIME_MILLISECONDS,
      "43": "mV",
      "44": TIME_MINUTES,
      "45": TIME_MINUTES,
      "46": "mm/hr",
      "47": TIME_MONTHS,
      "48": "MPH",
      "49": f"m/{TIME_SECONDS}",
      "50": "ohm",
      "51": "%",
      "52": "lb",
      "53": "power factor",
      "54": "ppm",
      "55": "pulse count",
      "57": TIME_SECONDS,
      "58": TIME_SECONDS,
      "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": TIME_YEARS,
      "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
  