From b1d59679e59f2b7c5b2e040c4b38cdc4c41a155f Mon Sep 17 00:00:00 2001 From: shbatm Date: Mon, 11 May 2020 21:32:19 -0500 Subject: [PATCH] Code conformance and sensor value clean-up on ISY994 (#35514) * Consolidate value conversion functions * Update to sensor * Fix property name * Revise sensors and state reporting per code standards * Update uom function and revert to property --- .../components/isy994/binary_sensor.py | 50 ++++----------- homeassistant/components/isy994/climate.py | 35 +++-------- homeassistant/components/isy994/const.py | 8 ++- homeassistant/components/isy994/cover.py | 32 +++------- homeassistant/components/isy994/entity.py | 17 +---- homeassistant/components/isy994/fan.py | 12 ++-- homeassistant/components/isy994/helpers.py | 29 +++++++++ homeassistant/components/isy994/light.py | 13 ++-- homeassistant/components/isy994/lock.py | 25 ++------ homeassistant/components/isy994/sensor.py | 63 ++++++++++--------- homeassistant/components/isy994/switch.py | 9 ++- 11 files changed, 123 insertions(+), 170 deletions(-) diff --git a/homeassistant/components/isy994/binary_sensor.py b/homeassistant/components/isy994/binary_sensor.py index cb93efb1567..3b5de4b8eca 100644 --- a/homeassistant/components/isy994/binary_sensor.py +++ b/homeassistant/components/isy994/binary_sensor.py @@ -24,7 +24,6 @@ from homeassistant.components.binary_sensor import ( BinarySensorEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN from homeassistant.core import callback from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.typing import HomeAssistantType @@ -136,7 +135,7 @@ async def async_setup_entry( # the initial state is forced "OFF"/"NORMAL" if the # parent device has a valid state. This is corrected # upon connection to the ISY event stream if subnode has a valid state. - initial_state = None if parent_device.state == STATE_UNKNOWN else False + initial_state = None if parent_device.state is None else False if subnode_id == SUBNODE_DUSK_DAWN: # Subnode 2 is the Dusk/Dawn sensor device = ISYInsteonBinarySensorEntity(node, DEVICE_CLASS_LIGHT) @@ -216,18 +215,10 @@ class ISYBinarySensorEntity(ISYNodeEntity, BinarySensorEntity): @property def is_on(self) -> bool: - """Get whether the ISY994 binary sensor device is on. - - Note: This method will return false if the current state is UNKNOWN - """ - return bool(self.value) - - @property - def state(self): - """Return the state of the binary sensor.""" - if self.value == ISY_VALUE_UNKNOWN: - return STATE_UNKNOWN - return STATE_ON if self.is_on else STATE_OFF + """Get whether the ISY994 binary sensor device is on.""" + if self._node.status == ISY_VALUE_UNKNOWN: + return None + return bool(self._node.status) @property def device_class(self) -> str: @@ -354,8 +345,8 @@ class ISYInsteonBinarySensorEntity(ISYBinarySensorEntity): self._heartbeat() @property - def value(self) -> object: - """Get the current value of the device. + def is_on(self) -> bool: + """Get whether the ISY994 binary sensor device is on. Insteon leak sensors set their primary node to On when the state is DRY, not WET, so we invert the binary state if the user indicates @@ -370,13 +361,6 @@ class ISYInsteonBinarySensorEntity(ISYBinarySensorEntity): return self._computed_state - @property - def state(self): - """Return the state of the binary sensor.""" - if self._computed_state is None: - return STATE_UNKNOWN - return STATE_ON if self.is_on else STATE_OFF - class ISYBinarySensorHeartbeat(ISYNodeEntity, BinarySensorEntity): """Representation of the battery state of an ISY994 sensor.""" @@ -394,7 +378,7 @@ class ISYBinarySensorHeartbeat(ISYNodeEntity, BinarySensorEntity): self._parent_device = parent_device self._heartbeat_timer = None self._computed_state = None - if self.state != STATE_UNKNOWN: + if self.state is None: self._computed_state = False async def async_added_to_hass(self) -> None: @@ -459,25 +443,15 @@ class ISYBinarySensorHeartbeat(ISYNodeEntity, BinarySensorEntity): We listen directly to the Control events for this device. """ - @property - def value(self) -> object: - """Get the current value of this sensor.""" - return self._computed_state - @property def is_on(self) -> bool: """Get whether the ISY994 binary sensor device is on. Note: This method will return false if the current state is UNKNOWN + which occurs after a restart until the first heartbeat or control + parent control event is received. """ - return bool(self.value) - - @property - def state(self): - """Return the state of the binary sensor.""" - if self._computed_state is None: - return None - return STATE_ON if self.is_on else STATE_OFF + return bool(self._computed_state) @property def device_class(self) -> str: @@ -502,4 +476,4 @@ class ISYBinarySensorProgramEntity(ISYProgramEntity, BinarySensorEntity): @property def is_on(self) -> bool: """Get whether the ISY994 binary sensor device is on.""" - return bool(self.value) + return bool(self._node.status) diff --git a/homeassistant/components/isy994/climate.py b/homeassistant/components/isy994/climate.py index d085cfd1530..8299265a381 100644 --- a/homeassistant/components/isy994/climate.py +++ b/homeassistant/components/isy994/climate.py @@ -1,10 +1,9 @@ """Support for Insteon Thermostats via ISY994 Platform.""" -from typing import Callable, List, Optional, Union +from typing import Callable, List, Optional from pyisy.constants import ( CMD_CLIMATE_FAN_SETTING, CMD_CLIMATE_MODE, - ISY_VALUE_UNKNOWN, PROP_HEAT_COOL_STATE, PROP_HUMIDITY, PROP_SETPOINT_COOL, @@ -42,19 +41,17 @@ from .const import ( HA_HVAC_TO_ISY, ISY994_NODES, ISY_HVAC_MODES, - UOM_DOUBLE_TEMP, UOM_FAN_MODES, UOM_HVAC_ACTIONS, UOM_HVAC_MODE_GENERIC, UOM_HVAC_MODE_INSTEON, UOM_ISY_CELSIUS, UOM_ISY_FAHRENHEIT, - UOM_ISYV4_DEGREES, UOM_ISYV4_NONE, UOM_TO_STATES, ) from .entity import ISYNodeEntity -from .helpers import migrate_old_unique_ids +from .helpers import convert_isy_value_to_hass, migrate_old_unique_ids from .services import async_setup_device_services ISY_SUPPORTED_FEATURES = ( @@ -79,26 +76,6 @@ async def async_setup_entry( async_setup_device_services(hass) -def convert_isy_temp_to_hass( - temp: Union[int, float, None], uom: str, precision: str -) -> float: - """Fix Insteon Thermostats' Reported Temperature. - - Insteon Thermostats report temperature in 0.5-deg precision as an int - by sending a value of 2 times the Temp. Correct by dividing by 2 here. - - Z-Wave Thermostats report temps in tenths as an integer and precision. - Correct by shifting the decimal place left by the value of precision. - """ - if temp is None or temp == ISY_VALUE_UNKNOWN: - return None - if uom in [UOM_DOUBLE_TEMP, UOM_ISYV4_DEGREES]: - return round(float(temp) / 2.0, 1) - if precision != "0": - return round(float(temp) / 10 ** int(precision), int(precision)) - return round(float(temp), 1) - - class ISYThermostatEntity(ISYNodeEntity, ClimateEntity): """Representation of an ISY994 thermostat entity.""" @@ -180,7 +157,9 @@ class ISYThermostatEntity(ISYNodeEntity, ClimateEntity): @property def current_temperature(self) -> Optional[float]: """Return the current temperature.""" - return convert_isy_temp_to_hass(self._node.status, self._uom, self._node.prec) + return convert_isy_value_to_hass( + self._node.status, self._uom, self._node.prec, 1 + ) @property def target_temperature_step(self) -> Optional[float]: @@ -202,7 +181,7 @@ class ISYThermostatEntity(ISYNodeEntity, ClimateEntity): target = self._node.aux_properties.get(PROP_SETPOINT_COOL) if not target: return None - return convert_isy_temp_to_hass(target.value, target.uom, target.prec) + return convert_isy_value_to_hass(target.value, target.uom, target.prec, 1) @property def target_temperature_low(self) -> Optional[float]: @@ -210,7 +189,7 @@ class ISYThermostatEntity(ISYNodeEntity, ClimateEntity): target = self._node.aux_properties.get(PROP_SETPOINT_HEAT) if not target: return None - return convert_isy_temp_to_hass(target.value, target.uom, target.prec) + return convert_isy_value_to_hass(target.value, target.uom, target.prec, 1) @property def fan_modes(self): diff --git a/homeassistant/components/isy994/const.py b/homeassistant/components/isy994/const.py index a3ad9798511..cc63f78738d 100644 --- a/homeassistant/components/isy994/const.py +++ b/homeassistant/components/isy994/const.py @@ -301,6 +301,8 @@ UOM_HVAC_ACTIONS = "66" UOM_HVAC_MODE_GENERIC = "67" UOM_HVAC_MODE_INSTEON = "98" UOM_FAN_MODES = "99" +UOM_INDEX = "25" +UOM_ON_OFF = "2" UOM_FRIENDLY_NAME = { "1": "A", @@ -324,7 +326,7 @@ UOM_FRIENDLY_NAME = { "22": "%RH", "23": PRESSURE_INHG, "24": f"{LENGTH_INCHES}/{TIME_HOURS}", - "25": "index", + UOM_INDEX: "index", # Index type. Use "node.formatted" for value "26": TEMP_KELVIN, "27": "keyword", "28": MASS_KILOGRAMS, @@ -383,7 +385,7 @@ UOM_FRIENDLY_NAME = { "91": DEGREE, "92": f"{DEGREE} South", "100": "", # Range 0-255, no unit. - "101": f"{DEGREE} (x2)", + UOM_DOUBLE_TEMP: UOM_DOUBLE_TEMP, "102": "kWs", "103": "$", "104": "ยข", @@ -398,7 +400,7 @@ UOM_FRIENDLY_NAME = { "113": "", # raw 3-byte signed value "114": "", # raw 4-byte signed value "116": LENGTH_MILES, - "117": "mb", + "117": "mbar", "118": "hPa", "119": f"{POWER_WATT}{TIME_HOURS}", "120": f"{LENGTH_INCHES}/{TIME_DAYS}", diff --git a/homeassistant/components/isy994/cover.py b/homeassistant/components/isy994/cover.py index abfae82ba0b..e0e47592a37 100644 --- a/homeassistant/components/isy994/cover.py +++ b/homeassistant/components/isy994/cover.py @@ -5,16 +5,9 @@ from pyisy.constants import ISY_VALUE_UNKNOWN from homeassistant.components.cover import DOMAIN as COVER, CoverEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN from homeassistant.helpers.typing import HomeAssistantType -from .const import ( - _LOGGER, - DOMAIN as ISY994_DOMAIN, - ISY994_NODES, - ISY994_PROGRAMS, - UOM_TO_STATES, -) +from .const import _LOGGER, DOMAIN as ISY994_DOMAIN, ISY994_NODES, ISY994_PROGRAMS from .entity import ISYNodeEntity, ISYProgramEntity from .helpers import migrate_old_unique_ids from .services import async_setup_device_services @@ -45,21 +38,16 @@ class ISYCoverEntity(ISYNodeEntity, CoverEntity): @property def current_cover_position(self) -> int: """Return the current cover position.""" - if self.value in [None, ISY_VALUE_UNKNOWN]: - return STATE_UNKNOWN - return sorted((0, self.value, 100))[1] + if self._node.status == ISY_VALUE_UNKNOWN: + return None + return sorted((0, self._node.status, 100))[1] @property def is_closed(self) -> bool: """Get whether the ISY994 cover device is closed.""" - return self.state == STATE_CLOSED - - @property - def state(self) -> str: - """Get the state of the ISY994 cover device.""" - if self.value == ISY_VALUE_UNKNOWN: - return STATE_UNKNOWN - return UOM_TO_STATES["97"].get(self.value, STATE_OPEN) + if self._node.status == ISY_VALUE_UNKNOWN: + return None + return self._node.status == 0 def open_cover(self, **kwargs) -> None: """Send the open cover command to the ISY994 cover device.""" @@ -76,9 +64,9 @@ class ISYCoverProgramEntity(ISYProgramEntity, CoverEntity): """Representation of an ISY994 cover program.""" @property - def state(self) -> str: - """Get the state of the ISY994 cover program.""" - return STATE_CLOSED if bool(self.value) else STATE_OPEN + def is_closed(self) -> bool: + """Get whether the ISY994 cover program is closed.""" + return bool(self._node.status) def open_cover(self, **kwargs) -> None: """Send the open cover command to the ISY994 cover program.""" diff --git a/homeassistant/components/isy994/entity.py b/homeassistant/components/isy994/entity.py index 6b44b116a60..a8805dc12cd 100644 --- a/homeassistant/components/isy994/entity.py +++ b/homeassistant/components/isy994/entity.py @@ -4,13 +4,12 @@ from pyisy.constants import ( COMMAND_FRIENDLY_NAME, EMPTY_TIME, EVENT_PROPS_IGNORED, - ISY_VALUE_UNKNOWN, PROTO_GROUP, PROTO_ZWAVE, ) from pyisy.helpers import NodeProperty -from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN +from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.helpers.entity import Entity from homeassistant.helpers.typing import Dict @@ -51,7 +50,7 @@ class ISYEntity(Entity): "precision": event.prec, } - if event.value is None or event.control not in EVENT_PROPS_IGNORED: + if event.control not in EVENT_PROPS_IGNORED: # New state attributes may be available, update the state. self.schedule_update_ha_state() @@ -128,18 +127,6 @@ class ISYEntity(Entity): """No polling required since we're using the subscription.""" return False - @property - def value(self) -> int: - """Get the current value of the device.""" - return self._node.status - - @property - def state(self): - """Return the state of the ISY device.""" - if self.value == ISY_VALUE_UNKNOWN: - return STATE_UNKNOWN - return super().state - class ISYNodeEntity(ISYEntity): """Representation of a ISY Nodebase (Node/Group) entity.""" diff --git a/homeassistant/components/isy994/fan.py b/homeassistant/components/isy994/fan.py index d1b2f718da1..96aa2144b1c 100644 --- a/homeassistant/components/isy994/fan.py +++ b/homeassistant/components/isy994/fan.py @@ -1,6 +1,8 @@ """Support for ISY994 fans.""" from typing import Callable +from pyisy.constants import ISY_VALUE_UNKNOWN + from homeassistant.components.fan import ( DOMAIN as FAN, SPEED_HIGH, @@ -58,12 +60,14 @@ class ISYFanEntity(ISYNodeEntity, FanEntity): @property def speed(self) -> str: """Return the current speed.""" - return VALUE_TO_STATE.get(self.value) + return VALUE_TO_STATE.get(self._node.status) @property def is_on(self) -> bool: """Get if the fan is on.""" - return self.value != 0 + if self._node.status == ISY_VALUE_UNKNOWN: + return None + return self._node.status != 0 def set_speed(self, speed: str) -> None: """Send the set speed command to the ISY994 fan device.""" @@ -94,12 +98,12 @@ class ISYFanProgramEntity(ISYProgramEntity, FanEntity): @property def speed(self) -> str: """Return the current speed.""" - return VALUE_TO_STATE.get(self.value) + return VALUE_TO_STATE.get(self._node.status) @property def is_on(self) -> bool: """Get if the fan is on.""" - return self.value != 0 + return self._node.status != 0 def turn_off(self, **kwargs) -> None: """Send the turn on command to ISY994 fan program.""" diff --git a/homeassistant/components/isy994/helpers.py b/homeassistant/components/isy994/helpers.py index a62611ff2da..c8e39ec605d 100644 --- a/homeassistant/components/isy994/helpers.py +++ b/homeassistant/components/isy994/helpers.py @@ -2,6 +2,7 @@ from typing import Any, List, Optional, Union from pyisy.constants import ( + ISY_VALUE_UNKNOWN, PROTO_GROUP, PROTO_INSTEON, PROTO_PROGRAM, @@ -46,6 +47,8 @@ from .const import ( SUPPORTED_PROGRAM_PLATFORMS, TYPE_CATEGORY_SENSOR_ACTUATORS, TYPE_EZIO2X4, + UOM_DOUBLE_TEMP, + UOM_ISYV4_DEGREES, ) BINARY_SENSOR_UOMS = ["2", "78"] @@ -394,3 +397,29 @@ async def migrate_old_unique_ids( registry.async_update_entity( old_entity_id_2, new_unique_id=device.unique_id ) + + +def convert_isy_value_to_hass( + value: Union[int, float, None], + uom: str, + precision: str, + fallback_precision: Optional[int] = None, +) -> Union[float, int]: + """Fix ISY Reported Values. + + ISY provides float values as an integer and precision component. + Correct by shifting the decimal place left by the value of precision. + (e.g. value=2345, prec="2" == 23.45) + + Insteon Thermostats report temperature in 0.5-deg precision as an int + by sending a value of 2 times the Temp. Correct by dividing by 2 here. + """ + if value is None or value == ISY_VALUE_UNKNOWN: + return None + if uom in [UOM_DOUBLE_TEMP, UOM_ISYV4_DEGREES]: + return round(float(value) / 2.0, 1) + if precision != "0": + return round(float(value) / 10 ** int(precision), int(precision)) + if fallback_precision: + return round(float(value), fallback_precision) + return value diff --git a/homeassistant/components/isy994/light.py b/homeassistant/components/isy994/light.py index 346988f24f6..8d58a7f5796 100644 --- a/homeassistant/components/isy994/light.py +++ b/homeassistant/components/isy994/light.py @@ -9,7 +9,6 @@ from homeassistant.components.light import ( LightEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_UNKNOWN from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import HomeAssistantType @@ -58,14 +57,16 @@ class ISYLightEntity(ISYNodeEntity, LightEntity, RestoreEntity): @property def is_on(self) -> bool: """Get whether the ISY994 light is on.""" - if self.value == ISY_VALUE_UNKNOWN: + if self._node.status == ISY_VALUE_UNKNOWN: return False - return int(self.value) != 0 + return int(self._node.status) != 0 @property def brightness(self) -> float: """Get the brightness of the ISY994 light.""" - return STATE_UNKNOWN if self.value == ISY_VALUE_UNKNOWN else int(self.value) + if self._node.status == ISY_VALUE_UNKNOWN: + return None + return int(self._node.status) def turn_off(self, **kwargs) -> None: """Send the turn off command to the ISY994 light device.""" @@ -75,8 +76,8 @@ class ISYLightEntity(ISYNodeEntity, LightEntity, RestoreEntity): def on_update(self, event: object) -> None: """Save brightness in the update event from the ISY994 Node.""" - if self.value not in (0, ISY_VALUE_UNKNOWN): - self._last_brightness = self.value + if self._node.status not in (0, ISY_VALUE_UNKNOWN): + self._last_brightness = self._node.status super().on_update(event) # pylint: disable=arguments-differ diff --git a/homeassistant/components/isy994/lock.py b/homeassistant/components/isy994/lock.py index fe85c9f99de..da50e4e704a 100644 --- a/homeassistant/components/isy994/lock.py +++ b/homeassistant/components/isy994/lock.py @@ -5,7 +5,6 @@ from pyisy.constants import ISY_VALUE_UNKNOWN from homeassistant.components.lock import DOMAIN as LOCK, LockEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_LOCKED, STATE_UNKNOWN, STATE_UNLOCKED from homeassistant.helpers.typing import HomeAssistantType from .const import _LOGGER, DOMAIN as ISY994_DOMAIN, ISY994_NODES, ISY994_PROGRAMS @@ -13,7 +12,7 @@ from .entity import ISYNodeEntity, ISYProgramEntity from .helpers import migrate_old_unique_ids from .services import async_setup_device_services -VALUE_TO_STATE = {0: STATE_UNLOCKED, 100: STATE_LOCKED} +VALUE_TO_STATE = {0: False, 100: True} async def async_setup_entry( @@ -41,29 +40,20 @@ class ISYLockEntity(ISYNodeEntity, LockEntity): @property def is_locked(self) -> bool: """Get whether the lock is in locked state.""" - return self.state == STATE_LOCKED - - @property - def state(self) -> str: - """Get the state of the lock.""" - if self.value == ISY_VALUE_UNKNOWN: - return STATE_UNKNOWN - return VALUE_TO_STATE.get(self.value, STATE_UNKNOWN) + if self._node.status == ISY_VALUE_UNKNOWN: + return None + return VALUE_TO_STATE.get(self._node.status) def lock(self, **kwargs) -> None: """Send the lock command to the ISY994 device.""" if not self._node.secure_lock(): _LOGGER.error("Unable to lock device") - self._node.update(0.5) - def unlock(self, **kwargs) -> None: """Send the unlock command to the ISY994 device.""" if not self._node.secure_unlock(): _LOGGER.error("Unable to lock device") - self._node.update(0.5) - class ISYLockProgramEntity(ISYProgramEntity, LockEntity): """Representation of a ISY lock program.""" @@ -71,12 +61,7 @@ class ISYLockProgramEntity(ISYProgramEntity, LockEntity): @property def is_locked(self) -> bool: """Return true if the device is locked.""" - return bool(self.value) - - @property - def state(self) -> str: - """Return the state of the lock.""" - return STATE_LOCKED if self.is_locked else STATE_UNLOCKED + return bool(self._node.status) def lock(self, **kwargs) -> None: """Lock the device.""" diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index 1e42df49837..8ae646b2791 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -1,11 +1,11 @@ """Support for ISY994 sensors.""" -from typing import Callable, Dict +from typing import Callable, Dict, Union from pyisy.constants import ISY_VALUE_UNKNOWN from homeassistant.components.sensor import DOMAIN as SENSOR from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.helpers.typing import HomeAssistantType from .const import ( @@ -13,11 +13,12 @@ from .const import ( DOMAIN as ISY994_DOMAIN, ISY994_NODES, ISY994_VARIABLES, + UOM_DOUBLE_TEMP, UOM_FRIENDLY_NAME, UOM_TO_STATES, ) from .entity import ISYEntity, ISYNodeEntity -from .helpers import migrate_old_unique_ids +from .helpers import convert_isy_value_to_hass, migrate_old_unique_ids from .services import async_setup_device_services @@ -46,48 +47,52 @@ class ISYSensorEntity(ISYNodeEntity): """Representation of an ISY994 sensor device.""" @property - def raw_unit_of_measurement(self) -> str: + def raw_unit_of_measurement(self) -> Union[dict, str]: """Get the raw unit of measurement for the ISY994 sensor device.""" uom = self._node.uom # Backwards compatibility for ISYv4 Firmware: if isinstance(uom, list): return UOM_FRIENDLY_NAME.get(uom[0], uom[0]) + + # Special cases for ISY UOM index units: + isy_states = UOM_TO_STATES.get(uom) + if isy_states: + return isy_states + return UOM_FRIENDLY_NAME.get(uom) @property def state(self) -> str: """Get the state of the ISY994 sensor device.""" - if self.value == ISY_VALUE_UNKNOWN: - return STATE_UNKNOWN + value = self._node.status + if value == ISY_VALUE_UNKNOWN: + return None - uom = self._node.uom - # Backwards compatibility for ISYv4 Firmware: - if isinstance(uom, list): - uom = uom[0] - if not uom: - return STATE_UNKNOWN + # Get the translated ISY Unit of Measurement + uom = self.raw_unit_of_measurement - states = UOM_TO_STATES.get(uom) - if states and states.get(self.value): - return states.get(self.value) - if self._node.prec and int(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 val - return self.value + # Check if this is a known index pair UOM + if isinstance(uom, dict): + return uom.get(value, value) + + # Handle ISY precision and rounding + value = convert_isy_value_to_hass(value, uom, self._node.prec) + + # Convert temperatures to Home Assistant's unit + if uom in (TEMP_CELSIUS, TEMP_FAHRENHEIT): + value = self.hass.config.units.temperature(value, uom) + + return value @property def unit_of_measurement(self) -> str: - """Get the unit of measurement for the ISY994 sensor device.""" + """Get the Home Assistant unit of measurement for the device.""" raw_units = self.raw_unit_of_measurement - if raw_units in (TEMP_FAHRENHEIT, TEMP_CELSIUS): + # Check if this is a known index pair UOM + if isinstance(raw_units, dict): + return None + if raw_units in (TEMP_FAHRENHEIT, TEMP_CELSIUS, UOM_DOUBLE_TEMP): return self.hass.config.units.temperature_unit return raw_units @@ -103,7 +108,7 @@ class ISYSensorVariableEntity(ISYEntity): @property def state(self): """Return the state of the variable.""" - return self.value + return self._node.status @property def device_state_attributes(self) -> Dict: diff --git a/homeassistant/components/isy994/switch.py b/homeassistant/components/isy994/switch.py index f5128dc16d9..0f79d3f218f 100644 --- a/homeassistant/components/isy994/switch.py +++ b/homeassistant/components/isy994/switch.py @@ -5,7 +5,6 @@ from pyisy.constants import ISY_VALUE_UNKNOWN, PROTO_GROUP from homeassistant.components.switch import DOMAIN as SWITCH, SwitchEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import STATE_UNKNOWN from homeassistant.helpers.typing import HomeAssistantType from .const import _LOGGER, DOMAIN as ISY994_DOMAIN, ISY994_NODES, ISY994_PROGRAMS @@ -39,9 +38,9 @@ class ISYSwitchEntity(ISYNodeEntity, SwitchEntity): @property def is_on(self) -> bool: """Get whether the ISY994 device is in the on state.""" - if self.value == ISY_VALUE_UNKNOWN: - return STATE_UNKNOWN - return bool(self.value) + if self._node.status == ISY_VALUE_UNKNOWN: + return None + return bool(self._node.status) def turn_off(self, **kwargs) -> None: """Send the turn off command to the ISY994 switch.""" @@ -67,7 +66,7 @@ class ISYSwitchProgramEntity(ISYProgramEntity, SwitchEntity): @property def is_on(self) -> bool: """Get whether the ISY994 switch program is on.""" - return bool(self.value) + return bool(self._node.status) def turn_on(self, **kwargs) -> None: """Send the turn on command to the ISY994 switch program."""