Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update to the new fan platform #542

Merged
merged 53 commits into from Jan 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
ec793a1
Update en.json
tinglis1 Aug 18, 2021
5217014
Update const.py
tinglis1 Aug 18, 2021
761a5f1
Update fan.py
tinglis1 Aug 18, 2021
4eb1806
Update fan.py
tinglis1 Aug 18, 2021
b8acadc
working
tinglis1 Aug 18, 2021
e613075
update to new fan entity percentage configuration
tinglis1 Aug 19, 2021
94c3af1
update to new fan entity percentage
tinglis1 Aug 19, 2021
4cc7ffc
update to new fan entity configuration
tinglis1 Aug 19, 2021
5cbf556
remove comments
tinglis1 Aug 19, 2021
45890cf
Merge branch 'master' into master
tinglis1 Aug 26, 2021
8196703
fixed logger error
tinglis1 Sep 2, 2021
3eacbbd
spelling error
tinglis1 Oct 5, 2021
3fca556
Update fan.py
psbankar Oct 6, 2021
3f2606a
Merge pull request #1 from psbankar/master
tinglis1 Oct 20, 2021
30f95d8
Merge branch 'master' into master
tinglis1 Oct 22, 2021
ec223ba
add presets and integer/string option
tinglis1 Oct 26, 2021
6f281a0
missing imports
tinglis1 Oct 26, 2021
0c59ddc
correctly split preset and speed in updates
tinglis1 Oct 26, 2021
7ac4fe5
Revert "correctly split preset and speed in updates"
tinglis1 Oct 26, 2021
0f43923
Revert "missing imports"
tinglis1 Oct 26, 2021
c8e660d
Revert "add presets and integer/string option"
tinglis1 Oct 26, 2021
c99a93b
Fix speed variable error
tinglis1 Nov 9, 2021
9bf9b9f
fix formatting errors
tinglis1 Nov 11, 2021
af9f11a
fix formatting errors
tinglis1 Nov 11, 2021
77f9159
workflow_dispatch
tinglis1 Nov 11, 2021
eda45fc
fix tox errors
tinglis1 Nov 11, 2021
e741b2f
Merge branch 'master' of https://github.com/tinglis1/localtuya
tinglis1 Nov 11, 2021
f175394
checks updates and tox fixes
tinglis1 Nov 11, 2021
c30c68d
tox
tinglis1 Nov 11, 2021
01b9b00
fix requirements
tinglis1 Nov 11, 2021
6e6cd08
fix tox errors
tinglis1 Nov 11, 2021
95ef271
more tox fixes
tinglis1 Nov 11, 2021
a171430
fix error
tinglis1 Nov 11, 2021
e55cd20
line length issue
tinglis1 Nov 11, 2021
8d5da3d
remove dev tool changes fro branch
tinglis1 Nov 11, 2021
c9c96ad
revert dev tool config files for master branch
tinglis1 Nov 11, 2021
44ba820
Merge pull request #2 from tinglis1/dev
tinglis1 Nov 11, 2021
e979a4b
update dev checker tools
tinglis1 Nov 11, 2021
994aac3
Merge branch 'add-string/integer-and-preset' of https://github.com/ti…
tinglis1 Nov 11, 2021
445b62e
tox.yaml
tinglis1 Nov 11, 2021
1c2f4ba
Delete settings.json
tinglis1 Nov 11, 2021
e8aa5ac
integration inputs
tinglis1 Nov 11, 2021
d600cba
fix tox errors
tinglis1 Nov 11, 2021
ced1550
Merge branch 'master' into master
tinglis1 Dec 17, 2021
88a5a73
Merge branch 'master' into master
rospogrigio Dec 21, 2021
e1ac7de
remove blank line
tinglis1 Dec 22, 2021
a25bf75
tox fixes
tinglis1 Dec 22, 2021
0e048af
fix issue with last tox fix
tinglis1 Dec 22, 2021
b748f04
Remove blank line
tinglis1 Dec 22, 2021
a6ef6a4
Merge branch 'add-string/integer-and-preset' into master
tinglis1 Dec 22, 2021
d878391
readme updated
tinglis1 Dec 22, 2021
ddee11f
Revert "Merge branch 'add-string/integer-and-preset' into master"
tinglis1 Dec 22, 2021
764993f
Merge branch 'master' into master
rospogrigio Jan 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Expand Up @@ -65,7 +65,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
Expand Down
9 changes: 6 additions & 3 deletions custom_components/localtuya/const.py
Expand Up @@ -35,9 +35,12 @@
# 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_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_DIRECTION_FWD = "fan_direction_forward"
CONF_FAN_DIRECTION_REV = "fan_direction_reverse"

# sensor
CONF_SCALING = "scaling"
Expand Down
213 changes: 153 additions & 60 deletions custom_components/localtuya/fan.py
@@ -1,26 +1,37 @@
"""Platform to locally control Tuya-based fan devices."""
import logging
import math
from functools import partial

import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from homeassistant.components.fan import (
DIRECTION_FORWARD,
DIRECTION_REVERSE,
DOMAIN,
SPEED_HIGH,
SPEED_LOW,
SPEED_MEDIUM,
SPEED_OFF,
SUPPORT_DIRECTION,
SUPPORT_OSCILLATE,
SUPPORT_SET_SPEED,
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_DIRECTION,
CONF_FAN_DIRECTION_FWD,
CONF_FAN_DIRECTION_REV,
CONF_FAN_ORDERED_LIST,
CONF_FAN_OSCILLATING_CONTROL,
CONF_FAN_SPEED_CONTROL,
CONF_FAN_SPEED_HIGH,
CONF_FAN_SPEED_LOW,
CONF_FAN_SPEED_MEDIUM,
CONF_FAN_SPEED_MAX,
CONF_FAN_SPEED_MIN,
)

_LOGGER = logging.getLogger(__name__)
Expand All @@ -31,15 +42,12 @@ 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", "big"]
),
vol.Optional(CONF_FAN_DIRECTION): 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,
}


Expand All @@ -56,28 +64,46 @@ def __init__(
"""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
self._speed_range = (
self._config.get(CONF_FAN_SPEED_MIN),
self._config.get(CONF_FAN_SPEED_MAX),
)
self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).split(",")
self._ordered_list_mode = None

if isinstance(self._ordered_list, list) and len(self._ordered_list) > 1:
self._use_ordered_list = True
_LOGGER.debug(
"Fan _use_ordered_list: %s > %s",
self._use_ordered_list,
self._ordered_list,
)
else:
self._use_ordered_list = False
_LOGGER.debug("Fan _use_ordered_list: %s", self._use_ordered_list)

@property
def oscillating(self):
"""Return current oscillating status."""
return self._oscillating

@property
def current_direction(self):
"""Return the current direction of the fan."""
return self._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

@property
def speed_list(self) -> list:
"""Get the list of available speeds."""
return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
def percentage(self):
"""Return the current percentage."""
return self._percentage

async def async_turn_on(
self,
Expand All @@ -87,76 +113,143 @@ async def async_turn_on(
**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(percentage)
else:
self.schedule_update_ha_state()

async def async_turn_off(self, **kwargs) -> None:
"""Turn off the entity."""
_LOGGER.debug("Fan async_turn_off")

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 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)
)
_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._use_ordered_list:
await self._device.set_dp(
str(
percentage_to_ordered_list_item(self._ordered_list, percentage)
),
self._config.get(CONF_FAN_SPEED_CONTROL),
)
_LOGGER.debug(
"Fan async_set_percentage: %s > %s",
percentage,
percentage_to_ordered_list_item(self._ordered_list, percentage),
)

self.schedule_update_ha_state()
else:
await self._device.set_dp(
str(
math.ceil(
percentage_to_ranged_value(self._speed_range, percentage)
)
),
self._config.get(CONF_FAN_SPEED_CONTROL),
)
_LOGGER.debug(
"Fan async_set_percentage: %s > %s",
percentage,
percentage_to_ranged_value(self._speed_range, percentage),
)
self.schedule_update_ha_state()

async def async_oscillate(self, oscillating: bool) -> None:
"""Set oscillation."""
_LOGGER.debug("Fan async_oscillate: %s", oscillating)
await self._device.set_dp(
oscillating, self._config.get(CONF_FAN_OSCILLATING_CONTROL)
)
self.schedule_update_ha_state()

async def async_set_direction(self, direction):
"""Set the direction of the fan."""
_LOGGER.debug("Fan async_set_direction: %s", direction)

if direction == DIRECTION_FORWARD:
value = self._config.get(CONF_FAN_DIRECTION_FWD)

if direction == DIRECTION_REVERSE:
value = self._config.get(CONF_FAN_DIRECTION_REV)
await self._device.set_dp(value, self._config.get(CONF_FAN_DIRECTION))
self.schedule_update_ha_state()

@property
def supported_features(self) -> int:
"""Flag supported features."""
supports = 0
features = 0

if self.has_config(CONF_FAN_OSCILLATING_CONTROL):
supports |= SUPPORT_OSCILLATE
features |= SUPPORT_OSCILLATE

if self.has_config(CONF_FAN_SPEED_CONTROL):
supports |= SUPPORT_SET_SPEED
features |= SUPPORT_SET_SPEED

if self.has_config(CONF_FAN_DIRECTION):
features |= SUPPORT_DIRECTION

return supports
return features

@property
def speed_count(self) -> int:
"""Speed count for the fan."""
speed_count = int_states_in_range(self._speed_range)
_LOGGER.debug("Fan speed_count: %s", speed_count)
return speed_count

def status_updated(self):
"""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:
self.warning(
"%s/%s: Ignoring unknown fan controller state: %s",
self.name,
self.entity_id,
self.dps_conf(CONF_FAN_SPEED_CONTROL),
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)
)
self._speed = None

_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:
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)
5 changes: 1 addition & 4 deletions custom_components/localtuya/number.py
Expand Up @@ -4,10 +4,7 @@

import voluptuous as vol
from homeassistant.components.number import DOMAIN, NumberEntity
from homeassistant.const import (
CONF_DEVICE_CLASS,
STATE_UNKNOWN,
)
from homeassistant.const import CONF_DEVICE_CLASS, STATE_UNKNOWN

from .common import LocalTuyaEntity, async_setup_entry

Expand Down
5 changes: 1 addition & 4 deletions custom_components/localtuya/select.py
Expand Up @@ -4,10 +4,7 @@

import voluptuous as vol
from homeassistant.components.select import DOMAIN, SelectEntity
from homeassistant.const import (
CONF_DEVICE_CLASS,
STATE_UNKNOWN,
)
from homeassistant.const import CONF_DEVICE_CLASS, STATE_UNKNOWN

from .common import LocalTuyaEntity, async_setup_entry

Expand Down