Merge pull request #1022 from sibowler/fix_restart_behaviour

Implementing safe default for devices not requiring RESET/Passive functionality - Fix for bug in 4.1.0
This commit is contained in:
rospogrigio
2022-10-11 11:47:46 +02:00
committed by GitHub
8 changed files with 65 additions and 40 deletions

View File

@@ -92,9 +92,9 @@ If you have selected one entry, you only need to input the device's Friendly Nam
Setting the scan interval is optional, it is only needed if energy/power values are not updating frequently enough by default. Values less than 10 seconds may cause stability issues.
Setting the 'Manual DPS To Add' is optional, it is only needed if the device doesn't advertise the DPS correctly until the entity has been properly initiailised. This setting can often be avoided by first connecting/initialising the device with the Tuya App, then closing the app and then adding the device in the integration.
Setting the 'Manual DPS To Add' is optional, it is only needed if the device doesn't advertise the DPS correctly until the entity has been properly initiailised. This setting can often be avoided by first connecting/initialising the device with the Tuya App, then closing the app and then adding the device in the integration. **Note: Any DPS added using this option will have a -1 value during setup.**
Setting the 'DPIDs to send in RESET command' is optional. It is used when a device doesn't respond to any Tuya commands after a power cycle, but can be connected to (zombie state). The DPids will vary between devices, but typically "18,19,20" is used (and will be the default if none specified). If the wrong entries are added here, then the device may not come out of the zombie state. Typically only sensor DPIDs entered here.
Setting the 'DPIDs to send in RESET command' is optional. It is used when a device doesn't respond to any Tuya commands after a power cycle, but can be connected to (zombie state). This scenario mostly occurs when the device is blocked from accessing the internet. The DPids will vary between devices, but typically "18,19,20" is used. If the wrong entries are added here, then the device may not come out of the zombie state. Typically only sensor DPIDs entered here.
Once you press "Submit", the connection is tested to check that everything works.
@@ -107,8 +107,11 @@ After you have defined all the needed entities, leave the "Do not add more entit
![entity_type](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/3-entity_type.png)
For each entity, the associated DP has to be selected. All the options requiring to select a DP will provide a drop-down menu showing
all the available DPs found on the device (with their current status!!) for easy identification. Each entity type has different options
to be configured. Here is an example for the "switch" entity:
all the available DPs found on the device (with their current status!!) for easy identification.
**Note: If your device requires an LocalTuya to send an initialisation value to the entity for it to work, this can be configured (in supported entities) through the 'Passive entity' option. Optionally you can specify the initialisation value to be sent**
Each entity type has different options to be configured. Here is an example for the "switch" entity:
![entity](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/4-entity.png)

View File

@@ -36,6 +36,7 @@ from .const import (
ATTR_STATE,
CONF_RESTORE_ON_RECONNECT,
CONF_RESET_DPIDS,
CONF_PASSIVE_ENTITY,
)
_LOGGER = logging.getLogger(__name__)
@@ -208,6 +209,9 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger):
except Exception as ex: # pylint: disable=broad-except
try:
if (self._default_reset_dpids is not None) and (
len(self._default_reset_dpids) > 0
):
self.debug(
"Initial state update failed, trying reset command "
+ "for DP IDs: %s",
@@ -372,6 +376,9 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
# Default value is available to be provided by Platform entities if required
self._default_value = self._config.get(CONF_DEFAULT_VALUE)
# Determine whether is a passive entity
self._is_passive_entity = self._config.get(CONF_PASSIVE_ENTITY) or False
""" Restore on connect setting is available to be provided by Platform entities
if required"""
self._restore_on_reconnect = (
@@ -552,10 +559,13 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
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):
if (not self.restore_on_reconnect) and (
(str(self._dp_id) in self._status) or (not self._is_passive_entity)
):
self.debug(
"Entity %s (DP %d) - Not restoring as restore on reconnect is \
disabled for this entity and the entity has an initial status",
"Entity %s (DP %d) - Not restoring as restore on reconnect is "
+ "disabled for this entity and the entity has an initial status "
+ "or it is not a passive entity",
self.name,
self._dp_id,
)
@@ -572,8 +582,12 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger):
# If no current or saved state, then use the default value
if restore_state is None:
if self._is_passive_entity:
self.debug("No last restored state - using default")
restore_state = self.default_value()
else:
self.debug("Not a passive entity and no state found - aborting restore")
return
self.debug(
"Entity %s (DP %d) - Restoring state: %s",

View File

@@ -248,12 +248,6 @@ async def validate_input(hass: core.HomeAssistant, data):
data[CONF_LOCAL_KEY],
float(data[CONF_PROTOCOL_VERSION]),
)
try:
detected_dps = await interface.detect_available_dps()
except Exception: # pylint: disable=broad-except
try:
_LOGGER.debug("Initial state update failed, trying reset command")
if CONF_RESET_DPIDS in data:
reset_ids_str = data[CONF_RESET_DPIDS].split(",")
reset_ids = []
@@ -264,6 +258,12 @@ async def validate_input(hass: core.HomeAssistant, data):
data[CONF_RESET_DPIDS],
reset_ids,
)
try:
detected_dps = await interface.detect_available_dps()
except Exception: # pylint: disable=broad-except
try:
_LOGGER.debug("Initial state update failed, trying reset command")
if len(reset_ids) > 0:
await interface.reset(reset_ids)
detected_dps = await interface.detect_available_dps()
except Exception: # pylint: disable=broad-except
@@ -282,7 +282,7 @@ async def validate_input(hass: core.HomeAssistant, data):
for new_dps in manual_dps_list + (reset_ids or []):
# If the DPS not in the detected dps list, then add with a
# default value indicating that it has been manually added
if new_dps not in detected_dps:
if str(new_dps) not in detected_dps:
detected_dps[new_dps] = -1
except (ConnectionRefusedError, ConnectionResetError) as ex:

View File

@@ -44,6 +44,7 @@ CONF_NO_CLOUD = "no_cloud"
CONF_MANUAL_DPS = "manual_dps_strings"
CONF_DEFAULT_VALUE = "dps_default_value"
CONF_RESET_DPIDS = "reset_dpids"
CONF_PASSIVE_ENTITY = "is_passive_entity"
# light
CONF_BRIGHTNESS_LOWER = "brightness_lower"

View File

@@ -14,6 +14,7 @@ from .const import (
CONF_DEFAULT_VALUE,
CONF_RESTORE_ON_RECONNECT,
CONF_STEPSIZE_VALUE,
CONF_PASSIVE_ENTITY,
)
_LOGGER = logging.getLogger(__name__)
@@ -38,8 +39,9 @@ def flow_schema(dps):
vol.Coerce(float),
vol.Range(min=0.0, max=1000000.0),
),
vol.Optional(CONF_DEFAULT_VALUE): str,
vol.Required(CONF_RESTORE_ON_RECONNECT): bool,
vol.Required(CONF_PASSIVE_ENTITY): bool,
vol.Optional(CONF_DEFAULT_VALUE): str,
}

View File

@@ -16,6 +16,7 @@ from .const import (
CONF_OPTIONS_FRIENDLY,
CONF_DEFAULT_VALUE,
CONF_RESTORE_ON_RECONNECT,
CONF_PASSIVE_ENTITY,
)
@@ -24,8 +25,9 @@ def flow_schema(dps):
return {
vol.Required(CONF_OPTIONS): str,
vol.Optional(CONF_OPTIONS_FRIENDLY): str,
vol.Optional(CONF_DEFAULT_VALUE): str,
vol.Required(CONF_RESTORE_ON_RECONNECT): bool,
vol.Required(CONF_PASSIVE_ENTITY): bool,
vol.Optional(CONF_DEFAULT_VALUE): str,
}

View File

@@ -16,6 +16,7 @@ from .const import (
CONF_VOLTAGE,
CONF_DEFAULT_VALUE,
CONF_RESTORE_ON_RECONNECT,
CONF_PASSIVE_ENTITY,
)
_LOGGER = logging.getLogger(__name__)
@@ -27,8 +28,9 @@ def flow_schema(dps):
vol.Optional(CONF_CURRENT): vol.In(dps),
vol.Optional(CONF_CURRENT_CONSUMPTION): vol.In(dps),
vol.Optional(CONF_VOLTAGE): vol.In(dps),
vol.Optional(CONF_DEFAULT_VALUE): str,
vol.Required(CONF_RESTORE_ON_RECONNECT): bool,
vol.Required(CONF_PASSIVE_ENTITY): bool,
vol.Optional(CONF_DEFAULT_VALUE): str,
}
@@ -82,7 +84,7 @@ class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity):
# Default value is the "OFF" state
def entity_default_value(self):
"""Return False as the defaualt value for this entity type."""
"""Return False as the default value for this entity type."""
return False

View File

@@ -189,7 +189,8 @@
"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"
"step_size": "Minimum increment between numbers",
"is_passive_entity": "Passive entity - requires integration to send initialisation value"
}
}
}