1 """Support for Template fans."""
3 from __future__
import annotations
8 import voluptuous
as vol
38 from .const
import DOMAIN
39 from .template_entity
import (
40 TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY,
42 rewrite_common_legacy_to_modern_conf,
45 _LOGGER = logging.getLogger(__name__)
48 CONF_SPEED_COUNT =
"speed_count"
49 CONF_PRESET_MODES =
"preset_modes"
50 CONF_PERCENTAGE_TEMPLATE =
"percentage_template"
51 CONF_PRESET_MODE_TEMPLATE =
"preset_mode_template"
52 CONF_OSCILLATING_TEMPLATE =
"oscillating_template"
53 CONF_DIRECTION_TEMPLATE =
"direction_template"
54 CONF_ON_ACTION =
"turn_on"
55 CONF_OFF_ACTION =
"turn_off"
56 CONF_SET_PERCENTAGE_ACTION =
"set_percentage"
57 CONF_SET_OSCILLATING_ACTION =
"set_oscillating"
58 CONF_SET_DIRECTION_ACTION =
"set_direction"
59 CONF_SET_PRESET_MODE_ACTION =
"set_preset_mode"
61 _VALID_DIRECTIONS = [DIRECTION_FORWARD, DIRECTION_REVERSE]
64 cv.deprecated(CONF_ENTITY_ID),
67 vol.Optional(CONF_FRIENDLY_NAME): cv.string,
68 vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
69 vol.Optional(CONF_PERCENTAGE_TEMPLATE): cv.template,
70 vol.Optional(CONF_PRESET_MODE_TEMPLATE): cv.template,
71 vol.Optional(CONF_OSCILLATING_TEMPLATE): cv.template,
72 vol.Optional(CONF_DIRECTION_TEMPLATE): cv.template,
73 vol.Required(CONF_ON_ACTION): cv.SCRIPT_SCHEMA,
74 vol.Required(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA,
75 vol.Optional(CONF_SET_PERCENTAGE_ACTION): cv.SCRIPT_SCHEMA,
76 vol.Optional(CONF_SET_PRESET_MODE_ACTION): cv.SCRIPT_SCHEMA,
77 vol.Optional(CONF_SET_OSCILLATING_ACTION): cv.SCRIPT_SCHEMA,
78 vol.Optional(CONF_SET_DIRECTION_ACTION): cv.SCRIPT_SCHEMA,
79 vol.Optional(CONF_SPEED_COUNT): vol.Coerce(int),
80 vol.Optional(CONF_PRESET_MODES): cv.ensure_list,
81 vol.Optional(CONF_ENTITY_ID): cv.entity_ids,
82 vol.Optional(CONF_UNIQUE_ID): cv.string,
84 ).extend(TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY.schema),
87 PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(
88 {vol.Required(CONF_FANS): cv.schema_with_slug_keys(FAN_SCHEMA)}
93 """Create the Template Fans."""
96 for object_id, entity_config
in config[CONF_FANS].items():
99 unique_id = entity_config.get(CONF_UNIQUE_ID)
116 async_add_entities: AddEntitiesCallback,
117 discovery_info: DiscoveryInfoType |
None =
None,
119 """Set up the template fans."""
124 """A template fan component."""
126 _attr_should_poll =
False
127 _enable_turn_on_off_backwards_compatibility =
False
136 """Initialize the fan."""
138 hass, config=config, fallback_name=object_id, unique_id=unique_id
142 ENTITY_ID_FORMAT, object_id, hass=hass
146 self.
_template_template = config.get(CONF_VALUE_TEMPLATE)
156 if set_percentage_action := config.get(CONF_SET_PERCENTAGE_ACTION):
158 hass, set_percentage_action, friendly_name, DOMAIN
162 if set_preset_mode_action := config.get(CONF_SET_PRESET_MODE_ACTION):
164 hass, set_preset_mode_action, friendly_name, DOMAIN
168 if set_oscillating_action := config.get(CONF_SET_OSCILLATING_ACTION):
170 hass, set_oscillating_action, friendly_name, DOMAIN
174 if set_direction_action := config.get(CONF_SET_DIRECTION_ACTION):
176 hass, set_direction_action, friendly_name, DOMAIN
179 self.
_state_state: bool |
None =
False
192 self._attr_supported_features |= FanEntityFeature.SET_SPEED
194 self._attr_supported_features |= FanEntityFeature.PRESET_MODE
196 self._attr_supported_features |= FanEntityFeature.OSCILLATE
198 self._attr_supported_features |= FanEntityFeature.DIRECTION
199 self._attr_supported_features |= (
200 FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
207 """Return the number of speeds the fan supports."""
212 """Get the list of available preset modes."""
217 """Return true if device is on."""
222 """Return the current preset mode."""
227 """Return the current speed percentage."""
232 """Return the oscillation state."""
237 """Return the oscillation state."""
242 percentage: int |
None =
None,
243 preset_mode: str |
None =
None,
246 """Turn on the fan."""
250 ATTR_PERCENTAGE: percentage,
251 ATTR_PRESET_MODE: preset_mode,
256 if preset_mode
is not None:
258 elif percentage
is not None:
266 """Turn off the fan."""
274 """Set the percentage speed of the fan."""
280 run_variables={ATTR_PERCENTAGE: self.
_percentage_percentage},
285 self.
_state_state = percentage != 0
289 """Set the preset_mode of the fan."""
295 run_variables={ATTR_PRESET_MODE: self.
_preset_mode_preset_mode},
304 """Set oscillation of the fan."""
316 """Set the direction of the fan."""
320 if direction
in _VALID_DIRECTIONS:
324 run_variables={ATTR_DIRECTION: direction},
329 "Received invalid direction: %s for entity %s. Expected: %s",
332 ", ".join(_VALID_DIRECTIONS),
338 if isinstance(result, TemplateError):
342 if isinstance(result, bool):
343 self.
_state_state = result
346 if isinstance(result, str):
347 self.
_state_state = result.lower()
in (
"true", STATE_ON)
354 """Set up templates."""
366 none_on_template_error=
True,
374 none_on_template_error=
True,
382 none_on_template_error=
True,
390 none_on_template_error=
True,
399 except (ValueError, TypeError):
401 "Received invalid percentage: %s for entity %s",
408 if 0 <= percentage <= 100:
412 "Received invalid percentage: %s for entity %s",
421 preset_mode =
str(preset_mode)
425 elif preset_mode
in (STATE_UNAVAILABLE, STATE_UNKNOWN):
429 "Received invalid preset_mode: %s for entity %s. Expected: %s",
439 if oscillating ==
"True" or oscillating
is True:
441 elif oscillating ==
"False" or oscillating
is False:
443 elif oscillating
in (STATE_UNAVAILABLE, STATE_UNKNOWN):
447 "Received invalid oscillating: %s for entity %s. Expected: True/False",
456 if direction
in _VALID_DIRECTIONS:
458 elif direction
in (STATE_UNAVAILABLE, STATE_UNKNOWN):
462 "Received invalid direction: %s for entity %s. Expected: %s",
465 ", ".join(_VALID_DIRECTIONS),
list[str]|None preset_modes(self)
str|None preset_mode(self)
bool|None oscillating(self)
None async_set_percentage(self, int percentage)
None async_set_preset_mode(self, str preset_mode)
def _update_preset_mode(self, preset_mode)
None async_turn_off(self, **Any kwargs)
list[str] preset_modes(self)
str|None preset_mode(self)
None async_set_percentage(self, int percentage)
def __init__(self, hass, object_id, config, unique_id)
None async_oscillate(self, bool oscillating)
def _update_direction(self, direction)
None _async_setup_templates(self)
def _update_oscillating(self, oscillating)
def _update_state(self, result)
str|None current_direction(self)
None async_set_direction(self, str direction)
def _update_percentage(self, percentage)
int|None percentage(self)
None async_turn_on(self, int|None percentage=None, str|None preset_mode=None, **Any kwargs)
bool|None oscillating(self)
None async_set_preset_mode(self, str preset_mode)
None _update_state(self, str|TemplateError result)
None async_run_script(self, Script script, *_VarsType|None run_variables=None, Context|None context=None)
None add_template_attribute(self, str attribute, Template template, Callable[[Any], Any]|None validator=None, Callable[[Any], None]|None on_update=None, bool none_on_template_error=False)
None async_write_ha_state(self)
def _async_create_entities(hass, config)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
dict[str, Any] rewrite_common_legacy_to_modern_conf(HomeAssistant hass, dict[str, Any] entity_cfg, dict[str, str]|None extra_legacy_fields=None)
str async_generate_entity_id(str entity_id_format, str|None name, Iterable[str]|None current_ids=None, HomeAssistant|None hass=None)