From 761a5f133aa00710302d610a40a0c21623d55573 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 18 Aug 2021 11:07:17 +0930 Subject: [PATCH] Update fan.py --- custom_components/localtuya/fan.py | 170 ++++++++++++++++++++--------- 1 file changed, 120 insertions(+), 50 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index b45567e..f4bf87a 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,3 +1,9 @@ +# DPS [1] VALUE [True] --> fan on,off > True, False +# DPS [3] VALUE [6] --> fan speed > 1-6 +# DPS [4] VALUE [forward] --> fan direction > forward, reverse +# DPS [102] VALUE [normal] --> preset mode > normal, sleep, nature +# DPS [103] VALUE [off] --> timer > off, 1hour, 2hour, 4hour, 8hour + """Platform to locally control Tuya-based fan devices.""" import logging from functools import partial @@ -5,10 +11,9 @@ from functools import partial import voluptuous as vol from homeassistant.components.fan import ( DOMAIN, - SPEED_HIGH, - SPEED_LOW, - SPEED_MEDIUM, - SPEED_OFF, + DIRECTION_FORWARD, + DIRECTION_REVERSE, + SUPPORT_DIRECTION, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity, @@ -18,9 +23,16 @@ from .common import LocalTuyaEntity, async_setup_entry from .const import ( CONF_FAN_OSCILLATING_CONTROL, CONF_FAN_SPEED_CONTROL, - CONF_FAN_SPEED_HIGH, - CONF_FAN_SPEED_LOW, - CONF_FAN_SPEED_MEDIUM, + CONF_FAN_DIRECTION, + CONF_FAN_SPEED_COUNT +) + +from homeassistant.util.percentage import ( + ordered_list_item_to_percentage, + percentage_to_ordered_list_item, + percentage_to_ranged_value, + ranged_value_to_percentage, + int_states_in_range ) _LOGGER = logging.getLogger(__name__) @@ -31,17 +43,16 @@ def flow_schema(dps): return { vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps), - vol.Optional(CONF_FAN_SPEED_LOW, default=SPEED_LOW): vol.In( - [SPEED_LOW, "1", "2", "small"] - ), - vol.Optional(CONF_FAN_SPEED_MEDIUM, default=SPEED_MEDIUM): vol.In( - [SPEED_MEDIUM, "mid", "2", "3"] - ), - vol.Optional(CONF_FAN_SPEED_HIGH, default=SPEED_HIGH): vol.In( - [SPEED_HIGH, "auto", "3", "4", "large"] - ), + vol.Optional(CONF_FAN_DIRECTION): vol.In(dps), + vol.Optional(CONF_FAN_SPEED_COUNT, default=3): cv.positive_int } +# make this configurable later +DIRECTION_TO_TUYA = { + DIRECTION_REVERSE: "reverse", + DIRECTION_FORWARD: "forward", +} +TUYA_DIRECTION_TO_HA = {v: k for (k, v) in DIRECTION_TO_TUYA.items()} class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Representation of a Tuya fan.""" @@ -56,28 +67,40 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Initialize the entity.""" super().__init__(device, config_entry, fanid, _LOGGER, **kwargs) self._is_on = False - self._speed = None self._oscillating = None + self._direction = None + self._percentage = None @property def oscillating(self): """Return current oscillating status.""" return self._oscillating + @property + def current_direction(self): + """Return the current direction of the fan.""" + direction = self.service.value(CharacteristicsTypes.ROTATION_DIRECTION) + return TUYA_DIRECTION_TO_HA[direction] + @property def is_on(self): """Check if Tuya fan is on.""" return self._is_on @property - def speed(self) -> str: - """Return the current speed.""" - return self._speed + def percentage(self): + """Return the current percentage.""" + return self._percentage - @property - def speed_list(self) -> list: - """Get the list of available speeds.""" - return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] + # @property + # def speed(self) -> str: + # """Return the current speed.""" + # return self._speed + + # @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(self, speed: str = None, **kwargs) -> None: """Turn on the entity.""" @@ -92,22 +115,38 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): await self._device.set_dp(False, self._dp_id) 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.""" - mapping = { - 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 percentage == 0: + return await self.async_turn_off() - if speed == SPEED_OFF: - await self._device.set_dp(False, self._dp_id) - else: + if not self.is_on: + await self.async_turn_on() + + if percentage is not None await self._device.set_dp( - mapping.get(speed), self._config.get(CONF_FAN_SPEED_CONTROL) - ) + math.ceil( + percentage_to_ranged_value(self._speed_range, percentage) + ), + self._config.get(CONF_FAN_SPEED_CONTROL) + ) - self.schedule_update_ha_state() + # async def async_set_speed(self, speed: str) -> None: + # """Set the speed of the fan.""" + # mapping = { + # 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: + # await self._device.set_dp(False, self._dp_id) + # else: + # await self._device.set_dp( + # mapping.get(speed), self._config.get(CONF_FAN_SPEED_CONTROL) + # ) + + # self.schedule_update_ha_state() async def async_oscillate(self, oscillating: bool) -> None: """Set oscillation.""" @@ -116,38 +155,69 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): ) self.schedule_update_ha_state() + async def async_set_direction(self, direction): + """Set the direction of the fan.""" + await self.async_put_characteristics( + {CharacteristicsTypes.ROTATION_DIRECTION: DIRECTION_TO_TUYA[direction]} + ) + @property def supported_features(self) -> int: """Flag supported features.""" - supports = 0 + features = 0 if self.has_config(CONF_FAN_OSCILLATING_CONTROL): - supports |= SUPPORT_OSCILLATE - if self.has_config(CONF_FAN_SPEED_CONTROL): - supports |= SUPPORT_SET_SPEED + features |= SUPPORT_OSCILLATE - 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.""" + return self.has_config(CONF_FAN_SPEED_COUNT) + ) def status_updated(self): """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) - if self.has_config(CONF_FAN_SPEED_CONTROL): - self._speed = mappings.get(self.dps_conf(CONF_FAN_SPEED_CONTROL)) - if self.speed is None: + if self.has_config(CONF_FAN_SPEED_COUNT): + self._percentage = ranged_value_to_percentage( + self._speed_range, self.dps_conf(CONF_FAN_SPEED_CONTROL) + ) + if self._percentage is None: self.warning( "%s/%s: Ignoring unknown fan controller state: %s", self.name, self.entity_id, self.dps_conf(CONF_FAN_SPEED_CONTROL), ) - self._speed = None + self._percentage = None + + + # 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, + # } + + # if self.has_config(CONF_FAN_SPEED_CONTROL): + # self._speed = mappings.get(self.dps_conf(CONF_FAN_SPEED_CONTROL)) + # if self.speed is None: + # self.warning( + # "%s/%s: Ignoring unknown fan controller state: %s", + # self.name, + # self.entity_id, + # self.dps_conf(CONF_FAN_SPEED_CONTROL), + # ) + # self._speed = None if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL)