diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index a91a8c1..0209bb3 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -30,6 +30,13 @@ CONF_SET_POSITION_DP = "set_position_dp" CONF_POSITION_INVERTED = "position_inverted" 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" + # sensor CONF_SCALING = "scaling" diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 95eb0b1..beb4001 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,111 +1,156 @@ -"""Platform to locally control Tuya-based fan devices.""" -import logging -from functools import partial - -from homeassistant.components.fan import ( - DOMAIN, - SPEED_HIGH, - SPEED_LOW, - SPEED_MEDIUM, - SPEED_OFF, - SUPPORT_OSCILLATE, - SUPPORT_SET_SPEED, - FanEntity, -) - -from .common import LocalTuyaEntity, async_setup_entry - -_LOGGER = logging.getLogger(__name__) - - -def flow_schema(dps): - """Return schema used in config flow.""" - return {} - - -class LocaltuyaFan(LocalTuyaEntity, FanEntity): - """Representation of a Tuya fan.""" - - def __init__( - self, - device, - config_entry, - fanid, - **kwargs, - ): - """Initialize the entity.""" - super().__init__(device, config_entry, fanid, **kwargs) - self._is_on = False - self._speed = None - self._oscillating = None - - @property - def oscillating(self): - """Return current oscillating status.""" - return self._oscillating - - @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 - - @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.""" - await self._device.set_dp(True, "1") - if speed is not None: - await self.async_set_speed(speed) - else: - self.schedule_update_ha_state() - - async def async_turn_off(self, **kwargs) -> None: - """Turn off the entity.""" - await self._device.set_dp(False, "1") - self.schedule_update_ha_state() - - async def async_set_speed(self, speed: str) -> None: - """Set the speed of the fan.""" - if speed == SPEED_OFF: - await self._device.set_dp(False, "1") - elif speed == SPEED_LOW: - await self._device.set_dp("1", "2") - elif speed == SPEED_MEDIUM: - await self._device.set_dp("2", "2") - elif speed == SPEED_HIGH: - await self._device.set_dp("3", "2") - self.schedule_update_ha_state() - - async def async_oscillate(self, oscillating: bool) -> None: - """Set oscillation.""" - await self._device.set_dp(oscillating, "8") - self.schedule_update_ha_state() - - @property - def supported_features(self) -> int: - """Flag supported features.""" - return SUPPORT_SET_SPEED | SUPPORT_OSCILLATE - - def status_updated(self): - """Get state of Tuya fan.""" - self._is_on = self._status["dps"]["1"] - if not self._status["dps"]["1"]: - self._speed = SPEED_OFF - elif self._status["dps"]["2"] == "1": - self._speed = SPEED_LOW - elif self._status["dps"]["2"] == "2": - self._speed = SPEED_MEDIUM - elif self._status["dps"]["2"] == "3": - self._speed = SPEED_HIGH - self._oscillating = self._status["dps"]["8"] - - -async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) +"""Platform to locally control Tuya-based fan devices.""" +import logging +from functools import partial + +import voluptuous as vol +from homeassistant.components.fan import ( + DOMAIN, + SPEED_HIGH, + SPEED_LOW, + SPEED_MEDIUM, + SPEED_OFF, + 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_SPEED_HIGH, + CONF_FAN_SPEED_LOW, + CONF_FAN_SPEED_MEDIUM, +) + +_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_SPEED_LOW, default=SPEED_LOW): vol.In( + [SPEED_LOW, "1", "2"] + ), + 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"] + ), + } + + +class LocaltuyaFan(LocalTuyaEntity, FanEntity): + """Representation of a Tuya fan.""" + + def __init__( + self, + device, + config_entry, + fanid, + **kwargs, + ): + """Initialize the entity.""" + super().__init__(device, config_entry, fanid, **kwargs) + self._is_on = False + self._speed = None + self._oscillating = None + + @property + def oscillating(self): + """Return current oscillating status.""" + return self._oscillating + + @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 + + @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.""" + await self._device.set_dp(True, self._dp_id) + if speed is not None: + await self.async_set_speed(speed) + else: + self.schedule_update_ha_state() + + async def async_turn_off(self, **kwargs) -> None: + """Turn off the entity.""" + await self._device.set_dp(False, self._dp_id) + 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.""" + await self._device.set_dp( + oscillating, self._config.get(CONF_FAN_OSCILLATING_CONTROL) + ) + self.schedule_update_ha_state() + + @property + def supported_features(self) -> int: + """Flag supported features.""" + supports = 0 + + if self.has_config(CONF_FAN_OSCILLATING_CONTROL): + supports |= SUPPORT_OSCILLATE + if self.has_config(CONF_FAN_SPEED_CONTROL): + supports |= SUPPORT_SET_SPEED + + return supports + + 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._dps_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: + _LOGGER.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) + + +async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) diff --git a/custom_components/localtuya/light.py b/custom_components/localtuya/light.py index ab7b8f7..4484696 100644 --- a/custom_components/localtuya/light.py +++ b/custom_components/localtuya/light.py @@ -4,7 +4,6 @@ import textwrap from functools import partial import homeassistant.util.color as color_util - import voluptuous as vol from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -18,11 +17,7 @@ from homeassistant.components.light import ( SUPPORT_EFFECT, LightEntity, ) -from homeassistant.const import ( - CONF_BRIGHTNESS, - CONF_COLOR_TEMP, - CONF_SCENE, -) +from homeassistant.const import CONF_BRIGHTNESS, CONF_COLOR_TEMP, CONF_SCENE from .common import LocalTuyaEntity, async_setup_entry from .const import ( diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 5012420..e70ad50 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -65,7 +65,12 @@ "color_temp_min_kelvin": "Minimum Color Temperature in K", "color_temp_max_kelvin": "Maximum Color Temperature in K", "music_mode": "Music mode available", - "scene": "Scene" + "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" } } } @@ -111,7 +116,12 @@ "color_temp_min_kelvin": "Minimum Color Temperature in K", "color_temp_max_kelvin": "Maximum Color Temperature in K", "music_mode": "Music mode available", - "scene": "Scene" + "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" } }, "yaml_import": { @@ -121,4 +131,4 @@ } }, "title": "LocalTuya" -} \ No newline at end of file +}