Clean zwave_js platform typing (#72439)
* Fix binary sensor * Fix climate * Fix cover * Fix fan * Fix light * Fix lock * Fix number * Fix select * Fix sensor * Add back type ignore until library bump
This commit is contained in:
parent
777c9c08ff
commit
6cac1dadeb
9 changed files with 97 additions and 40 deletions
|
@ -248,7 +248,7 @@ PROPERTY_SENSOR_MAPPINGS: dict[str, PropertyZWaveJSEntityDescription] = {
|
|||
|
||||
|
||||
# Mappings for boolean sensors
|
||||
BOOLEAN_SENSOR_MAPPINGS: dict[str, BinarySensorEntityDescription] = {
|
||||
BOOLEAN_SENSOR_MAPPINGS: dict[int, BinarySensorEntityDescription] = {
|
||||
CommandClass.BATTERY: BinarySensorEntityDescription(
|
||||
key=str(CommandClass.BATTERY),
|
||||
device_class=BinarySensorDeviceClass.BATTERY,
|
||||
|
@ -304,9 +304,13 @@ async def async_setup_entry(
|
|||
config_entry, driver, info, state_key, notification_description
|
||||
)
|
||||
)
|
||||
elif info.platform_hint == "property" and (
|
||||
property_description := PROPERTY_SENSOR_MAPPINGS.get(
|
||||
info.primary_value.property_name
|
||||
elif (
|
||||
info.platform_hint == "property"
|
||||
and info.primary_value.property_name
|
||||
and (
|
||||
property_description := PROPERTY_SENSOR_MAPPINGS.get(
|
||||
info.primary_value.property_name
|
||||
)
|
||||
)
|
||||
):
|
||||
entities.append(
|
||||
|
|
|
@ -138,7 +138,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||
self._current_mode = self.get_zwave_value(
|
||||
THERMOSTAT_MODE_PROPERTY, command_class=CommandClass.THERMOSTAT_MODE
|
||||
)
|
||||
self._setpoint_values: dict[ThermostatSetpointType, ZwaveValue] = {}
|
||||
self._setpoint_values: dict[ThermostatSetpointType, ZwaveValue | None] = {}
|
||||
for enum in ThermostatSetpointType:
|
||||
self._setpoint_values[enum] = self.get_zwave_value(
|
||||
THERMOSTAT_SETPOINT_PROPERTY,
|
||||
|
@ -233,9 +233,9 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||
self._hvac_presets = all_presets
|
||||
|
||||
@property
|
||||
def _current_mode_setpoint_enums(self) -> list[ThermostatSetpointType | None]:
|
||||
def _current_mode_setpoint_enums(self) -> list[ThermostatSetpointType]:
|
||||
"""Return the list of enums that are relevant to the current thermostat mode."""
|
||||
if self._current_mode is None:
|
||||
if self._current_mode is None or self._current_mode.value is None:
|
||||
# Thermostat(valve) with no support for setting a mode is considered heating-only
|
||||
return [ThermostatSetpointType.HEATING]
|
||||
return THERMOSTAT_MODE_SETPOINT_MAP.get(int(self._current_mode.value), []) # type: ignore[no-any-return]
|
||||
|
@ -329,12 +329,13 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||
@property
|
||||
def preset_mode(self) -> str | None:
|
||||
"""Return the current preset mode, e.g., home, away, temp."""
|
||||
if self._current_mode and self._current_mode.value is None:
|
||||
if self._current_mode is None or 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(
|
||||
str(self._current_mode.value)
|
||||
if int(self._current_mode.value) not in THERMOSTAT_MODES:
|
||||
return_val: str = cast(
|
||||
str,
|
||||
self._current_mode.metadata.states.get(str(self._current_mode.value)),
|
||||
)
|
||||
return return_val
|
||||
return PRESET_NONE
|
||||
|
@ -468,6 +469,9 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set new target preset mode."""
|
||||
if self._current_mode is None:
|
||||
# Thermostat(valve) has no support for setting a mode, so we make it a no-op
|
||||
return
|
||||
if preset_mode == PRESET_NONE:
|
||||
# try to restore to the (translated) main hvac mode
|
||||
await self.async_set_hvac_mode(self.hvac_mode)
|
||||
|
|
|
@ -28,6 +28,7 @@ from homeassistant.components.cover import (
|
|||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
|
@ -140,6 +141,8 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity):
|
|||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||
"""Move the cover to a specific position."""
|
||||
target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY)
|
||||
if target_value is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
await self.info.node.async_set_value(
|
||||
target_value, percent_to_zwave_position(kwargs[ATTR_POSITION])
|
||||
)
|
||||
|
@ -147,11 +150,15 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity):
|
|||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
"""Open the cover."""
|
||||
target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY)
|
||||
if target_value is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
await self.info.node.async_set_value(target_value, 99)
|
||||
|
||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||
"""Close cover."""
|
||||
target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY)
|
||||
if target_value is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
await self.info.node.async_set_value(target_value, 0)
|
||||
|
||||
async def async_stop_cover(self, **kwargs: Any) -> None:
|
||||
|
@ -207,7 +214,9 @@ class ZWaveTiltCover(ZWaveCover):
|
|||
None is unknown, 0 is closed, 100 is fully open.
|
||||
"""
|
||||
value = self.data_template.current_tilt_value(self.info.platform_data)
|
||||
return zwave_tilt_to_percent(value.value) if value else None
|
||||
if value is None or value.value is None:
|
||||
return None
|
||||
return zwave_tilt_to_percent(int(value.value))
|
||||
|
||||
async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
|
||||
"""Move the cover tilt to a specific position."""
|
||||
|
@ -241,8 +250,10 @@ class ZwaveMotorizedBarrier(ZWaveBaseEntity, CoverEntity):
|
|||
) -> None:
|
||||
"""Initialize a ZwaveMotorizedBarrier entity."""
|
||||
super().__init__(config_entry, driver, info)
|
||||
self._target_state: ZwaveValue = self.get_zwave_value(
|
||||
TARGET_STATE_PROPERTY, add_to_watched_value_ids=False
|
||||
# TARGET_STATE_PROPERTY is required in the discovery schema.
|
||||
self._target_state = cast(
|
||||
ZwaveValue,
|
||||
self.get_zwave_value(TARGET_STATE_PROPERTY, add_to_watched_value_ids=False),
|
||||
)
|
||||
|
||||
@property
|
||||
|
|
|
@ -96,7 +96,9 @@ class ZwaveFan(ZWaveBaseEntity, FanEntity):
|
|||
percentage_to_ranged_value(DEFAULT_SPEED_RANGE, percentage)
|
||||
)
|
||||
|
||||
await self.info.node.async_set_value(self._target_value, zwave_speed)
|
||||
if (target_value := self._target_value) is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
await self.info.node.async_set_value(target_value, zwave_speed)
|
||||
|
||||
async def async_turn_on(
|
||||
self,
|
||||
|
@ -110,12 +112,16 @@ class ZwaveFan(ZWaveBaseEntity, FanEntity):
|
|||
elif preset_mode is not None:
|
||||
await self.async_set_preset_mode(preset_mode)
|
||||
else:
|
||||
if (target_value := self._target_value) is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
# Value 255 tells device to return to previous value
|
||||
await self.info.node.async_set_value(self._target_value, 255)
|
||||
await self.info.node.async_set_value(target_value, 255)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the device off."""
|
||||
await self.info.node.async_set_value(self._target_value, 0)
|
||||
if (target_value := self._target_value) is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
await self.info.node.async_set_value(target_value, 0)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
|
@ -160,14 +166,18 @@ class ValueMappingZwaveFan(ZwaveFan):
|
|||
|
||||
async def async_set_percentage(self, percentage: int) -> None:
|
||||
"""Set the speed percentage of the fan."""
|
||||
if (target_value := self._target_value) is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
zwave_speed = self.percentage_to_zwave_speed(percentage)
|
||||
await self.info.node.async_set_value(self._target_value, zwave_speed)
|
||||
await self.info.node.async_set_value(target_value, zwave_speed)
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set new preset mode."""
|
||||
if (target_value := self._target_value) is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
for zwave_value, mapped_preset_mode in self.fan_value_mapping.presets.items():
|
||||
if preset_mode == mapped_preset_mode:
|
||||
await self.info.node.async_set_value(self._target_value, zwave_value)
|
||||
await self.info.node.async_set_value(target_value, zwave_value)
|
||||
return
|
||||
|
||||
raise NotValidPresetModeError(
|
||||
|
@ -210,7 +220,9 @@ class ValueMappingZwaveFan(ZwaveFan):
|
|||
@property
|
||||
def preset_mode(self) -> str | None:
|
||||
"""Return the current preset mode."""
|
||||
return self.fan_value_mapping.presets.get(self.info.primary_value.value)
|
||||
if (value := self.info.primary_value.value) is None:
|
||||
return None
|
||||
return self.fan_value_mapping.presets.get(value)
|
||||
|
||||
@property
|
||||
def has_fan_value_mapping(self) -> bool:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.const import (
|
||||
|
@ -24,6 +24,7 @@ from zwave_js_server.const.command_class.color_switch import (
|
|||
ColorComponent,
|
||||
)
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.value import Value
|
||||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
|
@ -301,10 +302,14 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
"""Set (multiple) defined colors to given value(s)."""
|
||||
# prefer the (new) combined color property
|
||||
# https://github.com/zwave-js/node-zwave-js/pull/1782
|
||||
combined_color_val = self.get_zwave_value(
|
||||
"targetColor",
|
||||
CommandClass.SWITCH_COLOR,
|
||||
value_property_key=None,
|
||||
# Setting colors is only done if there's a target color value.
|
||||
combined_color_val = cast(
|
||||
Value,
|
||||
self.get_zwave_value(
|
||||
"targetColor",
|
||||
CommandClass.SWITCH_COLOR,
|
||||
value_property_key=None,
|
||||
),
|
||||
)
|
||||
zwave_transition = None
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ from zwave_js_server.const.command_class.lock import (
|
|||
LOCK_CMD_CLASS_TO_PROPERTY_MAP,
|
||||
DoorLockMode,
|
||||
)
|
||||
from zwave_js_server.model.value import Value as ZwaveValue
|
||||
from zwave_js_server.util.lock import clear_usercode, set_usercode
|
||||
|
||||
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN, LockEntity
|
||||
|
@ -111,8 +110,10 @@ class ZWaveLock(ZWaveBaseEntity, LockEntity):
|
|||
|
||||
async def _set_lock_state(self, target_state: str, **kwargs: Any) -> None:
|
||||
"""Set the lock state."""
|
||||
target_value: ZwaveValue = self.get_zwave_value(
|
||||
LOCK_CMD_CLASS_TO_PROPERTY_MAP[self.info.primary_value.command_class]
|
||||
target_value = self.get_zwave_value(
|
||||
LOCK_CMD_CLASS_TO_PROPERTY_MAP[
|
||||
CommandClass(self.info.primary_value.command_class)
|
||||
]
|
||||
)
|
||||
if target_value is not None:
|
||||
await self.info.node.async_set_value(
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
"""Support for Z-Wave controls using the number platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import cast
|
||||
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.const import TARGET_VALUE_PROPERTY
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.value import Value
|
||||
|
||||
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN, NumberEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
|
@ -55,6 +59,7 @@ class ZwaveNumberEntity(ZWaveBaseEntity, NumberEntity):
|
|||
) -> None:
|
||||
"""Initialize a ZwaveNumberEntity entity."""
|
||||
super().__init__(config_entry, driver, info)
|
||||
self._target_value: Value | None
|
||||
if self.info.primary_value.metadata.writeable:
|
||||
self._target_value = self.info.primary_value
|
||||
else:
|
||||
|
@ -95,7 +100,9 @@ class ZwaveNumberEntity(ZWaveBaseEntity, NumberEntity):
|
|||
|
||||
async def async_set_value(self, value: float) -> None:
|
||||
"""Set new value."""
|
||||
await self.info.node.async_set_value(self._target_value, value)
|
||||
if (target_value := self._target_value) is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
await self.info.node.async_set_value(target_value, value)
|
||||
|
||||
|
||||
class ZwaveVolumeNumberEntity(ZWaveBaseEntity, NumberEntity):
|
||||
|
@ -106,9 +113,9 @@ class ZwaveVolumeNumberEntity(ZWaveBaseEntity, NumberEntity):
|
|||
) -> None:
|
||||
"""Initialize a ZwaveVolumeNumberEntity entity."""
|
||||
super().__init__(config_entry, driver, info)
|
||||
self.correction_factor = int(
|
||||
self.info.primary_value.metadata.max - self.info.primary_value.metadata.min
|
||||
)
|
||||
max_value = cast(int, self.info.primary_value.metadata.max)
|
||||
min_value = cast(int, self.info.primary_value.metadata.min)
|
||||
self.correction_factor = max_value - min_value
|
||||
# Fallback in case we can't properly calculate correction factor
|
||||
if self.correction_factor == 0:
|
||||
self.correction_factor = 1
|
||||
|
|
|
@ -11,6 +11,7 @@ from zwave_js_server.model.driver import Driver
|
|||
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN, SelectEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
@ -174,5 +175,7 @@ class ZwaveMultilevelSwitchSelectEntity(ZWaveBaseEntity, SelectEntity):
|
|||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
if (target_value := self._target_value) is None:
|
||||
raise HomeAssistantError("Missing target value on device.")
|
||||
key = next(key for key, val in self._lookup_map.items() if val == option)
|
||||
await self.info.node.async_set_value(self._target_value, int(key))
|
||||
await self.info.node.async_set_value(target_value, int(key))
|
||||
|
|
|
@ -26,6 +26,7 @@ from homeassistant.components.sensor import (
|
|||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_platform
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
|
||||
|
@ -224,7 +225,7 @@ async def async_setup_entry(
|
|||
LOGGER.warning(
|
||||
"Sensor not implemented for %s/%s",
|
||||
info.platform_hint,
|
||||
info.primary_value.propertyname,
|
||||
info.primary_value.property_name,
|
||||
)
|
||||
return
|
||||
|
||||
|
@ -352,13 +353,15 @@ class ZWaveMeterSensor(ZWaveNumericSensor):
|
|||
"""Reset meter(s) on device."""
|
||||
node = self.info.node
|
||||
primary_value = self.info.primary_value
|
||||
if (endpoint := primary_value.endpoint) is None:
|
||||
raise HomeAssistantError("Missing endpoint on device.")
|
||||
options = {}
|
||||
if meter_type is not None:
|
||||
options[RESET_METER_OPTION_TYPE] = meter_type
|
||||
if value is not None:
|
||||
options[RESET_METER_OPTION_TARGET_VALUE] = value
|
||||
args = [options] if options else []
|
||||
await node.endpoints[primary_value.endpoint].async_invoke_cc_api(
|
||||
await node.endpoints[endpoint].async_invoke_cc_api(
|
||||
CommandClass.METER, "reset", *args, wait_for_result=False
|
||||
)
|
||||
LOGGER.debug(
|
||||
|
@ -385,11 +388,12 @@ class ZWaveListSensor(ZwaveSensorBase):
|
|||
config_entry, driver, info, entity_description, unit_of_measurement
|
||||
)
|
||||
|
||||
property_key_name = self.info.primary_value.property_key_name
|
||||
# Entity class attributes
|
||||
self._attr_name = self.generate_name(
|
||||
include_value_name=True,
|
||||
alternate_value_name=self.info.primary_value.property_name,
|
||||
additional_info=[self.info.primary_value.property_key_name],
|
||||
additional_info=[property_key_name] if property_key_name else None,
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -409,8 +413,10 @@ class ZWaveListSensor(ZwaveSensorBase):
|
|||
@property
|
||||
def extra_state_attributes(self) -> dict[str, str] | None:
|
||||
"""Return the device specific state attributes."""
|
||||
if (value := self.info.primary_value.value) is None:
|
||||
return None
|
||||
# add the value's int value as property for multi-value (list) items
|
||||
return {ATTR_VALUE: self.info.primary_value.value}
|
||||
return {ATTR_VALUE: value}
|
||||
|
||||
|
||||
class ZWaveConfigParameterSensor(ZwaveSensorBase):
|
||||
|
@ -430,11 +436,12 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase):
|
|||
)
|
||||
self._primary_value = cast(ConfigurationValue, self.info.primary_value)
|
||||
|
||||
property_key_name = self.info.primary_value.property_key_name
|
||||
# Entity class attributes
|
||||
self._attr_name = self.generate_name(
|
||||
include_value_name=True,
|
||||
alternate_value_name=self.info.primary_value.property_name,
|
||||
additional_info=[self.info.primary_value.property_key_name],
|
||||
additional_info=[property_key_name] if property_key_name else None,
|
||||
name_suffix="Config Parameter",
|
||||
)
|
||||
|
||||
|
@ -458,10 +465,13 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase):
|
|||
@property
|
||||
def extra_state_attributes(self) -> dict[str, str] | None:
|
||||
"""Return the device specific state attributes."""
|
||||
if self._primary_value.configuration_value_type == ConfigurationValueType.RANGE:
|
||||
if (
|
||||
self._primary_value.configuration_value_type == ConfigurationValueType.RANGE
|
||||
or (value := self.info.primary_value.value) is None
|
||||
):
|
||||
return None
|
||||
# add the value's int value as property for multi-value (list) items
|
||||
return {ATTR_VALUE: self.info.primary_value.value}
|
||||
return {ATTR_VALUE: value}
|
||||
|
||||
|
||||
class ZWaveNodeStatusSensor(SensorEntity):
|
||||
|
|
Loading…
Add table
Reference in a new issue