Skip to content

Commit

Permalink
Fixes 3.5.2.2 #190 #191
Browse files Browse the repository at this point in the history
* Fix error if brightness is not defiend
* Allow low-power device to connect while it sleep.
* Restore old states if 0 in manual dps.
* Diagnostics Obfuscate private data.
  • Loading branch information
xZetsubou committed Apr 4, 2024
1 parent e1f6792 commit 8380743
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 25 deletions.
11 changes: 6 additions & 5 deletions custom_components/localtuya/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@

_LOGGER = logging.getLogger(__name__)
RESTORE_STATES = {"0": "restore"}
BYPASS_STATUS = {"0": "bypass"}


async def async_setup_entry(
Expand Down Expand Up @@ -361,7 +360,7 @@ def _new_entity_handler(entity_id):
await asyncio.gather(*connect_sub_devices)

if "0" in self._device_config.manual_dps.split(","):
self.status_updated(BYPASS_STATUS)
self.status_updated(RESTORE_STATES)

if self._pending_status:
await self.set_dps(self._pending_status)
Expand Down Expand Up @@ -570,7 +569,9 @@ def __init__(

""" Restore on connect setting is available to be provided by Platform entities
if required"""
self.set_logger(logger, self._device_config.id)
self.set_logger(
logger, self._device_config.id, self._device_config.enable_debug
)
self.debug(f"Initialized {self._config.get(CONF_PLATFORM)} [{self.name}]")

async def async_added_to_hass(self):
Expand All @@ -589,11 +590,11 @@ def _update_handler(status):
status = {}
if self._status != status:
if status == RESTORE_STATES:
status = {}
if stored_data and stored_data.state != STATE_UNAVAILABLE:
self.debug(f"{self.name}: restore state: {stored_data.state}")
status = {self._dp_id: stored_data.state}
status[self._dp_id] = stored_data.state
self._status = status.copy()

if status:
self.status_updated()

Expand Down
38 changes: 28 additions & 10 deletions custom_components/localtuya/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,9 @@ def dps_string_list(dps_data: dict[str, dict], cloud_dp_codes: dict[str, dict])

# Merge DPs that found through cloud with local.
for dp, func in cloud_dp_codes.items():
if dp not in dps_data and (value := str(func.get("value"))):
# Default Manual dp value is -1, we will replace it if it in cloud.
add_dp = dp not in dps_data or dps_data.get(dp) == -1
if add_dp and (value := str(func.get("value"))):
dps_data[dp] = f"{value}, cloud pull"

for dp, value in dps_data.items():
Expand Down Expand Up @@ -349,6 +351,8 @@ async def validate_input(hass: core.HomeAssistant, entry_id, data):
interface = None
reset_ids = None
close = True
bypass_connection = False # On users risk, only used for low-power power devices
bypass_handshake = False # In-case device is passive.

cid = data.get(CONF_NODE_ID, None)
localtuya_devices = hass.data[DOMAIN][entry_id].devices
Expand Down Expand Up @@ -394,6 +398,10 @@ async def validate_input(hass: core.HomeAssistant, entry_id, data):
raise ValueError(ex)
except:
continue
finally:
if not auto_protocol and data.get(CONF_DEVICE_SLEEP_TIME, 0) > 0:
bypass_connection = True

if CONF_RESET_DPIDS in data:
reset_ids_str = data[CONF_RESET_DPIDS].split(",")
reset_ids = []
Expand Down Expand Up @@ -423,6 +431,8 @@ async def validate_input(hass: core.HomeAssistant, entry_id, data):
detected_dps = {}

# if manual DPs are set, merge these.
# detected_dps_device used to pervent user from bypass handshake manual dps.
detected_dps_device = detected_dps.copy()
_LOGGER.debug("Detected DPS: %s", detected_dps)
if CONF_MANUAL_DPS in data:
manual_dps_list = [dps.strip() for dps in data[CONF_MANUAL_DPS].split(",")]
Expand All @@ -433,7 +443,10 @@ async def validate_input(hass: core.HomeAssistant, entry_id, 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 str(new_dps) not in detected_dps and not str(new_dps) == "0":
if str(new_dps) == "0":
bypass_handshake = True
continue
if str(new_dps) not in detected_dps:
detected_dps[new_dps] = -1

except (ConnectionRefusedError, ConnectionResetError) as ex:
Expand All @@ -444,20 +457,25 @@ async def validate_input(hass: core.HomeAssistant, entry_id, data):
if interface and close:
await interface.close()

# Indicate an error if no datapoints found as the rest of the flow
# won't work in this case
if error:
raise ValueError(error)
if not detected_dps:
raise EmptyDpsList

_LOGGER.debug("Total DPS: %s", detected_dps)
# Get DP descriptions from the cloud, if the device is there.
cloud_dp_codes = {}
cloud_data: TuyaCloudApi = hass.data[DOMAIN][entry_id].cloud_data
if device_cloud_data := cloud_data.device_list.get(data[CONF_DEVICE_ID]):
cloud_dp_codes = device_cloud_data.get("dps_data", {})

# Indicate an error if no datapoints found as the rest of the flow
# won't work in this case
if not bypass_connection and error:
raise ValueError(error)
# If bypass handshake. otherwise raise faild to make handshake with device.
# --- Cloud: We will use the DPS found on cloud if exists.
# --- No cloud: user will have to input the DPS manually.
if not detected_dps_device and not (
(cloud_dp_codes or detected_dps) and bypass_handshake
):
raise EmptyDpsList

_LOGGER.debug("Total DPS: %s", detected_dps)
return {
CONF_DPS_STRINGS: dps_string_list(detected_dps, cloud_dp_codes),
CONF_PROTOCOL_VERSION: conf_protocol,
Expand Down
21 changes: 13 additions & 8 deletions custom_components/localtuya/diagnostics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Diagnostics support for LocalTuya."""

from __future__ import annotations

import copy
Expand All @@ -19,6 +20,8 @@

_LOGGER = logging.getLogger(__name__)

DATA_OBFUSCATE = {"ip": 1, "uid": 3, CONF_LOCAL_KEY: 3, "lat": 0, "lon": 0}


async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
Expand All @@ -37,12 +40,10 @@ async def async_get_config_entry_diagnostics(
local_key_obfuscated = obfuscate(local_key)
dev[CONF_LOCAL_KEY] = local_key_obfuscated
data[CLOUD_DEVICES] = tuya_api.device_list
for dev_id, dev in data[CLOUD_DEVICES].items():
local_key = data[CLOUD_DEVICES][dev_id][CONF_LOCAL_KEY]
local_key_obfuscated = obfuscate(local_key)
if ip := data[CLOUD_DEVICES][dev_id].get("ip"):
data[CLOUD_DEVICES][dev_id]["ip"] = obfuscate(ip, 1, 1)
data[CLOUD_DEVICES][dev_id][CONF_LOCAL_KEY] = local_key_obfuscated
for dev_id, dev in data[CLOUD_DEVICES].copy().items():
for obf, obf_len in DATA_OBFUSCATE.items():
if ob := data[CLOUD_DEVICES][dev_id].get(obf):
data[CLOUD_DEVICES][dev_id][obf] = obfuscate(ob, obf_len, obf_len)
return data


Expand All @@ -61,8 +62,9 @@ async def async_get_device_diagnostics(
tuya_api = hass_localtuya.cloud_data
if dev_id in tuya_api.device_list:
data[DEVICE_CLOUD_INFO] = tuya_api.device_list[dev_id]
if ip := data[DEVICE_CLOUD_INFO].get("ip"):
data[DEVICE_CLOUD_INFO]["ip"] = obfuscate(ip, 1, 1)
for obf, obf_len in DATA_OBFUSCATE.items():
if ob := data[DEVICE_CLOUD_INFO].get(obf):
data[DEVICE_CLOUD_INFO][obf] = obfuscate(ob, obf_len, obf_len)
# NOT censoring private information on device diagnostic data
# local_key = data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY]
# local_key_obfuscated = "{local_key[0:3]}...{local_key[-3:]}"
Expand All @@ -74,4 +76,7 @@ async def async_get_device_diagnostics(

def obfuscate(key, start_characters=3, end_characters=3) -> str:
"""Return obfuscated text by removing characters between [start_characters and end_characters]"""
if start_characters <= 0 and end_characters <= 0:
return ""

return f"{key[0:start_characters]}...{key[-end_characters:]}"
2 changes: 1 addition & 1 deletion custom_components/localtuya/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def is_on(self):
@property
def brightness(self):
"""Return the brightness of the light."""
if self.is_color_mode or self.is_white_mode:
if self._brightness is not None and (self.is_color_mode or self.is_white_mode):
return map_range(
self._brightness, self._lower_brightness, self._upper_brightness, 0, 255
)
Expand Down
2 changes: 1 addition & 1 deletion custom_components/localtuya/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"iot_class": "local_push",
"issue_tracker": "https://github.com/xZetsubou/hass-localtuya/issues",
"requirements": [],
"version": "3.2.5"
"version": "3.2.5.2"
}

0 comments on commit 8380743

Please sign in to comment.