Merge remote-tracking branch 'upstream/master' into climate
This commit is contained in:
@@ -71,6 +71,7 @@ localtuya:
|
|||||||
color_mode: 21 # Optional, usually 2 or 21, default: "none"
|
color_mode: 21 # Optional, usually 2 or 21, default: "none"
|
||||||
brightness: 22 # Optional, usually 3 or 22, default: "none"
|
brightness: 22 # Optional, usually 3 or 22, default: "none"
|
||||||
color_temp: 23 # Optional, usually 4 or 23, default: "none"
|
color_temp: 23 # Optional, usually 4 or 23, default: "none"
|
||||||
|
color_temp_reverse: false # Optional, default: false
|
||||||
color: 24 # Optional, usually 5 (RGB_HSV) or 24 (HSV), default: "none"
|
color: 24 # Optional, usually 5 (RGB_HSV) or 24 (HSV), default: "none"
|
||||||
brightness_lower: 29 # Optional, usually 0 or 29, default: 29
|
brightness_lower: 29 # Optional, usually 0 or 29, default: 29
|
||||||
brightness_upper: 1000 # Optional, usually 255 or 1000, default: 1000
|
brightness_upper: 1000 # Optional, usually 255 or 1000, default: 1000
|
||||||
@@ -79,7 +80,6 @@ localtuya:
|
|||||||
scene: 25 # Optional, usually 6 (RGB_HSV) or 25 (HSV), default: "none"
|
scene: 25 # Optional, usually 6 (RGB_HSV) or 25 (HSV), default: "none"
|
||||||
music_mode: False # Optional, some use internal mic, others, phone mic. Only internal mic is supported, default: "False"
|
music_mode: False # Optional, some use internal mic, others, phone mic. Only internal mic is supported, default: "False"
|
||||||
|
|
||||||
|
|
||||||
- platform: sensor
|
- platform: sensor
|
||||||
friendly_name: Plug Voltage
|
friendly_name: Plug Voltage
|
||||||
id: 20
|
id: 20
|
||||||
@@ -107,6 +107,8 @@ Start by going to Configuration - Integration and pressing the "+" button to cre
|
|||||||
Wait for 6 seconds for the scanning of the devices in your LAN. Then, a drop-down menu will appear containing the list of detected devices: you can
|
Wait for 6 seconds for the scanning of the devices in your LAN. Then, a drop-down menu will appear containing the list of detected devices: you can
|
||||||
select one of these, or manually input all the parameters.
|
select one of these, or manually input all the parameters.
|
||||||
|
|
||||||
|
> **Note: The tuya app on your device must be closed for the following steps to work reliably.**
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you have selected one entry, you only need to input the device's Friendly Name and the localKey.
|
If you have selected one entry, you only need to input the device's Friendly Name and the localKey.
|
||||||
|
@@ -116,6 +116,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self.dps_to_request = {}
|
self.dps_to_request = {}
|
||||||
self._is_closing = False
|
self._is_closing = False
|
||||||
self._connect_task = None
|
self._connect_task = None
|
||||||
|
self._disconnect_task = None
|
||||||
self.set_logger(_LOGGER, config_entry[CONF_DEVICE_ID])
|
self.set_logger(_LOGGER, config_entry[CONF_DEVICE_ID])
|
||||||
|
|
||||||
# This has to be done in case the device type is type_0d
|
# This has to be done in case the device type is type_0d
|
||||||
@@ -133,6 +134,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self._connect_task = asyncio.create_task(self._make_connection())
|
self._connect_task = asyncio.create_task(self._make_connection())
|
||||||
|
|
||||||
async def _make_connection(self):
|
async def _make_connection(self):
|
||||||
|
"""Subscribe localtuya entity events."""
|
||||||
self.debug("Connecting to %s", self._config_entry[CONF_HOST])
|
self.debug("Connecting to %s", self._config_entry[CONF_HOST])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -151,6 +153,19 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
raise Exception("Failed to retrieve status")
|
raise Exception("Failed to retrieve status")
|
||||||
|
|
||||||
self.status_updated(status)
|
self.status_updated(status)
|
||||||
|
|
||||||
|
def _new_entity_handler(entity_id):
|
||||||
|
self.debug(
|
||||||
|
"New entity %s was added to %s",
|
||||||
|
entity_id,
|
||||||
|
self._config_entry[CONF_HOST],
|
||||||
|
)
|
||||||
|
self._dispatch_status()
|
||||||
|
|
||||||
|
signal = f"localtuya_entity_{self._config_entry[CONF_DEVICE_ID]}"
|
||||||
|
self._disconnect_task = async_dispatcher_connect(
|
||||||
|
self._hass, signal, _new_entity_handler
|
||||||
|
)
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
self.exception(f"Connect to {self._config_entry[CONF_HOST]} failed")
|
self.exception(f"Connect to {self._config_entry[CONF_HOST]} failed")
|
||||||
if self._interface is not None:
|
if self._interface is not None:
|
||||||
@@ -166,6 +181,8 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
await self._connect_task
|
await self._connect_task
|
||||||
if self._interface is not None:
|
if self._interface is not None:
|
||||||
await self._interface.close()
|
await self._interface.close()
|
||||||
|
if self._disconnect_task is not None:
|
||||||
|
self._disconnect_task()
|
||||||
|
|
||||||
async def set_dp(self, state, dp_index):
|
async def set_dp(self, state, dp_index):
|
||||||
"""Change value of a DP of the Tuya device."""
|
"""Change value of a DP of the Tuya device."""
|
||||||
@@ -195,7 +212,9 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
def status_updated(self, status):
|
def status_updated(self, status):
|
||||||
"""Device updated status."""
|
"""Device updated status."""
|
||||||
self._status.update(status)
|
self._status.update(status)
|
||||||
|
self._dispatch_status()
|
||||||
|
|
||||||
|
def _dispatch_status(self):
|
||||||
signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}"
|
signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}"
|
||||||
async_dispatcher_send(self._hass, signal, self._status)
|
async_dispatcher_send(self._hass, signal, self._status)
|
||||||
|
|
||||||
@@ -243,10 +262,14 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
|
|||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
signal = f"localtuya_{self._config_entry.data[CONF_DEVICE_ID]}"
|
signal = f"localtuya_{self._config_entry.data[CONF_DEVICE_ID]}"
|
||||||
|
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
async_dispatcher_connect(self.hass, signal, _update_handler)
|
async_dispatcher_connect(self.hass, signal, _update_handler)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
signal = f"localtuya_entity_{self._config_entry.data[CONF_DEVICE_ID]}"
|
||||||
|
async_dispatcher_send(self.hass, signal, self.entity_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self):
|
||||||
"""Return device information for the device registry."""
|
"""Return device information for the device registry."""
|
||||||
|
@@ -16,6 +16,7 @@ CONF_COLOR = "color"
|
|||||||
CONF_COLOR_MODE = "color_mode"
|
CONF_COLOR_MODE = "color_mode"
|
||||||
CONF_COLOR_TEMP_MIN_KELVIN = "color_temp_min_kelvin"
|
CONF_COLOR_TEMP_MIN_KELVIN = "color_temp_min_kelvin"
|
||||||
CONF_COLOR_TEMP_MAX_KELVIN = "color_temp_max_kelvin"
|
CONF_COLOR_TEMP_MAX_KELVIN = "color_temp_max_kelvin"
|
||||||
|
CONF_COLOR_TEMP_REVERSE = "color_temp_reverse"
|
||||||
CONF_MUSIC_MODE = "music_mode"
|
CONF_MUSIC_MODE = "music_mode"
|
||||||
|
|
||||||
# switch
|
# switch
|
||||||
@@ -64,6 +65,16 @@ DATA_DISCOVERY = "discovery"
|
|||||||
DOMAIN = "localtuya"
|
DOMAIN = "localtuya"
|
||||||
|
|
||||||
# Platforms in this list must support config flows
|
# Platforms in this list must support config flows
|
||||||
PLATFORMS = ["binary_sensor", "cover", "climate", "fan", "light", "sensor", "switch"]
|
PLATFORMS = [
|
||||||
|
"binary_sensor",
|
||||||
|
"climate",
|
||||||
|
"cover",
|
||||||
|
"fan",
|
||||||
|
"light",
|
||||||
|
"number",
|
||||||
|
"select",
|
||||||
|
"sensor",
|
||||||
|
"switch",
|
||||||
|
]
|
||||||
|
|
||||||
TUYA_DEVICE = "tuya_device"
|
TUYA_DEVICE = "tuya_device"
|
||||||
|
@@ -79,7 +79,13 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity):
|
|||||||
"""Get the list of available speeds."""
|
"""Get the list of available speeds."""
|
||||||
return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
|
return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH]
|
||||||
|
|
||||||
async def async_turn_on(self, speed: str = None, **kwargs) -> None:
|
async def async_turn_on(
|
||||||
|
self,
|
||||||
|
speed: str = None,
|
||||||
|
percentage: int = None,
|
||||||
|
preset_mode: str = None,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
"""Turn on the entity."""
|
"""Turn on the entity."""
|
||||||
await self._device.set_dp(True, self._dp_id)
|
await self._device.set_dp(True, self._dp_id)
|
||||||
if speed is not None:
|
if speed is not None:
|
||||||
|
@@ -27,6 +27,7 @@ from .const import (
|
|||||||
CONF_COLOR_MODE,
|
CONF_COLOR_MODE,
|
||||||
CONF_COLOR_TEMP_MAX_KELVIN,
|
CONF_COLOR_TEMP_MAX_KELVIN,
|
||||||
CONF_COLOR_TEMP_MIN_KELVIN,
|
CONF_COLOR_TEMP_MIN_KELVIN,
|
||||||
|
CONF_COLOR_TEMP_REVERSE,
|
||||||
CONF_MUSIC_MODE,
|
CONF_MUSIC_MODE,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,6 +37,8 @@ MIRED_TO_KELVIN_CONST = 1000000
|
|||||||
DEFAULT_MIN_KELVIN = 2700 # MIRED 370
|
DEFAULT_MIN_KELVIN = 2700 # MIRED 370
|
||||||
DEFAULT_MAX_KELVIN = 6500 # MIRED 153
|
DEFAULT_MAX_KELVIN = 6500 # MIRED 153
|
||||||
|
|
||||||
|
DEFAULT_COLOR_TEMP_REVERSE = False
|
||||||
|
|
||||||
DEFAULT_LOWER_BRIGHTNESS = 29
|
DEFAULT_LOWER_BRIGHTNESS = 29
|
||||||
DEFAULT_UPPER_BRIGHTNESS = 1000
|
DEFAULT_UPPER_BRIGHTNESS = 1000
|
||||||
|
|
||||||
@@ -117,6 +120,11 @@ def flow_schema(dps):
|
|||||||
vol.Optional(CONF_COLOR_TEMP_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All(
|
vol.Optional(CONF_COLOR_TEMP_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All(
|
||||||
vol.Coerce(int), vol.Range(min=1500, max=8000)
|
vol.Coerce(int), vol.Range(min=1500, max=8000)
|
||||||
),
|
),
|
||||||
|
vol.Optional(
|
||||||
|
CONF_COLOR_TEMP_REVERSE,
|
||||||
|
default=DEFAULT_COLOR_TEMP_REVERSE,
|
||||||
|
description={"suggested_value": DEFAULT_COLOR_TEMP_REVERSE},
|
||||||
|
): bool,
|
||||||
vol.Optional(CONF_SCENE): vol.In(dps),
|
vol.Optional(CONF_SCENE): vol.In(dps),
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_MUSIC_MODE, default=False, description={"suggested_value": False}
|
CONF_MUSIC_MODE, default=False, description={"suggested_value": False}
|
||||||
@@ -154,6 +162,9 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity):
|
|||||||
MIRED_TO_KELVIN_CONST
|
MIRED_TO_KELVIN_CONST
|
||||||
/ self._config.get(CONF_COLOR_TEMP_MAX_KELVIN, DEFAULT_MAX_KELVIN)
|
/ self._config.get(CONF_COLOR_TEMP_MAX_KELVIN, DEFAULT_MAX_KELVIN)
|
||||||
)
|
)
|
||||||
|
self._color_temp_reverse = self._config.get(
|
||||||
|
CONF_COLOR_TEMP_REVERSE, DEFAULT_COLOR_TEMP_REVERSE
|
||||||
|
)
|
||||||
self._hs = None
|
self._hs = None
|
||||||
self._effect = None
|
self._effect = None
|
||||||
self._effect_list = []
|
self._effect_list = []
|
||||||
@@ -199,11 +210,16 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity):
|
|||||||
def color_temp(self):
|
def color_temp(self):
|
||||||
"""Return the color_temp of the light."""
|
"""Return the color_temp of the light."""
|
||||||
if self.has_config(CONF_COLOR_TEMP) and self.is_white_mode:
|
if self.has_config(CONF_COLOR_TEMP) and self.is_white_mode:
|
||||||
|
color_temp_value = (
|
||||||
|
self._upper_color_temp - self._color_temp
|
||||||
|
if self._color_temp_reverse
|
||||||
|
else self._color_temp
|
||||||
|
)
|
||||||
return int(
|
return int(
|
||||||
self._max_mired
|
self._max_mired
|
||||||
- (
|
- (
|
||||||
((self._max_mired - self._min_mired) / self._upper_color_temp)
|
((self._max_mired - self._min_mired) / self._upper_color_temp)
|
||||||
* self._color_temp
|
* color_temp_value
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
@@ -364,10 +380,16 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity):
|
|||||||
if ATTR_COLOR_TEMP in kwargs and (features & SUPPORT_COLOR_TEMP):
|
if ATTR_COLOR_TEMP in kwargs and (features & SUPPORT_COLOR_TEMP):
|
||||||
if brightness is None:
|
if brightness is None:
|
||||||
brightness = self._brightness
|
brightness = self._brightness
|
||||||
|
color_temp_value = (
|
||||||
|
(self._max_mired - self._min_mired)
|
||||||
|
- (int(kwargs[ATTR_COLOR_TEMP]) - self._min_mired)
|
||||||
|
if self._color_temp_reverse
|
||||||
|
else (int(kwargs[ATTR_COLOR_TEMP]) - self._min_mired)
|
||||||
|
)
|
||||||
color_temp = int(
|
color_temp = int(
|
||||||
self._upper_color_temp
|
self._upper_color_temp
|
||||||
- (self._upper_color_temp / (self._max_mired - self._min_mired))
|
- (self._upper_color_temp / (self._max_mired - self._min_mired))
|
||||||
* (int(kwargs[ATTR_COLOR_TEMP]) - self._min_mired)
|
* color_temp_value
|
||||||
)
|
)
|
||||||
states[self._config.get(CONF_COLOR_MODE)] = MODE_WHITE
|
states[self._config.get(CONF_COLOR_MODE)] = MODE_WHITE
|
||||||
states[self._config.get(CONF_BRIGHTNESS)] = brightness
|
states[self._config.get(CONF_BRIGHTNESS)] = brightness
|
||||||
|
87
custom_components/localtuya/number.py
Normal file
87
custom_components/localtuya/number.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
"""Platform to present any Tuya DP as a number."""
|
||||||
|
import logging
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
from homeassistant.components.number import DOMAIN, NumberEntity
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_DEVICE_CLASS,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .common import LocalTuyaEntity, async_setup_entry
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF_MIN_VALUE = "min_value"
|
||||||
|
CONF_MAX_VALUE = "max_value"
|
||||||
|
|
||||||
|
DEFAULT_MIN = 0
|
||||||
|
DEFAULT_MAX = 100000
|
||||||
|
|
||||||
|
|
||||||
|
def flow_schema(dps):
|
||||||
|
"""Return schema used in config flow."""
|
||||||
|
return {
|
||||||
|
vol.Optional(CONF_MIN_VALUE, default=DEFAULT_MIN): vol.All(
|
||||||
|
vol.Coerce(float),
|
||||||
|
vol.Range(min=-1000000.0, max=1000000.0),
|
||||||
|
),
|
||||||
|
vol.Required(CONF_MAX_VALUE, default=DEFAULT_MAX): vol.All(
|
||||||
|
vol.Coerce(float),
|
||||||
|
vol.Range(min=-1000000.0, max=1000000.0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class LocaltuyaNumber(LocalTuyaEntity, NumberEntity):
|
||||||
|
"""Representation of a Tuya Number."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
device,
|
||||||
|
config_entry,
|
||||||
|
sensorid,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""Initialize the Tuya sensor."""
|
||||||
|
super().__init__(device, config_entry, sensorid, _LOGGER, **kwargs)
|
||||||
|
self._state = STATE_UNKNOWN
|
||||||
|
|
||||||
|
self._min_value = DEFAULT_MIN
|
||||||
|
if CONF_MIN_VALUE in self._config:
|
||||||
|
self._min_value = self._config.get(CONF_MIN_VALUE)
|
||||||
|
|
||||||
|
self._max_value = self._config.get(CONF_MAX_VALUE)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self) -> float:
|
||||||
|
"""Return sensor state."""
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_value(self) -> float:
|
||||||
|
"""Return the minimum value."""
|
||||||
|
return self._min_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_value(self) -> float:
|
||||||
|
"""Return the maximum value."""
|
||||||
|
return self._max_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Return the class of this device."""
|
||||||
|
return self._config.get(CONF_DEVICE_CLASS)
|
||||||
|
|
||||||
|
async def async_set_value(self, value: float) -> None:
|
||||||
|
"""Update the current value."""
|
||||||
|
await self._device.set_dp(value, self._dp_id)
|
||||||
|
|
||||||
|
def status_updated(self):
|
||||||
|
"""Device status was updated."""
|
||||||
|
state = self.dps(self._dp_id)
|
||||||
|
self._state = state
|
||||||
|
|
||||||
|
|
||||||
|
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaNumber, flow_schema)
|
103
custom_components/localtuya/select.py
Normal file
103
custom_components/localtuya/select.py
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
"""Platform to present any Tuya DP as an enumeration."""
|
||||||
|
import logging
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
from homeassistant.components.select import DOMAIN, SelectEntity
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_DEVICE_CLASS,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .common import LocalTuyaEntity, async_setup_entry
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF_OPTIONS = "select_options"
|
||||||
|
CONF_OPTIONS_FRIENDLY = "select_options_friendly"
|
||||||
|
|
||||||
|
|
||||||
|
def flow_schema(dps):
|
||||||
|
"""Return schema used in config flow."""
|
||||||
|
return {
|
||||||
|
vol.Required(CONF_OPTIONS): str,
|
||||||
|
vol.Optional(CONF_OPTIONS_FRIENDLY): str,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class LocaltuyaSelect(LocalTuyaEntity, SelectEntity):
|
||||||
|
"""Representation of a Tuya Enumeration."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
device,
|
||||||
|
config_entry,
|
||||||
|
sensorid,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""Initialize the Tuya sensor."""
|
||||||
|
super().__init__(device, config_entry, sensorid, _LOGGER, **kwargs)
|
||||||
|
self._state = STATE_UNKNOWN
|
||||||
|
self._state_friendly = ""
|
||||||
|
self._valid_options = self._config.get(CONF_OPTIONS).split(";")
|
||||||
|
|
||||||
|
# Set Display options
|
||||||
|
self._display_options = []
|
||||||
|
display_options_str = ""
|
||||||
|
if CONF_OPTIONS_FRIENDLY in self._config:
|
||||||
|
display_options_str = self._config.get(CONF_OPTIONS_FRIENDLY).strip()
|
||||||
|
_LOGGER.debug("Display Options Configured: %s", display_options_str)
|
||||||
|
|
||||||
|
if display_options_str.find(";") >= 0:
|
||||||
|
self._display_options = display_options_str.split(";")
|
||||||
|
elif len(display_options_str.strip()) > 0:
|
||||||
|
self._display_options.append(display_options_str)
|
||||||
|
else:
|
||||||
|
# Default display string to raw string
|
||||||
|
_LOGGER.debug("No Display options configured - defaulting to raw values")
|
||||||
|
self._display_options = self._valid_options
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Total Raw Options: %s - Total Display Options: %s",
|
||||||
|
str(len(self._valid_options)),
|
||||||
|
str(len(self._display_options)),
|
||||||
|
)
|
||||||
|
if len(self._valid_options) > len(self._display_options):
|
||||||
|
# If list of display items smaller than list of valid items,
|
||||||
|
# then default remaining items to be the raw value
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Valid options is larger than display options - \
|
||||||
|
filling up with raw values"
|
||||||
|
)
|
||||||
|
for i in range(len(self._display_options), len(self._valid_options)):
|
||||||
|
self._display_options.append(self._valid_options[i])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_option(self) -> str:
|
||||||
|
"""Return the current value."""
|
||||||
|
return self._state_friendly
|
||||||
|
|
||||||
|
@property
|
||||||
|
def options(self) -> list:
|
||||||
|
"""Return the list of values."""
|
||||||
|
return self._display_options
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Return the class of this device."""
|
||||||
|
return self._config.get(CONF_DEVICE_CLASS)
|
||||||
|
|
||||||
|
async def async_select_option(self, option: str) -> None:
|
||||||
|
"""Update the current value."""
|
||||||
|
option_value = self._valid_options[self._display_options.index(option)]
|
||||||
|
_LOGGER.debug("Sending Option: " + option + " -> " + option_value)
|
||||||
|
await self._device.set_dp(option_value, self._dp_id)
|
||||||
|
|
||||||
|
def status_updated(self):
|
||||||
|
"""Device status was updated."""
|
||||||
|
state = self.dps(self._dp_id)
|
||||||
|
self._state_friendly = self._display_options[self._valid_options.index(state)]
|
||||||
|
self._state = state
|
||||||
|
|
||||||
|
|
||||||
|
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaSelect, flow_schema)
|
@@ -75,6 +75,10 @@
|
|||||||
"fan_speed_low": "Fan Low Speed Setting",
|
"fan_speed_low": "Fan Low Speed Setting",
|
||||||
"fan_speed_medium": "Fan Medium Speed Setting",
|
"fan_speed_medium": "Fan Medium Speed Setting",
|
||||||
"fan_speed_high": "Fan High Speed Setting",
|
"fan_speed_high": "Fan High Speed Setting",
|
||||||
|
"max_value": "Maximum Value",
|
||||||
|
"min_value": "Minimum Value",
|
||||||
|
"select_options": "Valid entries, separate entries by a ;",
|
||||||
|
"select_options_friendly": "User Friendly options, separate entries by a ;",
|
||||||
"current_temperature_dp": "Current Temperature",
|
"current_temperature_dp": "Current Temperature",
|
||||||
"target_temperature_dp": "Target Temperature",
|
"target_temperature_dp": "Target Temperature",
|
||||||
"temperature_step": "Temperature Step (optional)",
|
"temperature_step": "Temperature Step (optional)",
|
||||||
@@ -144,6 +148,10 @@
|
|||||||
"fan_speed_low": "Fan Low Speed Setting",
|
"fan_speed_low": "Fan Low Speed Setting",
|
||||||
"fan_speed_medium": "Fan Medium Speed Setting",
|
"fan_speed_medium": "Fan Medium Speed Setting",
|
||||||
"fan_speed_high": "Fan High Speed Setting",
|
"fan_speed_high": "Fan High Speed Setting",
|
||||||
|
"max_value": "Maximum Value",
|
||||||
|
"min_value": "Minimum Value",
|
||||||
|
"select_options": "Valid entries, separate entries by a ;",
|
||||||
|
"select_options_friendly": "User Friendly options, separate entries by a ;",
|
||||||
"current_temperature_dp": "Current Temperature",
|
"current_temperature_dp": "Current Temperature",
|
||||||
"target_temperature_dp": "Target Temperature",
|
"target_temperature_dp": "Target Temperature",
|
||||||
"temperature_step": "Temperature Step (optional)",
|
"temperature_step": "Temperature Step (optional)",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Local Tuya",
|
"name": "Local Tuya",
|
||||||
"domains": ["climate", "cover", "fan", "light", "sensor", "switch"],
|
"domains": ["climate", "cover", "fan", "light", "number", "select", "sensor", "switch"],
|
||||||
"homeassistant": "0.116.0",
|
"homeassistant": "0.116.0",
|
||||||
"iot_class": ["Local Push"]
|
"iot_class": ["Local Push"]
|
||||||
}
|
}
|
||||||
|
13
info.md
13
info.md
@@ -66,7 +66,18 @@ localtuya:
|
|||||||
|
|
||||||
- platform: light
|
- platform: light
|
||||||
friendly_name: Device Light
|
friendly_name: Device Light
|
||||||
id: 4
|
id: 4 # Usually 1 or 20
|
||||||
|
color_mode: 21 # Optional, usually 2 or 21, default: "none"
|
||||||
|
brightness: 22 # Optional, usually 3 or 22, default: "none"
|
||||||
|
color_temp: 23 # Optional, usually 4 or 23, default: "none"
|
||||||
|
color_temp_reverse: false # Optional, default: false
|
||||||
|
color: 24 # Optional, usually 5 (RGB_HSV) or 24 (HSV), default: "none"
|
||||||
|
brightness_lower: 29 # Optional, usually 0 or 29, default: 29
|
||||||
|
brightness_upper: 1000 # Optional, usually 255 or 1000, default: 1000
|
||||||
|
color_temp_min_kelvin: 2700 # Optional, default: 2700
|
||||||
|
color_temp_max_kelvin: 6500 # Optional, default: 6500
|
||||||
|
scene: 25 # Optional, usually 6 (RGB_HSV) or 25 (HSV), default: "none"
|
||||||
|
music_mode: False # Optional, some use internal mic, others, phone mic. Only internal mic is supported, default: "False"
|
||||||
|
|
||||||
- platform: sensor
|
- platform: sensor
|
||||||
friendly_name: Plug Voltage
|
friendly_name: Plug Voltage
|
||||||
|
@@ -3,7 +3,7 @@ codespell==2.0.0
|
|||||||
flake8==3.9.2
|
flake8==3.9.2
|
||||||
mypy==0.901
|
mypy==0.901
|
||||||
pydocstyle==6.1.1
|
pydocstyle==6.1.1
|
||||||
cryptography==3.2
|
cryptography==3.3.2
|
||||||
pylint==2.8.2
|
pylint==2.8.2
|
||||||
pylint-strict-informational==0.1
|
pylint-strict-informational==0.1
|
||||||
homeassistant==2021.1.4
|
homeassistant==2021.7.1
|
||||||
|
@@ -12,7 +12,7 @@ warn_incomplete_stub = true
|
|||||||
warn_redundant_casts = true
|
warn_redundant_casts = true
|
||||||
warn_unused_configs = true
|
warn_unused_configs = true
|
||||||
|
|
||||||
[mypy-homeassistant.block_async_io,homeassistant.bootstrap,homeassistant.components,homeassistant.config_entries,homeassistant.config,homeassistant.const,homeassistant.core,homeassistant.data_entry_flow,homeassistant.exceptions,homeassistant.__init__,homeassistant.loader,homeassistant.__main__,homeassistant.requirements,homeassistant.runner,homeassistant.setup,homeassistant.util,homeassistant.auth.*,homeassistant.components.automation.*,homeassistant.components.binary_sensor.*,homeassistant.components.calendar.*,homeassistant.components.cover.*,homeassistant.components.device_automation.*,homeassistant.components.frontend.*,homeassistant.components.geo_location.*,homeassistant.components.group.*,homeassistant.components.history.*,homeassistant.components.http.*,homeassistant.components.image_processing.*,homeassistant.components.integration.*,homeassistant.components.light.*,homeassistant.components.lock.*,homeassistant.components.mailbox.*,homeassistant.components.media_player.*,homeassistant.components.notify.*,homeassistant.components.persistent_notification.*,homeassistant.components.proximity.*,homeassistant.components.remote.*,homeassistant.components.scene.*,homeassistant.components.sensor.*,homeassistant.components.sun.*,homeassistant.components.switch.*,homeassistant.components.systemmonitor.*,homeassistant.components.tts.*,homeassistant.components.vacuum.*,homeassistant.components.water_heater.*,homeassistant.components.weather.*,homeassistant.components.websocket_api.*,homeassistant.components.zone.*,homeassistant.helpers.*,homeassistant.scripts.*,homeassistant.util.*]
|
[mypy-homeassistant.block_async_io,homeassistant.bootstrap,homeassistant.components,homeassistant.config_entries,homeassistant.config,homeassistant.const,homeassistant.core,homeassistant.data_entry_flow,homeassistant.exceptions,homeassistant.__init__,homeassistant.loader,homeassistant.__main__,homeassistant.requirements,homeassistant.runner,homeassistant.setup,homeassistant.util,homeassistant.auth.*,homeassistant.components.automation.*,homeassistant.components.binary_sensor.*,homeassistant.components.calendar.*,homeassistant.components.cover.*,homeassistant.components.device_automation.*,homeassistant.components.frontend.*,homeassistant.components.geo_location.*,homeassistant.components.group.*,homeassistant.components.history.*,homeassistant.components.http.*,homeassistant.components.image_processing.*,homeassistant.components.integration.*,homeassistant.components.light.*,homeassistant.components.lock.*,homeassistant.components.mailbox.*,homeassistant.components.media_player.*,homeassistant.components.notify.*,homeassistant.components.persistent_notification.*,homeassistant.components.proximity.*,homeassistant.components.remote.*,homeassistant.components.scene.*,homeassistant.components.sensor.*,homeassistant.components.sun.*,homeassistant.components.switch.*,homeassistant.components.systemmonitor.*,homeassistant.components.tts.*,homeassistant.components.vacuum.*,homeassistant.components.water_heater.*,homeassistant.components.weather.*,homeassistant.components.websocket_api.*,homeassistant.components.zone.*,homeassistant.helpers.*,homeassistant.scripts.*,homeassistant.util.*,homeassistant.components.select.*,homeassistant.components.number.*]
|
||||||
strict = true
|
strict = true
|
||||||
ignore_errors = false
|
ignore_errors = false
|
||||||
warn_unreachable = true
|
warn_unreachable = true
|
||||||
|
Reference in New Issue
Block a user