1 """Support for Z-Wave fans."""
3 from __future__
import annotations
6 from typing
import Any, cast
8 from zwave_js_server.client
import Client
as ZwaveClient
9 from zwave_js_server.const
import TARGET_VALUE_PROPERTY, CommandClass
10 from zwave_js_server.const.command_class.multilevel_switch
import SET_TO_PREVIOUS_VALUE
11 from zwave_js_server.const.command_class.thermostat
import (
12 THERMOSTAT_FAN_OFF_PROPERTY,
13 THERMOSTAT_FAN_STATE_PROPERTY,
15 from zwave_js_server.model.driver
import Driver
16 from zwave_js_server.model.value
import Value
as ZwaveValue
29 percentage_to_ranged_value,
30 ranged_value_to_percentage,
33 from .const
import DATA_CLIENT, DOMAIN
34 from .discovery
import ZwaveDiscoveryInfo
35 from .discovery_data_template
import FanValueMapping, FanValueMappingDataTemplate
36 from .entity
import ZWaveBaseEntity
37 from .helpers
import get_value_of_zwave_value
41 DEFAULT_SPEED_RANGE = (1, 99)
43 ATTR_FAN_STATE =
"fan_state"
48 config_entry: ConfigEntry,
49 async_add_entities: AddEntitiesCallback,
51 """Set up Z-Wave Fan from Config Entry."""
52 client: ZwaveClient = config_entry.runtime_data[DATA_CLIENT]
55 def async_add_fan(info: ZwaveDiscoveryInfo) ->
None:
57 driver = client.driver
58 assert driver
is not None
59 entities: list[ZWaveBaseEntity] = []
60 if info.platform_hint ==
"has_fan_value_mapping":
62 elif info.platform_hint ==
"thermostat_fan":
65 entities.append(
ZwaveFan(config_entry, driver, info))
69 config_entry.async_on_unload(
72 f
"{DOMAIN}_{config_entry.entry_id}_add_{FAN_DOMAIN}",
79 """Representation of a Z-Wave fan."""
81 _attr_supported_features = (
82 FanEntityFeature.SET_SPEED
83 | FanEntityFeature.TURN_OFF
84 | FanEntityFeature.TURN_ON
86 _enable_turn_on_off_backwards_compatibility =
False
89 self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
91 """Initialize the fan."""
92 super().
__init__(config_entry, driver, info)
93 target_value = self.
get_zwave_valueget_zwave_value(TARGET_VALUE_PROPERTY)
100 """Set the speed percentage of the fan."""
104 zwave_speed = math.ceil(
112 percentage: int |
None =
None,
113 preset_mode: str |
None =
None,
116 """Turn the device on."""
117 if percentage
is not None:
119 elif preset_mode
is not None:
122 if self.
infoinfo.primary_value.command_class != CommandClass.SWITCH_MULTILEVEL:
124 "`percentage` or `preset_mode` must be provided"
135 """Turn the device off."""
140 """Return true if device is on (speed above 0)."""
144 if self.
infoinfo.primary_value.value
is None:
147 return bool(self.
infoinfo.primary_value.value > 0)
151 """Return the current speed percentage."""
152 if self.
infoinfo.primary_value.value
is None:
156 DEFAULT_SPEED_RANGE, self.
infoinfo.primary_value.value
161 """Return the step size for percentage."""
166 """A Zwave fan with a value mapping data (e.g., 1-24 is low)."""
169 self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
171 """Initialize the fan."""
172 super().
__init__(config_entry, driver, info)
174 FanValueMappingDataTemplate, self.
infoinfo.platform_data_template
178 """Set the speed percentage of the fan."""
183 """Set new preset mode."""
184 for zwave_value, mapped_preset_mode
in self.
fan_value_mappingfan_value_mapping.presets.items():
185 if preset_mode == mapped_preset_mode:
191 """Return whether the entity is available."""
196 """Return the current speed percentage."""
197 if self.
infoinfo.primary_value.value
is None:
208 """Return the step size for percentage."""
216 """Return the available preset modes."""
224 """Return the current preset mode."""
225 if (value := self.
infoinfo.primary_value.value)
is None:
231 """Check if the speed configuration is valid."""
233 self.
data_templatedata_template.get_fan_value_mapping(self.
infoinfo.platform_data)
239 """Return the speed configuration for this fan."""
240 fan_value_mapping = self.
data_templatedata_template.get_fan_value_mapping(
241 self.
infoinfo.platform_data
245 assert fan_value_mapping
is not None
247 return fan_value_mapping
251 """Return the number of speeds the fan supports."""
256 """Flag supported features."""
258 FanEntityFeature.SET_SPEED
259 | FanEntityFeature.TURN_OFF
260 | FanEntityFeature.TURN_ON
263 flags |= FanEntityFeature.PRESET_MODE
268 """Map a percentage to a ZWave speed."""
275 (_, max_speed) = speed_range
282 assert step_percentage
284 if percentage <= step_percentage:
290 """Convert a Zwave speed to a percentage.
292 This method may return None if the device's value mapping doesn't cover
293 the specified Z-Wave speed.
300 (min_speed, max_speed) = speed_range
302 if min_speed <= zwave_speed <= max_speed:
306 return round(percentage)
313 """Representation of a Z-Wave thermostat fan."""
315 _fan_mode: ZwaveValue
316 _fan_off: ZwaveValue |
None =
None
317 _fan_state: ZwaveValue |
None =
None
320 self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
322 """Initialize the thermostat fan."""
323 super().
__init__(config_entry, driver, info)
328 THERMOSTAT_FAN_OFF_PROPERTY,
329 CommandClass.THERMOSTAT_FAN_MODE,
330 add_to_watched_value_ids=
True,
333 THERMOSTAT_FAN_STATE_PROPERTY,
334 CommandClass.THERMOSTAT_FAN_STATE,
335 add_to_watched_value_ids=
True,
340 percentage: int |
None =
None,
341 preset_mode: str |
None =
None,
344 """Turn the device on."""
350 """Turn the device off."""
357 """Return true if device is on."""
360 return not cast(bool, value)
364 """Return the current preset mode, e.g., auto, smart, interval, favorite."""
366 if value
is None or str(value)
not in self.
_fan_mode_fan_mode.metadata.states:
368 return cast(str, self.
_fan_mode_fan_mode.metadata.states[
str(value)])
371 """Set new preset mode."""
376 for state, label
in self.
_fan_mode_fan_mode.metadata.states.items()
377 if label == preset_mode
379 except StopIteration:
380 raise ValueError(f
"Received an invalid fan mode: {preset_mode}")
from None
386 """Return a list of available preset modes."""
387 if not self.
_fan_mode_fan_mode.metadata.states:
389 return list(self.
_fan_mode_fan_mode.metadata.states.values())
393 """Flag supported features."""
395 return FanEntityFeature.PRESET_MODE
397 FanEntityFeature.PRESET_MODE
398 | FanEntityFeature.TURN_ON
399 | FanEntityFeature.TURN_OFF
404 """Return the current state, Idle, Running, etc."""
409 or str(value)
not in self.
_fan_state_fan_state.metadata.states
412 return cast(str, self.
_fan_state_fan_state.metadata.states[
str(value)])
416 """Return the optional state attributes."""
420 attrs[ATTR_FAN_STATE] = state
str|None preset_mode(self)
float percentage_step(self)
None async_set_percentage(self, int percentage)
None async_set_preset_mode(self, str preset_mode)
SetValueResult|None _async_set_value(self, ZwaveValue value, Any new_value, dict|None options=None, bool|None wait_for_result=None)
ZwaveValue|None get_zwave_value(self, str|int value_property, int|None command_class=None, int|None endpoint=None, int|str|None value_property_key=None, bool add_to_watched_value_ids=True, bool check_all_endpoints=False)
bool has_fan_value_mapping(self)
int|None percentage(self)
FanValueMapping fan_value_mapping(self)
None async_set_preset_mode(self, str preset_mode)
int|None zwave_speed_to_percentage(self, int zwave_speed)
float percentage_step(self)
None __init__(self, ConfigEntry config_entry, Driver driver, ZwaveDiscoveryInfo info)
str|None preset_mode(self)
list[str] preset_modes(self)
FanEntityFeature supported_features(self)
None async_set_percentage(self, int percentage)
int percentage_to_zwave_speed(self, int percentage)
float percentage_step(self)
int|None percentage(self)
None async_turn_off(self, **Any kwargs)
None __init__(self, ConfigEntry config_entry, Driver driver, ZwaveDiscoveryInfo info)
None async_set_percentage(self, int percentage)
None async_turn_on(self, int|None percentage=None, str|None preset_mode=None, **Any kwargs)
None async_turn_on(self, int|None percentage=None, str|None preset_mode=None, **Any kwargs)
FanEntityFeature supported_features(self)
None async_turn_off(self, **Any kwargs)
list[str]|None preset_modes(self)
dict[str, str] extra_state_attributes(self)
None __init__(self, ConfigEntry config_entry, Driver driver, ZwaveDiscoveryInfo info)
None async_set_preset_mode(self, str preset_mode)
str|None preset_mode(self)
None async_write_ha_state(self)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Any|None get_value_of_zwave_value(ZwaveValue|None value)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
float percentage_to_ranged_value(tuple[float, float] low_high_range, float percentage)
int ranged_value_to_percentage(tuple[float, float] low_high_range, float value)