Merge pull request #542 from tinglis1/master

update to the new fan platform
This commit is contained in:
rospogrigio
2022-01-03 14:45:28 +01:00
committed by GitHub
6 changed files with 187 additions and 90 deletions

View File

@@ -65,7 +65,16 @@ localtuya:
- platform: fan - platform: fan
friendly_name: Device Fan friendly_name: Device Fan
id: 3 id: 3 # dps for on/off state
fan_direction: 4 # Optional, dps for fan direction
fan_direction_fwd: forward # String for the forward direction
fan_direction_rev: reverse # String for the reverse direction
fan_ordered_list: low,medium,high,auto # Optional, If this is used it will not use the min and max integers.
fan_oscilating_control: 4 # Optional, dps for fan osciallation
fan_speed_control: 3 # Optional, if ordered list not used, dps for speed control
fan_speed_min: 1 # Optional, if ordered list not used, minimum integer for speed range
fan_speed_max: 10 # Optional, if ordered list not used, maximum integer for speed range
- platform: light - platform: light
friendly_name: Device Light friendly_name: Device Light

View File

@@ -35,9 +35,12 @@ CONF_SPAN_TIME = "span_time"
# fan # fan
CONF_FAN_SPEED_CONTROL = "fan_speed_control" CONF_FAN_SPEED_CONTROL = "fan_speed_control"
CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control" CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control"
CONF_FAN_SPEED_LOW = "fan_speed_low" CONF_FAN_SPEED_MIN = "fan_speed_min"
CONF_FAN_SPEED_MEDIUM = "fan_speed_medium" CONF_FAN_SPEED_MAX = "fan_speed_max"
CONF_FAN_SPEED_HIGH = "fan_speed_high" CONF_FAN_ORDERED_LIST = "fan_speed_ordered_list"
CONF_FAN_DIRECTION = "fan_direction"
CONF_FAN_DIRECTION_FWD = "fan_direction_forward"
CONF_FAN_DIRECTION_REV = "fan_direction_reverse"
# sensor # sensor
CONF_SCALING = "scaling" CONF_SCALING = "scaling"

View File

@@ -1,26 +1,37 @@
"""Platform to locally control Tuya-based fan devices.""" """Platform to locally control Tuya-based fan devices."""
import logging import logging
import math
from functools import partial from functools import partial
import homeassistant.helpers.config_validation as cv
import voluptuous as vol import voluptuous as vol
from homeassistant.components.fan import ( from homeassistant.components.fan import (
DIRECTION_FORWARD,
DIRECTION_REVERSE,
DOMAIN, DOMAIN,
SPEED_HIGH, SUPPORT_DIRECTION,
SPEED_LOW,
SPEED_MEDIUM,
SPEED_OFF,
SUPPORT_OSCILLATE, SUPPORT_OSCILLATE,
SUPPORT_SET_SPEED, SUPPORT_SET_SPEED,
FanEntity, FanEntity,
) )
from homeassistant.util.percentage import (
int_states_in_range,
ordered_list_item_to_percentage,
percentage_to_ordered_list_item,
percentage_to_ranged_value,
ranged_value_to_percentage,
)
from .common import LocalTuyaEntity, async_setup_entry from .common import LocalTuyaEntity, async_setup_entry
from .const import ( from .const import (
CONF_FAN_DIRECTION,
CONF_FAN_DIRECTION_FWD,
CONF_FAN_DIRECTION_REV,
CONF_FAN_ORDERED_LIST,
CONF_FAN_OSCILLATING_CONTROL, CONF_FAN_OSCILLATING_CONTROL,
CONF_FAN_SPEED_CONTROL, CONF_FAN_SPEED_CONTROL,
CONF_FAN_SPEED_HIGH, CONF_FAN_SPEED_MAX,
CONF_FAN_SPEED_LOW, CONF_FAN_SPEED_MIN,
CONF_FAN_SPEED_MEDIUM,
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -31,15 +42,12 @@ def flow_schema(dps):
return { return {
vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps),
vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps),
vol.Optional(CONF_FAN_SPEED_LOW, default=SPEED_LOW): vol.In( vol.Optional(CONF_FAN_DIRECTION): vol.In(dps),
[SPEED_LOW, "1", "2", "small"] vol.Optional(CONF_FAN_DIRECTION_FWD, default="forward"): cv.string,
), vol.Optional(CONF_FAN_DIRECTION_REV, default="reverse"): cv.string,
vol.Optional(CONF_FAN_SPEED_MEDIUM, default=SPEED_MEDIUM): vol.In( vol.Optional(CONF_FAN_SPEED_MIN, default=1): cv.positive_int,
[SPEED_MEDIUM, "mid", "2", "3"] vol.Optional(CONF_FAN_SPEED_MAX, default=9): cv.positive_int,
), vol.Optional(CONF_FAN_ORDERED_LIST, default="disabled"): cv.string,
vol.Optional(CONF_FAN_SPEED_HIGH, default=SPEED_HIGH): vol.In(
[SPEED_HIGH, "auto", "3", "4", "large", "big"]
),
} }
@@ -56,28 +64,46 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity):
"""Initialize the entity.""" """Initialize the entity."""
super().__init__(device, config_entry, fanid, _LOGGER, **kwargs) super().__init__(device, config_entry, fanid, _LOGGER, **kwargs)
self._is_on = False self._is_on = False
self._speed = None
self._oscillating = None self._oscillating = None
self._direction = None
self._percentage = None
self._speed_range = (
self._config.get(CONF_FAN_SPEED_MIN),
self._config.get(CONF_FAN_SPEED_MAX),
)
self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).split(",")
self._ordered_list_mode = None
if isinstance(self._ordered_list, list) and len(self._ordered_list) > 1:
self._use_ordered_list = True
_LOGGER.debug(
"Fan _use_ordered_list: %s > %s",
self._use_ordered_list,
self._ordered_list,
)
else:
self._use_ordered_list = False
_LOGGER.debug("Fan _use_ordered_list: %s", self._use_ordered_list)
@property @property
def oscillating(self): def oscillating(self):
"""Return current oscillating status.""" """Return current oscillating status."""
return self._oscillating return self._oscillating
@property
def current_direction(self):
"""Return the current direction of the fan."""
return self._direction
@property @property
def is_on(self): def is_on(self):
"""Check if Tuya fan is on.""" """Check if Tuya fan is on."""
return self._is_on return self._is_on
@property @property
def speed(self) -> str: def percentage(self):
"""Return the current speed.""" """Return the current percentage."""
return self._speed return self._percentage
@property
def speed_list(self) -> list:
"""Get the list of available speeds."""
return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
async def async_turn_on( async def async_turn_on(
self, self,
@@ -87,76 +113,143 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity):
**kwargs, **kwargs,
) -> None: ) -> None:
"""Turn on the entity.""" """Turn on the entity."""
_LOGGER.debug("Fan async_turn_on")
await self._device.set_dp(True, self._dp_id) await self._device.set_dp(True, self._dp_id)
if speed is not None: if percentage is not None:
await self.async_set_speed(speed) await self.async_set_percentage(percentage)
else: else:
self.schedule_update_ha_state() self.schedule_update_ha_state()
async def async_turn_off(self, **kwargs) -> None: async def async_turn_off(self, **kwargs) -> None:
"""Turn off the entity.""" """Turn off the entity."""
_LOGGER.debug("Fan async_turn_off")
await self._device.set_dp(False, self._dp_id) await self._device.set_dp(False, self._dp_id)
self.schedule_update_ha_state() self.schedule_update_ha_state()
async def async_set_speed(self, speed: str) -> None: async def async_set_percentage(self, percentage):
"""Set the speed of the fan.""" """Set the speed of the fan."""
mapping = { _LOGGER.debug("Fan async_set_percentage: %s", percentage)
SPEED_LOW: self._config.get(CONF_FAN_SPEED_LOW),
SPEED_MEDIUM: self._config.get(CONF_FAN_SPEED_MEDIUM),
SPEED_HIGH: self._config.get(CONF_FAN_SPEED_HIGH),
}
if speed == SPEED_OFF: if percentage is not None:
await self._device.set_dp(False, self._dp_id) if percentage == 0:
else: return await self.async_turn_off()
await self._device.set_dp( if not self.is_on:
mapping.get(speed), self._config.get(CONF_FAN_SPEED_CONTROL) await self.async_turn_on()
) if self._use_ordered_list:
await self._device.set_dp(
str(
percentage_to_ordered_list_item(self._ordered_list, percentage)
),
self._config.get(CONF_FAN_SPEED_CONTROL),
)
_LOGGER.debug(
"Fan async_set_percentage: %s > %s",
percentage,
percentage_to_ordered_list_item(self._ordered_list, percentage),
)
self.schedule_update_ha_state() else:
await self._device.set_dp(
str(
math.ceil(
percentage_to_ranged_value(self._speed_range, percentage)
)
),
self._config.get(CONF_FAN_SPEED_CONTROL),
)
_LOGGER.debug(
"Fan async_set_percentage: %s > %s",
percentage,
percentage_to_ranged_value(self._speed_range, percentage),
)
self.schedule_update_ha_state()
async def async_oscillate(self, oscillating: bool) -> None: async def async_oscillate(self, oscillating: bool) -> None:
"""Set oscillation.""" """Set oscillation."""
_LOGGER.debug("Fan async_oscillate: %s", oscillating)
await self._device.set_dp( await self._device.set_dp(
oscillating, self._config.get(CONF_FAN_OSCILLATING_CONTROL) oscillating, self._config.get(CONF_FAN_OSCILLATING_CONTROL)
) )
self.schedule_update_ha_state() self.schedule_update_ha_state()
async def async_set_direction(self, direction):
"""Set the direction of the fan."""
_LOGGER.debug("Fan async_set_direction: %s", direction)
if direction == DIRECTION_FORWARD:
value = self._config.get(CONF_FAN_DIRECTION_FWD)
if direction == DIRECTION_REVERSE:
value = self._config.get(CONF_FAN_DIRECTION_REV)
await self._device.set_dp(value, self._config.get(CONF_FAN_DIRECTION))
self.schedule_update_ha_state()
@property @property
def supported_features(self) -> int: def supported_features(self) -> int:
"""Flag supported features.""" """Flag supported features."""
supports = 0 features = 0
if self.has_config(CONF_FAN_OSCILLATING_CONTROL): if self.has_config(CONF_FAN_OSCILLATING_CONTROL):
supports |= SUPPORT_OSCILLATE features |= SUPPORT_OSCILLATE
if self.has_config(CONF_FAN_SPEED_CONTROL):
supports |= SUPPORT_SET_SPEED
return supports if self.has_config(CONF_FAN_SPEED_CONTROL):
features |= SUPPORT_SET_SPEED
if self.has_config(CONF_FAN_DIRECTION):
features |= SUPPORT_DIRECTION
return features
@property
def speed_count(self) -> int:
"""Speed count for the fan."""
speed_count = int_states_in_range(self._speed_range)
_LOGGER.debug("Fan speed_count: %s", speed_count)
return speed_count
def status_updated(self): def status_updated(self):
"""Get state of Tuya fan.""" """Get state of Tuya fan."""
mappings = {
self._config.get(CONF_FAN_SPEED_LOW): SPEED_LOW,
self._config.get(CONF_FAN_SPEED_MEDIUM): SPEED_MEDIUM,
self._config.get(CONF_FAN_SPEED_HIGH): SPEED_HIGH,
}
self._is_on = self.dps(self._dp_id) self._is_on = self.dps(self._dp_id)
if self.has_config(CONF_FAN_SPEED_CONTROL): current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL)
self._speed = mappings.get(self.dps_conf(CONF_FAN_SPEED_CONTROL)) if self._use_ordered_list:
if self.speed is None: _LOGGER.debug(
self.warning( "Fan current_speed ordered_list_item_to_percentage: %s from %s",
"%s/%s: Ignoring unknown fan controller state: %s", current_speed,
self.name, self._ordered_list,
self.entity_id, )
self.dps_conf(CONF_FAN_SPEED_CONTROL), if current_speed is not None:
self._percentage = ordered_list_item_to_percentage(
self._ordered_list, current_speed
) )
self._speed = None
else:
_LOGGER.debug(
"Fan current_speed ranged_value_to_percentage: %s from %s",
current_speed,
self._speed_range,
)
if current_speed is not None:
self._percentage = ranged_value_to_percentage(
self._speed_range, int(current_speed)
)
_LOGGER.debug("Fan current_percentage: %s", self._percentage)
if self.has_config(CONF_FAN_OSCILLATING_CONTROL): if self.has_config(CONF_FAN_OSCILLATING_CONTROL):
self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL)
_LOGGER.debug("Fan current_oscillating : %s", self._oscillating)
if self.has_config(CONF_FAN_DIRECTION):
value = self.dps_conf(CONF_FAN_DIRECTION)
if value is not None:
if value == self._config.get(CONF_FAN_DIRECTION_FWD):
self._direction = DIRECTION_FORWARD
if value == self._config.get(CONF_FAN_DIRECTION_REV):
self._direction = DIRECTION_REVERSE
_LOGGER.debug("Fan current_direction : %s > %s", value, self._direction)
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema)

View File

@@ -4,10 +4,7 @@ from functools import partial
import voluptuous as vol import voluptuous as vol
from homeassistant.components.number import DOMAIN, NumberEntity from homeassistant.components.number import DOMAIN, NumberEntity
from homeassistant.const import ( from homeassistant.const import CONF_DEVICE_CLASS, STATE_UNKNOWN
CONF_DEVICE_CLASS,
STATE_UNKNOWN,
)
from .common import LocalTuyaEntity, async_setup_entry from .common import LocalTuyaEntity, async_setup_entry

View File

@@ -4,10 +4,7 @@ from functools import partial
import voluptuous as vol import voluptuous as vol
from homeassistant.components.select import DOMAIN, SelectEntity from homeassistant.components.select import DOMAIN, SelectEntity
from homeassistant.const import ( from homeassistant.const import CONF_DEVICE_CLASS, STATE_UNKNOWN
CONF_DEVICE_CLASS,
STATE_UNKNOWN,
)
from .common import LocalTuyaEntity, async_setup_entry from .common import LocalTuyaEntity, async_setup_entry

View File

@@ -72,15 +72,14 @@
"color_temp_max_kelvin": "Maximum Color Temperature in K", "color_temp_max_kelvin": "Maximum Color Temperature in K",
"music_mode": "Music mode available", "music_mode": "Music mode available",
"scene": "Scene", "scene": "Scene",
"fan_speed_control": "Fan Speed Control", "fan_speed_control": "Fan Speed Control dps",
"fan_oscillating_control": "Fan Oscillating Control", "fan_oscillating_control": "Fan Oscillating Control dps",
"fan_speed_low": "Fan Low Speed Setting", "fan_speed_min": "minimum fan speed integer",
"fan_speed_medium": "Fan Medium Speed Setting", "fan_speed_max": "maximum fan speed integer",
"fan_speed_high": "Fan High Speed Setting", "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)",
"max_value": "Maximum Value", "fan_direction":"fan direction dps",
"min_value": "Minimum Value", "fan_direction_forward": "forward dps string",
"select_options": "Valid entries, separate entries by a ;", "fan_direction_reverse": "reverse dps string"
"select_options_friendly": "User Friendly options, separate entries by a ;"
} }
} }
} }
@@ -130,15 +129,14 @@
"color_temp_max_kelvin": "Maximum Color Temperature in K", "color_temp_max_kelvin": "Maximum Color Temperature in K",
"music_mode": "Music mode available", "music_mode": "Music mode available",
"scene": "Scene", "scene": "Scene",
"fan_speed_control": "Fan Speed Control", "fan_speed_control": "Fan Speed Control dps",
"fan_oscillating_control": "Fan Oscillating Control", "fan_oscillating_control": "Fan Oscillating Control dps",
"fan_speed_low": "Fan Low Speed Setting", "fan_speed_min": "minimum fan speed integer",
"fan_speed_medium": "Fan Medium Speed Setting", "fan_speed_max": "maximum fan speed integer",
"fan_speed_high": "Fan High Speed Setting", "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)",
"max_value": "Maximum Value", "fan_direction":"fan direction dps",
"min_value": "Minimum Value", "fan_direction_forward": "forward dps string",
"select_options": "Valid entries, separate entries by a ;", "fan_direction_reverse": "reverse dps string"
"select_options_friendly": "User Friendly options, separate entries by a ;"
} }
}, },
"yaml_import": { "yaml_import": {