Add support for min/max dimmer brightness in Tuya (#58165)

This commit is contained in:
Franck Nijhof 2021-10-22 20:28:16 +02:00 committed by GitHub
parent c7b4542624
commit b413f7434f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 194 additions and 14 deletions

View file

@ -1136,6 +1136,7 @@ omit =
homeassistant/components/tuya/sensor.py
homeassistant/components/tuya/siren.py
homeassistant/components/tuya/switch.py
homeassistant/components/tuya/util.py
homeassistant/components/tuya/vacuum.py
homeassistant/components/twentemilieu/const.py
homeassistant/components/twentemilieu/sensor.py

View file

@ -12,6 +12,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo, Entity
from .const import DOMAIN, TUYA_HA_SIGNAL_UPDATE_ENTITY
from .util import remap_value
_LOGGER = logging.getLogger(__name__)
@ -53,9 +54,7 @@ class IntegerTypeData:
reverse: bool = False,
) -> float:
"""Remap a value from this range to a new range."""
if reverse:
value = self.max - value + self.min
return ((value - self.min) / (self.max - self.min)) * (to_max - to_min) + to_min
return remap_value(value, self.min, self.max, to_min, to_max, reverse)
def remap_value_from(
self,
@ -65,11 +64,7 @@ class IntegerTypeData:
reverse: bool = False,
) -> float:
"""Remap a value from its current range to this range."""
if reverse:
value = from_max - value + from_min
return ((value - from_min) / (from_max - from_min)) * (
self.max - self.min
) + self.min
return remap_value(value, from_min, from_max, self.min, self.max, reverse)
@classmethod
def from_json(cls, data: str) -> IntegerTypeData:

View file

@ -140,6 +140,12 @@ class DPCode(str, Enum):
BRIGHT_VALUE_2 = "bright_value_2"
BRIGHT_VALUE_3 = "bright_value_3"
BRIGHT_VALUE_V2 = "bright_value_v2"
BRIGHTNESS_MAX_1 = "brightness_max_1"
BRIGHTNESS_MAX_2 = "brightness_max_2"
BRIGHTNESS_MAX_3 = "brightness_max_3"
BRIGHTNESS_MIN_1 = "brightness_min_1"
BRIGHTNESS_MIN_2 = "brightness_min_2"
BRIGHTNESS_MIN_3 = "brightness_min_3"
C_F = "c_f" # Temperature unit switching
CH2O_STATE = "ch2o_state"
CH2O_VALUE = "ch2o_value"

View file

@ -26,16 +26,19 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import HomeAssistantTuyaData
from .base import IntegerTypeData, TuyaEntity
from .const import DOMAIN, TUYA_DISCOVERY_NEW, DPCode, WorkMode
from .util import remap_value
@dataclass
class TuyaLightEntityDescription(LightEntityDescription):
"""Describe an Tuya light entity."""
color_mode: DPCode | None = None
brightness_max: DPCode | None = None
brightness_min: DPCode | None = None
brightness: DPCode | tuple[DPCode, ...] | None = None
color_temp: DPCode | tuple[DPCode, ...] | None = None
color_data: DPCode | tuple[DPCode, ...] | None = None
color_mode: DPCode | None = None
color_temp: DPCode | tuple[DPCode, ...] | None = None
LIGHTS: dict[str, tuple[TuyaLightEntityDescription, ...]] = {
@ -120,16 +123,22 @@ LIGHTS: dict[str, tuple[TuyaLightEntityDescription, ...]] = {
key=DPCode.SWITCH_LED_1,
name="Light",
brightness=DPCode.BRIGHT_VALUE_1,
brightness_max=DPCode.BRIGHTNESS_MAX_1,
brightness_min=DPCode.BRIGHTNESS_MIN_1,
),
TuyaLightEntityDescription(
key=DPCode.SWITCH_LED_2,
name="Light 2",
brightness=DPCode.BRIGHT_VALUE_2,
brightness_max=DPCode.BRIGHTNESS_MAX_2,
brightness_min=DPCode.BRIGHTNESS_MIN_2,
),
TuyaLightEntityDescription(
key=DPCode.SWITCH_LED_3,
name="Light 3",
brightness=DPCode.BRIGHT_VALUE_3,
brightness_max=DPCode.BRIGHTNESS_MAX_3,
brightness_min=DPCode.BRIGHTNESS_MIN_3,
),
),
# Dimmer
@ -276,6 +285,8 @@ class TuyaLightEntity(TuyaEntity, LightEntity):
entity_description: TuyaLightEntityDescription
_brightness_dpcode: DPCode | None = None
_brightness_max_type: IntegerTypeData | None = None
_brightness_min_type: IntegerTypeData | None = None
_brightness_type: IntegerTypeData | None = None
_color_data_dpcode: DPCode | None = None
_color_data_type: ColorTypeData | None = None
@ -349,6 +360,20 @@ class TuyaLightEntity(TuyaEntity, LightEntity):
device.status_range[self._brightness_dpcode].values
)
# Check if min/max capable
if (
description.brightness_max is not None
and description.brightness_min is not None
and description.brightness_max in device.function
and description.brightness_min in device.function
):
self._brightness_max_type = IntegerTypeData.from_json(
device.status_range[description.brightness_max].values
)
self._brightness_min_type = IntegerTypeData.from_json(
device.status_range[description.brightness_min].values
)
# Update internals based on found color temperature dpcode
if self._color_temp_dpcode:
self._attr_supported_color_modes.add(COLOR_MODE_COLOR_TEMP)
@ -456,12 +481,47 @@ class TuyaLightEntity(TuyaEntity, LightEntity):
and self.color_mode != COLOR_MODE_HS
and self._brightness_type
):
brightness = kwargs[ATTR_BRIGHTNESS]
# If there is a min/max value, the brightness is actually limited.
# Meaning it is actually not on a 0-255 scale.
if (
self._brightness_max_type is not None
and self._brightness_min_type is not None
and self.entity_description.brightness_max is not None
and self.entity_description.brightness_min is not None
and (
brightness_max := self.device.status.get(
self.entity_description.brightness_max
)
)
is not None
and (
brightness_min := self.device.status.get(
self.entity_description.brightness_min
)
)
is not None
):
# Remap values onto our scale
brightness_max = self._brightness_max_type.remap_value_to(
brightness_max
)
brightness_min = self._brightness_min_type.remap_value_to(
brightness_min
)
# Remap the brightness value from their min-max to our 0-255 scale
brightness = remap_value(
brightness,
to_min=brightness_min,
to_max=brightness_max,
)
commands += [
{
"code": self._brightness_dpcode,
"value": round(
self._brightness_type.remap_value_from(kwargs[ATTR_BRIGHTNESS])
),
"value": round(self._brightness_type.remap_value_from(brightness)),
},
]
@ -485,7 +545,41 @@ class TuyaLightEntity(TuyaEntity, LightEntity):
if brightness is None:
return None
return round(self._brightness_type.remap_value_to(brightness))
# Remap value to our scale
brightness = self._brightness_type.remap_value_to(brightness)
# If there is a min/max value, the brightness is actually limited.
# Meaning it is actually not on a 0-255 scale.
if (
self._brightness_max_type is not None
and self._brightness_min_type is not None
and self.entity_description.brightness_max is not None
and self.entity_description.brightness_min is not None
and (
brightness_max := self.device.status.get(
self.entity_description.brightness_max
)
)
is not None
and (
brightness_min := self.device.status.get(
self.entity_description.brightness_min
)
)
is not None
):
# Remap values onto our scale
brightness_max = self._brightness_max_type.remap_value_to(brightness_max)
brightness_min = self._brightness_min_type.remap_value_to(brightness_min)
# Remap the brightness value from their min-max to our 0-255 scale
brightness = remap_value(
brightness,
from_min=brightness_min,
from_max=brightness_max,
)
return round(brightness)
@property
def color_temp(self) -> int | None:

View file

@ -78,6 +78,74 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = {
entity_category=ENTITY_CATEGORY_CONFIG,
),
),
# Dimmer Switch
# https://developer.tuya.com/en/docs/iot/categorytgkg?id=Kaiuz0ktx7m0o
"tgkg": (
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MIN_1,
name="Minimum Brightness",
icon="mdi:lightbulb-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MAX_1,
name="Maximum Brightness",
icon="mdi:lightbulb-on-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MIN_2,
name="Minimum Brightness 2",
icon="mdi:lightbulb-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MAX_2,
name="Maximum Brightness 2",
icon="mdi:lightbulb-on-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MIN_3,
name="Minimum Brightness 3",
icon="mdi:lightbulb-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MAX_3,
name="Maximum Brightness 3",
icon="mdi:lightbulb-on-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
),
# Dimmer Switch
# https://developer.tuya.com/en/docs/iot/categorytgkg?id=Kaiuz0ktx7m0o
"tgq": (
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MIN_1,
name="Minimum Brightness",
icon="mdi:lightbulb-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MAX_1,
name="Maximum Brightness",
icon="mdi:lightbulb-on-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MIN_2,
name="Minimum Brightness 2",
icon="mdi:lightbulb-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
NumberEntityDescription(
key=DPCode.BRIGHTNESS_MAX_2,
name="Maximum Brightness 2",
icon="mdi:lightbulb-on-outline",
entity_category=ENTITY_CATEGORY_CONFIG,
),
),
# Vibration Sensor
# https://developer.tuya.com/en/docs/iot/categoryzd?id=Kaiuz3a5vrzno
"zd": (

View file

@ -0,0 +1,16 @@
"""Utility methods for the Tuya integration."""
from __future__ import annotations
def remap_value(
value: float | int,
from_min: float | int = 0,
from_max: float | int = 255,
to_min: float | int = 0,
to_max: float | int = 255,
reverse: bool = False,
) -> float:
"""Remap a value from its current range, to a new range."""
if reverse:
value = from_max - value + from_min
return ((value - from_min) / (from_max - from_min)) * (to_max - to_min) + to_min