Add TURN_OFF/TURN_ON feature flags for fan (#121447)
This commit is contained in:
parent
172778053c
commit
ca4c617d4b
58 changed files with 858 additions and 132 deletions
|
@ -43,7 +43,10 @@ class BAFFan(BAFEntity, FanEntity):
|
||||||
FanEntityFeature.SET_SPEED
|
FanEntityFeature.SET_SPEED
|
||||||
| FanEntityFeature.DIRECTION
|
| FanEntityFeature.DIRECTION
|
||||||
| FanEntityFeature.PRESET_MODE
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
_attr_preset_modes = [PRESET_MODE_AUTO]
|
_attr_preset_modes = [PRESET_MODE_AUTO]
|
||||||
_attr_speed_count = SPEED_COUNT
|
_attr_speed_count = SPEED_COUNT
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
|
|
@ -32,7 +32,12 @@ async def async_setup_entry(
|
||||||
class BalboaPumpFanEntity(BalboaEntity, FanEntity):
|
class BalboaPumpFanEntity(BalboaEntity, FanEntity):
|
||||||
"""Representation of a Balboa Spa pump fan entity."""
|
"""Representation of a Balboa Spa pump fan entity."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
_attr_translation_key = "pump"
|
_attr_translation_key = "pump"
|
||||||
|
|
||||||
def __init__(self, control: SpaControl) -> None:
|
def __init__(self, control: SpaControl) -> None:
|
||||||
|
|
|
@ -69,7 +69,7 @@ class BondFan(BondEntity, FanEntity):
|
||||||
super().__init__(data, device)
|
super().__init__(data, device)
|
||||||
if self._device.has_action(Action.BREEZE_ON):
|
if self._device.has_action(Action.BREEZE_ON):
|
||||||
self._attr_preset_modes = [PRESET_MODE_BREEZE]
|
self._attr_preset_modes = [PRESET_MODE_BREEZE]
|
||||||
features = FanEntityFeature(0)
|
features = FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
if self._device.supports_speed():
|
if self._device.supports_speed():
|
||||||
features |= FanEntityFeature.SET_SPEED
|
features |= FanEntityFeature.SET_SPEED
|
||||||
if self._device.supports_direction():
|
if self._device.supports_direction():
|
||||||
|
|
|
@ -62,7 +62,13 @@ class ComfoConnectFan(FanEntity):
|
||||||
|
|
||||||
_attr_icon = "mdi:air-conditioner"
|
_attr_icon = "mdi:air-conditioner"
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
_attr_preset_modes = PRESET_MODES
|
_attr_preset_modes = PRESET_MODES
|
||||||
current_speed: float | None = None
|
current_speed: float | None = None
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,12 @@ class DeconzFan(DeconzDevice[Light], FanEntity):
|
||||||
TYPE = DOMAIN
|
TYPE = DOMAIN
|
||||||
_default_on_speed = LightFanSpeed.PERCENT_50
|
_default_on_speed = LightFanSpeed.PERCENT_50
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, device: Light, hub: DeconzHub) -> None:
|
def __init__(self, device: Light, hub: DeconzHub) -> None:
|
||||||
"""Set up fan."""
|
"""Set up fan."""
|
||||||
|
|
|
@ -15,9 +15,15 @@ PRESET_MODE_SLEEP = "sleep"
|
||||||
PRESET_MODE_ON = "on"
|
PRESET_MODE_ON = "on"
|
||||||
|
|
||||||
FULL_SUPPORT = (
|
FULL_SUPPORT = (
|
||||||
FanEntityFeature.SET_SPEED | FanEntityFeature.OSCILLATE | FanEntityFeature.DIRECTION
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.OSCILLATE
|
||||||
|
| FanEntityFeature.DIRECTION
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
LIMITED_SUPPORT = (
|
||||||
|
FanEntityFeature.SET_SPEED | FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
LIMITED_SUPPORT = FanEntityFeature.SET_SPEED
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
|
@ -75,7 +81,9 @@ async def async_setup_entry(
|
||||||
hass,
|
hass,
|
||||||
"fan5",
|
"fan5",
|
||||||
"Preset Only Limited Fan",
|
"Preset Only Limited Fan",
|
||||||
FanEntityFeature.PRESET_MODE,
|
FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON,
|
||||||
[
|
[
|
||||||
PRESET_MODE_AUTO,
|
PRESET_MODE_AUTO,
|
||||||
PRESET_MODE_SMART,
|
PRESET_MODE_SMART,
|
||||||
|
@ -92,6 +100,7 @@ class BaseDemoFan(FanEntity):
|
||||||
|
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
_attr_translation_key = "demo"
|
_attr_translation_key = "demo"
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -45,6 +45,7 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity):
|
||||||
"""A fan implementation for ESPHome."""
|
"""A fan implementation for ESPHome."""
|
||||||
|
|
||||||
_supports_speed_levels: bool = True
|
_supports_speed_levels: bool = True
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
async def async_set_percentage(self, percentage: int) -> None:
|
async def async_set_percentage(self, percentage: int) -> None:
|
||||||
"""Set the speed percentage of the fan."""
|
"""Set the speed percentage of the fan."""
|
||||||
|
@ -148,7 +149,7 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity):
|
||||||
api_version = self._api_version
|
api_version = self._api_version
|
||||||
supports_speed_levels = api_version.major == 1 and api_version.minor > 3
|
supports_speed_levels = api_version.major == 1 and api_version.minor > 3
|
||||||
self._supports_speed_levels = supports_speed_levels
|
self._supports_speed_levels = supports_speed_levels
|
||||||
flags = FanEntityFeature(0)
|
flags = FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
if static_info.supports_oscillation:
|
if static_info.supports_oscillation:
|
||||||
flags |= FanEntityFeature.OSCILLATE
|
flags |= FanEntityFeature.OSCILLATE
|
||||||
if static_info.supports_speed:
|
if static_info.supports_speed:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from enum import IntFlag
|
from enum import IntFlag
|
||||||
import functools as ft
|
import functools as ft
|
||||||
|
@ -30,6 +31,7 @@ from homeassistant.helpers.deprecation import (
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.entity import ToggleEntity, ToggleEntityDescription
|
from homeassistant.helpers.entity import ToggleEntity, ToggleEntityDescription
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.util.percentage import (
|
from homeassistant.util.percentage import (
|
||||||
|
@ -53,6 +55,8 @@ class FanEntityFeature(IntFlag):
|
||||||
OSCILLATE = 2
|
OSCILLATE = 2
|
||||||
DIRECTION = 4
|
DIRECTION = 4
|
||||||
PRESET_MODE = 8
|
PRESET_MODE = 8
|
||||||
|
TURN_OFF = 16
|
||||||
|
TURN_ON = 32
|
||||||
|
|
||||||
|
|
||||||
# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.
|
# These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.
|
||||||
|
@ -132,9 +136,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
vol.Optional(ATTR_PRESET_MODE): cv.string,
|
vol.Optional(ATTR_PRESET_MODE): cv.string,
|
||||||
},
|
},
|
||||||
"async_handle_turn_on_service",
|
"async_handle_turn_on_service",
|
||||||
|
[FanEntityFeature.TURN_ON],
|
||||||
|
)
|
||||||
|
component.async_register_entity_service(
|
||||||
|
SERVICE_TURN_OFF, {}, "async_turn_off", [FanEntityFeature.TURN_OFF]
|
||||||
|
)
|
||||||
|
component.async_register_entity_service(
|
||||||
|
SERVICE_TOGGLE,
|
||||||
|
{},
|
||||||
|
"async_toggle",
|
||||||
|
[FanEntityFeature.TURN_OFF, FanEntityFeature.TURN_ON],
|
||||||
)
|
)
|
||||||
component.async_register_entity_service(SERVICE_TURN_OFF, {}, "async_turn_off")
|
|
||||||
component.async_register_entity_service(SERVICE_TOGGLE, {}, "async_toggle")
|
|
||||||
component.async_register_entity_service(
|
component.async_register_entity_service(
|
||||||
SERVICE_INCREASE_SPEED,
|
SERVICE_INCREASE_SPEED,
|
||||||
{
|
{
|
||||||
|
@ -228,6 +240,99 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||||
_attr_speed_count: int
|
_attr_speed_count: int
|
||||||
_attr_supported_features: FanEntityFeature = FanEntityFeature(0)
|
_attr_supported_features: FanEntityFeature = FanEntityFeature(0)
|
||||||
|
|
||||||
|
__mod_supported_features: FanEntityFeature = FanEntityFeature(0)
|
||||||
|
# Integrations should set `_enable_turn_on_off_backwards_compatibility` to False
|
||||||
|
# once migrated and set the feature flags TURN_ON/TURN_OFF as needed.
|
||||||
|
_enable_turn_on_off_backwards_compatibility: bool = True
|
||||||
|
|
||||||
|
def __getattribute__(self, __name: str) -> Any:
|
||||||
|
"""Get attribute.
|
||||||
|
|
||||||
|
Modify return of `supported_features` to
|
||||||
|
include `_mod_supported_features` if attribute is set.
|
||||||
|
"""
|
||||||
|
if __name != "supported_features":
|
||||||
|
return super().__getattribute__(__name)
|
||||||
|
|
||||||
|
# Convert the supported features to ClimateEntityFeature.
|
||||||
|
# Remove this compatibility shim in 2025.1 or later.
|
||||||
|
_supported_features: FanEntityFeature = super().__getattribute__(
|
||||||
|
"supported_features"
|
||||||
|
)
|
||||||
|
_mod_supported_features: FanEntityFeature = super().__getattribute__(
|
||||||
|
"_FanEntity__mod_supported_features"
|
||||||
|
)
|
||||||
|
if type(_supported_features) is int: # noqa: E721
|
||||||
|
_features = FanEntityFeature(_supported_features)
|
||||||
|
self._report_deprecated_supported_features_values(_features)
|
||||||
|
else:
|
||||||
|
_features = _supported_features
|
||||||
|
|
||||||
|
if not _mod_supported_features:
|
||||||
|
return _features
|
||||||
|
|
||||||
|
# Add automatically calculated FanEntityFeature.TURN_OFF/TURN_ON to
|
||||||
|
# supported features and return it
|
||||||
|
return _features | _mod_supported_features
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def add_to_platform_start(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
platform: EntityPlatform,
|
||||||
|
parallel_updates: asyncio.Semaphore | None,
|
||||||
|
) -> None:
|
||||||
|
"""Start adding an entity to a platform."""
|
||||||
|
super().add_to_platform_start(hass, platform, parallel_updates)
|
||||||
|
|
||||||
|
def _report_turn_on_off(feature: str, method: str) -> None:
|
||||||
|
"""Log warning not implemented turn on/off feature."""
|
||||||
|
report_issue = self._suggest_report_issue()
|
||||||
|
message = (
|
||||||
|
"Entity %s (%s) does not set FanEntityFeature.%s"
|
||||||
|
" but implements the %s method. Please %s"
|
||||||
|
)
|
||||||
|
_LOGGER.warning(
|
||||||
|
message,
|
||||||
|
self.entity_id,
|
||||||
|
type(self),
|
||||||
|
feature,
|
||||||
|
method,
|
||||||
|
report_issue,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Adds FanEntityFeature.TURN_OFF/TURN_ON depending on service calls implemented
|
||||||
|
# This should be removed in 2025.2.
|
||||||
|
if self._enable_turn_on_off_backwards_compatibility is False:
|
||||||
|
# Return if integration has migrated already
|
||||||
|
return
|
||||||
|
|
||||||
|
supported_features = self.supported_features
|
||||||
|
if supported_features & (FanEntityFeature.TURN_ON | FanEntityFeature.TURN_OFF):
|
||||||
|
# The entity supports both turn_on and turn_off, the backwards compatibility
|
||||||
|
# checks are not needed
|
||||||
|
return
|
||||||
|
|
||||||
|
if not supported_features & FanEntityFeature.TURN_OFF and (
|
||||||
|
type(self).async_turn_off is not ToggleEntity.async_turn_off
|
||||||
|
or type(self).turn_off is not ToggleEntity.turn_off
|
||||||
|
):
|
||||||
|
# turn_off implicitly supported by implementing turn_off method
|
||||||
|
_report_turn_on_off("TURN_OFF", "turn_off")
|
||||||
|
self.__mod_supported_features |= ( # pylint: disable=unused-private-member
|
||||||
|
FanEntityFeature.TURN_OFF
|
||||||
|
)
|
||||||
|
|
||||||
|
if not supported_features & FanEntityFeature.TURN_ON and (
|
||||||
|
type(self).async_turn_on is not FanEntity.async_turn_on
|
||||||
|
or type(self).turn_on is not FanEntity.turn_on
|
||||||
|
):
|
||||||
|
# turn_on implicitly supported by implementing turn_on method
|
||||||
|
_report_turn_on_off("TURN_ON", "turn_on")
|
||||||
|
self.__mod_supported_features |= ( # pylint: disable=unused-private-member
|
||||||
|
FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
def set_percentage(self, percentage: int) -> None:
|
def set_percentage(self, percentage: int) -> None:
|
||||||
"""Set the speed of the fan, as a percentage."""
|
"""Set the speed of the fan, as a percentage."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -388,7 +493,7 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||||
def capability_attributes(self) -> dict[str, list[str] | None]:
|
def capability_attributes(self) -> dict[str, list[str] | None]:
|
||||||
"""Return capability attributes."""
|
"""Return capability attributes."""
|
||||||
attrs = {}
|
attrs = {}
|
||||||
supported_features = self.supported_features_compat
|
supported_features = self.supported_features
|
||||||
|
|
||||||
if (
|
if (
|
||||||
FanEntityFeature.SET_SPEED in supported_features
|
FanEntityFeature.SET_SPEED in supported_features
|
||||||
|
@ -403,7 +508,7 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||||
def state_attributes(self) -> dict[str, float | str | None]:
|
def state_attributes(self) -> dict[str, float | str | None]:
|
||||||
"""Return optional state attributes."""
|
"""Return optional state attributes."""
|
||||||
data: dict[str, float | str | None] = {}
|
data: dict[str, float | str | None] = {}
|
||||||
supported_features = self.supported_features_compat
|
supported_features = self.supported_features
|
||||||
|
|
||||||
if FanEntityFeature.DIRECTION in supported_features:
|
if FanEntityFeature.DIRECTION in supported_features:
|
||||||
data[ATTR_DIRECTION] = self.current_direction
|
data[ATTR_DIRECTION] = self.current_direction
|
||||||
|
@ -427,19 +532,6 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
return self._attr_supported_features
|
return self._attr_supported_features
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features_compat(self) -> FanEntityFeature:
|
|
||||||
"""Return the supported features as FanEntityFeature.
|
|
||||||
|
|
||||||
Remove this compatibility shim in 2025.1 or later.
|
|
||||||
"""
|
|
||||||
features = self.supported_features
|
|
||||||
if type(features) is int: # noqa: E721
|
|
||||||
new_features = FanEntityFeature(features)
|
|
||||||
self._report_deprecated_supported_features_values(new_features)
|
|
||||||
return new_features
|
|
||||||
return features
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def preset_mode(self) -> str | None:
|
def preset_mode(self) -> str | None:
|
||||||
"""Return the current preset mode, e.g., auto, smart, interval, favorite.
|
"""Return the current preset mode, e.g., auto, smart, interval, favorite.
|
||||||
|
|
|
@ -31,6 +31,8 @@ turn_on:
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
domain: fan
|
domain: fan
|
||||||
|
supported_features:
|
||||||
|
- fan.FanEntityFeature.TURN_ON
|
||||||
fields:
|
fields:
|
||||||
percentage:
|
percentage:
|
||||||
filter:
|
filter:
|
||||||
|
@ -53,6 +55,8 @@ turn_off:
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
domain: fan
|
domain: fan
|
||||||
|
supported_features:
|
||||||
|
- fan.FanEntityFeature.TURN_OFF
|
||||||
|
|
||||||
oscillate:
|
oscillate:
|
||||||
target:
|
target:
|
||||||
|
|
|
@ -65,7 +65,13 @@ async def async_setup_entry(
|
||||||
class Fan(CoordinatorEntity[FjaraskupanCoordinator], FanEntity):
|
class Fan(CoordinatorEntity[FjaraskupanCoordinator], FanEntity):
|
||||||
"""Fan entity."""
|
"""Fan entity."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ class FreedomproFan(CoordinatorEntity[FreedomproDataUpdateCoordinator], FanEntit
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
_attr_is_on = False
|
_attr_is_on = False
|
||||||
_attr_percentage = 0
|
_attr_percentage = 0
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -62,8 +63,11 @@ class FreedomproFan(CoordinatorEntity[FreedomproDataUpdateCoordinator], FanEntit
|
||||||
model=device["type"],
|
model=device["type"],
|
||||||
name=device["name"],
|
name=device["name"],
|
||||||
)
|
)
|
||||||
|
self._attr_supported_features = (
|
||||||
|
FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
if "rotationSpeed" in self._characteristics:
|
if "rotationSpeed" in self._characteristics:
|
||||||
self._attr_supported_features = FanEntityFeature.SET_SPEED
|
self._attr_supported_features |= FanEntityFeature.SET_SPEED
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool | None:
|
def is_on(self) -> bool | None:
|
||||||
|
|
|
@ -47,6 +47,8 @@ SUPPORTED_FLAGS = {
|
||||||
FanEntityFeature.SET_SPEED,
|
FanEntityFeature.SET_SPEED,
|
||||||
FanEntityFeature.DIRECTION,
|
FanEntityFeature.DIRECTION,
|
||||||
FanEntityFeature.OSCILLATE,
|
FanEntityFeature.OSCILLATE,
|
||||||
|
FanEntityFeature.TURN_OFF,
|
||||||
|
FanEntityFeature.TURN_ON,
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_NAME = "Fan Group"
|
DEFAULT_NAME = "Fan Group"
|
||||||
|
@ -107,6 +109,7 @@ class FanGroup(GroupEntity, FanEntity):
|
||||||
"""Representation of a FanGroup."""
|
"""Representation of a FanGroup."""
|
||||||
|
|
||||||
_attr_available: bool = False
|
_attr_available: bool = False
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> None:
|
def __init__(self, unique_id: str | None, name: str, entities: list[str]) -> None:
|
||||||
"""Initialize a FanGroup entity."""
|
"""Initialize a FanGroup entity."""
|
||||||
|
@ -200,11 +203,15 @@ class FanGroup(GroupEntity, FanEntity):
|
||||||
if percentage is not None:
|
if percentage is not None:
|
||||||
await self.async_set_percentage(percentage)
|
await self.async_set_percentage(percentage)
|
||||||
return
|
return
|
||||||
await self._async_call_all_entities(SERVICE_TURN_ON)
|
await self._async_call_supported_entities(
|
||||||
|
SERVICE_TURN_ON, FanEntityFeature.TURN_ON, {}
|
||||||
|
)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the fans off."""
|
"""Turn the fans off."""
|
||||||
await self._async_call_all_entities(SERVICE_TURN_OFF)
|
await self._async_call_supported_entities(
|
||||||
|
SERVICE_TURN_OFF, FanEntityFeature.TURN_OFF, {}
|
||||||
|
)
|
||||||
|
|
||||||
async def _async_call_supported_entities(
|
async def _async_call_supported_entities(
|
||||||
self, service: str, support_flag: int, data: dict[str, Any]
|
self, service: str, support_flag: int, data: dict[str, Any]
|
||||||
|
|
|
@ -42,6 +42,7 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity):
|
||||||
# This must be set in subclasses to the name of a boolean characteristic
|
# This must be set in subclasses to the name of a boolean characteristic
|
||||||
# that controls whether the fan is on or off.
|
# that controls whether the fan is on or off.
|
||||||
on_characteristic: str
|
on_characteristic: str
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_reconfigure(self) -> None:
|
def _async_reconfigure(self) -> None:
|
||||||
|
@ -113,7 +114,7 @@ class BaseHomeKitFan(HomeKitEntity, FanEntity):
|
||||||
@cached_property
|
@cached_property
|
||||||
def supported_features(self) -> FanEntityFeature:
|
def supported_features(self) -> FanEntityFeature:
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
features = FanEntityFeature(0)
|
features = FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
|
||||||
if self.service.has(CharacteristicsTypes.ROTATION_DIRECTION):
|
if self.service.has(CharacteristicsTypes.ROTATION_DIRECTION):
|
||||||
features |= FanEntityFeature.DIRECTION
|
features |= FanEntityFeature.DIRECTION
|
||||||
|
|
|
@ -50,8 +50,13 @@ async def async_setup_entry(
|
||||||
class InsteonFanEntity(InsteonEntity, FanEntity):
|
class InsteonFanEntity(InsteonEntity, FanEntity):
|
||||||
"""An INSTEON fan entity."""
|
"""An INSTEON fan entity."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
_attr_speed_count = 3
|
_attr_speed_count = 3
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percentage(self) -> int | None:
|
def percentage(self) -> int | None:
|
||||||
|
|
|
@ -75,7 +75,12 @@ class IntellifireFan(IntellifireEntity, FanEntity):
|
||||||
"""Fan entity for the fireplace."""
|
"""Fan entity for the fireplace."""
|
||||||
|
|
||||||
entity_description: IntellifireFanEntityDescription
|
entity_description: IntellifireFanEntityDescription
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
|
|
|
@ -48,7 +48,12 @@ async def async_setup_entry(
|
||||||
class ISYFanEntity(ISYNodeEntity, FanEntity):
|
class ISYFanEntity(ISYNodeEntity, FanEntity):
|
||||||
"""Representation of an ISY fan device."""
|
"""Representation of an ISY fan device."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percentage(self) -> int | None:
|
def percentage(self) -> int | None:
|
||||||
|
|
|
@ -43,6 +43,7 @@ class KNXFan(KnxEntity, FanEntity):
|
||||||
"""Representation of a KNX fan."""
|
"""Representation of a KNX fan."""
|
||||||
|
|
||||||
_device: XknxFan
|
_device: XknxFan
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, xknx: XKNX, config: ConfigType) -> None:
|
def __init__(self, xknx: XKNX, config: ConfigType) -> None:
|
||||||
"""Initialize of KNX fan."""
|
"""Initialize of KNX fan."""
|
||||||
|
@ -79,7 +80,11 @@ class KNXFan(KnxEntity, FanEntity):
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> FanEntityFeature:
|
def supported_features(self) -> FanEntityFeature:
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
flags = FanEntityFeature.SET_SPEED
|
flags = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
)
|
||||||
|
|
||||||
if self._device.supports_oscillation:
|
if self._device.supports_oscillation:
|
||||||
flags |= FanEntityFeature.OSCILLATE
|
flags |= FanEntityFeature.OSCILLATE
|
||||||
|
|
|
@ -44,9 +44,14 @@ class LutronFan(LutronDevice, FanEntity):
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
_attr_speed_count = 3
|
_attr_speed_count = 3
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
_lutron_device: Output
|
_lutron_device: Output
|
||||||
_prev_percentage: int | None = None
|
_prev_percentage: int | None = None
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def set_percentage(self, percentage: int) -> None:
|
def set_percentage(self, percentage: int) -> None:
|
||||||
"""Set the speed of the fan, as a percentage."""
|
"""Set the speed of the fan, as a percentage."""
|
||||||
|
|
|
@ -40,8 +40,13 @@ async def async_setup_entry(
|
||||||
class LutronCasetaFan(LutronCasetaDeviceUpdatableEntity, FanEntity):
|
class LutronCasetaFan(LutronCasetaDeviceUpdatableEntity, FanEntity):
|
||||||
"""Representation of a Lutron Caseta fan. Including Fan Speed."""
|
"""Representation of a Lutron Caseta fan. Including Fan Speed."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
_attr_speed_count = len(ORDERED_NAMED_FAN_SPEEDS)
|
_attr_speed_count = len(ORDERED_NAMED_FAN_SPEEDS)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percentage(self) -> int | None:
|
def percentage(self) -> int | None:
|
||||||
|
|
|
@ -57,6 +57,7 @@ class MatterFan(MatterEntity, FanEntity):
|
||||||
"""Representation of a Matter fan."""
|
"""Representation of a Matter fan."""
|
||||||
|
|
||||||
_last_known_preset_mode: str | None = None
|
_last_known_preset_mode: str | None = None
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
async def async_turn_on(
|
async def async_turn_on(
|
||||||
self,
|
self,
|
||||||
|
@ -294,6 +295,10 @@ class MatterFan(MatterEntity, FanEntity):
|
||||||
if feature_map & FanControlFeature.kAirflowDirection:
|
if feature_map & FanControlFeature.kAirflowDirection:
|
||||||
self._attr_supported_features |= FanEntityFeature.DIRECTION
|
self._attr_supported_features |= FanEntityFeature.DIRECTION
|
||||||
|
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Discovery schema(s) to map Matter Attributes to HA entities
|
# Discovery schema(s) to map Matter Attributes to HA entities
|
||||||
DISCOVERY_SCHEMAS = [
|
DISCOVERY_SCHEMAS = [
|
||||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components.fan import FanEntity
|
from homeassistant.components.fan import FanEntity, FanEntityFeature
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
@ -38,6 +38,18 @@ async def async_setup_platform(
|
||||||
class ModbusFan(BaseSwitch, FanEntity):
|
class ModbusFan(BaseSwitch, FanEntity):
|
||||||
"""Class representing a Modbus fan."""
|
"""Class representing a Modbus fan."""
|
||||||
|
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, hub: ModbusHub, config: dict[str, Any]
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the fan."""
|
||||||
|
super().__init__(hass, hub, config)
|
||||||
|
if self.command_on is not None and self._command_off is not None:
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
async def async_turn_on(
|
async def async_turn_on(
|
||||||
self,
|
self,
|
||||||
percentage: int | None = None,
|
percentage: int | None = None,
|
||||||
|
|
|
@ -70,8 +70,14 @@ class ModernFormsFanEntity(FanEntity, ModernFormsDeviceEntity):
|
||||||
|
|
||||||
SPEED_RANGE = (1, 6) # off is not included
|
SPEED_RANGE = (1, 6) # off is not included
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.DIRECTION | FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.DIRECTION
|
||||||
|
| FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
_attr_translation_key = "fan"
|
_attr_translation_key = "fan"
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, entry_id: str, coordinator: ModernFormsDataUpdateCoordinator
|
self, entry_id: str, coordinator: ModernFormsDataUpdateCoordinator
|
||||||
|
|
|
@ -224,6 +224,7 @@ class MqttFan(MqttEntity, FanEntity):
|
||||||
_optimistic_preset_mode: bool
|
_optimistic_preset_mode: bool
|
||||||
_payload: dict[str, Any]
|
_payload: dict[str, Any]
|
||||||
_speed_range: tuple[int, int]
|
_speed_range: tuple[int, int]
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def config_schema() -> VolSchemaType:
|
def config_schema() -> VolSchemaType:
|
||||||
|
@ -289,7 +290,9 @@ class MqttFan(MqttEntity, FanEntity):
|
||||||
optimistic or self._topic[CONF_PRESET_MODE_STATE_TOPIC] is None
|
optimistic or self._topic[CONF_PRESET_MODE_STATE_TOPIC] is None
|
||||||
)
|
)
|
||||||
|
|
||||||
self._attr_supported_features = FanEntityFeature(0)
|
self._attr_supported_features = (
|
||||||
|
FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
self._attr_supported_features |= (
|
self._attr_supported_features |= (
|
||||||
self._topic[CONF_OSCILLATION_COMMAND_TOPIC] is not None
|
self._topic[CONF_OSCILLATION_COMMAND_TOPIC] is not None
|
||||||
and FanEntityFeature.OSCILLATE
|
and FanEntityFeature.OSCILLATE
|
||||||
|
|
|
@ -51,6 +51,7 @@ class NetatmoFan(NetatmoModuleEntity, FanEntity):
|
||||||
_attr_configuration_url = CONF_URL_CONTROL
|
_attr_configuration_url = CONF_URL_CONTROL
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
device: NaModules.Fan
|
device: NaModules.Fan
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, netatmo_device: NetatmoDevice) -> None:
|
def __init__(self, netatmo_device: NetatmoDevice) -> None:
|
||||||
"""Initialize of Netatmo fan."""
|
"""Initialize of Netatmo fan."""
|
||||||
|
|
|
@ -49,7 +49,13 @@ async def async_setup_entry(
|
||||||
class RabbitAirFanEntity(RabbitAirBaseEntity, FanEntity):
|
class RabbitAirFanEntity(RabbitAirBaseEntity, FanEntity):
|
||||||
"""Fan control functions of the Rabbit Air air purifier."""
|
"""Fan control functions of the Rabbit Air air purifier."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.PRESET_MODE | FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -122,7 +122,12 @@ class RensonFan(RensonEntity, FanEntity):
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
_attr_translation_key = "fan"
|
_attr_translation_key = "fan"
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, api: RensonVentilation, coordinator: RensonCoordinator) -> None:
|
def __init__(self, api: RensonVentilation, coordinator: RensonCoordinator) -> None:
|
||||||
"""Initialize the Renson fan."""
|
"""Initialize the Renson fan."""
|
||||||
|
|
|
@ -70,6 +70,7 @@ class SmartThingsFan(SmartThingsEntity, FanEntity):
|
||||||
"""Define a SmartThings Fan."""
|
"""Define a SmartThings Fan."""
|
||||||
|
|
||||||
_attr_speed_count = int_states_in_range(SPEED_RANGE)
|
_attr_speed_count = int_states_in_range(SPEED_RANGE)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, device):
|
def __init__(self, device):
|
||||||
"""Init the class."""
|
"""Init the class."""
|
||||||
|
@ -77,7 +78,7 @@ class SmartThingsFan(SmartThingsEntity, FanEntity):
|
||||||
self._attr_supported_features = self._determine_features()
|
self._attr_supported_features = self._determine_features()
|
||||||
|
|
||||||
def _determine_features(self):
|
def _determine_features(self):
|
||||||
flags = FanEntityFeature(0)
|
flags = FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
|
||||||
if self._device.get_capability(Capability.fan_speed):
|
if self._device.get_capability(Capability.fan_speed):
|
||||||
flags |= FanEntityFeature.SET_SPEED
|
flags |= FanEntityFeature.SET_SPEED
|
||||||
|
|
|
@ -46,7 +46,12 @@ class SmartyFan(FanEntity):
|
||||||
|
|
||||||
_attr_icon = "mdi:air-conditioner"
|
_attr_icon = "mdi:air-conditioner"
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, name, smarty):
|
def __init__(self, name, smarty):
|
||||||
"""Initialize the entity."""
|
"""Initialize the entity."""
|
||||||
|
|
|
@ -75,10 +75,15 @@ class SnoozFan(FanEntity, RestoreEntity):
|
||||||
|
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
_is_on: bool | None = None
|
_is_on: bool | None = None
|
||||||
_percentage: int | None = None
|
_percentage: int | None = None
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, data: SnoozConfigurationData) -> None:
|
def __init__(self, data: SnoozConfigurationData) -> None:
|
||||||
"""Initialize a Snooz fan entity."""
|
"""Initialize a Snooz fan entity."""
|
||||||
|
|
|
@ -4,7 +4,11 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components.fan import DOMAIN as FAN_DOMAIN, FanEntity
|
from homeassistant.components.fan import (
|
||||||
|
DOMAIN as FAN_DOMAIN,
|
||||||
|
FanEntity,
|
||||||
|
FanEntityFeature,
|
||||||
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_ENTITY_ID
|
from homeassistant.const import CONF_ENTITY_ID
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
@ -41,6 +45,9 @@ async def async_setup_entry(
|
||||||
class FanSwitch(BaseToggleEntity, FanEntity):
|
class FanSwitch(BaseToggleEntity, FanEntity):
|
||||||
"""Represents a Switch as a Fan."""
|
"""Represents a Switch as a Fan."""
|
||||||
|
|
||||||
|
_attr_supported_features = FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool | None:
|
def is_on(self) -> bool | None:
|
||||||
"""Return true if the entity is on.
|
"""Return true if the entity is on.
|
||||||
|
|
|
@ -65,9 +65,14 @@ class TasmotaFan(
|
||||||
):
|
):
|
||||||
"""Representation of a Tasmota fan."""
|
"""Representation of a Tasmota fan."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
_fan_speed = tasmota_const.FAN_SPEED_MEDIUM
|
_fan_speed = tasmota_const.FAN_SPEED_MEDIUM
|
||||||
_tasmota_entity: tasmota_fan.TasmotaFan
|
_tasmota_entity: tasmota_fan.TasmotaFan
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, **kwds: Any) -> None:
|
def __init__(self, **kwds: Any) -> None:
|
||||||
"""Initialize the Tasmota fan."""
|
"""Initialize the Tasmota fan."""
|
||||||
|
|
|
@ -124,6 +124,7 @@ class TemplateFan(TemplateEntity, FanEntity):
|
||||||
"""A template fan component."""
|
"""A template fan component."""
|
||||||
|
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -195,6 +196,9 @@ class TemplateFan(TemplateEntity, FanEntity):
|
||||||
self._attr_supported_features |= FanEntityFeature.OSCILLATE
|
self._attr_supported_features |= FanEntityFeature.OSCILLATE
|
||||||
if self._direction_template:
|
if self._direction_template:
|
||||||
self._attr_supported_features |= FanEntityFeature.DIRECTION
|
self._attr_supported_features |= FanEntityFeature.DIRECTION
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
self._attr_assumed_state = self._template is None
|
self._attr_assumed_state = self._template is None
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components.fan import FanEntity
|
from homeassistant.components.fan import FanEntity, FanEntityFeature
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
@ -27,6 +27,8 @@ class ToloFan(ToloSaunaCoordinatorEntity, FanEntity):
|
||||||
"""Sauna fan control."""
|
"""Sauna fan control."""
|
||||||
|
|
||||||
_attr_translation_key = "fan"
|
_attr_translation_key = "fan"
|
||||||
|
_attr_supported_features = FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, coordinator: ToloSaunaUpdateCoordinator, entry: ConfigEntry
|
self, coordinator: ToloSaunaUpdateCoordinator, entry: ConfigEntry
|
||||||
|
|
|
@ -59,7 +59,12 @@ class TPLinkFanEntity(CoordinatedTPLinkEntity, FanEntity):
|
||||||
"""Representation of a fan for a TPLink Fan device."""
|
"""Representation of a fan for a TPLink Fan device."""
|
||||||
|
|
||||||
_attr_speed_count = int_states_in_range(SPEED_RANGE)
|
_attr_speed_count = int_states_in_range(SPEED_RANGE)
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -55,7 +55,12 @@ class TradfriAirPurifierFan(TradfriBaseEntity, FanEntity):
|
||||||
"""The platform class required by Home Assistant."""
|
"""The platform class required by Home Assistant."""
|
||||||
|
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
_attr_supported_features = FanEntityFeature.PRESET_MODE | FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
)
|
||||||
_attr_preset_modes = [ATTR_AUTO]
|
_attr_preset_modes = [ATTR_AUTO]
|
||||||
# These are the steps:
|
# These are the steps:
|
||||||
# 0 = Off
|
# 0 = Off
|
||||||
|
@ -64,6 +69,7 @@ class TradfriAirPurifierFan(TradfriBaseEntity, FanEntity):
|
||||||
# ... with step size 1
|
# ... with step size 1
|
||||||
# 50 = Max
|
# 50 = Max
|
||||||
_attr_speed_count = ATTR_MAX_FAN_STEPS
|
_attr_speed_count = ATTR_MAX_FAN_STEPS
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -66,6 +66,7 @@ class TuyaFanEntity(TuyaEntity, FanEntity):
|
||||||
_speeds: EnumTypeData | None = None
|
_speeds: EnumTypeData | None = None
|
||||||
_switch: DPCode | None = None
|
_switch: DPCode | None = None
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -116,6 +117,10 @@ class TuyaFanEntity(TuyaEntity, FanEntity):
|
||||||
):
|
):
|
||||||
self._direction = enum_type
|
self._direction = enum_type
|
||||||
self._attr_supported_features |= FanEntityFeature.DIRECTION
|
self._attr_supported_features |= FanEntityFeature.DIRECTION
|
||||||
|
if self._switch is not None:
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
FanEntityFeature.TURN_ON | FanEntityFeature.TURN_OFF
|
||||||
|
)
|
||||||
|
|
||||||
def set_preset_mode(self, preset_mode: str) -> None:
|
def set_preset_mode(self, preset_mode: str) -> None:
|
||||||
"""Set the preset mode of the fan."""
|
"""Set the preset mode of the fan."""
|
||||||
|
|
|
@ -77,7 +77,13 @@ class ValloxFanEntity(ValloxEntity, FanEntity):
|
||||||
"""Representation of the fan."""
|
"""Representation of the fan."""
|
||||||
|
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
_attr_supported_features = FanEntityFeature.PRESET_MODE | FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -84,8 +84,14 @@ def _setup_entities(devices, async_add_entities):
|
||||||
class VeSyncFanHA(VeSyncDevice, FanEntity):
|
class VeSyncFanHA(VeSyncDevice, FanEntity):
|
||||||
"""Representation of a VeSync fan."""
|
"""Representation of a VeSync fan."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, fan) -> None:
|
def __init__(self, fan) -> None:
|
||||||
"""Initialize the VeSync fan device."""
|
"""Initialize the VeSync fan device."""
|
||||||
|
|
|
@ -74,9 +74,14 @@ async def async_setup_entry(
|
||||||
class WemoHumidifier(WemoBinaryStateEntity, FanEntity):
|
class WemoHumidifier(WemoBinaryStateEntity, FanEntity):
|
||||||
"""Representation of a WeMo humidifier."""
|
"""Representation of a WeMo humidifier."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
wemo: Humidifier
|
wemo: Humidifier
|
||||||
_last_fan_on_mode: FanMode
|
_last_fan_on_mode: FanMode
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, coordinator: DeviceCoordinator) -> None:
|
def __init__(self, coordinator: DeviceCoordinator) -> None:
|
||||||
"""Initialize the WeMo switch."""
|
"""Initialize the WeMo switch."""
|
||||||
|
|
|
@ -57,7 +57,13 @@ class WiLightFan(WiLightDevice, FanEntity):
|
||||||
|
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
_attr_speed_count = len(ORDERED_NAMED_FAN_SPEEDS)
|
_attr_speed_count = len(ORDERED_NAMED_FAN_SPEEDS)
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.DIRECTION
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.DIRECTION
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, api_device: PyWiLightDevice, index: str, item_name: str) -> None:
|
def __init__(self, api_device: PyWiLightDevice, index: str, item_name: str) -> None:
|
||||||
"""Initialize the device."""
|
"""Initialize the device."""
|
||||||
|
|
|
@ -294,6 +294,7 @@ class XiaomiGenericDevice(XiaomiCoordinatedMiioEntity, FanEntity):
|
||||||
"""Representation of a generic Xiaomi device."""
|
"""Representation of a generic Xiaomi device."""
|
||||||
|
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(self, device, entry, unique_id, coordinator):
|
def __init__(self, device, entry, unique_id, coordinator):
|
||||||
"""Initialize the generic Xiaomi device."""
|
"""Initialize the generic Xiaomi device."""
|
||||||
|
@ -479,6 +480,9 @@ class XiaomiAirPurifier(XiaomiGenericAirPurifier):
|
||||||
self._preset_modes = PRESET_MODES_AIRPURIFIER
|
self._preset_modes = PRESET_MODES_AIRPURIFIER
|
||||||
self._attr_supported_features = FanEntityFeature.PRESET_MODE
|
self._attr_supported_features = FanEntityFeature.PRESET_MODE
|
||||||
self._speed_count = 1
|
self._speed_count = 1
|
||||||
|
self._attr_supported_features |= (
|
||||||
|
FanEntityFeature.TURN_OFF | FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
self._state = self.coordinator.data.is_on
|
self._state = self.coordinator.data.is_on
|
||||||
self._state_attrs.update(
|
self._state_attrs.update(
|
||||||
|
@ -609,7 +613,11 @@ class XiaomiAirPurifierMB4(XiaomiGenericAirPurifier):
|
||||||
|
|
||||||
self._device_features = FEATURE_FLAGS_AIRPURIFIER_3C
|
self._device_features = FEATURE_FLAGS_AIRPURIFIER_3C
|
||||||
self._preset_modes = PRESET_MODES_AIRPURIFIER_3C
|
self._preset_modes = PRESET_MODES_AIRPURIFIER_3C
|
||||||
self._attr_supported_features = FanEntityFeature.PRESET_MODE
|
self._attr_supported_features = (
|
||||||
|
FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
self._state = self.coordinator.data.is_on
|
self._state = self.coordinator.data.is_on
|
||||||
self._mode = self.coordinator.data.mode.value
|
self._mode = self.coordinator.data.mode.value
|
||||||
|
@ -663,7 +671,10 @@ class XiaomiAirFresh(XiaomiGenericAirPurifier):
|
||||||
self._speed_count = 4
|
self._speed_count = 4
|
||||||
self._preset_modes = PRESET_MODES_AIRFRESH
|
self._preset_modes = PRESET_MODES_AIRFRESH
|
||||||
self._attr_supported_features = (
|
self._attr_supported_features = (
|
||||||
FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
|
|
||||||
self._state = self.coordinator.data.is_on
|
self._state = self.coordinator.data.is_on
|
||||||
|
@ -756,7 +767,10 @@ class XiaomiAirFreshA1(XiaomiGenericAirPurifier):
|
||||||
self._device_features = FEATURE_FLAGS_AIRFRESH_A1
|
self._device_features = FEATURE_FLAGS_AIRFRESH_A1
|
||||||
self._preset_modes = PRESET_MODES_AIRFRESH_A1
|
self._preset_modes = PRESET_MODES_AIRFRESH_A1
|
||||||
self._attr_supported_features = (
|
self._attr_supported_features = (
|
||||||
FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
|
|
||||||
self._state = self.coordinator.data.is_on
|
self._state = self.coordinator.data.is_on
|
||||||
|
@ -851,6 +865,8 @@ class XiaomiGenericFan(XiaomiGenericDevice):
|
||||||
FanEntityFeature.SET_SPEED
|
FanEntityFeature.SET_SPEED
|
||||||
| FanEntityFeature.OSCILLATE
|
| FanEntityFeature.OSCILLATE
|
||||||
| FanEntityFeature.PRESET_MODE
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
if self._model != MODEL_FAN_1C:
|
if self._model != MODEL_FAN_1C:
|
||||||
self._attr_supported_features |= FanEntityFeature.DIRECTION
|
self._attr_supported_features |= FanEntityFeature.DIRECTION
|
||||||
|
|
|
@ -43,8 +43,13 @@ async def async_setup_entry(
|
||||||
class ZhaFan(FanEntity, ZHAEntity):
|
class ZhaFan(FanEntity, ZHAEntity):
|
||||||
"""Representation of a ZHA fan."""
|
"""Representation of a ZHA fan."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
_attr_translation_key: str = "fan"
|
_attr_translation_key: str = "fan"
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preset_mode(self) -> str | None:
|
def preset_mode(self) -> str | None:
|
||||||
|
|
|
@ -78,7 +78,12 @@ async def async_setup_entry(
|
||||||
class ZwaveFan(ZWaveBaseEntity, FanEntity):
|
class ZwaveFan(ZWaveBaseEntity, FanEntity):
|
||||||
"""Representation of a Z-Wave fan."""
|
"""Representation of a Z-Wave fan."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
|
self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
|
||||||
|
@ -249,7 +254,11 @@ class ValueMappingZwaveFan(ZwaveFan):
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> FanEntityFeature:
|
def supported_features(self) -> FanEntityFeature:
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
flags = FanEntityFeature.SET_SPEED
|
flags = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
if self.has_fan_value_mapping and self.fan_value_mapping.presets:
|
if self.has_fan_value_mapping and self.fan_value_mapping.presets:
|
||||||
flags |= FanEntityFeature.PRESET_MODE
|
flags |= FanEntityFeature.PRESET_MODE
|
||||||
|
|
||||||
|
@ -382,7 +391,13 @@ class ZwaveThermostatFan(ZWaveBaseEntity, FanEntity):
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> FanEntityFeature:
|
def supported_features(self) -> FanEntityFeature:
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
return FanEntityFeature.PRESET_MODE
|
if not self._fan_off:
|
||||||
|
return FanEntityFeature.PRESET_MODE
|
||||||
|
return (
|
||||||
|
FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fan_state(self) -> str | None:
|
def fan_state(self) -> str | None:
|
||||||
|
|
|
@ -44,7 +44,12 @@ async def async_setup_entry(
|
||||||
class ZWaveMeFan(ZWaveMeEntity, FanEntity):
|
class ZWaveMeFan(ZWaveMeEntity, FanEntity):
|
||||||
"""Representation of a ZWaveMe Fan."""
|
"""Representation of a ZWaveMe Fan."""
|
||||||
|
|
||||||
_attr_supported_features = FanEntityFeature.SET_SPEED
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percentage(self) -> int:
|
def percentage(self) -> int:
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
'original_name': 'Pump 1',
|
'original_name': 'Pump 1',
|
||||||
'platform': 'balboa',
|
'platform': 'balboa',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
'translation_key': 'pump',
|
'translation_key': 'pump',
|
||||||
'unique_id': 'FakeSpa-Pump 1-c0ffee',
|
'unique_id': 'FakeSpa-Pump 1-c0ffee',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
'percentage_step': 50.0,
|
'percentage_step': 50.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.fakespa_pump_1',
|
'entity_id': 'fan.fakespa_pump_1',
|
||||||
|
|
23
tests/components/fan/conftest.py
Normal file
23
tests/components/fan/conftest.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
"""Fixtures for Fan platform tests."""
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigFlow
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from tests.common import mock_config_flow, mock_platform
|
||||||
|
|
||||||
|
|
||||||
|
class MockFlow(ConfigFlow):
|
||||||
|
"""Test flow."""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def config_flow_fixture(hass: HomeAssistant) -> Generator[None]:
|
||||||
|
"""Mock config flow."""
|
||||||
|
mock_platform(hass, "test.config_flow")
|
||||||
|
|
||||||
|
with mock_config_flow("test", MockFlow):
|
||||||
|
yield
|
|
@ -1,5 +1,7 @@
|
||||||
"""Tests for fan platforms."""
|
"""Tests for fan platforms."""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import fan
|
from homeassistant.components import fan
|
||||||
|
@ -12,15 +14,23 @@ from homeassistant.components.fan import (
|
||||||
FanEntityFeature,
|
FanEntityFeature,
|
||||||
NotValidPresetModeError,
|
NotValidPresetModeError,
|
||||||
)
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import SERVICE_TURN_OFF, SERVICE_TURN_ON
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
import homeassistant.helpers.entity_registry as er
|
import homeassistant.helpers.entity_registry as er
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from .common import MockFan
|
from .common import MockFan
|
||||||
|
|
||||||
from tests.common import (
|
from tests.common import (
|
||||||
|
MockConfigEntry,
|
||||||
|
MockModule,
|
||||||
|
MockPlatform,
|
||||||
help_test_all,
|
help_test_all,
|
||||||
import_and_test_deprecated_constant_enum,
|
import_and_test_deprecated_constant_enum,
|
||||||
|
mock_integration,
|
||||||
|
mock_platform,
|
||||||
setup_test_component_platform,
|
setup_test_component_platform,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -167,7 +177,10 @@ def test_deprecated_constants(
|
||||||
enum: fan.FanEntityFeature,
|
enum: fan.FanEntityFeature,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test deprecated constants."""
|
"""Test deprecated constants."""
|
||||||
import_and_test_deprecated_constant_enum(caplog, fan, enum, "SUPPORT_", "2025.1")
|
if not FanEntityFeature.TURN_OFF and not FanEntityFeature.TURN_ON:
|
||||||
|
import_and_test_deprecated_constant_enum(
|
||||||
|
caplog, fan, enum, "SUPPORT_", "2025.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) -> None:
|
def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) -> None:
|
||||||
|
@ -180,11 +193,288 @@ def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) ->
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
entity = MockFan()
|
entity = MockFan()
|
||||||
assert entity.supported_features_compat is FanEntityFeature(1)
|
assert entity.supported_features is FanEntityFeature(1)
|
||||||
assert "MockFan" in caplog.text
|
assert "MockFan" in caplog.text
|
||||||
assert "is using deprecated supported features values" in caplog.text
|
assert "is using deprecated supported features values" in caplog.text
|
||||||
assert "Instead it should use" in caplog.text
|
assert "Instead it should use" in caplog.text
|
||||||
assert "FanEntityFeature.SET_SPEED" in caplog.text
|
assert "FanEntityFeature.SET_SPEED" in caplog.text
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert entity.supported_features_compat is FanEntityFeature(1)
|
assert entity.supported_features is FanEntityFeature(1)
|
||||||
assert "is using deprecated supported features values" not in caplog.text
|
assert "is using deprecated supported features values" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_warning_not_implemented_turn_on_off_feature(
|
||||||
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, config_flow_fixture: None
|
||||||
|
) -> None:
|
||||||
|
"""Test adding feature flag and warn if missing when methods are set."""
|
||||||
|
|
||||||
|
called = []
|
||||||
|
|
||||||
|
class MockFanEntityTest(MockFan):
|
||||||
|
"""Mock Fan device."""
|
||||||
|
|
||||||
|
def turn_on(
|
||||||
|
self,
|
||||||
|
percentage: int | None = None,
|
||||||
|
preset_mode: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Turn on."""
|
||||||
|
called.append("turn_on")
|
||||||
|
|
||||||
|
def turn_off(self) -> None:
|
||||||
|
"""Turn off."""
|
||||||
|
called.append("turn_off")
|
||||||
|
|
||||||
|
async def async_setup_entry_init(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry
|
||||||
|
) -> bool:
|
||||||
|
"""Set up test config entry."""
|
||||||
|
await hass.config_entries.async_forward_entry_setups(config_entry, [DOMAIN])
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def async_setup_entry_fan_platform(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up test fan platform via config entry."""
|
||||||
|
async_add_entities([MockFanEntityTest(name="test", entity_id="fan.test")])
|
||||||
|
|
||||||
|
mock_integration(
|
||||||
|
hass,
|
||||||
|
MockModule(
|
||||||
|
"test",
|
||||||
|
async_setup_entry=async_setup_entry_init,
|
||||||
|
),
|
||||||
|
built_in=False,
|
||||||
|
)
|
||||||
|
mock_platform(
|
||||||
|
hass,
|
||||||
|
"test.fan",
|
||||||
|
MockPlatform(async_setup_entry=async_setup_entry_fan_platform),
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
MockFanEntityTest, "__module__", "tests.custom_components.fan.test_init"
|
||||||
|
):
|
||||||
|
config_entry = MockConfigEntry(domain="test")
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("fan.test")
|
||||||
|
assert state is not None
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Entity fan.test (<class 'tests.custom_components.fan.test_init.test_warning_not_implemented_turn_on_off_feature.<locals>.MockFanEntityTest'>) "
|
||||||
|
"does not set FanEntityFeature.TURN_OFF but implements the turn_off method. Please report it to the author of the 'test' custom integration"
|
||||||
|
in caplog.text
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
"Entity fan.test (<class 'tests.custom_components.fan.test_init.test_warning_not_implemented_turn_on_off_feature.<locals>.MockFanEntityTest'>) "
|
||||||
|
"does not set FanEntityFeature.TURN_ON but implements the turn_on method. Please report it to the author of the 'test' custom integration"
|
||||||
|
in caplog.text
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{
|
||||||
|
"entity_id": "fan.test",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{
|
||||||
|
"entity_id": "fan.test",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(called) == 2
|
||||||
|
assert "turn_on" in called
|
||||||
|
assert "turn_off" in called
|
||||||
|
|
||||||
|
|
||||||
|
async def test_no_warning_implemented_turn_on_off_feature(
|
||||||
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, config_flow_fixture: None
|
||||||
|
) -> None:
|
||||||
|
"""Test no warning when feature flags are set."""
|
||||||
|
|
||||||
|
class MockFanEntityTest(MockFan):
|
||||||
|
"""Mock Fan device."""
|
||||||
|
|
||||||
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.DIRECTION
|
||||||
|
| FanEntityFeature.OSCILLATE
|
||||||
|
| FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_setup_entry_init(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry
|
||||||
|
) -> bool:
|
||||||
|
"""Set up test config entry."""
|
||||||
|
await hass.config_entries.async_forward_entry_setups(config_entry, [DOMAIN])
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def async_setup_entry_fan_platform(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up test fan platform via config entry."""
|
||||||
|
async_add_entities([MockFanEntityTest(name="test", entity_id="fan.test")])
|
||||||
|
|
||||||
|
mock_integration(
|
||||||
|
hass,
|
||||||
|
MockModule(
|
||||||
|
"test",
|
||||||
|
async_setup_entry=async_setup_entry_init,
|
||||||
|
),
|
||||||
|
built_in=False,
|
||||||
|
)
|
||||||
|
mock_platform(
|
||||||
|
hass,
|
||||||
|
"test.fan",
|
||||||
|
MockPlatform(async_setup_entry=async_setup_entry_fan_platform),
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
MockFanEntityTest, "__module__", "tests.custom_components.fan.test_init"
|
||||||
|
):
|
||||||
|
config_entry = MockConfigEntry(domain="test")
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("fan.test")
|
||||||
|
assert state is not None
|
||||||
|
|
||||||
|
assert "does not set FanEntityFeature.TURN_OFF" not in caplog.text
|
||||||
|
assert "does not set FanEntityFeature.TURN_ON" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_no_warning_integration_has_migrated(
|
||||||
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, config_flow_fixture: None
|
||||||
|
) -> None:
|
||||||
|
"""Test no warning when integration migrated using `_enable_turn_on_off_backwards_compatibility`."""
|
||||||
|
|
||||||
|
class MockFanEntityTest(MockFan):
|
||||||
|
"""Mock Fan device."""
|
||||||
|
|
||||||
|
_enable_turn_on_off_backwards_compatibility = False
|
||||||
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.DIRECTION
|
||||||
|
| FanEntityFeature.OSCILLATE
|
||||||
|
| FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_setup_entry_init(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry
|
||||||
|
) -> bool:
|
||||||
|
"""Set up test config entry."""
|
||||||
|
await hass.config_entries.async_forward_entry_setups(config_entry, [DOMAIN])
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def async_setup_entry_fan_platform(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up test fan platform via config entry."""
|
||||||
|
async_add_entities([MockFanEntityTest(name="test", entity_id="fan.test")])
|
||||||
|
|
||||||
|
mock_integration(
|
||||||
|
hass,
|
||||||
|
MockModule(
|
||||||
|
"test",
|
||||||
|
async_setup_entry=async_setup_entry_init,
|
||||||
|
),
|
||||||
|
built_in=False,
|
||||||
|
)
|
||||||
|
mock_platform(
|
||||||
|
hass,
|
||||||
|
"test.fan",
|
||||||
|
MockPlatform(async_setup_entry=async_setup_entry_fan_platform),
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
MockFanEntityTest, "__module__", "tests.custom_components.fan.test_init"
|
||||||
|
):
|
||||||
|
config_entry = MockConfigEntry(domain="test")
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("fan.test")
|
||||||
|
assert state is not None
|
||||||
|
|
||||||
|
assert "does not set FanEntityFeature.TURN_OFF" not in caplog.text
|
||||||
|
assert "does not set FanEntityFeature.TURN_ON" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
async def test_no_warning_integration_implement_feature_flags(
|
||||||
|
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, config_flow_fixture: None
|
||||||
|
) -> None:
|
||||||
|
"""Test no warning when integration uses the correct feature flags."""
|
||||||
|
|
||||||
|
class MockFanEntityTest(MockFan):
|
||||||
|
"""Mock Fan device."""
|
||||||
|
|
||||||
|
_attr_supported_features = (
|
||||||
|
FanEntityFeature.DIRECTION
|
||||||
|
| FanEntityFeature.OSCILLATE
|
||||||
|
| FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_setup_entry_init(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry
|
||||||
|
) -> bool:
|
||||||
|
"""Set up test config entry."""
|
||||||
|
await hass.config_entries.async_forward_entry_setups(config_entry, [DOMAIN])
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def async_setup_entry_fan_platform(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up test fan platform via config entry."""
|
||||||
|
async_add_entities([MockFanEntityTest(name="test", entity_id="fan.test")])
|
||||||
|
|
||||||
|
mock_integration(
|
||||||
|
hass,
|
||||||
|
MockModule(
|
||||||
|
"test",
|
||||||
|
async_setup_entry=async_setup_entry_init,
|
||||||
|
),
|
||||||
|
built_in=False,
|
||||||
|
)
|
||||||
|
mock_platform(
|
||||||
|
hass,
|
||||||
|
"test.fan",
|
||||||
|
MockPlatform(async_setup_entry=async_setup_entry_fan_platform),
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
MockFanEntityTest, "__module__", "tests.custom_components.fan.test_init"
|
||||||
|
):
|
||||||
|
config_entry = MockConfigEntry(domain="test")
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("fan.test")
|
||||||
|
assert state is not None
|
||||||
|
|
||||||
|
assert "does not set FanEntityFeature.TURN_OFF" not in caplog.text
|
||||||
|
assert "does not set FanEntityFeature.TURN_ON" not in caplog.text
|
||||||
|
|
|
@ -103,7 +103,7 @@
|
||||||
'original_name': 'Airversa AP2 1808 AirPurifier',
|
'original_name': 'Airversa AP2 1808 AirPurifier',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_1_32832',
|
'unique_id': '00:00:00:00:00:00_1_32832',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
'percentage_step': 20.0,
|
'percentage_step': 20.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.airversa_ap2_1808_airpurifier',
|
'entity_id': 'fan.airversa_ap2_1808_airpurifier',
|
||||||
'state': 'off',
|
'state': 'off',
|
||||||
|
@ -6869,7 +6869,7 @@
|
||||||
'original_name': 'HAA-C718B3',
|
'original_name': 'HAA-C718B3',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_1_8',
|
'unique_id': '00:00:00:00:00:00_1_8',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -6881,7 +6881,7 @@
|
||||||
'percentage_step': 33.333333333333336,
|
'percentage_step': 33.333333333333336,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.haa_c718b3',
|
'entity_id': 'fan.haa_c718b3',
|
||||||
'state': 'on',
|
'state': 'on',
|
||||||
|
@ -7837,7 +7837,7 @@
|
||||||
'original_name': 'Ceiling Fan',
|
'original_name': 'Ceiling Fan',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_766313939_8',
|
'unique_id': '00:00:00:00:00:00_766313939_8',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -7849,7 +7849,7 @@
|
||||||
'percentage_step': 1.0,
|
'percentage_step': 1.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.ceiling_fan',
|
'entity_id': 'fan.ceiling_fan',
|
||||||
'state': 'off',
|
'state': 'off',
|
||||||
|
@ -8034,7 +8034,7 @@
|
||||||
'original_name': 'Living Room Fan',
|
'original_name': 'Living Room Fan',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 5>,
|
'supported_features': <FanEntityFeature: 53>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_1256851357_8',
|
'unique_id': '00:00:00:00:00:00_1256851357_8',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -8047,7 +8047,7 @@
|
||||||
'percentage_step': 1.0,
|
'percentage_step': 1.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 5>,
|
'supported_features': <FanEntityFeature: 53>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.living_room_fan',
|
'entity_id': 'fan.living_room_fan',
|
||||||
'state': 'off',
|
'state': 'off',
|
||||||
|
@ -8223,7 +8223,7 @@
|
||||||
'original_name': '89 Living Room',
|
'original_name': '89 Living Room',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 3>,
|
'supported_features': <FanEntityFeature: 51>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_1233851541_175',
|
'unique_id': '00:00:00:00:00:00_1233851541_175',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -8236,7 +8236,7 @@
|
||||||
'percentage_step': 33.333333333333336,
|
'percentage_step': 33.333333333333336,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 3>,
|
'supported_features': <FanEntityFeature: 51>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.89_living_room',
|
'entity_id': 'fan.89_living_room',
|
||||||
'state': 'on',
|
'state': 'on',
|
||||||
|
@ -9219,7 +9219,7 @@
|
||||||
'original_name': 'Ceiling Fan',
|
'original_name': 'Ceiling Fan',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_766313939_8',
|
'unique_id': '00:00:00:00:00:00_766313939_8',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -9231,7 +9231,7 @@
|
||||||
'percentage_step': 1.0,
|
'percentage_step': 1.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.ceiling_fan',
|
'entity_id': 'fan.ceiling_fan',
|
||||||
'state': 'off',
|
'state': 'off',
|
||||||
|
@ -9416,7 +9416,7 @@
|
||||||
'original_name': 'Living Room Fan',
|
'original_name': 'Living Room Fan',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 7>,
|
'supported_features': <FanEntityFeature: 55>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_1256851357_8',
|
'unique_id': '00:00:00:00:00:00_1256851357_8',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -9430,7 +9430,7 @@
|
||||||
'percentage_step': 1.0,
|
'percentage_step': 1.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 7>,
|
'supported_features': <FanEntityFeature: 55>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.living_room_fan',
|
'entity_id': 'fan.living_room_fan',
|
||||||
'state': 'off',
|
'state': 'off',
|
||||||
|
@ -9619,7 +9619,7 @@
|
||||||
'original_name': 'Living Room Fan',
|
'original_name': 'Living Room Fan',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 7>,
|
'supported_features': <FanEntityFeature: 55>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_1256851357_8',
|
'unique_id': '00:00:00:00:00:00_1256851357_8',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -9633,7 +9633,7 @@
|
||||||
'percentage_step': 1.0,
|
'percentage_step': 1.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 7>,
|
'supported_features': <FanEntityFeature: 55>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.living_room_fan',
|
'entity_id': 'fan.living_room_fan',
|
||||||
'state': 'off',
|
'state': 'off',
|
||||||
|
@ -9818,7 +9818,7 @@
|
||||||
'original_name': '89 Living Room',
|
'original_name': '89 Living Room',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 3>,
|
'supported_features': <FanEntityFeature: 51>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_1233851541_175',
|
'unique_id': '00:00:00:00:00:00_1233851541_175',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -9831,7 +9831,7 @@
|
||||||
'percentage_step': 33.333333333333336,
|
'percentage_step': 33.333333333333336,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 3>,
|
'supported_features': <FanEntityFeature: 51>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.89_living_room',
|
'entity_id': 'fan.89_living_room',
|
||||||
'state': 'on',
|
'state': 'on',
|
||||||
|
@ -14233,7 +14233,7 @@
|
||||||
'original_name': 'Caséta® Wireless Fan Speed Control',
|
'original_name': 'Caséta® Wireless Fan Speed Control',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_21474836482_2',
|
'unique_id': '00:00:00:00:00:00_21474836482_2',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -14245,7 +14245,7 @@
|
||||||
'percentage_step': 25.0,
|
'percentage_step': 25.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.caseta_r_wireless_fan_speed_control',
|
'entity_id': 'fan.caseta_r_wireless_fan_speed_control',
|
||||||
'state': 'off',
|
'state': 'off',
|
||||||
|
@ -17881,7 +17881,7 @@
|
||||||
'original_name': 'SIMPLEconnect Fan-06F674 Hunter Fan',
|
'original_name': 'SIMPLEconnect Fan-06F674 Hunter Fan',
|
||||||
'platform': 'homekit_controller',
|
'platform': 'homekit_controller',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 5>,
|
'supported_features': <FanEntityFeature: 53>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '00:00:00:00:00:00_1_8',
|
'unique_id': '00:00:00:00:00:00_1_8',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -17894,7 +17894,7 @@
|
||||||
'percentage_step': 25.0,
|
'percentage_step': 25.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 5>,
|
'supported_features': <FanEntityFeature: 53>,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.simpleconnect_fan_06f674_hunter_fan',
|
'entity_id': 'fan.simpleconnect_fan_06f674_hunter_fan',
|
||||||
'state': 'off',
|
'state': 'off',
|
||||||
|
|
|
@ -29,14 +29,22 @@ async def test_fan_add_feature_at_runtime(
|
||||||
fan_state = hass.states.get("fan.living_room_fan")
|
fan_state = hass.states.get("fan.living_room_fan")
|
||||||
assert (
|
assert (
|
||||||
fan_state.attributes[ATTR_SUPPORTED_FEATURES]
|
fan_state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
is FanEntityFeature.SET_SPEED | FanEntityFeature.DIRECTION
|
is FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.DIRECTION
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
|
|
||||||
fan = entity_registry.async_get("fan.ceiling_fan")
|
fan = entity_registry.async_get("fan.ceiling_fan")
|
||||||
assert fan.unique_id == "00:00:00:00:00:00_766313939_8"
|
assert fan.unique_id == "00:00:00:00:00:00_766313939_8"
|
||||||
|
|
||||||
fan_state = hass.states.get("fan.ceiling_fan")
|
fan_state = hass.states.get("fan.ceiling_fan")
|
||||||
assert fan_state.attributes[ATTR_SUPPORTED_FEATURES] is FanEntityFeature.SET_SPEED
|
assert (
|
||||||
|
fan_state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
|
is FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
# Now change the config to add oscillation
|
# Now change the config to add oscillation
|
||||||
accessories = await setup_accessories_from_file(
|
accessories = await setup_accessories_from_file(
|
||||||
|
@ -50,9 +58,16 @@ async def test_fan_add_feature_at_runtime(
|
||||||
is FanEntityFeature.SET_SPEED
|
is FanEntityFeature.SET_SPEED
|
||||||
| FanEntityFeature.DIRECTION
|
| FanEntityFeature.DIRECTION
|
||||||
| FanEntityFeature.OSCILLATE
|
| FanEntityFeature.OSCILLATE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
fan_state = hass.states.get("fan.ceiling_fan")
|
fan_state = hass.states.get("fan.ceiling_fan")
|
||||||
assert fan_state.attributes[ATTR_SUPPORTED_FEATURES] is FanEntityFeature.SET_SPEED
|
assert (
|
||||||
|
fan_state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
|
is FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_fan_remove_feature_at_runtime(
|
async def test_fan_remove_feature_at_runtime(
|
||||||
|
@ -75,13 +90,20 @@ async def test_fan_remove_feature_at_runtime(
|
||||||
is FanEntityFeature.SET_SPEED
|
is FanEntityFeature.SET_SPEED
|
||||||
| FanEntityFeature.DIRECTION
|
| FanEntityFeature.DIRECTION
|
||||||
| FanEntityFeature.OSCILLATE
|
| FanEntityFeature.OSCILLATE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
|
|
||||||
fan = entity_registry.async_get("fan.ceiling_fan")
|
fan = entity_registry.async_get("fan.ceiling_fan")
|
||||||
assert fan.unique_id == "00:00:00:00:00:00_766313939_8"
|
assert fan.unique_id == "00:00:00:00:00:00_766313939_8"
|
||||||
|
|
||||||
fan_state = hass.states.get("fan.ceiling_fan")
|
fan_state = hass.states.get("fan.ceiling_fan")
|
||||||
assert fan_state.attributes[ATTR_SUPPORTED_FEATURES] is FanEntityFeature.SET_SPEED
|
assert (
|
||||||
|
fan_state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
|
is FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
# Now change the config to add oscillation
|
# Now change the config to add oscillation
|
||||||
accessories = await setup_accessories_from_file(
|
accessories = await setup_accessories_from_file(
|
||||||
|
@ -92,10 +114,18 @@ async def test_fan_remove_feature_at_runtime(
|
||||||
fan_state = hass.states.get("fan.living_room_fan")
|
fan_state = hass.states.get("fan.living_room_fan")
|
||||||
assert (
|
assert (
|
||||||
fan_state.attributes[ATTR_SUPPORTED_FEATURES]
|
fan_state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
is FanEntityFeature.SET_SPEED | FanEntityFeature.DIRECTION
|
is FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.DIRECTION
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
fan_state = hass.states.get("fan.ceiling_fan")
|
fan_state = hass.states.get("fan.ceiling_fan")
|
||||||
assert fan_state.attributes[ATTR_SUPPORTED_FEATURES] is FanEntityFeature.SET_SPEED
|
assert (
|
||||||
|
fan_state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
|
is FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_bridge_with_two_fans_one_removed(
|
async def test_bridge_with_two_fans_one_removed(
|
||||||
|
@ -119,13 +149,20 @@ async def test_bridge_with_two_fans_one_removed(
|
||||||
is FanEntityFeature.SET_SPEED
|
is FanEntityFeature.SET_SPEED
|
||||||
| FanEntityFeature.DIRECTION
|
| FanEntityFeature.DIRECTION
|
||||||
| FanEntityFeature.OSCILLATE
|
| FanEntityFeature.OSCILLATE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
|
|
||||||
fan = entity_registry.async_get("fan.ceiling_fan")
|
fan = entity_registry.async_get("fan.ceiling_fan")
|
||||||
assert fan.unique_id == "00:00:00:00:00:00_766313939_8"
|
assert fan.unique_id == "00:00:00:00:00:00_766313939_8"
|
||||||
|
|
||||||
fan_state = hass.states.get("fan.ceiling_fan")
|
fan_state = hass.states.get("fan.ceiling_fan")
|
||||||
assert fan_state.attributes[ATTR_SUPPORTED_FEATURES] is FanEntityFeature.SET_SPEED
|
assert (
|
||||||
|
fan_state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
|
is FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
# Now change the config to remove one of the fans
|
# Now change the config to remove one of the fans
|
||||||
accessories = await setup_accessories_from_file(
|
accessories = await setup_accessories_from_file(
|
||||||
|
@ -141,6 +178,8 @@ async def test_bridge_with_two_fans_one_removed(
|
||||||
is FanEntityFeature.SET_SPEED
|
is FanEntityFeature.SET_SPEED
|
||||||
| FanEntityFeature.DIRECTION
|
| FanEntityFeature.DIRECTION
|
||||||
| FanEntityFeature.OSCILLATE
|
| FanEntityFeature.OSCILLATE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
# The second fan should have been removed
|
# The second fan should have been removed
|
||||||
assert not hass.states.get("fan.ceiling_fan")
|
assert not hass.states.get("fan.ceiling_fan")
|
||||||
|
|
|
@ -1590,7 +1590,7 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature(0),
|
fan.FanEntityFeature.TURN_OFF | fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1605,7 +1605,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.OSCILLATE,
|
fan.FanEntityFeature.OSCILLATE
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1620,7 +1622,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.SET_SPEED,
|
fan.FanEntityFeature.SET_SPEED
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1651,7 +1655,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.PRESET_MODE,
|
fan.FanEntityFeature.PRESET_MODE
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1667,7 +1673,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.PRESET_MODE,
|
fan.FanEntityFeature.PRESET_MODE
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1682,7 +1690,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.SET_SPEED,
|
fan.FanEntityFeature.SET_SPEED
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1698,7 +1708,10 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.OSCILLATE | fan.FanEntityFeature.SET_SPEED,
|
fan.FanEntityFeature.OSCILLATE
|
||||||
|
| fan.FanEntityFeature.SET_SPEED
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1714,7 +1727,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.PRESET_MODE,
|
fan.FanEntityFeature.PRESET_MODE
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1730,7 +1745,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.PRESET_MODE,
|
fan.FanEntityFeature.PRESET_MODE
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1747,7 +1764,10 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.PRESET_MODE | fan.FanEntityFeature.OSCILLATE,
|
fan.FanEntityFeature.PRESET_MODE
|
||||||
|
| fan.FanEntityFeature.OSCILLATE
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1764,7 +1784,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.SET_SPEED,
|
fan.FanEntityFeature.SET_SPEED
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1831,7 +1853,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.PRESET_MODE,
|
fan.FanEntityFeature.PRESET_MODE
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
"some error",
|
"some error",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -1846,7 +1870,9 @@ async def test_attributes(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
True,
|
True,
|
||||||
fan.FanEntityFeature.DIRECTION,
|
fan.FanEntityFeature.DIRECTION
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON,
|
||||||
"some error",
|
"some error",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -39,7 +39,12 @@ async def test_entity_state(hass: HomeAssistant, device_factory) -> None:
|
||||||
# Dimmer 1
|
# Dimmer 1
|
||||||
state = hass.states.get("fan.fan_1")
|
state = hass.states.get("fan.fan_1")
|
||||||
assert state.state == "on"
|
assert state.state == "on"
|
||||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == FanEntityFeature.SET_SPEED
|
assert (
|
||||||
|
state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
|
== FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
assert state.attributes[ATTR_PERCENTAGE] == 66
|
assert state.attributes[ATTR_PERCENTAGE] == 66
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,7 +105,12 @@ async def test_setup_mode_capability(hass: HomeAssistant, device_factory) -> Non
|
||||||
# Assert
|
# Assert
|
||||||
state = hass.states.get("fan.fan_1")
|
state = hass.states.get("fan.fan_1")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == FanEntityFeature.PRESET_MODE
|
assert (
|
||||||
|
state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
|
== FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
assert state.attributes[ATTR_PRESET_MODE] == "high"
|
assert state.attributes[ATTR_PRESET_MODE] == "high"
|
||||||
assert state.attributes[ATTR_PRESET_MODES] == ["high", "low", "medium"]
|
assert state.attributes[ATTR_PRESET_MODES] == ["high", "low", "medium"]
|
||||||
|
|
||||||
|
@ -122,7 +132,12 @@ async def test_setup_speed_capability(hass: HomeAssistant, device_factory) -> No
|
||||||
# Assert
|
# Assert
|
||||||
state = hass.states.get("fan.fan_1")
|
state = hass.states.get("fan.fan_1")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == FanEntityFeature.SET_SPEED
|
assert (
|
||||||
|
state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
|
== FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
assert state.attributes[ATTR_PERCENTAGE] == 66
|
assert state.attributes[ATTR_PERCENTAGE] == 66
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,7 +166,10 @@ async def test_setup_both_capabilities(hass: HomeAssistant, device_factory) -> N
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert (
|
assert (
|
||||||
state.attributes[ATTR_SUPPORTED_FEATURES]
|
state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
== FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE
|
== FanEntityFeature.SET_SPEED
|
||||||
|
| FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
)
|
)
|
||||||
assert state.attributes[ATTR_PERCENTAGE] == 66
|
assert state.attributes[ATTR_PERCENTAGE] == 66
|
||||||
assert state.attributes[ATTR_PRESET_MODE] == "high"
|
assert state.attributes[ATTR_PRESET_MODE] == "high"
|
||||||
|
|
|
@ -44,7 +44,7 @@ async def test_default_state(hass: HomeAssistant) -> None:
|
||||||
state = hass.states.get("fan.wind_machine")
|
state = hass.states.get("fan.wind_machine")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == "unavailable"
|
assert state.state == "unavailable"
|
||||||
assert state.attributes["supported_features"] == 0
|
assert state.attributes["supported_features"] == 48
|
||||||
|
|
||||||
|
|
||||||
async def test_service_calls(hass: HomeAssistant) -> None:
|
async def test_service_calls(hass: HomeAssistant) -> None:
|
||||||
|
|
|
@ -61,7 +61,12 @@ async def test_controlling_state_via_mqtt(
|
||||||
state = hass.states.get("fan.tasmota")
|
state = hass.states.get("fan.tasmota")
|
||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
assert state.attributes["percentage"] is None
|
assert state.attributes["percentage"] is None
|
||||||
assert state.attributes["supported_features"] == fan.FanEntityFeature.SET_SPEED
|
assert (
|
||||||
|
state.attributes["supported_features"]
|
||||||
|
== fan.FanEntityFeature.SET_SPEED
|
||||||
|
| fan.FanEntityFeature.TURN_OFF
|
||||||
|
| fan.FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
assert not state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"FanSpeed":1}')
|
async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/STATE", '{"FanSpeed":1}')
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
'original_name': None,
|
'original_name': None,
|
||||||
'platform': 'tplink',
|
'platform': 'tplink',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '123456789ABCDEFGH',
|
'unique_id': '123456789ABCDEFGH',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
'percentage_step': 25.0,
|
'percentage_step': 25.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.my_device',
|
'entity_id': 'fan.my_device',
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
'original_name': 'my_fan_0',
|
'original_name': 'my_fan_0',
|
||||||
'platform': 'tplink',
|
'platform': 'tplink',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '123456789ABCDEFGH00',
|
'unique_id': '123456789ABCDEFGH00',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -95,7 +95,7 @@
|
||||||
'percentage_step': 25.0,
|
'percentage_step': 25.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.my_device_my_fan_0',
|
'entity_id': 'fan.my_device_my_fan_0',
|
||||||
|
@ -134,7 +134,7 @@
|
||||||
'original_name': 'my_fan_1',
|
'original_name': 'my_fan_1',
|
||||||
'platform': 'tplink',
|
'platform': 'tplink',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '123456789ABCDEFGH01',
|
'unique_id': '123456789ABCDEFGH01',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
'percentage_step': 25.0,
|
'percentage_step': 25.0,
|
||||||
'preset_mode': None,
|
'preset_mode': None,
|
||||||
'preset_modes': None,
|
'preset_modes': None,
|
||||||
'supported_features': <FanEntityFeature: 1>,
|
'supported_features': <FanEntityFeature: 49>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.my_device_my_fan_1',
|
'entity_id': 'fan.my_device_my_fan_1',
|
||||||
|
|
|
@ -52,7 +52,7 @@ async def test_fan_available(
|
||||||
assert state.attributes[ATTR_PERCENTAGE_STEP] == pytest.approx(2.040816)
|
assert state.attributes[ATTR_PERCENTAGE_STEP] == pytest.approx(2.040816)
|
||||||
assert state.attributes[ATTR_PRESET_MODES] == ["Auto"]
|
assert state.attributes[ATTR_PRESET_MODES] == ["Auto"]
|
||||||
assert state.attributes[ATTR_PRESET_MODE] is None
|
assert state.attributes[ATTR_PRESET_MODE] is None
|
||||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 9
|
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 57
|
||||||
|
|
||||||
await command_store.trigger_observe_callback(
|
await command_store.trigger_observe_callback(
|
||||||
hass, device, {ATTR_REACHABLE_STATE: 0}
|
hass, device, {ATTR_REACHABLE_STATE: 0}
|
||||||
|
@ -172,7 +172,7 @@ async def test_services(
|
||||||
assert state.attributes[ATTR_PERCENTAGE_STEP] == pytest.approx(2.040816)
|
assert state.attributes[ATTR_PERCENTAGE_STEP] == pytest.approx(2.040816)
|
||||||
assert state.attributes[ATTR_PRESET_MODES] == ["Auto"]
|
assert state.attributes[ATTR_PRESET_MODES] == ["Auto"]
|
||||||
assert state.attributes[ATTR_PRESET_MODE] is None
|
assert state.attributes[ATTR_PRESET_MODE] is None
|
||||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 9
|
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 57
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
FAN_DOMAIN,
|
FAN_DOMAIN,
|
||||||
|
|
|
@ -203,7 +203,7 @@
|
||||||
'auto',
|
'auto',
|
||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'supported_features': 9,
|
'supported_features': 57,
|
||||||
}),
|
}),
|
||||||
'entity_id': 'fan.fan',
|
'entity_id': 'fan.fan',
|
||||||
'last_changed': str,
|
'last_changed': str,
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
'original_name': None,
|
'original_name': None,
|
||||||
'platform': 'vesync',
|
'platform': 'vesync',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 9>,
|
'supported_features': <FanEntityFeature: 57>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': 'air-purifier',
|
'unique_id': 'air-purifier',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
'auto',
|
'auto',
|
||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'supported_features': <FanEntityFeature: 9>,
|
'supported_features': <FanEntityFeature: 57>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.air_purifier_131s',
|
'entity_id': 'fan.air_purifier_131s',
|
||||||
|
@ -157,7 +157,7 @@
|
||||||
'original_name': None,
|
'original_name': None,
|
||||||
'platform': 'vesync',
|
'platform': 'vesync',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 9>,
|
'supported_features': <FanEntityFeature: 57>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': 'asd_sdfKIHG7IJHGwJGJ7GJ_ag5h3G55',
|
'unique_id': 'asd_sdfKIHG7IJHGwJGJ7GJ_ag5h3G55',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -178,7 +178,7 @@
|
||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'screen_status': True,
|
'screen_status': True,
|
||||||
'supported_features': <FanEntityFeature: 9>,
|
'supported_features': <FanEntityFeature: 57>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.air_purifier_200s',
|
'entity_id': 'fan.air_purifier_200s',
|
||||||
|
@ -255,7 +255,7 @@
|
||||||
'original_name': None,
|
'original_name': None,
|
||||||
'platform': 'vesync',
|
'platform': 'vesync',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 9>,
|
'supported_features': <FanEntityFeature: 57>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '400s-purifier',
|
'unique_id': '400s-purifier',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -277,7 +277,7 @@
|
||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'screen_status': True,
|
'screen_status': True,
|
||||||
'supported_features': <FanEntityFeature: 9>,
|
'supported_features': <FanEntityFeature: 57>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.air_purifier_400s',
|
'entity_id': 'fan.air_purifier_400s',
|
||||||
|
@ -354,7 +354,7 @@
|
||||||
'original_name': None,
|
'original_name': None,
|
||||||
'platform': 'vesync',
|
'platform': 'vesync',
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <FanEntityFeature: 9>,
|
'supported_features': <FanEntityFeature: 57>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '600s-purifier',
|
'unique_id': '600s-purifier',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
|
@ -376,7 +376,7 @@
|
||||||
'sleep',
|
'sleep',
|
||||||
]),
|
]),
|
||||||
'screen_status': True,
|
'screen_status': True,
|
||||||
'supported_features': <FanEntityFeature: 9>,
|
'supported_features': <FanEntityFeature: 57>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'fan.air_purifier_600s',
|
'entity_id': 'fan.air_purifier_600s',
|
||||||
|
|
|
@ -653,7 +653,12 @@ async def test_thermostat_fan(
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert state.attributes.get(ATTR_FAN_STATE) == "Idle / off"
|
assert state.attributes.get(ATTR_FAN_STATE) == "Idle / off"
|
||||||
assert state.attributes.get(ATTR_PRESET_MODE) == "Auto low"
|
assert state.attributes.get(ATTR_PRESET_MODE) == "Auto low"
|
||||||
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == FanEntityFeature.PRESET_MODE
|
assert (
|
||||||
|
state.attributes.get(ATTR_SUPPORTED_FEATURES)
|
||||||
|
== FanEntityFeature.PRESET_MODE
|
||||||
|
| FanEntityFeature.TURN_OFF
|
||||||
|
| FanEntityFeature.TURN_ON
|
||||||
|
)
|
||||||
|
|
||||||
# Test setting preset mode
|
# Test setting preset mode
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
|
|
Loading…
Add table
Reference in a new issue