From 242ff045b92ed31bc2e3f298b06d77b02f6190cc Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Sat, 6 Feb 2021 14:02:03 +0100 Subject: [PATCH] Handle missing value in all platforms of zwave_js (#46081) --- homeassistant/components/zwave_js/climate.py | 15 +++++++++++++++ homeassistant/components/zwave_js/cover.py | 12 +++++++++--- homeassistant/components/zwave_js/entity.py | 8 +------- homeassistant/components/zwave_js/fan.py | 10 ++++++++-- homeassistant/components/zwave_js/lock.py | 3 +++ homeassistant/components/zwave_js/switch.py | 7 +++++-- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index b125c8bcd6a..a0b0648932c 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -207,6 +207,9 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): if self._current_mode is None: # Thermostat(valve) with no support for setting a mode is considered heating-only return HVAC_MODE_HEAT + if self._current_mode.value is None: + # guard missing value + return HVAC_MODE_HEAT return ZW_HVAC_MODE_MAP.get(int(self._current_mode.value), HVAC_MODE_HEAT_COOL) @property @@ -219,6 +222,9 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): """Return the current running hvac operation if supported.""" if not self._operating_state: return None + if self._operating_state.value is None: + # guard missing value + return None return HVAC_CURRENT_MAP.get(int(self._operating_state.value)) @property @@ -234,12 +240,18 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): @property def target_temperature(self) -> Optional[float]: """Return the temperature we try to reach.""" + if self._current_mode and self._current_mode.value is None: + # guard missing value + return None temp = self._setpoint_value(self._current_mode_setpoint_enums[0]) return temp.value if temp else None @property def target_temperature_high(self) -> Optional[float]: """Return the highbound target temperature we try to reach.""" + if self._current_mode and self._current_mode.value is None: + # guard missing value + return None temp = self._setpoint_value(self._current_mode_setpoint_enums[1]) return temp.value if temp else None @@ -251,6 +263,9 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): @property def preset_mode(self) -> Optional[str]: """Return the current preset mode, e.g., home, away, temp.""" + if self._current_mode and self._current_mode.value is None: + # guard missing value + return None if self._current_mode and int(self._current_mode.value) not in THERMOSTAT_MODES: return_val: str = self._current_mode.metadata.states.get( self._current_mode.value diff --git a/homeassistant/components/zwave_js/cover.py b/homeassistant/components/zwave_js/cover.py index b86cbeba944..38c891f7376 100644 --- a/homeassistant/components/zwave_js/cover.py +++ b/homeassistant/components/zwave_js/cover.py @@ -1,6 +1,6 @@ """Support for Z-Wave cover devices.""" import logging -from typing import Any, Callable, List +from typing import Any, Callable, List, Optional from zwave_js_server.client import Client as ZwaveClient @@ -59,13 +59,19 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity): """Representation of a Z-Wave Cover device.""" @property - def is_closed(self) -> bool: + def is_closed(self) -> Optional[bool]: """Return true if cover is closed.""" + if self.info.primary_value.value is None: + # guard missing value + return None return bool(self.info.primary_value.value == 0) @property - def current_cover_position(self) -> int: + def current_cover_position(self) -> Optional[int]: """Return the current position of cover where 0 means closed and 100 is fully open.""" + if self.info.primary_value.value is None: + # guard missing value + return None return round((self.info.primary_value.value / 99) * 100) async def async_set_cover_position(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/zwave_js/entity.py b/homeassistant/components/zwave_js/entity.py index 5d45cb9511a..911b0b7b2f8 100644 --- a/homeassistant/components/zwave_js/entity.py +++ b/homeassistant/components/zwave_js/entity.py @@ -102,13 +102,7 @@ class ZWaveBaseEntity(Entity): @property def available(self) -> bool: """Return entity availability.""" - return ( - self.client.connected - and bool(self.info.node.ready) - # a None value indicates something wrong with the device, - # or the value is simply not yet there (it will arrive later). - and self.info.primary_value.value is not None - ) + return self.client.connected and bool(self.info.node.ready) @callback def _value_changed(self, event_data: dict) -> None: diff --git a/homeassistant/components/zwave_js/fan.py b/homeassistant/components/zwave_js/fan.py index 6e62869f749..58b85eabef1 100644 --- a/homeassistant/components/zwave_js/fan.py +++ b/homeassistant/components/zwave_js/fan.py @@ -84,13 +84,19 @@ class ZwaveFan(ZWaveBaseEntity, FanEntity): await self.info.node.async_set_value(target_value, 0) @property - def is_on(self) -> bool: + def is_on(self) -> Optional[bool]: # type: ignore """Return true if device is on (speed above 0).""" + if self.info.primary_value.value is None: + # guard missing value + return None return bool(self.info.primary_value.value > 0) @property - def percentage(self) -> int: + def percentage(self) -> Optional[int]: """Return the current speed percentage.""" + if self.info.primary_value.value is None: + # guard missing value + return None return ranged_value_to_percentage(SPEED_RANGE, self.info.primary_value.value) @property diff --git a/homeassistant/components/zwave_js/lock.py b/homeassistant/components/zwave_js/lock.py index dedaf9a5e45..6f2a1a72c7d 100644 --- a/homeassistant/components/zwave_js/lock.py +++ b/homeassistant/components/zwave_js/lock.py @@ -90,6 +90,9 @@ class ZWaveLock(ZWaveBaseEntity, LockEntity): @property def is_locked(self) -> Optional[bool]: """Return true if the lock is locked.""" + if self.info.primary_value.value is None: + # guard missing value + return None return int( LOCK_CMD_CLASS_TO_LOCKED_STATE_MAP[ CommandClass(self.info.primary_value.command_class) diff --git a/homeassistant/components/zwave_js/switch.py b/homeassistant/components/zwave_js/switch.py index 2060894684c..8feba5911f8 100644 --- a/homeassistant/components/zwave_js/switch.py +++ b/homeassistant/components/zwave_js/switch.py @@ -1,7 +1,7 @@ """Representation of Z-Wave switches.""" import logging -from typing import Any, Callable, List +from typing import Any, Callable, List, Optional from zwave_js_server.client import Client as ZwaveClient @@ -44,8 +44,11 @@ class ZWaveSwitch(ZWaveBaseEntity, SwitchEntity): """Representation of a Z-Wave switch.""" @property - def is_on(self) -> bool: + def is_on(self) -> Optional[bool]: # type: ignore """Return a boolean for the state of the switch.""" + if self.info.primary_value.value is None: + # guard missing value + return None return bool(self.info.primary_value.value) async def async_turn_on(self, **kwargs: Any) -> None: