From ec793a1137b2d103d142310094e4bcd47cdb7140 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 18 Aug 2021 11:05:36 +0930 Subject: [PATCH 01/43] Update en.json --- custom_components/localtuya/translations/en.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 6ce233a..2b12013 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -72,9 +72,8 @@ "scene": "Scene", "fan_speed_control": "Fan Speed Control", "fan_oscillating_control": "Fan Oscillating Control", - "fan_speed_low": "Fan Low Speed Setting", - "fan_speed_medium": "Fan Medium Speed Setting", - "fan_speed_high": "Fan High Speed Setting" + "fan_direction": "Fan Direction Control", + "fan_speed_count": "Fan Speed Count" } } } From 5217014f224002ee169657746d3d3ad3b4eb981f Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 18 Aug 2021 11:06:41 +0930 Subject: [PATCH 02/43] Update const.py --- custom_components/localtuya/const.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index bd8a5d3..b110ed4 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -33,10 +33,10 @@ CONF_SPAN_TIME = "span_time" # fan CONF_FAN_SPEED_CONTROL = "fan_speed_control" -CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control" -CONF_FAN_SPEED_LOW = "fan_speed_low" -CONF_FAN_SPEED_MEDIUM = "fan_speed_medium" -CONF_FAN_SPEED_HIGH = "fan_speed_high" +# CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control" +CONF_FAN_DIRECTION = "fan_direction" +CONF_FAN_SPEED_COUNT = "fan_speed_count" + # sensor CONF_SCALING = "scaling" From 761a5f133aa00710302d610a40a0c21623d55573 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 18 Aug 2021 11:07:17 +0930 Subject: [PATCH 03/43] 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) From 4eb1806eb4a5e58849ae2a60115b191b1e3bcd50 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 18 Aug 2021 11:24:52 +0930 Subject: [PATCH 04/43] Update fan.py --- custom_components/localtuya/fan.py | 52 +++++++----------------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index f4bf87a..859b204 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -79,8 +79,8 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): @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] + return self._direction + @property def is_on(self): @@ -123,7 +123,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if not self.is_on: await self.async_turn_on() - if percentage is not None + if percentage is not None: await self._device.set_dp( math.ceil( percentage_to_ranged_value(self._speed_range, percentage) @@ -131,22 +131,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._config.get(CONF_FAN_SPEED_CONTROL) ) - # 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.""" @@ -157,9 +141,10 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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]} + await self._device.set_dp( + DIRECTION_TO_TUYA[direction], self._config.get(CONF_FAN_DIRECTION) ) + self.schedule_update_ha_state() @property def supported_features(self) -> int: @@ -181,7 +166,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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.""" @@ -201,26 +186,13 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): ) 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) + if self.has_config(CONF_FAN_DIRECTION): + self._oscillating = TUYA_DIRECTION_TO_HA[ + self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) + ] + async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) From b8acadc1956871d2e368fda4f135b287008d884e Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 18 Aug 2021 13:15:04 +0930 Subject: [PATCH 05/43] working add more config options and presets in future --- custom_components/localtuya/fan.py | 34 ++++++++++++++---------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 859b204..b0e1008 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,13 +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 +import math +import homeassistant.helpers.config_validation as cv import voluptuous as vol from homeassistant.components.fan import ( DOMAIN, @@ -70,6 +66,9 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._oscillating = None self._direction = None self._percentage = None + self._speed_range = ( + 1, self._config.get(CONF_FAN_SPEED_COUNT), + ) @property def oscillating(self): @@ -125,9 +124,9 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if percentage is not None: await self._device.set_dp( - math.ceil( + str(math.ceil( percentage_to_ranged_value(self._speed_range, percentage) - ), + )), self._config.get(CONF_FAN_SPEED_CONTROL) ) @@ -165,7 +164,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): @property def speed_count(self) -> int: """Speed count for the fan.""" - return self.has_config(CONF_FAN_SPEED_COUNT) + return int_states_in_range(self._speed_range) def status_updated(self): @@ -174,25 +173,24 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._is_on = self.dps(self._dp_id) 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: + value = int(self.dps_conf(CONF_FAN_SPEED_CONTROL)) + if value is not None: + self._percentage = ranged_value_to_percentage(self._speed_range, value) + else: self.warning( "%s/%s: Ignoring unknown fan controller state: %s", self.name, self.entity_id, - self.dps_conf(CONF_FAN_SPEED_CONTROL), + value, ) - self._percentage = None if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) if self.has_config(CONF_FAN_DIRECTION): - self._oscillating = TUYA_DIRECTION_TO_HA[ - self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) - ] + value = self.dps_conf(CONF_FAN_DIRECTION) + if value is not None: + self._direction = TUYA_DIRECTION_TO_HA[value] async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) From e61307597123ee8da9a0c858e2e191ed96767373 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 19 Aug 2021 19:53:32 +0930 Subject: [PATCH 06/43] update to new fan entity percentage configuration --- .../localtuya/translations/en.json | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 2b12013..de40762 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -70,10 +70,14 @@ "color_temp_max_kelvin": "Maximum Color Temperature in K", "music_mode": "Music mode available", "scene": "Scene", - "fan_speed_control": "Fan Speed Control", - "fan_oscillating_control": "Fan Oscillating Control", - "fan_direction": "Fan Direction Control", - "fan_speed_count": "Fan Speed Count" + "fan_speed_control": "Fan Speed Control dps", + "fan_oscillating_control": "Fan Oscillating Control dps", + "fan_speed_min": "minimum fan speed integer", + "fan_speed_max": "maximum fan speed integer", + "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", + "fan_direction":"fan direction dps", + "fan_direction_forward": "forward dps string", + "fan_direction_reverse": "reverser dps string" } } } @@ -121,11 +125,14 @@ "color_temp_max_kelvin": "Maximum Color Temperature in K", "music_mode": "Music mode available", "scene": "Scene", - "fan_speed_control": "Fan Speed Control", - "fan_oscillating_control": "Fan Oscillating Control", - "fan_speed_low": "Fan Low Speed Setting", - "fan_speed_medium": "Fan Medium Speed Setting", - "fan_speed_high": "Fan High Speed Setting" + "fan_speed_control": "Fan Speed Control dps", + "fan_oscillating_control": "Fan Oscillating Control dps", + "fan_speed_min": "minimum fan speed integer", + "fan_speed_max": "maximum fan speed integer", + "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", + "fan_direction":"fan direction dps", + "fan_direction_forward": "forward dps string", + "fan_direction_reverse": "reverser dps string" } }, "yaml_import": { From 94c3af1c7d49533f8a66b9e5490aae575bf458dc Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 19 Aug 2021 19:56:54 +0930 Subject: [PATCH 07/43] update to new fan entity percentage --- custom_components/localtuya/const.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index b110ed4..9662b3b 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -33,10 +33,13 @@ CONF_SPAN_TIME = "span_time" # fan CONF_FAN_SPEED_CONTROL = "fan_speed_control" -# CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control" +CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control" +CONF_FAN_SPEED_MIN = "fan_speed_min" +CONF_FAN_SPEED_MAX = "fan_speed_max" +CONF_FAN_ORDERED_LIST = "fan_speed_ordered_list" CONF_FAN_DIRECTION = "fan_direction" -CONF_FAN_SPEED_COUNT = "fan_speed_count" - +CONF_FAN_DIRECTION_FWD = "fan_direction_forward" +CONF_FAN_DIRECTION_REV = "fan_direction_reverse" # sensor CONF_SCALING = "scaling" From 4cc7ffc07273838236480031045051f232f6b2d4 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 19 Aug 2021 19:57:51 +0930 Subject: [PATCH 08/43] update to new fan entity configuration --- custom_components/localtuya/fan.py | 129 ++++++++++++++++++++--------- 1 file changed, 88 insertions(+), 41 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index b0e1008..4e2af6b 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 @@ -20,7 +26,11 @@ from .const import ( CONF_FAN_OSCILLATING_CONTROL, CONF_FAN_SPEED_CONTROL, CONF_FAN_DIRECTION, - CONF_FAN_SPEED_COUNT + CONF_FAN_DIRECTION_FWD, + CONF_FAN_DIRECTION_REV, + CONF_FAN_SPEED_MIN, + CONF_FAN_SPEED_MAX, + CONF_FAN_ORDERED_LIST ) from homeassistant.util.percentage import ( @@ -40,15 +50,13 @@ def flow_schema(dps): vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_DIRECTION): vol.In(dps), - vol.Optional(CONF_FAN_SPEED_COUNT, default=3): cv.positive_int + 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_MIN, default=1): cv.positive_int, + vol.Optional(CONF_FAN_SPEED_MAX, default=9): cv.positive_int, + vol.Optional(CONF_FAN_ORDERED_LIST, default="disabled"): cv.string, } -# 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.""" @@ -67,8 +75,20 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._direction = None self._percentage = None self._speed_range = ( - 1, self._config.get(CONF_FAN_SPEED_COUNT), + 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 (type(self._ordered_list) is 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 def oscillating(self): @@ -80,7 +100,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Return the current direction of the fan.""" return self._direction - @property def is_on(self): """Check if Tuya fan is on.""" @@ -91,18 +110,9 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Return the current percentage.""" return self._percentage - # @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.""" + _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) if speed is not None: await self.async_set_speed(speed) @@ -111,11 +121,17 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): async def async_turn_off(self, **kwargs) -> None: """Turn off the entity.""" + + _LOGGER.debug("Fan async_turn_on") + await self._device.set_dp(False, self._dp_id) self.schedule_update_ha_state() async def async_set_percentage(self, percentage): """Set the speed of the fan.""" + + _LOGGER.debug("Fan async_set_percentage: %s", percentage) + if percentage == 0: return await self.async_turn_off() @@ -123,16 +139,28 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): await self.async_turn_on() if percentage is not None: - await self._device.set_dp( - str(math.ceil( - percentage_to_ranged_value(self._speed_range, percentage) - )), - self._config.get(CONF_FAN_SPEED_CONTROL) - ) + 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)) + + 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._ordered_list, percentage)) async def async_oscillate(self, oscillating: bool) -> None: """Set oscillation.""" + _LOGGER.debug("Fan async_oscillate: %s", oscillating) await self._device.set_dp( oscillating, self._config.get(CONF_FAN_OSCILLATING_CONTROL) ) @@ -140,8 +168,16 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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( - DIRECTION_TO_TUYA[direction], self._config.get(CONF_FAN_DIRECTION) + value, self._config.get(CONF_FAN_DIRECTION) ) self.schedule_update_ha_state() @@ -164,7 +200,9 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): @property def speed_count(self) -> int: """Speed count for the fan.""" - return int_states_in_range(self._speed_range) + speed_count = int_states_in_range(self._speed_range) + _LOGGER.debug("Fan speed_count: %s", speed_count) + return speed_count def status_updated(self): @@ -172,25 +210,34 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._is_on = self.dps(self._dp_id) - if self.has_config(CONF_FAN_SPEED_COUNT): - value = int(self.dps_conf(CONF_FAN_SPEED_CONTROL)) - if value is not None: - self._percentage = ranged_value_to_percentage(self._speed_range, value) - else: - self.warning( - "%s/%s: Ignoring unknown fan controller state: %s", - self.name, - self.entity_id, - value, - ) + current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) + if self._use_ordered_list: + _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list) + if current_speed is not None: + self._percentage = ordered_list_item_to_percentage(self._ordered_list, current_speed) + + 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): 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: - self._direction = TUYA_DIRECTION_TO_HA[value] - + 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 > %s", value, self._direction, self._config.get(CONF_FAN_DIRECTION_FWD)) + async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) From 5cbf556b9136fdacbc435bc8b023b5fd3086518a Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 19 Aug 2021 19:59:20 +0930 Subject: [PATCH 09/43] remove comments --- custom_components/localtuya/fan.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 4e2af6b..b4dad12 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,9 +1,3 @@ -# 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 @@ -237,7 +231,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if value == self._config.get(CONF_FAN_DIRECTION_REV): self._direction = DIRECTION_REVERSE - _LOGGER.debug("Fan current_direction : %s > %s > %s", value, self._direction, self._config.get(CONF_FAN_DIRECTION_FWD)) + _LOGGER.debug("Fan current_direction : %s > %s", value, self._direction) async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) From 81967039c246646e6ae6900e8417c91b63158222 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 2 Sep 2021 10:35:34 +0930 Subject: [PATCH 10/43] fixed logger error --- custom_components/localtuya/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index b4dad12..e1e2b1f 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -149,7 +149,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): )), self._config.get(CONF_FAN_SPEED_CONTROL) ) - _LOGGER.debug("Fan async_set_percentage: %s > %s", percentage, percentage_to_ranged_value(self._ordered_list, percentage)) + _LOGGER.debug("Fan async_set_percentage: %s > %s", percentage, percentage_to_ranged_value(self._speed_range, percentage)) async def async_oscillate(self, oscillating: bool) -> None: From 3eacbbda5a8bbcf5b877661c7120912b056417cb Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 5 Oct 2021 15:57:42 +1030 Subject: [PATCH 11/43] spelling error --- custom_components/localtuya/translations/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index de40762..2cb903e 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -77,7 +77,7 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverser dps string" + "fan_direction_reverse": "reverse dps string" } } } @@ -132,7 +132,7 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverser dps string" + "fan_direction_reverse": "reverse dps string" } }, "yaml_import": { From 3fca5566bb38d5dfb4131a093a3672b61743faf5 Mon Sep 17 00:00:00 2001 From: Prasad Bankar <22224770+psbankar@users.noreply.github.com> Date: Wed, 6 Oct 2021 21:37:17 +0530 Subject: [PATCH 12/43] Update fan.py Changes: Changed set_speed in turn_on method Changed set_percentage method for better flow Logger changes --- custom_components/localtuya/fan.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index e1e2b1f..43377c9 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -104,35 +104,33 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Return the current percentage.""" return self._percentage - async def async_turn_on(self, speed: str = None, **kwargs) -> None: + async def async_turn_on(self, percentage: str = None, **kwargs) -> None: """Turn on the entity.""" _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) - if speed is not None: - await self.async_set_speed(speed) + if percentage is not None: + await self.async_set_percentage(speed) else: self.schedule_update_ha_state() async def async_turn_off(self, **kwargs) -> None: """Turn off the entity.""" - - _LOGGER.debug("Fan async_turn_on") - + _LOGGER.debug("Fan async_turn_off") + await self._device.set_dp(False, self._dp_id) self.schedule_update_ha_state() async def async_set_percentage(self, percentage): """Set the speed of the fan.""" - - _LOGGER.debug("Fan async_set_percentage: %s", percentage) - - if percentage == 0: - return await self.async_turn_off() - if not self.is_on: - await self.async_turn_on() + _LOGGER.debug("Fan async_set_percentage: %s", percentage) + if percentage is not None: + if percentage == 0: + return await self.async_turn_off() + elif not self.is_on: + await self.async_turn_on() if self._use_ordered_list: await self._device.set_dp( str( @@ -150,6 +148,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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: @@ -230,8 +229,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if value == self._config.get(CONF_FAN_DIRECTION_REV): self._direction = DIRECTION_REVERSE - - _LOGGER.debug("Fan current_direction : %s > %s", value, self._direction) + _LOGGER.debug("Fan current_direction : %s > %s", value, self._direction) async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) From ec223ba73d35d7ea2de64eff11ca647468fb43ae Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 26 Oct 2021 15:32:23 +1030 Subject: [PATCH 13/43] add presets and integer/string option --- custom_components/localtuya/const.py | 4 +- custom_components/localtuya/fan.py | 111 +++++++++++++----- .../localtuya/translations/en.json | 10 +- 3 files changed, 91 insertions(+), 34 deletions(-) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 9662b3b..3d3ac05 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -40,7 +40,9 @@ 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" - +CONF_FAN_SPEED_DPS_TYPE = "fan_speed_dps_type" +CONF_FAN_PRESET_CONTROL = "fan_preset_control" +CONF_FAN_PRESET_LIST = "fan_preset_list" # sensor CONF_SCALING = "scaling" diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 43377c9..4693764 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -24,7 +24,8 @@ from .const import ( CONF_FAN_DIRECTION_REV, CONF_FAN_SPEED_MIN, CONF_FAN_SPEED_MAX, - CONF_FAN_ORDERED_LIST + CONF_FAN_ORDERED_LIST, + CONF_FAN_SPEED_DPS_TYPE ) from homeassistant.util.percentage import ( @@ -44,11 +45,15 @@ def flow_schema(dps): vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_DIRECTION): vol.In(dps), + vol.Optional(CONF_FAN_PRESET_CONTROL): vol.In(dps), 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_MIN, default=1): cv.positive_int, 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_ORDERED_LIST): cv.string, + vol.Optional(CONF_FAN_PRESET_LIST): cv.string, + vol.Optional(CONF_FAN_SPEED_DPS_TYPE, default="string"): vol.In( + ["string", "integer", "list"]), } @@ -68,21 +73,21 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._oscillating = None self._direction = None self._percentage = None + self._preset = 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 (type(self._ordered_list) is 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) + self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).replace(" ","").split(",") + self._preset_list = self._config.get(CONF_FAN_PRESET_LIST).replace(" ","").split(",") + self._ordered_speed_dps_type = self._config.get(CONF_FAN_SPEED_DPS_TYPE) + if (self._ordered_speed_dps_type == "list" and type(self._ordered_list) is list + and len(self._ordered_list) > 1): + _LOGGER.debug("Fan _use_ordered_list: %s", self._ordered_list) + else: + _LOGGER.debug("Fan _use_ordered_list: Not a valid list") + @property def oscillating(self): @@ -125,13 +130,32 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan async_set_percentage: %s", percentage) - + if percentage is not None: if percentage == 0: return await self.async_turn_off() elif not self.is_on: await self.async_turn_on() - if self._use_ordered_list: + + if self._ordered_speed_dps_type == "string": + 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)) + + elif self._ordered_speed_dps_type == "integer": + await self._device.set_dp( + int(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)) + + elif self._ordered_speed_dps_type == "list": await self._device.set_dp( str( percentage_to_ordered_list_item(self._ordered_list, percentage) @@ -140,14 +164,23 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): ) _LOGGER.debug("Fan async_set_percentage: %s > %s", percentage, percentage_to_ordered_list_item(self._ordered_list, percentage)) - 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)) + # 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)) + + # 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() @@ -172,7 +205,15 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): await self._device.set_dp( value, self._config.get(CONF_FAN_DIRECTION) ) - self.schedule_update_ha_state() + self.schedule_update_ha_state() + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set the preset mode of the fan.""" + _LOGGER.debug("Fan set preset: %s", preset_mode) + await self._device.set_dp( + preset_mode, self._config.get(CONF_FAN_PRESET_CONTROL) + ) + self.schedule_update_ha_state() @property def supported_features(self) -> int: @@ -188,6 +229,9 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if self.has_config(CONF_FAN_DIRECTION): features |= SUPPORT_DIRECTION + if self.has_config(CONF_FAN_PRESET_CONTROL): + features |= SUPPORT_PRESET_MODE + return features @property @@ -203,18 +247,23 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._is_on = self.dps(self._dp_id) + # check for speed and preset. current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) - if self._use_ordered_list: - _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list) - if current_speed is not None: + if current_speed is not None: + + if current_speed in self._preset_list: + _LOGGER.debug("Fan current_speed in preset list: %s from %s", current_speed, self._preset_list) + self._preset = current_speed + + if self._ordered_speed_dps_type == "list": + _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list) self._percentage = ordered_list_item_to_percentage(self._ordered_list, current_speed) - - else: - _LOGGER.debug("Fan current_speed ranged_value_to_percentage: %s from %s", current_speed, self._speed_range) - if current_speed is not None: + + elif self._ordered_speed_dps_type == "string" or self._ordered_speed_dps_type == "integer" : + _LOGGER.debug("Fan current_speed ranged_value_to_percentage: %s from %s", current_speed, self._speed_range) self._percentage = ranged_value_to_percentage(self._speed_range, int(current_speed)) - _LOGGER.debug("Fan current_percentage: %s", self._percentage) + _LOGGER.debug("Fan current_percentage: %s", self._percentage) if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 2cb903e..e465784 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -77,7 +77,10 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string" + "fan_direction_reverse": "reverse dps string", + "fan_speed_dps_type": "type of speed dps control. integer/string/list", + "fan_preset_control": "Fan preset dps. CAn be the same as speed", + "fan_preset_list": "FAn preset list. Comma separated" } } } @@ -132,7 +135,10 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string" + "fan_direction_reverse": "reverse dps string", + "fan_speed_dps_type": "type of speed dps control. integer/string/list", + "fan_preset_control": "Fan preset dps. CAn be the same as speed", + "fan_preset_list": "FAn preset list. Comma separated" } }, "yaml_import": { From 6f281a08fc0825e6b718f251b7a36ad30c88767f Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 26 Oct 2021 15:35:18 +1030 Subject: [PATCH 14/43] missing imports --- custom_components/localtuya/fan.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 4693764..4aa2f45 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -12,6 +12,7 @@ from homeassistant.components.fan import ( SUPPORT_DIRECTION, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, + SUPPORT_PRESET_MODE, FanEntity, ) @@ -25,7 +26,8 @@ from .const import ( CONF_FAN_SPEED_MIN, CONF_FAN_SPEED_MAX, CONF_FAN_ORDERED_LIST, - CONF_FAN_SPEED_DPS_TYPE + CONF_FAN_SPEED_DPS_TYPE, + CONF_FAN_PRESET_LIST ) from homeassistant.util.percentage import ( From 0c59ddc7e0e008de8236658eae51ccf8ac56c485 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 26 Oct 2021 16:09:39 +1030 Subject: [PATCH 15/43] correctly split preset and speed in updates --- custom_components/localtuya/fan.py | 35 ++++++++++++------------------ 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 4aa2f45..89024c3 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -20,6 +20,7 @@ from .common import LocalTuyaEntity, async_setup_entry from .const import ( CONF_FAN_OSCILLATING_CONTROL, CONF_FAN_SPEED_CONTROL, + CONF_FAN_PRESET_CONTROL, CONF_FAN_DIRECTION, CONF_FAN_DIRECTION_FWD, CONF_FAN_DIRECTION_REV, @@ -84,7 +85,8 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._preset_list = self._config.get(CONF_FAN_PRESET_LIST).replace(" ","").split(",") self._ordered_speed_dps_type = self._config.get(CONF_FAN_SPEED_DPS_TYPE) - if (self._ordered_speed_dps_type == "list" and type(self._ordered_list) is list + if (self._ordered_speed_dps_type == "list" + and type(self._ordered_list) is list and len(self._ordered_list) > 1): _LOGGER.debug("Fan _use_ordered_list: %s", self._ordered_list) else: @@ -166,23 +168,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): ) _LOGGER.debug("Fan async_set_percentage: %s > %s", percentage, percentage_to_ordered_list_item(self._ordered_list, percentage)) - # 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)) - - # 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() @@ -249,15 +234,22 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._is_on = self.dps(self._dp_id) - # check for speed and preset. + if self.has_config(CONF_FAN_PRESET_CONTROL): + current_preset = self.dps_conf(CONF_FAN_PRESET_CONTROL) + if current_preset is not None and current_preset in self._preset_list: + _LOGGER.debug("Fan current_preset in preset list: %s from %s", current_preset, self._preset_list) + self._preset = current_preset + current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) if current_speed is not None: - if current_speed in self._preset_list: + if (self.has_config(CONF_FAN_PRESET_CONTROL) + and (CONF_FAN_SPEED_CONTROL == CONF_FAN_PRESET_CONTROL) + and (current_speed in self._preset_list)): _LOGGER.debug("Fan current_speed in preset list: %s from %s", current_speed, self._preset_list) self._preset = current_speed - if self._ordered_speed_dps_type == "list": + elif self._ordered_speed_dps_type == "list": _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list) self._percentage = ordered_list_item_to_percentage(self._ordered_list, current_speed) @@ -266,6 +258,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._percentage = ranged_value_to_percentage(self._speed_range, int(current_speed)) _LOGGER.debug("Fan current_percentage: %s", self._percentage) + _LOGGER.debug("Fan current_preset: %s", self._preset) if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) From 7ac4fe5a119c7b1f6ff9c0b835e273a57b3b7362 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 26 Oct 2021 21:41:51 +1030 Subject: [PATCH 16/43] Revert "correctly split preset and speed in updates" This reverts commit 0c59ddc7e0e008de8236658eae51ccf8ac56c485. --- custom_components/localtuya/fan.py | 35 ++++++++++++++++++------------ 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 89024c3..4aa2f45 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -20,7 +20,6 @@ from .common import LocalTuyaEntity, async_setup_entry from .const import ( CONF_FAN_OSCILLATING_CONTROL, CONF_FAN_SPEED_CONTROL, - CONF_FAN_PRESET_CONTROL, CONF_FAN_DIRECTION, CONF_FAN_DIRECTION_FWD, CONF_FAN_DIRECTION_REV, @@ -85,8 +84,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._preset_list = self._config.get(CONF_FAN_PRESET_LIST).replace(" ","").split(",") self._ordered_speed_dps_type = self._config.get(CONF_FAN_SPEED_DPS_TYPE) - if (self._ordered_speed_dps_type == "list" - and type(self._ordered_list) is list + if (self._ordered_speed_dps_type == "list" and type(self._ordered_list) is list and len(self._ordered_list) > 1): _LOGGER.debug("Fan _use_ordered_list: %s", self._ordered_list) else: @@ -168,6 +166,23 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): ) _LOGGER.debug("Fan async_set_percentage: %s > %s", percentage, percentage_to_ordered_list_item(self._ordered_list, percentage)) + # 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)) + + # 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() @@ -234,22 +249,15 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._is_on = self.dps(self._dp_id) - if self.has_config(CONF_FAN_PRESET_CONTROL): - current_preset = self.dps_conf(CONF_FAN_PRESET_CONTROL) - if current_preset is not None and current_preset in self._preset_list: - _LOGGER.debug("Fan current_preset in preset list: %s from %s", current_preset, self._preset_list) - self._preset = current_preset - + # check for speed and preset. current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) if current_speed is not None: - if (self.has_config(CONF_FAN_PRESET_CONTROL) - and (CONF_FAN_SPEED_CONTROL == CONF_FAN_PRESET_CONTROL) - and (current_speed in self._preset_list)): + if current_speed in self._preset_list: _LOGGER.debug("Fan current_speed in preset list: %s from %s", current_speed, self._preset_list) self._preset = current_speed - elif self._ordered_speed_dps_type == "list": + if self._ordered_speed_dps_type == "list": _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list) self._percentage = ordered_list_item_to_percentage(self._ordered_list, current_speed) @@ -258,7 +266,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._percentage = ranged_value_to_percentage(self._speed_range, int(current_speed)) _LOGGER.debug("Fan current_percentage: %s", self._percentage) - _LOGGER.debug("Fan current_preset: %s", self._preset) if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) From 0f43923c2e3d9c652306909313db170b7fa7f0e0 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 26 Oct 2021 21:41:56 +1030 Subject: [PATCH 17/43] Revert "missing imports" This reverts commit 6f281a08fc0825e6b718f251b7a36ad30c88767f. --- custom_components/localtuya/fan.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 4aa2f45..4693764 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -12,7 +12,6 @@ from homeassistant.components.fan import ( SUPPORT_DIRECTION, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, - SUPPORT_PRESET_MODE, FanEntity, ) @@ -26,8 +25,7 @@ from .const import ( CONF_FAN_SPEED_MIN, CONF_FAN_SPEED_MAX, CONF_FAN_ORDERED_LIST, - CONF_FAN_SPEED_DPS_TYPE, - CONF_FAN_PRESET_LIST + CONF_FAN_SPEED_DPS_TYPE ) from homeassistant.util.percentage import ( From c8e660dfac689440a4bcbd3a68c45af39b775451 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 26 Oct 2021 21:41:59 +1030 Subject: [PATCH 18/43] Revert "add presets and integer/string option" This reverts commit ec223ba73d35d7ea2de64eff11ca647468fb43ae. --- custom_components/localtuya/const.py | 4 +- custom_components/localtuya/fan.py | 111 +++++------------- .../localtuya/translations/en.json | 10 +- 3 files changed, 34 insertions(+), 91 deletions(-) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 3d3ac05..9662b3b 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -40,9 +40,7 @@ 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" -CONF_FAN_SPEED_DPS_TYPE = "fan_speed_dps_type" -CONF_FAN_PRESET_CONTROL = "fan_preset_control" -CONF_FAN_PRESET_LIST = "fan_preset_list" + # sensor CONF_SCALING = "scaling" diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 4693764..43377c9 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -24,8 +24,7 @@ from .const import ( CONF_FAN_DIRECTION_REV, CONF_FAN_SPEED_MIN, CONF_FAN_SPEED_MAX, - CONF_FAN_ORDERED_LIST, - CONF_FAN_SPEED_DPS_TYPE + CONF_FAN_ORDERED_LIST ) from homeassistant.util.percentage import ( @@ -45,15 +44,11 @@ def flow_schema(dps): vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_DIRECTION): vol.In(dps), - vol.Optional(CONF_FAN_PRESET_CONTROL): vol.In(dps), 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_MIN, default=1): cv.positive_int, vol.Optional(CONF_FAN_SPEED_MAX, default=9): cv.positive_int, - vol.Optional(CONF_FAN_ORDERED_LIST): cv.string, - vol.Optional(CONF_FAN_PRESET_LIST): cv.string, - vol.Optional(CONF_FAN_SPEED_DPS_TYPE, default="string"): vol.In( - ["string", "integer", "list"]), + vol.Optional(CONF_FAN_ORDERED_LIST, default="disabled"): cv.string, } @@ -73,21 +68,21 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._oscillating = None self._direction = None self._percentage = None - self._preset = 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).replace(" ","").split(",") - self._preset_list = self._config.get(CONF_FAN_PRESET_LIST).replace(" ","").split(",") - self._ordered_speed_dps_type = self._config.get(CONF_FAN_SPEED_DPS_TYPE) - - if (self._ordered_speed_dps_type == "list" and type(self._ordered_list) is list - and len(self._ordered_list) > 1): - _LOGGER.debug("Fan _use_ordered_list: %s", self._ordered_list) + self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).split(",") + self._ordered_list_mode = None + + if (type(self._ordered_list) is 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: - _LOGGER.debug("Fan _use_ordered_list: Not a valid list") - + self._use_ordered_list = False + _LOGGER.debug("Fan _use_ordered_list: %s", self._use_ordered_list) + @property def oscillating(self): @@ -130,32 +125,13 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan async_set_percentage: %s", percentage) - + if percentage is not None: if percentage == 0: return await self.async_turn_off() elif not self.is_on: await self.async_turn_on() - - if self._ordered_speed_dps_type == "string": - 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)) - - elif self._ordered_speed_dps_type == "integer": - await self._device.set_dp( - int(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)) - - elif self._ordered_speed_dps_type == "list": + if self._use_ordered_list: await self._device.set_dp( str( percentage_to_ordered_list_item(self._ordered_list, percentage) @@ -164,23 +140,14 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): ) _LOGGER.debug("Fan async_set_percentage: %s > %s", percentage, percentage_to_ordered_list_item(self._ordered_list, percentage)) - # 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)) - - # 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)) + 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() @@ -205,15 +172,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): await self._device.set_dp( value, self._config.get(CONF_FAN_DIRECTION) ) - self.schedule_update_ha_state() - - async def async_set_preset_mode(self, preset_mode: str) -> None: - """Set the preset mode of the fan.""" - _LOGGER.debug("Fan set preset: %s", preset_mode) - await self._device.set_dp( - preset_mode, self._config.get(CONF_FAN_PRESET_CONTROL) - ) - self.schedule_update_ha_state() + self.schedule_update_ha_state() @property def supported_features(self) -> int: @@ -229,9 +188,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if self.has_config(CONF_FAN_DIRECTION): features |= SUPPORT_DIRECTION - if self.has_config(CONF_FAN_PRESET_CONTROL): - features |= SUPPORT_PRESET_MODE - return features @property @@ -247,23 +203,18 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._is_on = self.dps(self._dp_id) - # check for speed and preset. current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) - if current_speed is not None: - - if current_speed in self._preset_list: - _LOGGER.debug("Fan current_speed in preset list: %s from %s", current_speed, self._preset_list) - self._preset = current_speed - - if self._ordered_speed_dps_type == "list": - _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list) + if self._use_ordered_list: + _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list) + if current_speed is not None: self._percentage = ordered_list_item_to_percentage(self._ordered_list, current_speed) - - elif self._ordered_speed_dps_type == "string" or self._ordered_speed_dps_type == "integer" : - _LOGGER.debug("Fan current_speed ranged_value_to_percentage: %s from %s", current_speed, self._speed_range) + + 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) + _LOGGER.debug("Fan current_percentage: %s", self._percentage) if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index e465784..2cb903e 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -77,10 +77,7 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string", - "fan_speed_dps_type": "type of speed dps control. integer/string/list", - "fan_preset_control": "Fan preset dps. CAn be the same as speed", - "fan_preset_list": "FAn preset list. Comma separated" + "fan_direction_reverse": "reverse dps string" } } } @@ -135,10 +132,7 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string", - "fan_speed_dps_type": "type of speed dps control. integer/string/list", - "fan_preset_control": "Fan preset dps. CAn be the same as speed", - "fan_preset_list": "FAn preset list. Comma separated" + "fan_direction_reverse": "reverse dps string" } }, "yaml_import": { From c99a93bd6c3f43ba17107720a97bd289a79c59e6 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 10 Nov 2021 06:59:58 +1030 Subject: [PATCH 19/43] Fix speed variable error --- custom_components/localtuya/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 43377c9..37ff370 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -109,7 +109,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) if percentage is not None: - await self.async_set_percentage(speed) + await self.async_set_percentage(percentage) else: self.schedule_update_ha_state() From 9bf9b9f55dc1b4942fdfcc48dc3d6524a7f0aabe Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 11:12:43 +1030 Subject: [PATCH 20/43] fix formatting errors --- .vscode/settings.json | 5 ++++ custom_components/localtuya/fan.py | 40 ++++++++++++++++-------------- 2 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a04b218 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "*.yaml": "home-assistant" + } +} \ No newline at end of file diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 37ff370..1a5b8d8 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -69,7 +69,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._direction = None self._percentage = None self._speed_range = ( - self._config.get(CONF_FAN_SPEED_MIN), + 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(",") @@ -77,12 +77,12 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if (type(self._ordered_list) is 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) + _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 def oscillating(self): @@ -125,7 +125,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan async_set_percentage: %s", percentage) - if percentage is not None: if percentage == 0: return await self.async_turn_off() @@ -137,8 +136,9 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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)) + ) + _LOGGER.debug("Fan async_set_percentage: %s > %s", + percentage, percentage_to_ordered_list_item(self._ordered_list, percentage)) else: await self._device.set_dp( @@ -146,11 +146,11 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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)) + ) + _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: """Set oscillation.""" _LOGGER.debug("Fan async_oscillate: %s", oscillating) @@ -165,14 +165,14 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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() + self.schedule_update_ha_state() @property def supported_features(self) -> int: @@ -188,7 +188,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if self.has_config(CONF_FAN_DIRECTION): features |= SUPPORT_DIRECTION - return features + return features @property def speed_count(self) -> int: @@ -197,7 +197,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan speed_count: %s", speed_count) return speed_count - def status_updated(self): """Get state of Tuya fan.""" @@ -205,12 +204,17 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) if self._use_ordered_list: - _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list) + _LOGGER.debug( + "Fan current_speed ordered_list_item_to_percentage: %s from %s", + current_speed, self._ordered_list + ) if current_speed is not None: - self._percentage = ordered_list_item_to_percentage(self._ordered_list, current_speed) + self._percentage = ordered_list_item_to_percentage( + self._ordered_list, current_speed) else: - _LOGGER.debug("Fan current_speed ranged_value_to_percentage: %s from %s", current_speed, self._speed_range) + _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)) @@ -220,7 +224,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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: @@ -230,6 +233,5 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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) \ No newline at end of file From af9f11ad10707d1e1dbaf686ea930727cfedf8f5 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 11:12:43 +1030 Subject: [PATCH 21/43] fix formatting errors --- .vscode/settings.json | 5 ++ custom_components/localtuya/fan.py | 114 +++++++++++++++++------------ 2 files changed, 71 insertions(+), 48 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a04b218 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "*.yaml": "home-assistant" + } +} \ No newline at end of file diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 37ff370..51a2191 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,38 +1,37 @@ """Platform to locally control Tuya-based fan devices.""" import logging -from functools import partial import math +from functools import partial import homeassistant.helpers.config_validation as cv import voluptuous as vol from homeassistant.components.fan import ( - DOMAIN, DIRECTION_FORWARD, DIRECTION_REVERSE, + DOMAIN, SUPPORT_DIRECTION, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity, ) - -from .common import LocalTuyaEntity, async_setup_entry -from .const import ( - CONF_FAN_OSCILLATING_CONTROL, - CONF_FAN_SPEED_CONTROL, - CONF_FAN_DIRECTION, - CONF_FAN_DIRECTION_FWD, - CONF_FAN_DIRECTION_REV, - CONF_FAN_SPEED_MIN, - CONF_FAN_SPEED_MAX, - CONF_FAN_ORDERED_LIST -) - 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, - int_states_in_range +) + +from .common import LocalTuyaEntity, async_setup_entry +from .const import ( + CONF_FAN_DIRECTION, + CONF_FAN_DIRECTION_FWD, + CONF_FAN_DIRECTION_REV, + CONF_FAN_ORDERED_LIST, + CONF_FAN_OSCILLATING_CONTROL, + CONF_FAN_SPEED_CONTROL, + CONF_FAN_SPEED_MAX, + CONF_FAN_SPEED_MIN, ) _LOGGER = logging.getLogger(__name__) @@ -69,20 +68,23 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._direction = None self._percentage = None self._speed_range = ( - self._config.get(CONF_FAN_SPEED_MIN), + 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 (type(self._ordered_list) is list and len(self._ordered_list) > 1): + if type(self._ordered_list) is 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) + _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 def oscillating(self): @@ -125,7 +127,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan async_set_percentage: %s", percentage) - if percentage is not None: if percentage == 0: return await self.async_turn_off() @@ -135,22 +136,31 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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._config.get(CONF_FAN_SPEED_CONTROL), + ) + _LOGGER.debug( + "Fan async_set_percentage: %s > %s", + percentage, + percentage_to_ordered_list_item(self._ordered_list, percentage), + ) 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)) + 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: """Set oscillation.""" _LOGGER.debug("Fan async_oscillate: %s", oscillating) @@ -165,14 +175,12 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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() + await self._device.set_dp(value, self._config.get(CONF_FAN_DIRECTION)) + self.schedule_update_ha_state() @property def supported_features(self) -> int: @@ -188,8 +196,8 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if self.has_config(CONF_FAN_DIRECTION): features |= SUPPORT_DIRECTION - return features - + return features + @property def speed_count(self) -> int: """Speed count for the fan.""" @@ -197,7 +205,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan speed_count: %s", speed_count) return speed_count - def status_updated(self): """Get state of Tuya fan.""" @@ -205,14 +212,26 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) if self._use_ordered_list: - _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", current_speed, self._ordered_list) + _LOGGER.debug( + "Fan current_speed ordered_list_item_to_percentage: %s from %s", + current_speed, + self._ordered_list, + ) if current_speed is not None: - self._percentage = ordered_list_item_to_percentage(self._ordered_list, current_speed) - + self._percentage = ordered_list_item_to_percentage( + self._ordered_list, current_speed + ) + else: - _LOGGER.debug("Fan current_speed ranged_value_to_percentage: %s from %s", current_speed, self._speed_range) + _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)) + self._percentage = ranged_value_to_percentage( + self._speed_range, int(current_speed) + ) _LOGGER.debug("Fan current_percentage: %s", self._percentage) @@ -220,16 +239,15 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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) From 77f91590048b0e5b35aca36ce0628d9d092c24dc Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 11:54:26 +1030 Subject: [PATCH 22/43] workflow_dispatch --- .github/workflows/tox.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index 40ca8d9..8fc6768 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -1,6 +1,6 @@ name: Tox PR CI -on: [pull_request] +on: [pull_request, workflow_dispatch] jobs: build: From eda45fc896f52bcb357977d316901fef9631d21f Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 12:14:49 +1030 Subject: [PATCH 23/43] fix tox errors --- custom_components/localtuya/fan.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 1a5b8d8..e02fd25 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -122,7 +122,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): async def async_set_percentage(self, percentage): """Set the speed of the fan.""" - _LOGGER.debug("Fan async_set_percentage: %s", percentage) if percentage is not None: @@ -199,7 +198,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): def status_updated(self): """Get state of Tuya fan.""" - self._is_on = self.dps(self._dp_id) current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) From f1753945ca5f6d26a6dc5fc281593e015a0106ce Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 13:08:32 +1030 Subject: [PATCH 24/43] checks updates and tox fixes --- requirements_test.txt | 6 +++--- tox.ini | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements_test.txt b/requirements_test.txt index df62e77..8a2a53d 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,9 +1,9 @@ black==21.4b0 codespell==2.0.0 flake8==3.9.2 -mypy==0.901 +mypy==0.910 pydocstyle==6.1.1 cryptography==3.2 -pylint==2.8.2 +pylint==2.11.1 pylint-strict-informational==0.1 -homeassistant==2021.1.4 +homeassistant==2021.11.2 diff --git a/tox.ini b/tox.ini index 49ccccb..75c6e56 100644 --- a/tox.ini +++ b/tox.ini @@ -1,13 +1,13 @@ [tox] skipsdist = true -envlist = py{37,38}, lint, typing +envlist = py{38,39}, lint, typing skip_missing_interpreters = True cs_exclude_words = hass,unvalid [gh-actions] python = - 3.7: clean, py37, lint, typing 3.8: clean, py38, lint, typing + 3.9: clean, py38, lint, typing [testenv] passenv = TOXENV CI From c30c68dbf540a70691845b0754853bf83cb0ad96 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 13:09:01 +1030 Subject: [PATCH 25/43] tox --- .github/workflows/tox.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index 8fc6768..67c0209 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -14,8 +14,8 @@ jobs: platform: - ubuntu-latest python-version: - - 3.7 - 3.8 + - 3.9 steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} From 01b9b00050c5970654aab7a2d03959fba169a074 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 13:12:31 +1030 Subject: [PATCH 26/43] fix requirements --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 8a2a53d..e65a9a5 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -3,7 +3,7 @@ codespell==2.0.0 flake8==3.9.2 mypy==0.910 pydocstyle==6.1.1 -cryptography==3.2 +cryptography==3.4.8 pylint==2.11.1 pylint-strict-informational==0.1 homeassistant==2021.11.2 From 6e6cd0861fa79d89cc66cecf1cdf1bfa5cb5b214 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 13:15:09 +1030 Subject: [PATCH 27/43] fix tox errors --- custom_components/localtuya/fan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 31076a3..6a1e219 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -245,4 +245,5 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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) \ No newline at end of file + +async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) From 95ef271dab36fcd7b204aec952e15796d895f7fc Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 13:50:18 +1030 Subject: [PATCH 28/43] more tox fixes --- custom_components/localtuya/fan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 6a1e219..51d9d5e 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -74,7 +74,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).split(",") self._ordered_list_mode = None - if type(self._ordered_list) is list and len(self._ordered_list) > 1: + 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", @@ -105,7 +105,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Return the current percentage.""" return self._percentage - async def async_turn_on(self, percentage: str = None, **kwargs) -> None: + async def async_turn_on(self, percentage: str = None, preset_mode: str = None, **kwargs: Any) -> None: """Turn on the entity.""" _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) From a171430cc877051582196a3456ecdaecdca864ec Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 13:53:51 +1030 Subject: [PATCH 29/43] fix error --- custom_components/localtuya/fan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 51d9d5e..17f0c32 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -105,7 +105,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Return the current percentage.""" return self._percentage - async def async_turn_on(self, percentage: str = None, preset_mode: str = None, **kwargs: Any) -> None: + async def async_turn_on(self, percentage: str = None, preset_mode: str = None, **kwargs) -> None: """Turn on the entity.""" _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) From e55cd2013faf9d37924cda0b51bf7c4d4283630d Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 13:57:18 +1030 Subject: [PATCH 30/43] line length issue --- custom_components/localtuya/fan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 17f0c32..553f577 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -105,7 +105,8 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Return the current percentage.""" return self._percentage - async def async_turn_on(self, percentage: str = None, preset_mode: str = None, **kwargs) -> None: + async def async_turn_on(self, percentage: str = None, + preset_mode: str = None, **kwargs) -> None: """Turn on the entity.""" _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) From 8d5da3d73f7d3cbbca197afbd3bc1b3d8e516be3 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 21:34:00 +1030 Subject: [PATCH 31/43] remove dev tool changes fro branch --- .github/workflows/tox.yaml | 4 ++-- .vscode/settings.json | 5 ----- requirements_test.txt | 8 ++++---- tox.ini | 6 +++--- 4 files changed, 9 insertions(+), 14 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index 67c0209..40ca8d9 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -1,6 +1,6 @@ name: Tox PR CI -on: [pull_request, workflow_dispatch] +on: [pull_request] jobs: build: @@ -14,8 +14,8 @@ jobs: platform: - ubuntu-latest python-version: + - 3.7 - 3.8 - - 3.9 steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index a04b218..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "files.associations": { - "*.yaml": "home-assistant" - } -} \ No newline at end of file diff --git a/requirements_test.txt b/requirements_test.txt index e65a9a5..d388fd6 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,9 +1,9 @@ black==21.4b0 codespell==2.0.0 flake8==3.9.2 -mypy==0.910 +mypy==0.901 pydocstyle==6.1.1 -cryptography==3.4.8 -pylint==2.11.1 +cryptography==3.2 +pylint==2.8.2 pylint-strict-informational==0.1 -homeassistant==2021.11.2 +homeassistant==2021.1.4 \ No newline at end of file diff --git a/tox.ini b/tox.ini index 75c6e56..1c72213 100644 --- a/tox.ini +++ b/tox.ini @@ -1,13 +1,13 @@ [tox] skipsdist = true -envlist = py{38,39}, lint, typing +envlist = py{37,38}, lint, typing skip_missing_interpreters = True cs_exclude_words = hass,unvalid [gh-actions] python = + 3.7: clean, py37, lint, typing 3.8: clean, py38, lint, typing - 3.9: clean, py38, lint, typing [testenv] passenv = TOXENV CI @@ -35,4 +35,4 @@ commands = [testenv:typing] commands = - mypy --ignore-missing-imports --follow-imports=skip custom_components + mypy --ignore-missing-imports --follow-imports=skip custom_components \ No newline at end of file From c9c96ad6359c9a2c3fb15276e35a8ab260ac672f Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 21:37:57 +1030 Subject: [PATCH 32/43] revert dev tool config files for master branch --- requirements_test.txt | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_test.txt b/requirements_test.txt index d388fd6..df62e77 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -6,4 +6,4 @@ pydocstyle==6.1.1 cryptography==3.2 pylint==2.8.2 pylint-strict-informational==0.1 -homeassistant==2021.1.4 \ No newline at end of file +homeassistant==2021.1.4 diff --git a/tox.ini b/tox.ini index 1c72213..49ccccb 100644 --- a/tox.ini +++ b/tox.ini @@ -35,4 +35,4 @@ commands = [testenv:typing] commands = - mypy --ignore-missing-imports --follow-imports=skip custom_components \ No newline at end of file + mypy --ignore-missing-imports --follow-imports=skip custom_components From e979a4b5cda15670b9f7127c3eb651f54f507d02 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 22:02:36 +1030 Subject: [PATCH 33/43] update dev checker tools --- .github/workflows/tox.yaml | 4 ++-- requirements_test.txt | 8 ++++---- tox.ini | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index 40ca8d9..67c0209 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -1,6 +1,6 @@ name: Tox PR CI -on: [pull_request] +on: [pull_request, workflow_dispatch] jobs: build: @@ -14,8 +14,8 @@ jobs: platform: - ubuntu-latest python-version: - - 3.7 - 3.8 + - 3.9 steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/requirements_test.txt b/requirements_test.txt index df62e77..e65a9a5 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,9 +1,9 @@ black==21.4b0 codespell==2.0.0 flake8==3.9.2 -mypy==0.901 +mypy==0.910 pydocstyle==6.1.1 -cryptography==3.2 -pylint==2.8.2 +cryptography==3.4.8 +pylint==2.11.1 pylint-strict-informational==0.1 -homeassistant==2021.1.4 +homeassistant==2021.11.2 diff --git a/tox.ini b/tox.ini index 49ccccb..75c6e56 100644 --- a/tox.ini +++ b/tox.ini @@ -1,13 +1,13 @@ [tox] skipsdist = true -envlist = py{37,38}, lint, typing +envlist = py{38,39}, lint, typing skip_missing_interpreters = True cs_exclude_words = hass,unvalid [gh-actions] python = - 3.7: clean, py37, lint, typing 3.8: clean, py38, lint, typing + 3.9: clean, py38, lint, typing [testenv] passenv = TOXENV CI From 445b62efd8a247bacf89ebc5a425426808add541 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 22:11:34 +1030 Subject: [PATCH 34/43] tox.yaml --- .github/workflows/tox.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index 67c0209..afa1588 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -1,6 +1,6 @@ name: Tox PR CI -on: [pull_request, workflow_dispatch] +on: [push, pull_request, workflow_dispatch] jobs: build: From 1c2f4ba0843c76deafe2b05efd21ea10501ce95e Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 22:30:57 +1030 Subject: [PATCH 35/43] Delete settings.json --- .vscode/settings.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index a04b218..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "files.associations": { - "*.yaml": "home-assistant" - } -} \ No newline at end of file From e8aa5ac9a8852a0e0f971ef247676eb80c6fe8c7 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 22:46:55 +1030 Subject: [PATCH 36/43] integration inputs --- custom_components/localtuya/const.py | 3 + custom_components/localtuya/fan.py | 197 ++++++++++-------- .../localtuya/translations/en.json | 10 +- 3 files changed, 123 insertions(+), 87 deletions(-) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 9662b3b..36db866 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -40,6 +40,9 @@ 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" +CONF_FAN_SPEED_DPS_TYPE = "fan_speed_dps_type" +CONF_FAN_PRESET_CONTROL = "fan_preset_control" +CONF_FAN_PRESET_LIST = "fan_preset_list" # sensor CONF_SCALING = "scaling" diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 553f577..efd2058 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,56 +1,63 @@ """Platform to locally control Tuya-based fan devices.""" import logging -import math from functools import partial +import math import homeassistant.helpers.config_validation as cv import voluptuous as vol from homeassistant.components.fan import ( + DOMAIN, DIRECTION_FORWARD, DIRECTION_REVERSE, - DOMAIN, SUPPORT_DIRECTION, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, + SUPPORT_PRESET_MODE, 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 .const import ( + CONF_FAN_OSCILLATING_CONTROL, + CONF_FAN_SPEED_CONTROL, + CONF_FAN_PRESET_CONTROL, CONF_FAN_DIRECTION, CONF_FAN_DIRECTION_FWD, CONF_FAN_DIRECTION_REV, - CONF_FAN_ORDERED_LIST, - CONF_FAN_OSCILLATING_CONTROL, - CONF_FAN_SPEED_CONTROL, - CONF_FAN_SPEED_MAX, CONF_FAN_SPEED_MIN, + CONF_FAN_SPEED_MAX, + CONF_FAN_ORDERED_LIST, + CONF_FAN_SPEED_DPS_TYPE, + CONF_FAN_PRESET_LIST +) + +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__) - def flow_schema(dps): """Return schema used in config flow.""" return { vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_DIRECTION): vol.In(dps), + vol.Optional(CONF_FAN_PRESET_CONTROL): vol.In(dps), 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_MIN, default=1): cv.positive_int, 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_ORDERED_LIST): cv.string, + vol.Optional(CONF_FAN_PRESET_LIST): cv.string, + vol.Optional(CONF_FAN_SPEED_DPS_TYPE, default="string"): vol.In( + ["string", "integer", "list"]), } - class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Representation of a Tuya fan.""" @@ -67,24 +74,22 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._oscillating = None self._direction = None self._percentage = None + self._preset = None self._speed_range = ( - self._config.get(CONF_FAN_SPEED_MIN), + 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, - ) + self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).replace(" ","").split(",") + self._preset_list = self._config.get(CONF_FAN_PRESET_LIST).replace(" ","").split(",") + self._ordered_speed_dps_type = self._config.get(CONF_FAN_SPEED_DPS_TYPE) + + if (self._ordered_speed_dps_type == "list" + and isinstance(self._ordered_list, list) + and len(self._ordered_list) > 1): + _LOGGER.debug("Fan _use_ordered_list: %s", self._ordered_list) else: - self._use_ordered_list = False - _LOGGER.debug("Fan _use_ordered_list: %s", self._use_ordered_list) - + _LOGGER.debug("Fan _use_ordered_list: Not a valid list") + @property def oscillating(self): """Return current oscillating status.""" @@ -105,13 +110,12 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Return the current percentage.""" return self._percentage - async def async_turn_on(self, percentage: str = None, - preset_mode: str = None, **kwargs) -> None: + async def async_turn_on(self, percentage: str = None, **kwargs) -> None: """Turn on the entity.""" _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) if percentage is not None: - await self.async_set_percentage(percentage) + await self.async_set_percentage(speed) else: self.schedule_update_ha_state() @@ -124,42 +128,44 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): async def async_set_percentage(self, percentage): """Set the speed of the fan.""" - _LOGGER.debug("Fan async_set_percentage: %s", percentage) + _LOGGER.debug("Fan async_set_percentage: %s", percentage) + if percentage is not None: if percentage == 0: return await self.async_turn_off() - elif not self.is_on: + + if not self.is_on: await self.async_turn_on() - if self._use_ordered_list: + + if self._ordered_speed_dps_type == "string": + send_speed = str(math.ceil( + percentage_to_ranged_value(self._speed_range, percentage))) 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), - ) + send_speed, self._config.get(CONF_FAN_SPEED_CONTROL)) + _LOGGER.debug("Fan async_set_percentage: %s > %s", + percentage, send_speed) + + elif self._ordered_speed_dps_type == "integer": + send_speed = int(math.ceil( + percentage_to_ranged_value(self._speed_range, percentage))) + await self._device.set_dp( + send_speed, self._config.get(CONF_FAN_SPEED_CONTROL)) + _LOGGER.debug("Fan async_set_percentage: %s > %s", + percentage, send_speed) - else: - await self._device.set_dp( - str( - math.ceil( - percentage_to_ranged_value(self._speed_range, percentage) + elif self._ordered_speed_dps_type == "list": + send_speed = 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_ranged_value(self._speed_range, percentage), - ) + await self._device.set_dp( + send_speed, self._config.get(CONF_FAN_SPEED_CONTROL)) + _LOGGER.debug("Fan async_set_percentage: %s > %s", + percentage, send_speed) + self.schedule_update_ha_state() + async def async_oscillate(self, oscillating: bool) -> None: """Set oscillation.""" _LOGGER.debug("Fan async_oscillate: %s", oscillating) @@ -174,10 +180,21 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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)) + + await self._device.set_dp( + value, self._config.get(CONF_FAN_DIRECTION) + ) + self.schedule_update_ha_state() + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set the preset mode of the fan.""" + _LOGGER.debug("Fan set preset: %s", preset_mode) + await self._device.set_dp( + preset_mode, self._config.get(CONF_FAN_PRESET_CONTROL) + ) self.schedule_update_ha_state() @property @@ -194,8 +211,11 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if self.has_config(CONF_FAN_DIRECTION): features |= SUPPORT_DIRECTION - return features + if self.has_config(CONF_FAN_PRESET_CONTROL): + features |= SUPPORT_PRESET_MODE + return features + @property def speed_count(self) -> int: """Speed count for the fan.""" @@ -203,34 +223,41 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan speed_count: %s", speed_count) return speed_count + def status_updated(self): """Get state of Tuya fan.""" + self._is_on = self.dps(self._dp_id) + if self.has_config(CONF_FAN_PRESET_CONTROL): + current_preset = self.dps_conf(CONF_FAN_PRESET_CONTROL) + if current_preset is not None and current_preset in self._preset_list: + _LOGGER.debug("Fan current_preset in preset list: %s from %s", + current_preset, self._preset_list) + self._preset = current_preset + current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) - if self._use_ordered_list: - _LOGGER.debug( - "Fan current_speed ordered_list_item_to_percentage: %s from %s", - current_speed, - self._ordered_list, - ) - if current_speed is not None: - self._percentage = ordered_list_item_to_percentage( - self._ordered_list, current_speed - ) + if current_speed is not 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) - ) + if (self.has_config(CONF_FAN_PRESET_CONTROL) + and (CONF_FAN_SPEED_CONTROL == CONF_FAN_PRESET_CONTROL) + and (current_speed in self._preset_list)): + _LOGGER.debug("Fan current_speed in preset list: %s from %s", + current_speed, self._preset_list) + self._preset = current_speed - _LOGGER.debug("Fan current_percentage: %s", self._percentage) + elif self._ordered_speed_dps_type == "list": + _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", + current_speed, self._ordered_list) + self._percentage = ordered_list_item_to_percentage(self._ordered_list, current_speed) + + elif self._ordered_speed_dps_type == "string" or self._ordered_speed_dps_type == "integer" : + _LOGGER.debug("Fan current_speed ranged_value_to_percentage: %s from %s", + current_speed, self._speed_range) + self._percentage = ranged_value_to_percentage(self._speed_range, int(current_speed)) + + _LOGGER.debug("Fan current_percentage: %s", self._percentage) + _LOGGER.debug("Fan current_preset: %s", self._preset) if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) @@ -241,10 +268,10 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 2cb903e..2014b0b 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -77,7 +77,10 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string" + "fan_direction_reverse": "reverse dps string", + "fan_speed_dps_type": "type of speed dps control. integer/string/list", + "fan_preset_control": "Fan preset dps. Can be the same as speed", + "fan_preset_list": "Fan preset list. Comma separated" } } } @@ -132,7 +135,10 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string" + "fan_direction_reverse": "reverse dps string", + "fan_speed_dps_type": "type of speed dps control. integer/string/list", + "fan_preset_control": "Fan preset dps. CAn be the same as speed", + "fan_preset_list": "Fan preset list. Comma separated" } }, "yaml_import": { From d600cba89cffec158c2d6d1049e5802ab76c7e63 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 11 Nov 2021 22:56:34 +1030 Subject: [PATCH 37/43] fix tox errors --- custom_components/localtuya/fan.py | 187 +++++++++++++++++------------ 1 file changed, 109 insertions(+), 78 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index efd2058..6896831 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,46 +1,46 @@ """Platform to locally control Tuya-based fan devices.""" import logging -from functools import partial import math +from functools import partial import homeassistant.helpers.config_validation as cv import voluptuous as vol from homeassistant.components.fan import ( - DOMAIN, DIRECTION_FORWARD, DIRECTION_REVERSE, + DOMAIN, SUPPORT_DIRECTION, SUPPORT_OSCILLATE, - SUPPORT_SET_SPEED, SUPPORT_PRESET_MODE, + SUPPORT_SET_SPEED, FanEntity, ) - -from .common import LocalTuyaEntity, async_setup_entry -from .const import ( - CONF_FAN_OSCILLATING_CONTROL, - CONF_FAN_SPEED_CONTROL, - CONF_FAN_PRESET_CONTROL, - CONF_FAN_DIRECTION, - CONF_FAN_DIRECTION_FWD, - CONF_FAN_DIRECTION_REV, - CONF_FAN_SPEED_MIN, - CONF_FAN_SPEED_MAX, - CONF_FAN_ORDERED_LIST, - CONF_FAN_SPEED_DPS_TYPE, - CONF_FAN_PRESET_LIST -) - 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, - int_states_in_range +) + +from .common import LocalTuyaEntity, async_setup_entry +from .const import ( + CONF_FAN_DIRECTION, + CONF_FAN_DIRECTION_FWD, + CONF_FAN_DIRECTION_REV, + CONF_FAN_ORDERED_LIST, + CONF_FAN_OSCILLATING_CONTROL, + CONF_FAN_PRESET_CONTROL, + CONF_FAN_PRESET_LIST, + CONF_FAN_SPEED_CONTROL, + CONF_FAN_SPEED_DPS_TYPE, + CONF_FAN_SPEED_MAX, + CONF_FAN_SPEED_MIN, ) _LOGGER = logging.getLogger(__name__) + def flow_schema(dps): """Return schema used in config flow.""" return { @@ -55,9 +55,11 @@ def flow_schema(dps): vol.Optional(CONF_FAN_ORDERED_LIST): cv.string, vol.Optional(CONF_FAN_PRESET_LIST): cv.string, vol.Optional(CONF_FAN_SPEED_DPS_TYPE, default="string"): vol.In( - ["string", "integer", "list"]), + ["string", "integer", "list"] + ), } + class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Representation of a Tuya fan.""" @@ -76,20 +78,26 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._percentage = None self._preset = None self._speed_range = ( - self._config.get(CONF_FAN_SPEED_MIN), + self._config.get(CONF_FAN_SPEED_MIN), self._config.get(CONF_FAN_SPEED_MAX), ) - self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).replace(" ","").split(",") - self._preset_list = self._config.get(CONF_FAN_PRESET_LIST).replace(" ","").split(",") + self._ordered_list = ( + self._config.get(CONF_FAN_ORDERED_LIST).replace(" ", "").split(",") + ) + self._preset_list = ( + self._config.get(CONF_FAN_PRESET_LIST).replace(" ", "").split(",") + ) self._ordered_speed_dps_type = self._config.get(CONF_FAN_SPEED_DPS_TYPE) - - if (self._ordered_speed_dps_type == "list" - and isinstance(self._ordered_list, list) - and len(self._ordered_list) > 1): + + if ( + self._ordered_speed_dps_type == "list" + and isinstance(self._ordered_list, list) + and len(self._ordered_list) > 1 + ): _LOGGER.debug("Fan _use_ordered_list: %s", self._ordered_list) else: _LOGGER.debug("Fan _use_ordered_list: Not a valid list") - + @property def oscillating(self): """Return current oscillating status.""" @@ -115,7 +123,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) if percentage is not None: - await self.async_set_percentage(speed) + await self.async_set_percentage(percentage) else: self.schedule_update_ha_state() @@ -128,44 +136,50 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): async def async_set_percentage(self, percentage): """Set the speed of the fan.""" - _LOGGER.debug("Fan async_set_percentage: %s", percentage) - + if percentage is not None: if percentage == 0: return await self.async_turn_off() - + if not self.is_on: await self.async_turn_on() - + if self._ordered_speed_dps_type == "string": - send_speed = str(math.ceil( - percentage_to_ranged_value(self._speed_range, percentage))) + send_speed = str( + math.ceil(percentage_to_ranged_value(self._speed_range, percentage)) + ) await self._device.set_dp( - send_speed, self._config.get(CONF_FAN_SPEED_CONTROL)) - _LOGGER.debug("Fan async_set_percentage: %s > %s", - percentage, send_speed) - + send_speed, self._config.get(CONF_FAN_SPEED_CONTROL) + ) + _LOGGER.debug( + "Fan async_set_percentage: %s > %s", percentage, send_speed + ) + elif self._ordered_speed_dps_type == "integer": - send_speed = int(math.ceil( - percentage_to_ranged_value(self._speed_range, percentage))) + send_speed = int( + math.ceil(percentage_to_ranged_value(self._speed_range, percentage)) + ) await self._device.set_dp( - send_speed, self._config.get(CONF_FAN_SPEED_CONTROL)) - _LOGGER.debug("Fan async_set_percentage: %s > %s", - percentage, send_speed) + send_speed, self._config.get(CONF_FAN_SPEED_CONTROL) + ) + _LOGGER.debug( + "Fan async_set_percentage: %s > %s", percentage, send_speed + ) elif self._ordered_speed_dps_type == "list": send_speed = str( - percentage_to_ordered_list_item(self._ordered_list, percentage) - ) + percentage_to_ordered_list_item(self._ordered_list, percentage) + ) await self._device.set_dp( - send_speed, self._config.get(CONF_FAN_SPEED_CONTROL)) - _LOGGER.debug("Fan async_set_percentage: %s > %s", - percentage, send_speed) + send_speed, self._config.get(CONF_FAN_SPEED_CONTROL) + ) + _LOGGER.debug( + "Fan async_set_percentage: %s > %s", percentage, send_speed + ) self.schedule_update_ha_state() - async def async_oscillate(self, oscillating: bool) -> None: """Set oscillation.""" _LOGGER.debug("Fan async_oscillate: %s", oscillating) @@ -180,17 +194,15 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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() + await self._device.set_dp(value, self._config.get(CONF_FAN_DIRECTION)) + self.schedule_update_ha_state() async def async_set_preset_mode(self, preset_mode: str) -> None: - """Set the preset mode of the fan.""" + """Set the preset mode of the fan.""" _LOGGER.debug("Fan set preset: %s", preset_mode) await self._device.set_dp( preset_mode, self._config.get(CONF_FAN_PRESET_CONTROL) @@ -214,8 +226,8 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if self.has_config(CONF_FAN_PRESET_CONTROL): features |= SUPPORT_PRESET_MODE - return features - + return features + @property def speed_count(self) -> int: """Speed count for the fan.""" @@ -223,38 +235,57 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): _LOGGER.debug("Fan speed_count: %s", speed_count) return speed_count - def status_updated(self): """Get state of Tuya fan.""" - self._is_on = self.dps(self._dp_id) if self.has_config(CONF_FAN_PRESET_CONTROL): current_preset = self.dps_conf(CONF_FAN_PRESET_CONTROL) if current_preset is not None and current_preset in self._preset_list: - _LOGGER.debug("Fan current_preset in preset list: %s from %s", - current_preset, self._preset_list) + _LOGGER.debug( + "Fan current_preset in preset list: %s from %s", + current_preset, + self._preset_list, + ) self._preset = current_preset current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) if current_speed is not None: - if (self.has_config(CONF_FAN_PRESET_CONTROL) - and (CONF_FAN_SPEED_CONTROL == CONF_FAN_PRESET_CONTROL) - and (current_speed in self._preset_list)): - _LOGGER.debug("Fan current_speed in preset list: %s from %s", - current_speed, self._preset_list) + if ( + self.has_config(CONF_FAN_PRESET_CONTROL) + and (CONF_FAN_SPEED_CONTROL == CONF_FAN_PRESET_CONTROL) + and (current_speed in self._preset_list) + ): + _LOGGER.debug( + "Fan current_speed in preset list: %s from %s", + current_speed, + self._preset_list, + ) self._preset = current_speed elif self._ordered_speed_dps_type == "list": - _LOGGER.debug("Fan current_speed ordered_list_item_to_percentage: %s from %s", - current_speed, self._ordered_list) - self._percentage = ordered_list_item_to_percentage(self._ordered_list, current_speed) - - elif self._ordered_speed_dps_type == "string" or self._ordered_speed_dps_type == "integer" : - _LOGGER.debug("Fan current_speed ranged_value_to_percentage: %s from %s", - current_speed, self._speed_range) - self._percentage = ranged_value_to_percentage(self._speed_range, int(current_speed)) + _LOGGER.debug( + "Fan current_speed ordered_list_item_to_percentage: %s from %s", + current_speed, + self._ordered_list, + ) + self._percentage = ordered_list_item_to_percentage( + self._ordered_list, current_speed + ) + + elif ( + self._ordered_speed_dps_type == "string" + or self._ordered_speed_dps_type == "integer" + ): + _LOGGER.debug( + "Fan current_speed ranged_value_to_percentage: %s from %s", + current_speed, + self._speed_range, + ) + self._percentage = ranged_value_to_percentage( + self._speed_range, int(current_speed) + ) _LOGGER.debug("Fan current_percentage: %s", self._percentage) _LOGGER.debug("Fan current_preset: %s", self._preset) @@ -268,10 +299,10 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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) From e1ac7deace562f4d1e200964b6a44bc3c852e9a4 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 22 Dec 2021 11:35:04 +1030 Subject: [PATCH 38/43] remove blank line --- custom_components/localtuya/fan.py | 1 - 1 file changed, 1 deletion(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index ca501e8..0d9aeed 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -111,7 +111,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): preset_mode: str = None, **kwargs, ) -> None: - """Turn on the entity.""" _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) From a25bf75a4d13679961585357c9dfa62f524fe58c Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 22 Dec 2021 19:45:16 +1030 Subject: [PATCH 39/43] tox fixes --- custom_components/localtuya/fan.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 0d9aeed..e742481 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -107,6 +107,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): async def async_turn_on( self, + speed: str = None, percentage: int = None, preset_mode: str = None, **kwargs, @@ -116,9 +117,12 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): await self._device.set_dp(True, self._dp_id) if percentage is not None: await self.async_set_percentage(percentage) + elif preset_mode is not None: + _LOGGER.debug("Preset_mode not supported yet") else: self.schedule_update_ha_state() + async def async_turn_off(self, **kwargs) -> None: """Turn off the entity.""" _LOGGER.debug("Fan async_turn_off") @@ -133,7 +137,7 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if percentage is not None: if percentage == 0: return await self.async_turn_off() - elif not self.is_on: + if not self.is_on: await self.async_turn_on() if self._use_ordered_list: await self._device.set_dp( From 0e048af29b82ea547fdd92e52b1aecca2d7b9b17 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 22 Dec 2021 20:01:39 +1030 Subject: [PATCH 40/43] fix issue with last tox fix --- custom_components/localtuya/fan.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index e742481..39cd896 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -117,8 +117,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): await self._device.set_dp(True, self._dp_id) if percentage is not None: await self.async_set_percentage(percentage) - elif preset_mode is not None: - _LOGGER.debug("Preset_mode not supported yet") else: self.schedule_update_ha_state() From b748f0425cb4e7ac31bc60c0f21187dfba964eb3 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 22 Dec 2021 20:48:46 +1030 Subject: [PATCH 41/43] Remove blank line --- custom_components/localtuya/fan.py | 1 - 1 file changed, 1 deletion(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 39cd896..d2b4583 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -120,7 +120,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): else: self.schedule_update_ha_state() - async def async_turn_off(self, **kwargs) -> None: """Turn off the entity.""" _LOGGER.debug("Fan async_turn_off") From d878391d5e27b04a94b67fe20b163076c3f83a0f Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 22 Dec 2021 23:02:08 +1030 Subject: [PATCH 42/43] readme updated --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d645eaa..705fc0b 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,16 @@ localtuya: - platform: 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 friendly_name: Device Light From ddee11f74123f8627a26cf5b57ef460759d75837 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 22 Dec 2021 23:05:33 +1030 Subject: [PATCH 43/43] Revert "Merge branch 'add-string/integer-and-preset' into master" This reverts commit a6ef6a4b7f07c5277beb93801f1c1f69f69f7497, reversing changes made to b748f0425cb4e7ac31bc60c0f21187dfba964eb3. --- .github/workflows/tox.yaml | 2 +- custom_components/localtuya/const.py | 3 - custom_components/localtuya/fan.py | 149 ++++++------------ .../localtuya/translations/en.json | 10 +- 4 files changed, 48 insertions(+), 116 deletions(-) diff --git a/.github/workflows/tox.yaml b/.github/workflows/tox.yaml index 81d0350..40ca8d9 100644 --- a/.github/workflows/tox.yaml +++ b/.github/workflows/tox.yaml @@ -1,6 +1,6 @@ name: Tox PR CI -on: [push, pull_request, workflow_dispatch] +on: [pull_request] jobs: build: diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index a4da5ac..159bc12 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -41,9 +41,6 @@ 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" -CONF_FAN_SPEED_DPS_TYPE = "fan_speed_dps_type" -CONF_FAN_PRESET_CONTROL = "fan_preset_control" -CONF_FAN_PRESET_LIST = "fan_preset_list" # sensor CONF_SCALING = "scaling" diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 4ea98f4..d2b4583 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -11,7 +11,6 @@ from homeassistant.components.fan import ( DOMAIN, SUPPORT_DIRECTION, SUPPORT_OSCILLATE, - SUPPORT_PRESET_MODE, SUPPORT_SET_SPEED, FanEntity, ) @@ -30,10 +29,7 @@ from .const import ( CONF_FAN_DIRECTION_REV, CONF_FAN_ORDERED_LIST, CONF_FAN_OSCILLATING_CONTROL, - CONF_FAN_PRESET_CONTROL, - CONF_FAN_PRESET_LIST, CONF_FAN_SPEED_CONTROL, - CONF_FAN_SPEED_DPS_TYPE, CONF_FAN_SPEED_MAX, CONF_FAN_SPEED_MIN, ) @@ -47,16 +43,11 @@ def flow_schema(dps): vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_DIRECTION): vol.In(dps), - vol.Optional(CONF_FAN_PRESET_CONTROL): vol.In(dps), 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_MIN, default=1): cv.positive_int, vol.Optional(CONF_FAN_SPEED_MAX, default=9): cv.positive_int, - vol.Optional(CONF_FAN_ORDERED_LIST): cv.string, - vol.Optional(CONF_FAN_PRESET_LIST): cv.string, - vol.Optional(CONF_FAN_SPEED_DPS_TYPE, default="string"): vol.In( - ["string", "integer", "list"] - ), + vol.Optional(CONF_FAN_ORDERED_LIST, default="disabled"): cv.string, } @@ -76,27 +67,23 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): self._oscillating = None self._direction = None self._percentage = None - self._preset = 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).replace(" ", "").split(",") - ) - self._preset_list = ( - self._config.get(CONF_FAN_PRESET_LIST).replace(" ", "").split(",") - ) - self._ordered_speed_dps_type = self._config.get(CONF_FAN_SPEED_DPS_TYPE) + self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).split(",") + self._ordered_list_mode = None - if ( - self._ordered_speed_dps_type == "list" - and isinstance(self._ordered_list, list) - and len(self._ordered_list) > 1 - ): - _LOGGER.debug("Fan _use_ordered_list: %s", self._ordered_list) + 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: - _LOGGER.debug("Fan _use_ordered_list: Not a valid list") + self._use_ordered_list = False + _LOGGER.debug("Fan _use_ordered_list: %s", self._use_ordered_list) @property def oscillating(self): @@ -147,43 +134,35 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if percentage is not None: if percentage == 0: return await self.async_turn_off() - if not self.is_on: await self.async_turn_on() - - if self._ordered_speed_dps_type == "string": - send_speed = str( - math.ceil(percentage_to_ranged_value(self._speed_range, percentage)) - ) + if self._use_ordered_list: await self._device.set_dp( - send_speed, self._config.get(CONF_FAN_SPEED_CONTROL) + 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, send_speed + "Fan async_set_percentage: %s > %s", + percentage, + percentage_to_ordered_list_item(self._ordered_list, percentage), ) - elif self._ordered_speed_dps_type == "integer": - send_speed = int( - math.ceil(percentage_to_ranged_value(self._speed_range, percentage)) - ) + else: await self._device.set_dp( - send_speed, self._config.get(CONF_FAN_SPEED_CONTROL) + 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, send_speed + "Fan async_set_percentage: %s > %s", + percentage, + percentage_to_ranged_value(self._speed_range, percentage), ) - - elif self._ordered_speed_dps_type == "list": - send_speed = str( - percentage_to_ordered_list_item(self._ordered_list, percentage) - ) - await self._device.set_dp( - send_speed, self._config.get(CONF_FAN_SPEED_CONTROL) - ) - _LOGGER.debug( - "Fan async_set_percentage: %s > %s", percentage, send_speed - ) - self.schedule_update_ha_state() async def async_oscillate(self, oscillating: bool) -> None: @@ -203,18 +182,9 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): 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() - async def async_set_preset_mode(self, preset_mode: str) -> None: - """Set the preset mode of the fan.""" - _LOGGER.debug("Fan set preset: %s", preset_mode) - await self._device.set_dp( - preset_mode, self._config.get(CONF_FAN_PRESET_CONTROL) - ) - self.schedule_update_ha_state() - @property def supported_features(self) -> int: """Flag supported features.""" @@ -229,9 +199,6 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): if self.has_config(CONF_FAN_DIRECTION): features |= SUPPORT_DIRECTION - if self.has_config(CONF_FAN_PRESET_CONTROL): - features |= SUPPORT_PRESET_MODE - return features @property @@ -245,56 +212,30 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Get state of Tuya fan.""" self._is_on = self.dps(self._dp_id) - if self.has_config(CONF_FAN_PRESET_CONTROL): - current_preset = self.dps_conf(CONF_FAN_PRESET_CONTROL) - if current_preset is not None and current_preset in self._preset_list: - _LOGGER.debug( - "Fan current_preset in preset list: %s from %s", - current_preset, - self._preset_list, - ) - self._preset = current_preset - current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) - if current_speed is not None: - - if ( - self.has_config(CONF_FAN_PRESET_CONTROL) - and (CONF_FAN_SPEED_CONTROL == CONF_FAN_PRESET_CONTROL) - and (current_speed in self._preset_list) - ): - _LOGGER.debug( - "Fan current_speed in preset list: %s from %s", - current_speed, - self._preset_list, - ) - self._preset = current_speed - - elif self._ordered_speed_dps_type == "list": - _LOGGER.debug( - "Fan current_speed ordered_list_item_to_percentage: %s from %s", - current_speed, - self._ordered_list, - ) + if self._use_ordered_list: + _LOGGER.debug( + "Fan current_speed ordered_list_item_to_percentage: %s from %s", + current_speed, + self._ordered_list, + ) + if current_speed is not None: self._percentage = ordered_list_item_to_percentage( self._ordered_list, current_speed ) - elif ( - self._ordered_speed_dps_type == "string" - or self._ordered_speed_dps_type == "integer" - ): - _LOGGER.debug( - "Fan current_speed ranged_value_to_percentage: %s from %s", - current_speed, - self._speed_range, - ) + 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) - _LOGGER.debug("Fan current_preset: %s", self._preset) + _LOGGER.debug("Fan current_percentage: %s", self._percentage) if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 2014b0b..2cb903e 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -77,10 +77,7 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string", - "fan_speed_dps_type": "type of speed dps control. integer/string/list", - "fan_preset_control": "Fan preset dps. Can be the same as speed", - "fan_preset_list": "Fan preset list. Comma separated" + "fan_direction_reverse": "reverse dps string" } } } @@ -135,10 +132,7 @@ "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", "fan_direction":"fan direction dps", "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string", - "fan_speed_dps_type": "type of speed dps control. integer/string/list", - "fan_preset_control": "Fan preset dps. CAn be the same as speed", - "fan_preset_list": "Fan preset list. Comma separated" + "fan_direction_reverse": "reverse dps string" } }, "yaml_import": {