Add fan platform to knx (#46161)

This commit is contained in:
Martin 2021-02-10 09:09:34 +01:00 committed by GitHub
parent 26f455223b
commit 175f2f0275
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 145 additions and 3 deletions

View File

@ -45,6 +45,7 @@ from .schema import (
ConnectionSchema, ConnectionSchema,
CoverSchema, CoverSchema,
ExposeSchema, ExposeSchema,
FanSchema,
LightSchema, LightSchema,
NotifySchema, NotifySchema,
SceneSchema, SceneSchema,
@ -136,6 +137,9 @@ CONFIG_SCHEMA = vol.Schema(
vol.Optional(SupportedPlatforms.weather.value): vol.All( vol.Optional(SupportedPlatforms.weather.value): vol.All(
cv.ensure_list, [WeatherSchema.SCHEMA] cv.ensure_list, [WeatherSchema.SCHEMA]
), ),
vol.Optional(SupportedPlatforms.fan.value): vol.All(
cv.ensure_list, [FanSchema.SCHEMA]
),
} }
), ),
) )

View File

@ -33,14 +33,15 @@ class ColorTempModes(Enum):
class SupportedPlatforms(Enum): class SupportedPlatforms(Enum):
"""Supported platforms.""" """Supported platforms."""
cover = "cover"
light = "light"
binary_sensor = "binary_sensor" binary_sensor = "binary_sensor"
climate = "climate" climate = "climate"
switch = "switch" cover = "cover"
fan = "fan"
light = "light"
notify = "notify" notify = "notify"
scene = "scene" scene = "scene"
sensor = "sensor" sensor = "sensor"
switch = "switch"
weather = "weather" weather = "weather"

View File

@ -8,6 +8,7 @@ from xknx.devices import (
ClimateMode as XknxClimateMode, ClimateMode as XknxClimateMode,
Cover as XknxCover, Cover as XknxCover,
Device as XknxDevice, Device as XknxDevice,
Fan as XknxFan,
Light as XknxLight, Light as XknxLight,
Notification as XknxNotification, Notification as XknxNotification,
Scene as XknxScene, Scene as XknxScene,
@ -24,6 +25,7 @@ from .schema import (
BinarySensorSchema, BinarySensorSchema,
ClimateSchema, ClimateSchema,
CoverSchema, CoverSchema,
FanSchema,
LightSchema, LightSchema,
SceneSchema, SceneSchema,
SensorSchema, SensorSchema,
@ -65,6 +67,9 @@ def create_knx_device(
if platform is SupportedPlatforms.weather: if platform is SupportedPlatforms.weather:
return _create_weather(knx_module, config) return _create_weather(knx_module, config)
if platform is SupportedPlatforms.fan:
return _create_fan(knx_module, config)
def _create_cover(knx_module: XKNX, config: ConfigType) -> XknxCover: def _create_cover(knx_module: XKNX, config: ConfigType) -> XknxCover:
"""Return a KNX Cover device to be used within XKNX.""" """Return a KNX Cover device to be used within XKNX."""
@ -353,3 +358,20 @@ def _create_weather(knx_module: XKNX, config: ConfigType) -> XknxWeather:
), ),
group_address_humidity=config.get(WeatherSchema.CONF_KNX_HUMIDITY_ADDRESS), group_address_humidity=config.get(WeatherSchema.CONF_KNX_HUMIDITY_ADDRESS),
) )
def _create_fan(knx_module: XKNX, config: ConfigType) -> XknxFan:
"""Return a KNX Fan device to be used within XKNX."""
fan = XknxFan(
knx_module,
name=config[CONF_NAME],
group_address_speed=config.get(CONF_ADDRESS),
group_address_speed_state=config.get(FanSchema.CONF_STATE_ADDRESS),
group_address_oscillation=config.get(FanSchema.CONF_OSCILLATION_ADDRESS),
group_address_oscillation_state=config.get(
FanSchema.CONF_OSCILLATION_STATE_ADDRESS
),
max_step=config.get(FanSchema.CONF_MAX_STEP),
)
return fan

View File

@ -0,0 +1,93 @@
"""Support for KNX/IP fans."""
import math
from typing import Any, Optional
from xknx.devices import Fan as XknxFan
from xknx.devices.fan import FanSpeedMode
from homeassistant.components.fan import SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity
from homeassistant.util.percentage import (
percentage_to_ranged_value,
ranged_value_to_percentage,
)
from .const import DOMAIN
from .knx_entity import KnxEntity
DEFAULT_PERCENTAGE = 50
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up fans for KNX platform."""
entities = []
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxFan):
entities.append(KNXFan(device))
async_add_entities(entities)
class KNXFan(KnxEntity, FanEntity):
"""Representation of a KNX fan."""
def __init__(self, device: XknxFan):
"""Initialize of KNX fan."""
super().__init__(device)
if self._device.mode == FanSpeedMode.Step:
self._step_range = (1, device.max_step)
async def async_set_percentage(self, percentage: int) -> None:
"""Set the speed of the fan, as a percentage."""
if self._device.mode == FanSpeedMode.Step:
step = math.ceil(percentage_to_ranged_value(self._step_range, percentage))
await self._device.set_speed(step)
else:
await self._device.set_speed(percentage)
@property
def supported_features(self) -> int:
"""Flag supported features."""
flags = SUPPORT_SET_SPEED
if self._device.supports_oscillation:
flags |= SUPPORT_OSCILLATE
return flags
@property
def percentage(self) -> Optional[int]:
"""Return the current speed as a percentage."""
if self._device.current_speed is None:
return None
if self._device.mode == FanSpeedMode.Step:
return ranged_value_to_percentage(
self._step_range, self._device.current_speed
)
return self._device.current_speed
async def async_turn_on(
self,
speed: Optional[str] = None,
percentage: Optional[int] = None,
preset_mode: Optional[str] = None,
**kwargs,
) -> None:
"""Turn on the fan."""
if percentage is None:
await self.async_set_percentage(DEFAULT_PERCENTAGE)
else:
await self.async_set_percentage(percentage)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the fan off."""
await self.async_set_percentage(0)
async def async_oscillate(self, oscillating: bool) -> None:
"""Oscillate the fan."""
await self._device.set_oscillation(oscillating)
@property
def oscillating(self):
"""Return whether or not the fan is currently oscillating."""
return self._device.current_oscillation

View File

@ -430,3 +430,25 @@ class WeatherSchema:
vol.Optional(CONF_KNX_HUMIDITY_ADDRESS): cv.string, vol.Optional(CONF_KNX_HUMIDITY_ADDRESS): cv.string,
} }
) )
class FanSchema:
"""Voluptuous schema for KNX fans."""
CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
CONF_OSCILLATION_ADDRESS = "oscillation_address"
CONF_OSCILLATION_STATE_ADDRESS = "oscillation_state_address"
CONF_MAX_STEP = "max_step"
DEFAULT_NAME = "KNX Fan"
SCHEMA = vol.Schema(
{
vol.Required(CONF_ADDRESS): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_STATE_ADDRESS): cv.string,
vol.Optional(CONF_OSCILLATION_ADDRESS): cv.string,
vol.Optional(CONF_OSCILLATION_STATE_ADDRESS): cv.string,
vol.Optional(CONF_MAX_STEP): cv.byte,
}
)