Rebase from upstream
This commit is contained in:
@@ -52,6 +52,8 @@ class LocaltuyaBinarySensor(LocalTuyaEntity, BinarySensorEntity):
|
|||||||
return self._config.get(CONF_DEVICE_CLASS)
|
return self._config.get(CONF_DEVICE_CLASS)
|
||||||
|
|
||||||
def status_updated(self):
|
def status_updated(self):
|
||||||
|
super().status_updated()
|
||||||
|
|
||||||
"""Device status was updated."""
|
"""Device status was updated."""
|
||||||
state = str(self.dps(self._dp_id)).lower()
|
state = str(self.dps(self._dp_id)).lower()
|
||||||
if state == self._config[CONF_STATE_ON].lower():
|
if state == self._config[CONF_STATE_ON].lower():
|
||||||
@@ -63,6 +65,11 @@ class LocaltuyaBinarySensor(LocalTuyaEntity, BinarySensorEntity):
|
|||||||
"State for entity %s did not match state patterns", self.entity_id
|
"State for entity %s did not match state patterns", self.entity_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# No need to restore state for a sensor
|
||||||
|
async def restore_state_when_connected(self):
|
||||||
|
"""Do nothing for a sensor"""
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
async_setup_entry = partial(
|
async_setup_entry = partial(
|
||||||
async_setup_entry, DOMAIN, LocaltuyaBinarySensor, flow_schema
|
async_setup_entry, DOMAIN, LocaltuyaBinarySensor, flow_schema
|
||||||
|
@@ -13,6 +13,7 @@ from homeassistant.const import (
|
|||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
CONF_SCAN_INTERVAL,
|
CONF_SCAN_INTERVAL,
|
||||||
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import (
|
||||||
@@ -31,6 +32,9 @@ from .const import (
|
|||||||
DATA_CLOUD,
|
DATA_CLOUD,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
TUYA_DEVICES,
|
TUYA_DEVICES,
|
||||||
|
CONF_DEFAULT_VALUE,
|
||||||
|
ATTR_STATE,
|
||||||
|
CONF_RESTORE_ON_RECONNECT,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -91,6 +95,8 @@ async def async_setup_entry(
|
|||||||
entity_config[CONF_ID],
|
entity_config[CONF_ID],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
#Once the entities have been created, add to the TuyaDevice instance
|
||||||
|
tuyainterface.add_entities(entities)
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
@@ -135,6 +141,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
self._connect_task = None
|
self._connect_task = None
|
||||||
self._disconnect_task = None
|
self._disconnect_task = None
|
||||||
self._unsub_interval = None
|
self._unsub_interval = None
|
||||||
|
self._entities = []
|
||||||
self._local_key = self._dev_config_entry[CONF_LOCAL_KEY]
|
self._local_key = self._dev_config_entry[CONF_LOCAL_KEY]
|
||||||
self.set_logger(_LOGGER, self._dev_config_entry[CONF_DEVICE_ID])
|
self.set_logger(_LOGGER, self._dev_config_entry[CONF_DEVICE_ID])
|
||||||
|
|
||||||
@@ -142,6 +149,10 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
for entity in self._dev_config_entry[CONF_ENTITIES]:
|
for entity in self._dev_config_entry[CONF_ENTITIES]:
|
||||||
self.dps_to_request[entity[CONF_ID]] = None
|
self.dps_to_request[entity[CONF_ID]] = None
|
||||||
|
|
||||||
|
def add_entities(self, entities):
|
||||||
|
"""Set the entities associated with this device"""
|
||||||
|
self._entities.extend(entities)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def connected(self):
|
def connected(self):
|
||||||
"""Return if connected to device."""
|
"""Return if connected to device."""
|
||||||
@@ -173,6 +184,10 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
|
|
||||||
self.status_updated(status)
|
self.status_updated(status)
|
||||||
|
|
||||||
|
# Attempt to restore status for all entites that need to first set the DPS value before the device will respond with status.
|
||||||
|
for entity in self._entities:
|
||||||
|
await entity.restore_state_when_connected()
|
||||||
|
|
||||||
def _new_entity_handler(entity_id):
|
def _new_entity_handler(entity_id):
|
||||||
self.debug(
|
self.debug(
|
||||||
"New entity %s was added to %s",
|
"New entity %s was added to %s",
|
||||||
@@ -254,7 +269,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
|
|||||||
try:
|
try:
|
||||||
await self._interface.set_dp(state, dp_index)
|
await self._interface.set_dp(state, dp_index)
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
self.exception("Failed to set DP %d to %d", dp_index, state)
|
self.exception("Failed to set DP %d to %s", dp_index, str(state))
|
||||||
else:
|
else:
|
||||||
self.error(
|
self.error(
|
||||||
"Not connected to device %s", self._dev_config_entry[CONF_FRIENDLY_NAME]
|
"Not connected to device %s", self._dev_config_entry[CONF_FRIENDLY_NAME]
|
||||||
@@ -305,6 +320,16 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
|
|||||||
self._config = get_entity_config(config_entry, dp_id)
|
self._config = get_entity_config(config_entry, dp_id)
|
||||||
self._dp_id = dp_id
|
self._dp_id = dp_id
|
||||||
self._status = {}
|
self._status = {}
|
||||||
|
self._state = None
|
||||||
|
self._last_state = None
|
||||||
|
|
||||||
|
#Default value is available to be provided by Platform entities if required
|
||||||
|
self._default_value = self._config.get(CONF_DEFAULT_VALUE)
|
||||||
|
|
||||||
|
#Restore on connect setting is available to be provided by Platform entities if required
|
||||||
|
self._restore_on_reconnect = (
|
||||||
|
self._config.get(CONF_RESTORE_ON_RECONNECT) or False
|
||||||
|
)
|
||||||
self.set_logger(logger, self._dev_config_entry[CONF_DEVICE_ID])
|
self.set_logger(logger, self._dev_config_entry[CONF_DEVICE_ID])
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
@@ -325,6 +350,8 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
|
|||||||
self._status = status.copy()
|
self._status = status.copy()
|
||||||
if status:
|
if status:
|
||||||
self.status_updated()
|
self.status_updated()
|
||||||
|
|
||||||
|
# Update HA
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
signal = f"localtuya_{self._dev_config_entry[CONF_DEVICE_ID]}"
|
signal = f"localtuya_{self._dev_config_entry[CONF_DEVICE_ID]}"
|
||||||
@@ -336,6 +363,20 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
|
|||||||
signal = f"localtuya_entity_{self._dev_config_entry[CONF_DEVICE_ID]}"
|
signal = f"localtuya_entity_{self._dev_config_entry[CONF_DEVICE_ID]}"
|
||||||
async_dispatcher_send(self.hass, signal, self.entity_id)
|
async_dispatcher_send(self.hass, signal, self.entity_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extra_state_attributes(self):
|
||||||
|
"""Return entity specific state attributes to be saved & then available for restore
|
||||||
|
when the entity is restored at startup.
|
||||||
|
"""
|
||||||
|
attributes = {}
|
||||||
|
if self._state is not None:
|
||||||
|
attributes[ATTR_STATE] = self._state
|
||||||
|
elif self._last_state is not None:
|
||||||
|
attributes[ATTR_STATE] = self._last_state
|
||||||
|
|
||||||
|
self.debug("Entity %s - Additional attributes: %s", self.name, attributes)
|
||||||
|
return attributes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self):
|
||||||
"""Return device information for the device registry."""
|
"""Return device information for the device registry."""
|
||||||
@@ -408,9 +449,83 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
|
|||||||
|
|
||||||
Override in subclasses and update entity specific state.
|
Override in subclasses and update entity specific state.
|
||||||
"""
|
"""
|
||||||
|
state = self.dps(self._dp_id)
|
||||||
|
self._state = state
|
||||||
|
|
||||||
|
# Keep record in last_state as long as not during connection/re-connection,
|
||||||
|
# as last state will be used to restore the previous state
|
||||||
|
if (state is not None) and (self._device._connect_task is None):
|
||||||
|
self._last_state = state
|
||||||
|
|
||||||
def status_restored(self, stored_state):
|
def status_restored(self, stored_state):
|
||||||
"""Device status was restored.
|
"""Device status was restored.
|
||||||
|
|
||||||
Override in subclasses and update entity specific state.
|
Override in subclasses and update entity specific state.
|
||||||
"""
|
"""
|
||||||
|
raw_state = stored_state.attributes.get(ATTR_STATE)
|
||||||
|
if raw_state is not None:
|
||||||
|
# (stored_state.state == "unavailable") | (stored_state.state == "unknown")
|
||||||
|
# ):
|
||||||
|
self._last_state = raw_state
|
||||||
|
self.debug(
|
||||||
|
"Restoring state for entity: %s - state: %s",
|
||||||
|
self.name,
|
||||||
|
str(self._last_state),
|
||||||
|
)
|
||||||
|
|
||||||
|
def default_value(self):
|
||||||
|
"""Default value of this entity
|
||||||
|
|
||||||
|
Override in subclasses to specify the default value for the entity.
|
||||||
|
"""
|
||||||
|
# Check if default value has been set - if not, default to the entity defaults.
|
||||||
|
if self._default_value is None:
|
||||||
|
self._default_value = self.entity_default_value()
|
||||||
|
|
||||||
|
return self._default_value
|
||||||
|
|
||||||
|
def entity_default_value(self):
|
||||||
|
"""Default value of the entity type
|
||||||
|
|
||||||
|
Override in subclasses to specify the default value for the entity.
|
||||||
|
"""
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def restore_on_reconnect(self):
|
||||||
|
"""Returns whether the last state should be restored on a reconnect - useful where the device loses settings if powered off"""
|
||||||
|
return self._restore_on_reconnect
|
||||||
|
|
||||||
|
async def restore_state_when_connected(self):
|
||||||
|
"""Restore if restore_on_reconnect is set, or if no status has been yet found - which indicates a DPS that needs to be set before it starts returning status"""
|
||||||
|
if not self.restore_on_reconnect and (str(self._dp_id) in self._status):
|
||||||
|
self.debug(
|
||||||
|
"Entity %s (DP %d) - Not restoring as restore on reconnect is disabled for this entity and the entity has an initial status",
|
||||||
|
self.name,
|
||||||
|
self._dp_id,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.debug("Attempting to restore state for entity: %s", self.name)
|
||||||
|
# Attempt to restore the current state - in case reset.
|
||||||
|
restore_state = self._state
|
||||||
|
|
||||||
|
# If no state stored in the entity currently, go from last saved state
|
||||||
|
if (restore_state == STATE_UNKNOWN) | (restore_state is None):
|
||||||
|
self.debug("No current state for entity")
|
||||||
|
restore_state = self._last_state
|
||||||
|
|
||||||
|
# If no current or saved state, then use the default value
|
||||||
|
if restore_state is None:
|
||||||
|
self.debug("No last restored state - using default")
|
||||||
|
restore_state = self.default_value()
|
||||||
|
|
||||||
|
self.debug(
|
||||||
|
"Entity %s (DP %d) - Restoring state: %s",
|
||||||
|
self.name,
|
||||||
|
self._dp_id,
|
||||||
|
str(restore_state),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Manually initialise
|
||||||
|
await self._device.set_dp(restore_state, self._dp_id)
|
||||||
|
@@ -44,6 +44,7 @@ from .const import (
|
|||||||
DATA_DISCOVERY,
|
DATA_DISCOVERY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
|
CONF_MANUAL_DPS,
|
||||||
)
|
)
|
||||||
from .discovery import discover
|
from .discovery import discover
|
||||||
|
|
||||||
@@ -88,6 +89,7 @@ CONFIGURE_DEVICE_SCHEMA = vol.Schema(
|
|||||||
vol.Required(CONF_DEVICE_ID): str,
|
vol.Required(CONF_DEVICE_ID): str,
|
||||||
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
||||||
vol.Optional(CONF_SCAN_INTERVAL): int,
|
vol.Optional(CONF_SCAN_INTERVAL): int,
|
||||||
|
vol.Optional(CONF_MANUAL_DPS): str,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -99,6 +101,7 @@ DEVICE_SCHEMA = vol.Schema(
|
|||||||
vol.Required(CONF_FRIENDLY_NAME): cv.string,
|
vol.Required(CONF_FRIENDLY_NAME): cv.string,
|
||||||
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
||||||
vol.Optional(CONF_SCAN_INTERVAL): int,
|
vol.Optional(CONF_SCAN_INTERVAL): int,
|
||||||
|
vol.Optional(CONF_MANUAL_DPS): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -140,6 +143,7 @@ def options_schema(entities):
|
|||||||
vol.Required(CONF_LOCAL_KEY): str,
|
vol.Required(CONF_LOCAL_KEY): str,
|
||||||
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]),
|
||||||
vol.Optional(CONF_SCAN_INTERVAL): int,
|
vol.Optional(CONF_SCAN_INTERVAL): int,
|
||||||
|
vol.Optional(CONF_MANUAL_DPS): str,
|
||||||
vol.Required(
|
vol.Required(
|
||||||
CONF_ENTITIES, description={"suggested_value": entity_names}
|
CONF_ENTITIES, description={"suggested_value": entity_names}
|
||||||
): cv.multi_select(entity_names),
|
): cv.multi_select(entity_names),
|
||||||
@@ -240,6 +244,22 @@ async def validate_input(hass: core.HomeAssistant, data):
|
|||||||
)
|
)
|
||||||
|
|
||||||
detected_dps = await interface.detect_available_dps()
|
detected_dps = await interface.detect_available_dps()
|
||||||
|
|
||||||
|
# if manual DPs are set, merge these.
|
||||||
|
_LOGGER.debug("Detected DPS: %s", detected_dps)
|
||||||
|
if CONF_MANUAL_DPS in data:
|
||||||
|
|
||||||
|
manual_dps_list = data[CONF_MANUAL_DPS].split(",")
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Manual DPS Setting: %s (%s)", data[CONF_MANUAL_DPS], manual_dps_list
|
||||||
|
)
|
||||||
|
# merge the lists
|
||||||
|
for new_dps in manual_dps_list:
|
||||||
|
# trim off any whitespace
|
||||||
|
new_dps = new_dps.strip()
|
||||||
|
if new_dps not in detected_dps:
|
||||||
|
detected_dps[new_dps] = -1
|
||||||
|
|
||||||
except (ConnectionRefusedError, ConnectionResetError) as ex:
|
except (ConnectionRefusedError, ConnectionResetError) as ex:
|
||||||
raise CannotConnect from ex
|
raise CannotConnect from ex
|
||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
@@ -253,6 +273,8 @@ async def validate_input(hass: core.HomeAssistant, data):
|
|||||||
if not detected_dps:
|
if not detected_dps:
|
||||||
raise EmptyDpsList
|
raise EmptyDpsList
|
||||||
|
|
||||||
|
_LOGGER.debug("Total DPS: %s", detected_dps)
|
||||||
|
|
||||||
return dps_string_list(detected_dps)
|
return dps_string_list(detected_dps)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -35,11 +35,14 @@ CONF_PRODUCT_KEY = "product_key"
|
|||||||
CONF_PRODUCT_NAME = "product_name"
|
CONF_PRODUCT_NAME = "product_name"
|
||||||
CONF_USER_ID = "user_id"
|
CONF_USER_ID = "user_id"
|
||||||
|
|
||||||
|
|
||||||
CONF_ACTION = "action"
|
CONF_ACTION = "action"
|
||||||
CONF_ADD_DEVICE = "add_device"
|
CONF_ADD_DEVICE = "add_device"
|
||||||
CONF_EDIT_DEVICE = "edit_device"
|
CONF_EDIT_DEVICE = "edit_device"
|
||||||
CONF_SETUP_CLOUD = "setup_cloud"
|
CONF_SETUP_CLOUD = "setup_cloud"
|
||||||
CONF_NO_CLOUD = "no_cloud"
|
CONF_NO_CLOUD = "no_cloud"
|
||||||
|
CONF_MANUAL_DPS = "manual_dps_strings"
|
||||||
|
CONF_DEFAULT_VALUE = "dps_default_value"
|
||||||
|
|
||||||
# light
|
# light
|
||||||
CONF_BRIGHTNESS_LOWER = "brightness_lower"
|
CONF_BRIGHTNESS_LOWER = "brightness_lower"
|
||||||
@@ -113,3 +116,16 @@ CONF_FAULT_DP = "fault_dp"
|
|||||||
CONF_PAUSED_STATE = "paused_state"
|
CONF_PAUSED_STATE = "paused_state"
|
||||||
CONF_RETURN_MODE = "return_mode"
|
CONF_RETURN_MODE = "return_mode"
|
||||||
CONF_STOP_STATUS = "stop_status"
|
CONF_STOP_STATUS = "stop_status"
|
||||||
|
|
||||||
|
# number
|
||||||
|
CONF_MIN_VALUE = "min_value"
|
||||||
|
CONF_MAX_VALUE = "max_value"
|
||||||
|
CONF_STEPSIZE_VALUE = "step_size"
|
||||||
|
|
||||||
|
# select
|
||||||
|
CONF_OPTIONS = "select_options"
|
||||||
|
CONF_OPTIONS_FRIENDLY = "select_options_friendly"
|
||||||
|
|
||||||
|
# States
|
||||||
|
ATTR_STATE = "raw_state"
|
||||||
|
CONF_RESTORE_ON_RECONNECT = "restore_on_reconnect"
|
||||||
|
@@ -189,6 +189,7 @@ class LocaltuyaCover(LocalTuyaEntity, CoverEntity):
|
|||||||
self.debug("Restored cover position %s", self._current_cover_position)
|
self.debug("Restored cover position %s", self._current_cover_position)
|
||||||
|
|
||||||
def status_updated(self):
|
def status_updated(self):
|
||||||
|
super.status_updated(self)
|
||||||
"""Device status was updated."""
|
"""Device status was updated."""
|
||||||
self._previous_state = self._state
|
self._previous_state = self._state
|
||||||
self._state = self.dps(self._dp_id)
|
self._state = self.dps(self._dp_id)
|
||||||
|
@@ -5,13 +5,19 @@ from functools import partial
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from homeassistant.components.number import DOMAIN, NumberEntity
|
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 homeassistant.helpers.restore_state import RestoreEntity
|
||||||
|
|
||||||
from .common import LocalTuyaEntity, async_setup_entry
|
from .common import LocalTuyaEntity, async_setup_entry
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF_MIN_VALUE = "min_value"
|
from .const import (
|
||||||
CONF_MAX_VALUE = "max_value"
|
CONF_DEFAULT_VALUE,
|
||||||
|
CONF_MIN_VALUE,
|
||||||
|
CONF_MAX_VALUE,
|
||||||
|
CONF_DEFAULT_VALUE,
|
||||||
|
CONF_RESTORE_ON_RECONNECT,
|
||||||
|
)
|
||||||
|
|
||||||
DEFAULT_MIN = 0
|
DEFAULT_MIN = 0
|
||||||
DEFAULT_MAX = 100000
|
DEFAULT_MAX = 100000
|
||||||
@@ -28,10 +34,12 @@ def flow_schema(dps):
|
|||||||
vol.Coerce(float),
|
vol.Coerce(float),
|
||||||
vol.Range(min=-1000000.0, max=1000000.0),
|
vol.Range(min=-1000000.0, max=1000000.0),
|
||||||
),
|
),
|
||||||
|
vol.Optional(CONF_DEFAULT_VALUE): str,
|
||||||
|
vol.Required(CONF_RESTORE_ON_RECONNECT): bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LocaltuyaNumber(LocalTuyaEntity, NumberEntity):
|
class LocaltuyaNumber(LocalTuyaEntity, NumberEntity, RestoreEntity):
|
||||||
"""Representation of a Tuya Number."""
|
"""Representation of a Tuya Number."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -51,6 +59,11 @@ class LocaltuyaNumber(LocalTuyaEntity, NumberEntity):
|
|||||||
|
|
||||||
self._max_value = self._config.get(CONF_MAX_VALUE)
|
self._max_value = self._config.get(CONF_MAX_VALUE)
|
||||||
|
|
||||||
|
#Override standard default value handling to cast to a float
|
||||||
|
default_value = self._config.get(CONF_DEFAULT_VALUE)
|
||||||
|
if default_value is not None:
|
||||||
|
self._default_value = float(default_value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self) -> float:
|
def value(self) -> float:
|
||||||
"""Return sensor state."""
|
"""Return sensor state."""
|
||||||
@@ -75,10 +88,9 @@ class LocaltuyaNumber(LocalTuyaEntity, NumberEntity):
|
|||||||
"""Update the current value."""
|
"""Update the current value."""
|
||||||
await self._device.set_dp(value, self._dp_id)
|
await self._device.set_dp(value, self._dp_id)
|
||||||
|
|
||||||
def status_updated(self):
|
# Default value is the minimum value
|
||||||
"""Device status was updated."""
|
def entity_default_value(self):
|
||||||
state = self.dps(self._dp_id)
|
return self._min_value
|
||||||
self._state = state
|
|
||||||
|
|
||||||
|
|
||||||
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaNumber, flow_schema)
|
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaNumber, flow_schema)
|
||||||
|
@@ -4,14 +4,21 @@ from functools import partial
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from homeassistant.components.select import DOMAIN, SelectEntity
|
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
|
from .common import LocalTuyaEntity, async_setup_entry
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF_OPTIONS = "select_options"
|
from .const import (
|
||||||
CONF_OPTIONS_FRIENDLY = "select_options_friendly"
|
CONF_OPTIONS,
|
||||||
|
CONF_OPTIONS_FRIENDLY,
|
||||||
|
CONF_DEFAULT_VALUE,
|
||||||
|
CONF_RESTORE_ON_RECONNECT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def flow_schema(dps):
|
def flow_schema(dps):
|
||||||
@@ -19,6 +26,8 @@ def flow_schema(dps):
|
|||||||
return {
|
return {
|
||||||
vol.Required(CONF_OPTIONS): str,
|
vol.Required(CONF_OPTIONS): str,
|
||||||
vol.Optional(CONF_OPTIONS_FRIENDLY): str,
|
vol.Optional(CONF_OPTIONS_FRIENDLY): str,
|
||||||
|
vol.Optional(CONF_DEFAULT_VALUE): str,
|
||||||
|
vol.Required(CONF_RESTORE_ON_RECONNECT): bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -91,10 +100,23 @@ class LocaltuyaSelect(LocalTuyaEntity, SelectEntity):
|
|||||||
await self._device.set_dp(option_value, self._dp_id)
|
await self._device.set_dp(option_value, self._dp_id)
|
||||||
|
|
||||||
def status_updated(self):
|
def status_updated(self):
|
||||||
|
super().status_updated()
|
||||||
"""Device status was updated."""
|
"""Device status was updated."""
|
||||||
state = self.dps(self._dp_id)
|
state = self.dps(self._dp_id)
|
||||||
self._state_friendly = self._display_options[self._valid_options.index(state)]
|
|
||||||
self._state = state
|
# Check that received status update for this entity.
|
||||||
|
if state is not None:
|
||||||
|
try:
|
||||||
|
self._state_friendly = self._display_options[
|
||||||
|
self._valid_options.index(state)
|
||||||
|
]
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
# Friendly value couldn't be mapped
|
||||||
|
self._state_friendly = state
|
||||||
|
|
||||||
|
# Default value is the first option
|
||||||
|
def entity_default_value(self):
|
||||||
|
return self._valid_options[0]
|
||||||
|
|
||||||
|
|
||||||
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaSelect, flow_schema)
|
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaSelect, flow_schema)
|
||||||
|
@@ -66,5 +66,10 @@ class LocaltuyaSensor(LocalTuyaEntity):
|
|||||||
state = round(state * scale_factor, DEFAULT_PRECISION)
|
state = round(state * scale_factor, DEFAULT_PRECISION)
|
||||||
self._state = state
|
self._state = state
|
||||||
|
|
||||||
|
# No need to restore state for a sensor
|
||||||
|
async def restore_state_when_connected(self):
|
||||||
|
"""Do nothing for a sensor"""
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaSensor, flow_schema)
|
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaSensor, flow_schema)
|
||||||
|
@@ -10,9 +10,12 @@ from .const import (
|
|||||||
ATTR_CURRENT,
|
ATTR_CURRENT,
|
||||||
ATTR_CURRENT_CONSUMPTION,
|
ATTR_CURRENT_CONSUMPTION,
|
||||||
ATTR_VOLTAGE,
|
ATTR_VOLTAGE,
|
||||||
|
ATTR_STATE,
|
||||||
CONF_CURRENT,
|
CONF_CURRENT,
|
||||||
CONF_CURRENT_CONSUMPTION,
|
CONF_CURRENT_CONSUMPTION,
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
|
CONF_DEFAULT_VALUE,
|
||||||
|
CONF_RESTORE_ON_RECONNECT,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -24,6 +27,8 @@ def flow_schema(dps):
|
|||||||
vol.Optional(CONF_CURRENT): vol.In(dps),
|
vol.Optional(CONF_CURRENT): vol.In(dps),
|
||||||
vol.Optional(CONF_CURRENT_CONSUMPTION): vol.In(dps),
|
vol.Optional(CONF_CURRENT_CONSUMPTION): vol.In(dps),
|
||||||
vol.Optional(CONF_VOLTAGE): vol.In(dps),
|
vol.Optional(CONF_VOLTAGE): vol.In(dps),
|
||||||
|
vol.Optional(CONF_DEFAULT_VALUE): str,
|
||||||
|
vol.Required(CONF_RESTORE_ON_RECONNECT): bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -59,6 +64,12 @@ class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity):
|
|||||||
)
|
)
|
||||||
if self.has_config(CONF_VOLTAGE):
|
if self.has_config(CONF_VOLTAGE):
|
||||||
attrs[ATTR_VOLTAGE] = self.dps(self._config[CONF_VOLTAGE]) / 10
|
attrs[ATTR_VOLTAGE] = self.dps(self._config[CONF_VOLTAGE]) / 10
|
||||||
|
|
||||||
|
# Store the state
|
||||||
|
if self._state is not None:
|
||||||
|
attrs[ATTR_STATE] = self._state
|
||||||
|
elif self._last_state is not None:
|
||||||
|
attrs[ATTR_STATE] = self._last_state
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
@@ -69,9 +80,9 @@ class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity):
|
|||||||
"""Turn Tuya switch off."""
|
"""Turn Tuya switch off."""
|
||||||
await self._device.set_dp(False, self._dp_id)
|
await self._device.set_dp(False, self._dp_id)
|
||||||
|
|
||||||
def status_updated(self):
|
# Default value is the "OFF" state
|
||||||
"""Device status was updated."""
|
def entity_default_value(self):
|
||||||
self._state = self.dps(self._dp_id)
|
return False
|
||||||
|
|
||||||
|
|
||||||
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaSwitch, flow_schema)
|
async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaSwitch, flow_schema)
|
||||||
|
@@ -96,7 +96,8 @@
|
|||||||
"local_key": "Local key",
|
"local_key": "Local key",
|
||||||
"protocol_version": "Protocol Version",
|
"protocol_version": "Protocol Version",
|
||||||
"scan_interval": "Scan interval (seconds, only when not updating automatically)",
|
"scan_interval": "Scan interval (seconds, only when not updating automatically)",
|
||||||
"entities": "Entities (uncheck an entity to remove it)"
|
"entities": "Entities (uncheck an entity to remove it)",
|
||||||
|
"manual_dps_strings": "Manual DPS to add (separated by commas ',') - used when detection is not working (optional)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pick_entity_type": {
|
"pick_entity_type": {
|
||||||
@@ -181,7 +182,12 @@
|
|||||||
"preset_set": "Presets Set (optional)",
|
"preset_set": "Presets Set (optional)",
|
||||||
"eco_dp": "Eco DP (optional)",
|
"eco_dp": "Eco DP (optional)",
|
||||||
"eco_value": "Eco value (optional)",
|
"eco_value": "Eco value (optional)",
|
||||||
"heuristic_action": "Enable heuristic action (optional)"
|
"heuristic_action": "Enable heuristic action (optional)",
|
||||||
|
"dps_default_value": "Default value when un-initialised (optional)",
|
||||||
|
"restore_on_reconnect": "Restore the last set value in HomeAssistant after a lost connection",
|
||||||
|
"min_value": "Minimum Value",
|
||||||
|
"max_value": "Maximum Value",
|
||||||
|
"step_size": "Minimum increment between numbers"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user