From a210f23e65e8250408057e713482760c6797cbef Mon Sep 17 00:00:00 2001 From: sibowler Date: Sat, 16 Oct 2021 07:41:31 +1100 Subject: [PATCH 01/52] Adding Number and Select as Tuya platforms. Provides support for a wider range of devices. --- custom_components/localtuya/const.py | 2 +- custom_components/localtuya/number.py | 86 +++++++++++++++++ custom_components/localtuya/select.py | 95 +++++++++++++++++++ .../localtuya/translations/en.json | 14 ++- 4 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 custom_components/localtuya/number.py create mode 100644 custom_components/localtuya/select.py diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index bd8a5d3..1bd74ca 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -46,6 +46,6 @@ DATA_DISCOVERY = "discovery" DOMAIN = "localtuya" # Platforms in this list must support config flows -PLATFORMS = ["binary_sensor", "cover", "fan", "light", "sensor", "switch"] +PLATFORMS = ["binary_sensor", "cover", "fan", "light", "sensor", "switch", "number", "select"] TUYA_DEVICE = "tuya_device" diff --git a/custom_components/localtuya/number.py b/custom_components/localtuya/number.py new file mode 100644 index 0000000..50c1919 --- /dev/null +++ b/custom_components/localtuya/number.py @@ -0,0 +1,86 @@ +"""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._minValue = DEFAULT_MIN + if (CONF_MIN_VALUE in self._config): + self._minValue = self._config.get(CONF_MIN_VALUE) + + self._maxValue = 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._minValue + + @property + def max_value(self) -> float: + """Return the maximum value.""" + return self._maxValue + + @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) diff --git a/custom_components/localtuya/select.py b/custom_components/localtuya/select.py new file mode 100644 index 0000000..2f70d99 --- /dev/null +++ b/custom_components/localtuya/select.py @@ -0,0 +1,95 @@ +"""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._validOptions = self._config.get(CONF_OPTIONS).split(';') + + #Set Display options + self._displayOptions = [] + displayOptionsStr = "" + if (CONF_OPTIONS_FRIENDLY in self._config): + displayOptionsStr = self._config.get(CONF_OPTIONS_FRIENDLY).strip() + _LOGGER.debug("Display Options Configured: " + displayOptionsStr) + + if (displayOptionsStr.find(";") >= 0): + self._displayOptions = displayOptionsStr.split(';') + elif (len(displayOptionsStr.strip()) > 0): + self._displayOptions.append(displayOptionsStr) + else: + #Default display string to raw string + _LOGGER.debug("No Display options configured - defaulting to raw values") + self._displayOptions = self._validOptions + + _LOGGER.debug("Total Raw Options: " + str(len(self._validOptions)) + " - Total Display Options: " + str(len(self._displayOptions))) + if (len(self._validOptions) > len(self._displayOptions)): + #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._displayOptions), len(self._validOptions)): + self._displayOptions.append(self._validOptions[i]) + + @property + def current_option(self) -> str: + """Return the current value.""" + return self._stateFriendly + + @property + def options(self) -> list: + """Return the list of values.""" + return self._displayOptions + + @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.""" + optionValue = self._validOptions[self._displayOptions.index(option)] + _LOGGER.debug("Sending Option: " + option + " -> " + optionValue) + await self._device.set_dp(optionValue, self._dp_id) + + + def status_updated(self): + """Device status was updated.""" + state = self.dps(self._dp_id) + self._stateFriendly = self._displayOptions[self._validOptions.index(state)] + self._state = state + + +async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaSelect, flow_schema) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 6ce233a..a833f73 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -74,7 +74,11 @@ "fan_oscillating_control": "Fan Oscillating Control", "fan_speed_low": "Fan Low 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 ;" } } } @@ -124,9 +128,13 @@ "scene": "Scene", "fan_speed_control": "Fan Speed Control", "fan_oscillating_control": "Fan Oscillating Control", - "fan_speed_low": "Fan Low Speed Setting", + "fan_speed_low": "Fan Low Speed Settingaa", "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 ;" } }, "yaml_import": { From f384ef2cbe5024b806556e8d5b3058fce0ddffbb Mon Sep 17 00:00:00 2001 From: sibowler Date: Sat, 16 Oct 2021 07:43:33 +1100 Subject: [PATCH 02/52] Fix for typo introduced during debugging. --- custom_components/localtuya/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index a833f73..f12c4ab 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -128,7 +128,7 @@ "scene": "Scene", "fan_speed_control": "Fan Speed Control", "fan_oscillating_control": "Fan Oscillating Control", - "fan_speed_low": "Fan Low Speed Settingaa", + "fan_speed_low": "Fan Low Speed Setting", "fan_speed_medium": "Fan Medium Speed Setting", "fan_speed_high": "Fan High Speed Setting", "max_value": "Maximum Value", From ec0c6c04b46b271f6f9eb511aba79e9bb0b22793 Mon Sep 17 00:00:00 2001 From: sibowler Date: Sat, 16 Oct 2021 07:46:56 +1100 Subject: [PATCH 03/52] Reordering platforms to be alphabetical --- custom_components/localtuya/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 1bd74ca..af67876 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -46,6 +46,6 @@ DATA_DISCOVERY = "discovery" DOMAIN = "localtuya" # Platforms in this list must support config flows -PLATFORMS = ["binary_sensor", "cover", "fan", "light", "sensor", "switch", "number", "select"] +PLATFORMS = ["binary_sensor", "cover", "fan", "light", "number", "select", "sensor", "switch"] TUYA_DEVICE = "tuya_device" From 9af91081a76714bf64efcc4c9e8c381b3f6676be Mon Sep 17 00:00:00 2001 From: sibowler Date: Mon, 13 Dec 2021 06:08:53 +1100 Subject: [PATCH 04/52] Fixing up style issues --- custom_components/localtuya/const.py | 3 ++- custom_components/localtuya/number.py | 5 ++--- custom_components/localtuya/select.py | 16 +++++++++------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index af67876..b4b5817 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -46,6 +46,7 @@ DATA_DISCOVERY = "discovery" DOMAIN = "localtuya" # Platforms in this list must support config flows -PLATFORMS = ["binary_sensor", "cover", "fan", "light", "number", "select", "sensor", "switch"] +PLATFORMS = ["binary_sensor", "cover", "fan", "light", "number", + "select", "sensor", "switch"] TUYA_DEVICE = "tuya_device" diff --git a/custom_components/localtuya/number.py b/custom_components/localtuya/number.py index 50c1919..1bd7ec4 100644 --- a/custom_components/localtuya/number.py +++ b/custom_components/localtuya/number.py @@ -19,8 +19,9 @@ CONF_MAX_VALUE = "max_value" DEFAULT_MIN = 0 DEFAULT_MAX = 100000 + def flow_schema(dps): -# """Return schema used in config flow.""" + """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), @@ -59,7 +60,6 @@ class LocaltuyaNumber(LocalTuyaEntity, NumberEntity): @property def min_value(self) -> float: """Return the minimum value.""" - return self._minValue @property @@ -76,7 +76,6 @@ class LocaltuyaNumber(LocalTuyaEntity, NumberEntity): """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) diff --git a/custom_components/localtuya/select.py b/custom_components/localtuya/select.py index 2f70d99..90187b8 100644 --- a/custom_components/localtuya/select.py +++ b/custom_components/localtuya/select.py @@ -18,7 +18,7 @@ CONF_OPTIONS_FRIENDLY = "select_options_friendly" def flow_schema(dps): -# """Return schema used in config flow.""" + """Return schema used in config flow.""" return { vol.Required(CONF_OPTIONS): str, vol.Optional(CONF_OPTIONS_FRIENDLY): str, @@ -40,7 +40,7 @@ class LocaltuyaSelect(LocalTuyaEntity, SelectEntity): self._state = STATE_UNKNOWN self._validOptions = self._config.get(CONF_OPTIONS).split(';') - #Set Display options + # Set Display options self._displayOptions = [] displayOptionsStr = "" if (CONF_OPTIONS_FRIENDLY in self._config): @@ -52,14 +52,17 @@ class LocaltuyaSelect(LocalTuyaEntity, SelectEntity): elif (len(displayOptionsStr.strip()) > 0): self._displayOptions.append(displayOptionsStr) else: - #Default display string to raw string + # Default display string to raw string _LOGGER.debug("No Display options configured - defaulting to raw values") self._displayOptions = self._validOptions - _LOGGER.debug("Total Raw Options: " + str(len(self._validOptions)) + " - Total Display Options: " + str(len(self._displayOptions))) + _LOGGER.debug("Total Raw Options: " + str(len(self._validOptions)) + + " - Total Display Options: " + str(len(self._displayOptions))) if (len(self._validOptions) > len(self._displayOptions)): - #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") + # 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._displayOptions), len(self._validOptions)): self._displayOptions.append(self._validOptions[i]) @@ -84,7 +87,6 @@ class LocaltuyaSelect(LocalTuyaEntity, SelectEntity): _LOGGER.debug("Sending Option: " + option + " -> " + optionValue) await self._device.set_dp(optionValue, self._dp_id) - def status_updated(self): """Device status was updated.""" state = self.dps(self._dp_id) From 98bf48227d5da36efe928257e299f60000c63bc1 Mon Sep 17 00:00:00 2001 From: sibowler Date: Tue, 14 Dec 2021 06:16:31 +1100 Subject: [PATCH 05/52] Fixing up style errors in line with tox. --- custom_components/localtuya/const.py | 12 +++++- custom_components/localtuya/number.py | 18 +++++---- custom_components/localtuya/select.py | 56 +++++++++++++++------------ 3 files changed, 51 insertions(+), 35 deletions(-) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index b4b5817..12fa66c 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -46,7 +46,15 @@ DATA_DISCOVERY = "discovery" DOMAIN = "localtuya" # Platforms in this list must support config flows -PLATFORMS = ["binary_sensor", "cover", "fan", "light", "number", - "select", "sensor", "switch"] +PLATFORMS = [ + "binary_sensor", + "cover", + "fan", + "light", + "number", + "select", + "sensor", + "switch", +] TUYA_DEVICE = "tuya_device" diff --git a/custom_components/localtuya/number.py b/custom_components/localtuya/number.py index 1bd7ec4..328f6a2 100644 --- a/custom_components/localtuya/number.py +++ b/custom_components/localtuya/number.py @@ -24,10 +24,12 @@ 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.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), + vol.Coerce(float), + vol.Range(min=-1000000.0, max=1000000.0), ), } @@ -46,11 +48,11 @@ class LocaltuyaNumber(LocalTuyaEntity, NumberEntity): super().__init__(device, config_entry, sensorid, _LOGGER, **kwargs) self._state = STATE_UNKNOWN - self._minValue = DEFAULT_MIN - if (CONF_MIN_VALUE in self._config): - self._minValue = self._config.get(CONF_MIN_VALUE) + self._min_value = DEFAULT_MIN + if CONF_MIN_VALUE in self._config: + self._min_value = self._config.get(CONF_MIN_VALUE) - self._maxValue = self._config.get(CONF_MAX_VALUE) + self._max_value = self._config.get(CONF_MAX_VALUE) @property def value(self) -> float: @@ -60,12 +62,12 @@ class LocaltuyaNumber(LocalTuyaEntity, NumberEntity): @property def min_value(self) -> float: """Return the minimum value.""" - return self._minValue + return self._min_value @property def max_value(self) -> float: """Return the maximum value.""" - return self._maxValue + return self._max_value @property def device_class(self): diff --git a/custom_components/localtuya/select.py b/custom_components/localtuya/select.py index 90187b8..4d9ce54 100644 --- a/custom_components/localtuya/select.py +++ b/custom_components/localtuya/select.py @@ -38,43 +38,49 @@ class LocaltuyaSelect(LocalTuyaEntity, SelectEntity): """Initialize the Tuya sensor.""" super().__init__(device, config_entry, sensorid, _LOGGER, **kwargs) self._state = STATE_UNKNOWN - self._validOptions = self._config.get(CONF_OPTIONS).split(';') + self._state_friendly = "" + self._valid_options = self._config.get(CONF_OPTIONS).split(";") # Set Display options - self._displayOptions = [] - displayOptionsStr = "" - if (CONF_OPTIONS_FRIENDLY in self._config): - displayOptionsStr = self._config.get(CONF_OPTIONS_FRIENDLY).strip() - _LOGGER.debug("Display Options Configured: " + displayOptionsStr) + 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 (displayOptionsStr.find(";") >= 0): - self._displayOptions = displayOptionsStr.split(';') - elif (len(displayOptionsStr.strip()) > 0): - self._displayOptions.append(displayOptionsStr) + 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._displayOptions = self._validOptions + self._display_options = self._valid_options - _LOGGER.debug("Total Raw Options: " + str(len(self._validOptions)) + - " - Total Display Options: " + str(len(self._displayOptions))) - if (len(self._validOptions) > len(self._displayOptions)): - # If list of display items smaller than list of valid items, + _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._displayOptions), len(self._validOptions)): - self._displayOptions.append(self._validOptions[i]) + _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._stateFriendly + return self._state_friendly @property def options(self) -> list: """Return the list of values.""" - return self._displayOptions + return self._display_options @property def device_class(self): @@ -83,14 +89,14 @@ class LocaltuyaSelect(LocalTuyaEntity, SelectEntity): async def async_select_option(self, option: str) -> None: """Update the current value.""" - optionValue = self._validOptions[self._displayOptions.index(option)] - _LOGGER.debug("Sending Option: " + option + " -> " + optionValue) - await self._device.set_dp(optionValue, self._dp_id) + 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._stateFriendly = self._displayOptions[self._validOptions.index(state)] + self._state_friendly = self._display_options[self._valid_options.index(state)] self._state = state From 7e95a746c5d4ce0bc3d0204c3753a5dd19458c89 Mon Sep 17 00:00:00 2001 From: sibowler Date: Tue, 14 Dec 2021 08:51:53 +1100 Subject: [PATCH 06/52] black styling --- custom_components/localtuya/select.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/localtuya/select.py b/custom_components/localtuya/select.py index 4d9ce54..43b32c9 100644 --- a/custom_components/localtuya/select.py +++ b/custom_components/localtuya/select.py @@ -60,7 +60,7 @@ class LocaltuyaSelect(LocalTuyaEntity, SelectEntity): _LOGGER.debug( "Total Raw Options: %s - Total Display Options: %s", str(len(self._valid_options)), - str(len(self._display_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, From f506cfab1871a4026c00f40ebba489e8097e4eb8 Mon Sep 17 00:00:00 2001 From: sibowler Date: Tue, 14 Dec 2021 19:49:10 +1100 Subject: [PATCH 07/52] Fix dependencies to account for Select type. Also fixup errors in Fan due to movement in dependencies. --- custom_components/localtuya/fan.py | 8 +++++++- requirements_test.txt | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 834e199..0c65dfa 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -79,7 +79,13 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Get the list of available speeds.""" 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.""" await self._device.set_dp(True, self._dp_id) if speed is not None: diff --git a/requirements_test.txt b/requirements_test.txt index df62e77..a4e209a 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -3,7 +3,7 @@ codespell==2.0.0 flake8==3.9.2 mypy==0.901 pydocstyle==6.1.1 -cryptography==3.2 +cryptography==3.3.2 pylint==2.8.2 pylint-strict-informational==0.1 -homeassistant==2021.1.4 +homeassistant==2021.7.1 From e1e10d0823d55ecabb0ac7c11aa0cd87aa2934dd Mon Sep 17 00:00:00 2001 From: sibowler Date: Tue, 14 Dec 2021 19:50:17 +1100 Subject: [PATCH 08/52] Adding Select and Number dependencies --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 2d94fd3..562dc77 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,7 +12,7 @@ warn_incomplete_stub = true warn_redundant_casts = 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 ignore_errors = false warn_unreachable = true From a7c9dea979f4304552a3a364f08a8ae0c304bb06 Mon Sep 17 00:00:00 2001 From: sibowler Date: Tue, 14 Dec 2021 19:51:39 +1100 Subject: [PATCH 09/52] Adding number and select types to hacs definition. --- hacs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hacs.json b/hacs.json index 4b6ec05..3c5a41a 100644 --- a/hacs.json +++ b/hacs.json @@ -1,6 +1,6 @@ { "name": "Local Tuya", - "domains": ["climate", "cover", "fan", "light", "sensor", "switch"], + "domains": ["climate", "cover", "fan", "light", "number", "select", "sensor", "switch"], "homeassistant": "0.116.0", "iot_class": ["Local Push"] } From b2a1350e56321cef23dacfd1fbac906106252f8b Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Fri, 13 May 2022 09:36:21 +0200 Subject: [PATCH 10/52] Introduced TuyaCloudApi class --- custom_components/localtuya/cloud_api.py | 103 +++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100755 custom_components/localtuya/cloud_api.py diff --git a/custom_components/localtuya/cloud_api.py b/custom_components/localtuya/cloud_api.py new file mode 100755 index 0000000..2385f25 --- /dev/null +++ b/custom_components/localtuya/cloud_api.py @@ -0,0 +1,103 @@ +"""Class to perform requests to Tuya Cloud APIs.""" +import functools +import hashlib +import hmac +import json +import requests +import sys +import time + + +# Signature algorithm. +def calc_sign(msg,key): + sign = hmac.new(msg=bytes(msg, 'latin-1'),key = bytes(key, 'latin-1'), digestmod = hashlib.sha256).hexdigest().upper() + return sign + + +class TuyaCloudApi: + """Class to send API calls.""" + + def __init__(self, hass, region_code, client_id, secret, user_id): + """Initialize the class.""" + self._hass = hass + self._base_url = f'https://openapi.tuya{region_code}.com' + self._access_token = "" + self._client_id = client_id + self._secret = secret + self._user_id = user_id + + def generate_payload(self, method, t, url, headers, body = None): + payload = self._client_id + self._access_token + t + + payload += (method + '\n' + + hashlib.sha256(bytes((body or "").encode('utf-8'))).hexdigest() + '\n' + # Content-SHA256 + ''.join( + ['%s:%s\n'%(key, headers[key]) # Headers + for key in headers.get("Signature-Headers", "").split(":") + if key in headers] + ) + + '\n/' + url.split('//', 1)[-1].split('/', 1)[-1]) # Url (extracted from 'url') + # print("PAYLOAD: {}".format(payload)) + return payload + + async def async_make_request(self, method, url, body=None, headers={}): + """Perform requests.""" + t = str(int(time.time()*1000)) + payload = self.generate_payload(method, t, url, headers, body) + default_par={ + 'client_id':self._client_id, + 'access_token':self._access_token, + 'sign':calc_sign(payload, self._secret), + 't':t, + 'sign_method':'HMAC-SHA256', + } + full_url = self._base_url + url + print("\n" + method + ": [{}]".format(full_url)) + + if method == "GET": + func = functools.partial(requests.get, full_url, headers=dict(default_par,**headers)) + elif method == "POST": + func = functools.partial(requests.post, full_url, headers=dict(default_par,**headers), data=json.dumps(body)) + print("BODY: [{}]".format(body)) + elif method == "PUT": + func = functools.partial(requests.put, full_url, headers=dict(default_par,**headers), data=json.dumps(body)) + + r = await self._hass.async_add_executor_job(func) + + #r = json.dumps(r.json(), indent=2, ensure_ascii=False) # Beautify the request result format for easy printing and viewing + return r + + async def async_get_access_token(self): + """Obtain a valid access token.""" + r = await self.async_make_request("GET", f'/v1.0/token?grant_type=1') + + if not r.ok: + print("Request failed, status {}".format(r.status)) + return "Request failed, status " + str(r.status) + + r_json = r.json() + if not r_json['success']: + print("Request failed, reply is {}".format(json.dumps(r_json, indent=2, ensure_ascii=False))) + return f"Error {r_json['code']}: {r_json['msg']}" + + print(r.json()) + self._access_token = r.json()['result']['access_token'] + print("GET_ACCESS_TOKEN: {}".format(self._access_token)) + return "ok" + + async def async_get_devices_list(self): + """Obtain the list of devices associated to a user.""" + r = await self.async_make_request("GET", url=f'/v1.0/users/{self._user_id}/devices') + + if not r.ok: + print("Request failed, status {}".format(r.status)) + return None + + r_json = r.json() + if not r_json['success']: + print("Request failed, reply is {}".format(json.dumps(r_json, indent=2, ensure_ascii=False))) + return None + + return r + + From b22cb29811f269bc7b6cea94e1a8ef23f0f2e53b Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Fri, 13 May 2022 09:57:58 +0200 Subject: [PATCH 11/52] Introduced diagnostics --- custom_components/localtuya/diagnostics.py | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 custom_components/localtuya/diagnostics.py diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py new file mode 100644 index 0000000..a18d5d6 --- /dev/null +++ b/custom_components/localtuya/diagnostics.py @@ -0,0 +1,35 @@ +"""Diagnostics support for LocalTuya.""" +from __future__ import annotations +from typing import Any + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceEntry + +from .const import DOMAIN + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, entry: ConfigEntry +) -> dict[str, Any]: + """Return diagnostics for a config entry.""" + data = {} + data = {**entry.data} + # print("DATA is {}".format(data)) + + # censoring private information + # data["token"] = re.sub(r"[^\-]", "*", data["token"]) + # data["userId"] = re.sub(r"[^\-]", "*", data["userId"]) + return data + + +async def async_get_device_diagnostics( + hass: HomeAssistant, entry: ConfigEntry, device: DeviceEntry +) -> dict[str, Any]: + """Return diagnostics for a device entry.""" + # dev_id = next(iter(device.identifiers))[1] + + data = {} + # data["device"] = device + # data["log"] = hass.data[DOMAIN][AIRBNK_DEVICES][dev_id].logger.retrieve_log() + return data From d4c20ebfed3cf114b113867fa2022b555a393417 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Tue, 17 May 2022 17:53:33 +0200 Subject: [PATCH 12/52] Modified config flow, introduced device deletion --- custom_components/localtuya/__init__.py | 206 ++++---- custom_components/localtuya/cloud_api.py | 124 +++-- custom_components/localtuya/common.py | 112 ++-- custom_components/localtuya/config_flow.py | 497 ++++++++++++------ custom_components/localtuya/const.py | 50 +- custom_components/localtuya/diagnostics.py | 4 +- custom_components/localtuya/discovery.py | 2 +- custom_components/localtuya/strings.json | 108 +++- .../localtuya/translations/en.json | 147 ++---- 9 files changed, 778 insertions(+), 472 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index ad879b0..e08980a 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -1,83 +1,18 @@ -"""The LocalTuya integration integration. - -Sample YAML config with all supported entity types (default values -are pre-filled for optional fields): - -localtuya: - - host: 192.168.1.x - device_id: xxxxx - local_key: xxxxx - friendly_name: Tuya Device - protocol_version: "3.3" - entities: - - platform: binary_sensor - friendly_name: Plug Status - id: 1 - device_class: power - state_on: "true" # Optional - state_off: "false" # Optional - - - platform: cover - friendly_name: Device Cover - id: 2 - commands_set: # Optional, default: "on_off_stop" - ["on_off_stop","open_close_stop","fz_zz_stop","1_2_3"] - positioning_mode: ["none","position","timed"] # Optional, default: "none" - currpos_dp: 3 # Optional, required only for "position" mode - setpos_dp: 4 # Optional, required only for "position" mode - position_inverted: [True,False] # Optional, default: False - span_time: 25 # Full movement time: Optional, required only for "timed" mode - - - platform: fan - friendly_name: Device Fan - id: 3 - - - platform: light - friendly_name: Device Light - id: 4 - brightness: 20 - brightness_lower: 29 # Optional - brightness_upper: 1000 # Optional - color_temp: 21 - - - platform: sensor - friendly_name: Plug Voltage - id: 20 - scaling: 0.1 # Optional - device_class: voltage # Optional - unit_of_measurement: "V" # Optional - - - platform: switch - friendly_name: Plug - id: 1 - current: 18 # Optional - current_consumption: 19 # Optional - voltage: 20 # Optional - - - platform: vacuum - friendly_name: Vacuum - id: 28 - idle_status_value: "standby,sleep" - returning_status_value: "docking" - docked_status_value: "charging,chargecompleted" - battery_dp: 14 - mode_dp: 27 - modes: "smart,standby,chargego,wall_follow,spiral,single" - fan_speed_dp: 30 - fan_speeds: "low,normal,high" - clean_time_dp: 33 - clean_area_dp: 32 -""" +"""The LocalTuya integration. """ import asyncio import logging +import time from datetime import timedelta import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.device_registry import DeviceEntry import homeassistant.helpers.entity_registry as er import voluptuous as vol -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry + +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_DEVICE_ID, + CONF_DEVICES, CONF_ENTITIES, CONF_HOST, CONF_ID, @@ -85,14 +20,22 @@ from homeassistant.const import ( EVENT_HOMEASSISTANT_STOP, SERVICE_RELOAD, ) -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.reload import async_integration_yaml_config +from .cloud_api import TuyaCloudApi from .common import TuyaDevice, async_config_entry_by_device_id from .config_flow import config_schema -from .const import CONF_PRODUCT_KEY, DATA_DISCOVERY, DOMAIN, TUYA_DEVICE +from .const import ( + ATTR_UPDATED_AT, + CONF_PRODUCT_KEY, + DATA_CLOUD, + DATA_DISCOVERY, + DOMAIN, + TUYA_DEVICE +) from .discovery import TuyaDiscovery _LOGGER = logging.getLogger(__name__) @@ -116,16 +59,6 @@ SERVICE_SET_DP_SCHEMA = vol.Schema( ) -@callback -def _async_update_config_entry_if_from_yaml(hass, entries_by_id, conf): - """Update a config entry with the latest yaml.""" - device_id = conf[CONF_DEVICE_ID] - - if device_id in entries_by_id and entries_by_id[device_id].source == SOURCE_IMPORT: - entry = entries_by_id[device_id] - hass.config_entries.async_update_entry(entry, data=conf.copy()) - - async def async_setup(hass: HomeAssistant, config: dict): """Set up the LocalTuya integration component.""" hass.data.setdefault(DOMAIN, {}) @@ -140,10 +73,7 @@ async def async_setup(hass: HomeAssistant, config: dict): return current_entries = hass.config_entries.async_entries(DOMAIN) - entries_by_id = {entry.data[CONF_DEVICE_ID]: entry for entry in current_entries} - - for conf in config[DOMAIN]: - _async_update_config_entry_if_from_yaml(hass, entries_by_id, conf) + # entries_by_id = {entry.data[CONF_DEVICE_ID]: entry for entry in current_entries} reload_tasks = [ hass.config_entries.async_reload(entry.entry_id) @@ -211,6 +141,18 @@ async def async_setup(hass: HomeAssistant, config: dict): device = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE] device.async_connect() + client_id = 'xx' + secret = 'xx' + uid = 'xx' + tuya_api = TuyaCloudApi(hass, "eu", client_id, secret, uid) + res = await tuya_api.async_get_access_token() + _LOGGER.debug("ACCESS TOKEN RES: %s", res) + res = await tuya_api.async_get_devices_list() + for dev_id, dev in tuya_api._device_list.items(): + print(f"Name: {dev['name']} \t dev_id {dev['id']} \t key {dev['local_key']} ") + hass.data[DOMAIN][DATA_CLOUD] = tuya_api + + _LOGGER.debug("\n\nSTARTING DISCOVERY") discovery = TuyaDiscovery(_device_discovered) def _shutdown(event): @@ -246,13 +188,13 @@ async def async_setup(hass: HomeAssistant, config: dict): DOMAIN, SERVICE_SET_DP, _handle_set_dp, schema=SERVICE_SET_DP_SCHEMA ) - for host_config in config.get(DOMAIN, []): - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=host_config - ) - ) + return True + +async def async_migrate_entry(domain): + """Migrate old entry.""" + print("WHYYYYYYY") + _LOGGER.error("WHHHYYYYYY") return True @@ -260,15 +202,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up LocalTuya integration from a config entry.""" unsub_listener = entry.add_update_listener(update_listener) - device = TuyaDevice(hass, entry.data) + # device = TuyaDevice(hass, entry.data) + # + # hass.data[DOMAIN][entry.entry_id] = { + # UNSUB_LISTENER: unsub_listener, + # TUYA_DEVICE: device, + # } + # + async def setup_entities(dev_id): + dev_entry = entry.data[CONF_DEVICES][dev_id] + device = TuyaDevice(hass, dev_entry) + hass.data[DOMAIN][dev_id] = { + UNSUB_LISTENER: unsub_listener, + TUYA_DEVICE: device, + } - hass.data[DOMAIN][entry.entry_id] = { - UNSUB_LISTENER: unsub_listener, - TUYA_DEVICE: device, - } - - async def setup_entities(): - platforms = set(entity[CONF_PLATFORM] for entity in entry.data[CONF_ENTITIES]) + platforms = set(entity[CONF_PLATFORM] for entity in dev_entry[CONF_ENTITIES]) + print("DEV {} platforms: {}".format(dev_id, platforms)) await asyncio.gather( *[ hass.config_entries.async_forward_entry_setup(entry, platform) @@ -277,9 +227,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): ) device.async_connect() - await async_remove_orphan_entities(hass, entry) + await async_remove_orphan_entities(hass, entry) - hass.async_create_task(setup_entities()) + for dev_id in entry.data[CONF_DEVICES]: + hass.async_create_task(setup_entities(dev_id)) return True @@ -310,13 +261,60 @@ async def update_listener(hass, config_entry): await hass.config_entries.async_reload(config_entry.entry_id) +async def async_remove_config_entry_device( + hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry +) -> bool: + """Remove a config entry from a device.""" + print("REMOVING {} FROM {}".format(device_entry.identifiers, config_entry.data)) + dev_id = list(device_entry.identifiers)[0][1].split("_")[-1] + _LOGGER.debug("Removing %s", dev_id) + + if dev_id not in config_entry.data[CONF_DEVICES]: + _LOGGER.debug( + "Device ID %s not found in config entry: finalizing device removal", + dev_id + ) + return True + + _LOGGER.debug("Closing device connection for %s", dev_id) + await hass.data[DOMAIN][dev_id][TUYA_DEVICE].close() + + new_data = config_entry.data.copy() + new_data[CONF_DEVICES].pop(dev_id) + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) + + hass.config_entries.async_update_entry( + config_entry, + data=new_data, + ) + _LOGGER.debug("Config entry updated") + + ent_reg = await er.async_get_registry(hass) + entities = { + ent.unique_id: ent.entity_id + for ent in er.async_entries_for_config_entry(ent_reg, config_entry.entry_id) + if dev_id in ent.unique_id + } + for entity_id in entities.values(): + ent_reg.async_remove(entity_id) + print("REMOVED {}".format(entity_id)) + _LOGGER.debug("Removed %s entities: finalizing device removal", len(entities)) + + return True + + async def async_remove_orphan_entities(hass, entry): """Remove entities associated with config entry that has been removed.""" ent_reg = await er.async_get_registry(hass) entities = { - int(ent.unique_id.split("_")[-1]): ent.entity_id + ent.unique_id: ent.entity_id for ent in er.async_entries_for_config_entry(ent_reg, entry.entry_id) } + print("ENTITIES ORPHAN {}".format(entities)) + return + res = ent_reg.async_remove('switch.aa') + print("RESULT ORPHAN {}".format(res)) + # del entities[101] for entity in entry.data[CONF_ENTITIES]: if entity[CONF_ID] in entities: diff --git a/custom_components/localtuya/cloud_api.py b/custom_components/localtuya/cloud_api.py index 2385f25..e4b4140 100755 --- a/custom_components/localtuya/cloud_api.py +++ b/custom_components/localtuya/cloud_api.py @@ -4,14 +4,21 @@ import hashlib import hmac import json import requests -import sys import time # Signature algorithm. -def calc_sign(msg,key): - sign = hmac.new(msg=bytes(msg, 'latin-1'),key = bytes(key, 'latin-1'), digestmod = hashlib.sha256).hexdigest().upper() - return sign +def calc_sign(msg, key): + sign = ( + hmac.new( + msg=bytes(msg, "latin-1"), + key=bytes(key, "latin-1"), + digestmod=hashlib.sha256, + ) + .hexdigest() + .upper() + ) + return sign class TuyaCloudApi: @@ -20,84 +27,107 @@ class TuyaCloudApi: def __init__(self, hass, region_code, client_id, secret, user_id): """Initialize the class.""" self._hass = hass - self._base_url = f'https://openapi.tuya{region_code}.com' - self._access_token = "" + self._base_url = f"https://openapi.tuya{region_code}.com" self._client_id = client_id self._secret = secret self._user_id = user_id + self._access_token = "" + self._device_list = {} - def generate_payload(self, method, t, url, headers, body = None): + def generate_payload(self, method, t, url, headers, body=None): payload = self._client_id + self._access_token + t - payload += (method + '\n' + - hashlib.sha256(bytes((body or "").encode('utf-8'))).hexdigest() + '\n' + # Content-SHA256 - ''.join( - ['%s:%s\n'%(key, headers[key]) # Headers - for key in headers.get("Signature-Headers", "").split(":") - if key in headers] - ) + - '\n/' + url.split('//', 1)[-1].split('/', 1)[-1]) # Url (extracted from 'url') + payload += method + "\n" + # Content-SHA256 + payload += hashlib.sha256(bytes((body or "").encode("utf-8"))).hexdigest() + payload += ( + "\n" + + "".join( + [ + "%s:%s\n" % (key, headers[key]) # Headers + for key in headers.get("Signature-Headers", "").split(":") + if key in headers + ] + ) + + "\n/" + + url.split("//", 1)[-1].split("/", 1)[-1] # Url + ) # print("PAYLOAD: {}".format(payload)) return payload - + async def async_make_request(self, method, url, body=None, headers={}): """Perform requests.""" - t = str(int(time.time()*1000)) - payload = self.generate_payload(method, t, url, headers, body) - default_par={ - 'client_id':self._client_id, - 'access_token':self._access_token, - 'sign':calc_sign(payload, self._secret), - 't':t, - 'sign_method':'HMAC-SHA256', - } + t = str(int(time.time() * 1000)) + payload = self.generate_payload(method, t, url, headers, body) + default_par = { + "client_id": self._client_id, + "access_token": self._access_token, + "sign": calc_sign(payload, self._secret), + "t": t, + "sign_method": "HMAC-SHA256", + } full_url = self._base_url + url print("\n" + method + ": [{}]".format(full_url)) if method == "GET": - func = functools.partial(requests.get, full_url, headers=dict(default_par,**headers)) + func = functools.partial( + requests.get, full_url, headers=dict(default_par, **headers) + ) elif method == "POST": - func = functools.partial(requests.post, full_url, headers=dict(default_par,**headers), data=json.dumps(body)) + func = functools.partial( + requests.post, + full_url, + headers=dict(default_par, **headers), + data=json.dumps(body), + ) print("BODY: [{}]".format(body)) elif method == "PUT": - func = functools.partial(requests.put, full_url, headers=dict(default_par,**headers), data=json.dumps(body)) - + func = functools.partial( + requests.put, + full_url, + headers=dict(default_par, **headers), + data=json.dumps(body), + ) + r = await self._hass.async_add_executor_job(func) - - #r = json.dumps(r.json(), indent=2, ensure_ascii=False) # Beautify the request result format for easy printing and viewing + # r = json.dumps(r.json(), indent=2, ensure_ascii=False) # Beautify the format return r async def async_get_access_token(self): """Obtain a valid access token.""" - r = await self.async_make_request("GET", f'/v1.0/token?grant_type=1') - + r = await self.async_make_request("GET", "/v1.0/token?grant_type=1") + if not r.ok: - print("Request failed, status {}".format(r.status)) return "Request failed, status " + str(r.status) r_json = r.json() - if not r_json['success']: - print("Request failed, reply is {}".format(json.dumps(r_json, indent=2, ensure_ascii=False))) + if not r_json["success"]: return f"Error {r_json['code']}: {r_json['msg']}" print(r.json()) - self._access_token = r.json()['result']['access_token'] + self._access_token = r.json()["result"]["access_token"] print("GET_ACCESS_TOKEN: {}".format(self._access_token)) return "ok" - + async def async_get_devices_list(self): """Obtain the list of devices associated to a user.""" - r = await self.async_make_request("GET", url=f'/v1.0/users/{self._user_id}/devices') - + r = await self.async_make_request( + "GET", url=f"/v1.0/users/{self._user_id}/devices" + ) + if not r.ok: - print("Request failed, status {}".format(r.status)) - return None + return "Request failed, status " + str(r.status) r_json = r.json() - if not r_json['success']: - print("Request failed, reply is {}".format(json.dumps(r_json, indent=2, ensure_ascii=False))) - return None + if not r_json["success"]: + print( + "Request failed, reply is {}".format( + json.dumps(r_json, indent=2, ensure_ascii=False) + ) + ) + return f"Error {r_json['code']}: {r_json['msg']}" - return r - + self._device_list = {dev['id']: dev for dev in r_json["result"]} + # print("DEV__LIST: {}".format(self._device_list)) + return "ok" diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index ea821a5..1c7f61d 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -5,10 +5,12 @@ from datetime import timedelta from homeassistant.const import ( CONF_DEVICE_ID, + CONF_DEVICES, CONF_ENTITIES, CONF_FRIENDLY_NAME, CONF_HOST, CONF_ID, + CONF_MODEL, CONF_PLATFORM, CONF_SCAN_INTERVAL, ) @@ -23,9 +25,9 @@ from homeassistant.helpers.restore_state import RestoreEntity from . import pytuya from .const import ( CONF_LOCAL_KEY, - CONF_PRODUCT_KEY, CONF_PROTOCOL_VERSION, DOMAIN, + DATA_CLOUD, TUYA_DEVICE, ) @@ -42,7 +44,7 @@ def prepare_setup_entities(hass, config_entry, platform): if not entities_to_setup: return None, None - tuyainterface = hass.data[DOMAIN][config_entry.entry_id][TUYA_DEVICE] + tuyainterface = [] return tuyainterface, entities_to_setup @@ -55,28 +57,44 @@ async def async_setup_entry( This is a generic method and each platform should lock domain and entity_class with functools.partial. """ - tuyainterface, entities_to_setup = prepare_setup_entities( - hass, config_entry, domain + print("ASYNC_SETUP_ENTRY: {} {} {}".format( + config_entry.data, + entity_class, + flow_schema(None).items()) ) - if not entities_to_setup: - return - - dps_config_fields = list(get_dps_for_platform(flow_schema)) - entities = [] - for device_config in entities_to_setup: - # Add DPS used by this platform to the request list - for dp_conf in dps_config_fields: - if dp_conf in device_config: - tuyainterface.dps_to_request[device_config[dp_conf]] = None - entities.append( - entity_class( - tuyainterface, - config_entry, - device_config[CONF_ID], - ) - ) + for dev_id in config_entry.data[CONF_DEVICES]: + # entities_to_setup = prepare_setup_entities( + # hass, config_entry.data[dev_id], domain + # ) + dev_entry = config_entry.data[CONF_DEVICES][dev_id] + entities_to_setup = [ + entity + for entity in dev_entry[CONF_ENTITIES] + if entity[CONF_PLATFORM] == domain + ] + + if len(entities_to_setup) > 0: + + tuyainterface = hass.data[DOMAIN][dev_id][TUYA_DEVICE] + + dps_config_fields = list(get_dps_for_platform(flow_schema)) + + for entity_config in entities_to_setup: + # Add DPS used by this platform to the request list + for dp_conf in dps_config_fields: + if dp_conf in entity_config: + tuyainterface.dps_to_request[entity_config[dp_conf]] = None + + entities.append( + entity_class( + tuyainterface, + dev_entry, + entity_config[CONF_ID], + ) + ) + print("ADDING {} entities".format(len(entities))) async_add_entities(entities) @@ -90,7 +108,7 @@ def get_dps_for_platform(flow_schema): def get_entity_config(config_entry, dp_id): """Return entity config for a given DPS id.""" - for entity in config_entry.data[CONF_ENTITIES]: + for entity in config_entry[CONF_ENTITIES]: if entity[CONF_ID] == dp_id: return entity raise Exception(f"missing entity config for id {dp_id}") @@ -121,6 +139,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._connect_task = None self._disconnect_task = None self._unsub_interval = None + self._local_key = self._config_entry[CONF_LOCAL_KEY] self.set_logger(_LOGGER, config_entry[CONF_DEVICE_ID]) # This has to be done in case the device type is type_0d @@ -145,7 +164,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._interface = await pytuya.connect( self._config_entry[CONF_HOST], self._config_entry[CONF_DEVICE_ID], - self._config_entry[CONF_LOCAL_KEY], + self._local_key, float(self._config_entry[CONF_PROTOCOL_VERSION]), self, ) @@ -180,8 +199,30 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._async_refresh, timedelta(seconds=self._config_entry[CONF_SCAN_INTERVAL]), ) - except Exception: # pylint: disable=broad-except + except UnicodeDecodeError as e: # pylint: disable=broad-except + dev_id = self._config_entry[CONF_DEVICE_ID] + cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD]._device_list + if dev_id in cloud_devs: + old_key = self._local_key + self._local_key = cloud_devs[dev_id].get(CONF_LOCAL_KEY) + self.error( + "New local key for %s: from %s to %s", + dev_id, + old_key, + self._local_key + ) + self.exception( + f"Connect to {self._config_entry[CONF_HOST]} failed: %s", + type(e) + ) + if self._interface is not None: + await self._interface.close() + self._interface = None + + except Exception as e: # pylint: disable=broad-except self.exception(f"Connect to {self._config_entry[CONF_HOST]} failed") + self.error("BBBB: %s", type(e)) + if self._interface is not None: await self._interface.close() self._interface = None @@ -201,6 +242,10 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): await self._interface.close() if self._disconnect_task is not None: self._disconnect_task() + self.debug( + "Closed connection with device %s.", + self._config_entry[CONF_FRIENDLY_NAME] + ) async def set_dp(self, state, dp_index): """Change value of a DP of the Tuya device.""" @@ -259,7 +304,7 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger): self._config = get_entity_config(config_entry, dp_id) self._dp_id = dp_id self._status = {} - self.set_logger(logger, self._config_entry.data[CONF_DEVICE_ID]) + self.set_logger(logger, self._config_entry[CONF_DEVICE_ID]) async def async_added_to_hass(self): """Subscribe localtuya events.""" @@ -281,27 +326,28 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger): self.status_updated() self.schedule_update_ha_state() - signal = f"localtuya_{self._config_entry.data[CONF_DEVICE_ID]}" + signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}" self.async_on_remove( async_dispatcher_connect(self.hass, signal, _update_handler) ) - signal = f"localtuya_entity_{self._config_entry.data[CONF_DEVICE_ID]}" + signal = f"localtuya_entity_{self._config_entry[CONF_DEVICE_ID]}" async_dispatcher_send(self.hass, signal, self.entity_id) @property def device_info(self): """Return device information for the device registry.""" + model = self._config_entry.get(CONF_MODEL, "Tuya generic") return { "identifiers": { # Serial numbers are unique identifiers within a specific domain - (DOMAIN, f"local_{self._config_entry.data[CONF_DEVICE_ID]}") + (DOMAIN, f"local_{self._config_entry[CONF_DEVICE_ID]}") }, - "name": self._config_entry.data[CONF_FRIENDLY_NAME], - "manufacturer": "Unknown", - "model": self._config_entry.data.get(CONF_PRODUCT_KEY, "Tuya generic"), - "sw_version": self._config_entry.data[CONF_PROTOCOL_VERSION], + "name": self._config_entry[CONF_FRIENDLY_NAME], + "manufacturer": "Tuya", + "model": f"{model} ({self._config_entry[CONF_DEVICE_ID]})", + "sw_version": self._config_entry[CONF_PROTOCOL_VERSION], } @property @@ -317,7 +363,7 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger): @property def unique_id(self): """Return unique device identifier.""" - return f"local_{self._config_entry.data[CONF_DEVICE_ID]}_{self._dp_id}" + return f"local_{self._config_entry[CONF_DEVICE_ID]}_{self._dp_id}" def has_config(self, attr): """Return if a config parameter has a valid value.""" diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 03bf80d..902efac 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -1,30 +1,45 @@ """Config flow for LocalTuya integration integration.""" import errno import logging +import time from importlib import import_module import homeassistant.helpers.config_validation as cv import voluptuous as vol from homeassistant import config_entries, core, exceptions -from homeassistant.config_entries import SOURCE_IMPORT from homeassistant.const import ( CONF_DEVICE_ID, + CONF_DEVICES, CONF_ENTITIES, CONF_FRIENDLY_NAME, CONF_HOST, CONF_ID, + CONF_MODEL, + CONF_NAME, CONF_PLATFORM, CONF_SCAN_INTERVAL, + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, + CONF_REGION, + ) from homeassistant.core import callback +from .cloud_api import TuyaCloudApi from .common import async_config_entry_by_device_id, pytuya -from .const import CONF_DPS_STRINGS # pylint: disable=unused-import from .const import ( + CONF_ACTION, + CONF_ADD_DEVICE, + CONF_EDIT_DEVICE, + CONF_SETUP_CLOUD, CONF_LOCAL_KEY, - CONF_PRODUCT_KEY, + CONF_PRODUCT_NAME, CONF_PROTOCOL_VERSION, + CONF_USER_ID, + CONF_DPS_STRINGS, + ATTR_UPDATED_AT, DATA_DISCOVERY, + DATA_CLOUD, DOMAIN, PLATFORMS, ) @@ -33,11 +48,32 @@ from .discovery import discover _LOGGER = logging.getLogger(__name__) PLATFORM_TO_ADD = "platform_to_add" -NO_ADDITIONAL_PLATFORMS = "no_additional_platforms" +NO_ADDITIONAL_ENTITIES = "no_additional_entities" DISCOVERED_DEVICE = "discovered_device" CUSTOM_DEVICE = "..." +CONF_ACTIONS = { + CONF_ADD_DEVICE: "Add a new device", + CONF_EDIT_DEVICE: "Edit a device", + CONF_SETUP_CLOUD: "Reconfigure Cloud API account", +} + +CONFIGURE_SCHEMA = vol.Schema( + { + vol.Required(CONF_ACTION, default=CONF_ADD_DEVICE): vol.In(CONF_ACTIONS), + } +) + +CLOUD_SETUP_SCHEMA = vol.Schema( + { + vol.Required(CONF_REGION, default="eu"): vol.In(["eu", "us", "cn", "in"]), + vol.Required(CONF_CLIENT_ID): cv.string, + vol.Required(CONF_CLIENT_SECRET): cv.string, + vol.Required(CONF_USER_ID): cv.string, + } +) + BASIC_INFO_SCHEMA = vol.Schema( { vol.Required(CONF_FRIENDLY_NAME): str, @@ -49,7 +85,6 @@ BASIC_INFO_SCHEMA = vol.Schema( } ) - DEVICE_SCHEMA = vol.Schema( { vol.Required(CONF_HOST): cv.string, @@ -62,23 +97,29 @@ DEVICE_SCHEMA = vol.Schema( ) PICK_ENTITY_SCHEMA = vol.Schema( - {vol.Required(PLATFORM_TO_ADD, default=PLATFORMS[0]): vol.In(PLATFORMS)} + {vol.Required(PLATFORM_TO_ADD, default="switch"): vol.In(PLATFORMS)} ) -def user_schema(devices, entries): - """Create schema for user step.""" - devices = {dev_id: dev["ip"] for dev_id, dev in devices.items()} - devices.update( - { - ent.data[CONF_DEVICE_ID]: ent.data[CONF_FRIENDLY_NAME] - for ent in entries - if ent.source != SOURCE_IMPORT - } - ) - device_list = [f"{key} ({value})" for key, value in devices.items()] +def devices_schema(discovered_devices, cloud_devices_list): + """Create schema for devices step.""" + devices = {} + for dev_id, dev in discovered_devices.items(): + dev_name = dev_id + if dev_id in cloud_devices_list.keys(): + dev_name = cloud_devices_list[dev_id][CONF_NAME] + devices[dev_id] = f"{dev_name} ({discovered_devices[dev_id]['ip']})" + + devices.update({CUSTOM_DEVICE: CUSTOM_DEVICE}) + + # devices.update( + # { + # ent.data[CONF_DEVICE_ID]: ent.data[CONF_FRIENDLY_NAME] + # for ent in entries + # } + # ) return vol.Schema( - {vol.Required(DISCOVERED_DEVICE): vol.In(device_list + [CUSTOM_DEVICE])} + {vol.Required(DISCOVERED_DEVICE): vol.In(devices)} ) @@ -210,10 +251,36 @@ async def validate_input(hass: core.HomeAssistant, data): return dps_string_list(detected_dps) +async def attempt_cloud_connection(hass, user_input): + """Create device.""" + tuya_api = TuyaCloudApi( + hass, + user_input.get(CONF_REGION), + user_input.get(CONF_CLIENT_ID), + user_input.get(CONF_CLIENT_SECRET), + user_input.get(CONF_USER_ID) + ) + + res = await tuya_api.async_get_access_token() + _LOGGER.debug("ACCESS TOKEN RES: %s", res) + if res != "ok": + return {"reason": "authentication_failed", "msg": res} + + res = await tuya_api.async_get_devices_list() + _LOGGER.debug("DEV LIST RES: %s", res) + if res != "ok": + return {"reason": "device_list_failed", "msg": res} + + for dev_id, dev in tuya_api._device_list.items(): + print(f"Name: {dev['name']} \t dev_id {dev['id']} \t key {dev['local_key']} ") + + return {} + + class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for LocalTuya integration.""" - VERSION = 1 + VERSION = 2 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL @staticmethod @@ -224,29 +291,139 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def __init__(self): """Initialize a new LocaltuyaConfigFlow.""" - self.basic_info = None - self.dps_strings = [] - self.platform = None - self.devices = {} - self.selected_device = None - self.entities = [] async def async_step_user(self, user_input=None): """Handle the initial step.""" errors = {} + placeholders = {} if user_input is not None: + print("ECCOCI") + res = await attempt_cloud_connection(self.hass, user_input) + + if len(res) == 0: + return await self._create_entry(user_input) + errors["base"] = res["reason"] + placeholders = {"msg": res["msg"]} + + defaults = {} + defaults[CONF_REGION] = 'eu' + defaults[CONF_CLIENT_ID] = 'xx' + defaults[CONF_CLIENT_SECRET] = 'xx' + defaults[CONF_USER_ID] = 'xx' + defaults.update(user_input or {}) + + return self.async_show_form( + step_id="user", + data_schema=schema_defaults(CLOUD_SETUP_SCHEMA, **defaults), + errors=errors, + description_placeholders=placeholders + ) + + async def _create_entry(self, user_input): + """Register new entry.""" + # if not self.unique_id: + # await self.async_set_unique_id(password) + # self._abort_if_unique_id_configured() + if self._async_current_entries(): + return self.async_abort(reason="already_configured") + + await self.async_set_unique_id(user_input.get(CONF_USER_ID)) + user_input[CONF_DEVICES] = {} + + return self.async_create_entry( + title="LocalTuya", + data=user_input, + ) + + async def async_step_import(self, user_input): + """Handle import from YAML.""" + _LOGGER.error("Configuration via YAML file is no longer supported by this integration.") + # await self.async_set_unique_id(user_input[CONF_DEVICE_ID]) + # self._abort_if_unique_id_configured(updates=user_input) + # return self.async_create_entry( + # title=f"{user_input[CONF_FRIENDLY_NAME]} (YAML)", data=user_input + # ) + + +class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): + """Handle options flow for LocalTuya integration.""" + + def __init__(self, config_entry): + """Initialize localtuya options flow.""" + self.config_entry = config_entry + # self.dps_strings = config_entry.data.get(CONF_DPS_STRINGS, gen_dps_strings()) + # self.entities = config_entry.data[CONF_ENTITIES] + self.selected_device = None + self.basic_info = None + self.dps_strings = [] + self.selected_platform = None + self.devices = {} + self.entities = [] + self.data = None + + async def async_step_init(self, user_input=None): + """Manage basic options.""" + # device_id = self.config_entry.data[CONF_DEVICE_ID] + if user_input is not None: + if user_input.get(CONF_ACTION) == CONF_SETUP_CLOUD: + return await self.async_step_cloud_setup() + if user_input.get(CONF_ACTION) == CONF_ADD_DEVICE: + return await self.async_step_add_device() + + return self.async_show_form( + step_id="init", + data_schema=CONFIGURE_SCHEMA, + ) + + async def async_step_cloud_setup(self, user_input=None): + """Handle the initial step.""" + errors = {} + placeholders = {} + if user_input is not None: + res = await attempt_cloud_connection(self.hass, user_input) + + if len(res) == 0: + new_data = self.config_entry.data.copy() + new_data.update(user_input) + print("CURR_ENTRY {}".format(self.config_entry)) + print("NEW DATA {}".format(new_data)) + + self.hass.config_entries.async_update_entry( + self.config_entry, + data=new_data, + ) + return self.async_create_entry(title="", data={}) + errors["base"] = res["reason"] + placeholders = {"msg": res["msg"]} + + defaults = self.config_entry.data.copy() + defaults.update(user_input or {}) + + return self.async_show_form( + step_id="cloud_setup", + data_schema=schema_defaults(CLOUD_SETUP_SCHEMA, **defaults), + errors=errors, + description_placeholders=placeholders + ) + + async def async_step_add_device(self, user_input=None): + """Handle adding a new device.""" + # Use cache if available or fallback to manual discovery + errors = {} + if user_input is not None: + print("Selected {}".format(user_input)) if user_input[DISCOVERED_DEVICE] != CUSTOM_DEVICE: - self.selected_device = user_input[DISCOVERED_DEVICE].split(" ")[0] + self.selected_device = user_input[DISCOVERED_DEVICE] return await self.async_step_basic_info() - # Use cache if available or fallback to manual discovery - devices = {} + discovered_devices = {} data = self.hass.data.get(DOMAIN) + if data and DATA_DISCOVERY in data: - devices = data[DATA_DISCOVERY].devices + discovered_devices = data[DATA_DISCOVERY].devices else: try: - devices = await discover() + discovered_devices = await discover() except OSError as ex: if ex.errno == errno.EADDRINUSE: errors["base"] = "address_in_use" @@ -258,29 +435,38 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self.devices = { ip: dev - for ip, dev in devices.items() - if dev["gwId"] not in self._async_current_ids() + for ip, dev in discovered_devices.items() + if dev["gwId"] not in self.config_entry.data[CONF_DEVICES] } return self.async_show_form( - step_id="user", + step_id="add_device", + data_schema=devices_schema( + self.devices, + self.hass.data[DOMAIN][DATA_CLOUD]._device_list + ), errors=errors, - data_schema=user_schema(self.devices, self._async_current_entries()), ) async def async_step_basic_info(self, user_input=None): """Handle input of basic info.""" errors = {} + dev_id = self.selected_device if user_input is not None: - await self.async_set_unique_id(user_input[CONF_DEVICE_ID]) + print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID)) try: self.basic_info = user_input - if self.selected_device is not None: - self.basic_info[CONF_PRODUCT_KEY] = self.devices[ - self.selected_device - ]["productKey"] + if dev_id is not None: + # self.basic_info[CONF_PRODUCT_KEY] = self.devices[ + # self.selected_device + # ]["productKey"] + cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD]._device_list + if dev_id in cloud_devs: + self.basic_info[CONF_MODEL] = cloud_devs[dev_id].get(CONF_PRODUCT_NAME) + self.dps_strings = await validate_input(self.hass, user_input) + print("ZIO KEN!! {} ".format(self.dps_strings)) return await self.async_step_pick_entity_type() except CannotConnect: errors["base"] = "cannot_connect" @@ -293,22 +479,27 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors["base"] = "unknown" # If selected device exists as a config entry, load config from it - if self.selected_device in self._async_current_ids(): - entry = async_config_entry_by_device_id(self.hass, self.selected_device) - await self.async_set_unique_id(entry.data[CONF_DEVICE_ID]) - self.basic_info = entry.data.copy() - self.dps_strings = self.basic_info.pop(CONF_DPS_STRINGS).copy() - self.entities = self.basic_info.pop(CONF_ENTITIES).copy() - return await self.async_step_pick_entity_type() + if self.selected_device in self.config_entry.data[CONF_DEVICES]: + print("ALREADY EXISTING!! {}".format(self.selected_device)) + # entry = self.config_entry.data[CONF_DEVICES][self.selected_device] + # await self.async_set_unique_id(entry.data[CONF_DEVICE_ID]) + # self.basic_info = entry.data.copy() + # self.dps_strings = self.basic_info.pop(CONF_DPS_STRINGS).copy() + # self.entities = self.basic_info.pop(CONF_ENTITIES).copy() + # return await self.async_step_pick_entity_type() - # Insert default values from discovery if present + # Insert default values from discovery and cloud if present defaults = {} defaults.update(user_input or {}) - if self.selected_device is not None: - device = self.devices[self.selected_device] + if dev_id is not None: + device = self.devices[dev_id] defaults[CONF_HOST] = device.get("ip") defaults[CONF_DEVICE_ID] = device.get("gwId") defaults[CONF_PROTOCOL_VERSION] = device.get("version") + cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD]._device_list + if dev_id in cloud_devs: + defaults[CONF_LOCAL_KEY] = cloud_devs[dev_id].get(CONF_LOCAL_KEY) + defaults[CONF_FRIENDLY_NAME] = cloud_devs[dev_id].get(CONF_NAME) return self.async_show_form( step_id="basic_info", @@ -316,116 +507,12 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors=errors, ) - async def async_step_pick_entity_type(self, user_input=None): - """Handle asking if user wants to add another entity.""" - if user_input is not None: - if user_input.get(NO_ADDITIONAL_PLATFORMS): - config = { - **self.basic_info, - CONF_DPS_STRINGS: self.dps_strings, - CONF_ENTITIES: self.entities, - } - entry = async_config_entry_by_device_id(self.hass, self.unique_id) - if entry: - self.hass.config_entries.async_update_entry(entry, data=config) - return self.async_abort(reason="device_updated") - return self.async_create_entry( - title=config[CONF_FRIENDLY_NAME], data=config - ) - - self.platform = user_input[PLATFORM_TO_ADD] - return await self.async_step_add_entity() - - # Add a checkbox that allows bailing out from config flow iff at least one - # entity has been added - schema = PICK_ENTITY_SCHEMA - if self.platform is not None: - schema = schema.extend( - {vol.Required(NO_ADDITIONAL_PLATFORMS, default=True): bool} - ) - - return self.async_show_form(step_id="pick_entity_type", data_schema=schema) - - async def async_step_add_entity(self, user_input=None): - """Handle adding a new entity.""" - errors = {} - if user_input is not None: - already_configured = any( - switch[CONF_ID] == int(user_input[CONF_ID].split(" ")[0]) - for switch in self.entities - ) - if not already_configured: - user_input[CONF_PLATFORM] = self.platform - self.entities.append(strip_dps_values(user_input, self.dps_strings)) - return await self.async_step_pick_entity_type() - - errors["base"] = "entity_already_configured" - - return self.async_show_form( - step_id="add_entity", - data_schema=platform_schema(self.platform, self.dps_strings), - errors=errors, - description_placeholders={"platform": self.platform}, - ) - - async def async_step_import(self, user_input): - """Handle import from YAML.""" - await self.async_set_unique_id(user_input[CONF_DEVICE_ID]) - self._abort_if_unique_id_configured(updates=user_input) - return self.async_create_entry( - title=f"{user_input[CONF_FRIENDLY_NAME]} (YAML)", data=user_input - ) - - -class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): - """Handle options flow for LocalTuya integration.""" - - def __init__(self, config_entry): - """Initialize localtuya options flow.""" - self.config_entry = config_entry - self.dps_strings = config_entry.data.get(CONF_DPS_STRINGS, gen_dps_strings()) - self.entities = config_entry.data[CONF_ENTITIES] - self.data = None - - async def async_step_init(self, user_input=None): - """Manage basic options.""" - device_id = self.config_entry.data[CONF_DEVICE_ID] - if user_input is not None: - self.data = user_input.copy() - self.data.update( - { - CONF_DEVICE_ID: device_id, - CONF_DPS_STRINGS: self.dps_strings, - CONF_ENTITIES: [], - } - ) - if len(user_input[CONF_ENTITIES]) > 0: - entity_ids = [ - int(entity.split(" ")[0]) for entity in user_input[CONF_ENTITIES] - ] - self.entities = [ - entity - for entity in self.config_entry.data[CONF_ENTITIES] - if entity[CONF_ID] in entity_ids - ] - return await self.async_step_entity() - - # Not supported for YAML imports - if self.config_entry.source == config_entries.SOURCE_IMPORT: - return await self.async_step_yaml_import() - - return self.async_show_form( - step_id="init", - data_schema=schema_defaults( - options_schema(self.entities), **self.config_entry.data - ), - description_placeholders={"device_id": device_id}, - ) - async def async_step_entity(self, user_input=None): """Manage entity settings.""" errors = {} if user_input is not None: + print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID)) + print("ZIO KEN!! {} ".format(self.dps_strings)) entity = strip_dps_values(user_input, self.dps_strings) entity[CONF_ID] = self.current_entity[CONF_ID] entity[CONF_PLATFORM] = self.current_entity[CONF_PLATFORM] @@ -454,11 +541,105 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): }, ) + async def async_step_pick_entity_type(self, user_input=None): + """Handle asking if user wants to add another entity.""" + if user_input is not None: + print("INPUT3!! {} {}".format(user_input, self.basic_info)) + print("AAAZIO KEN!! {} ".format(self.dps_strings)) + print("NAAA!! {} ".format(user_input.get(NO_ADDITIONAL_ENTITIES))) + if user_input.get(NO_ADDITIONAL_ENTITIES): + print("INPUT4!! {}".format(self.dps_strings)) + print("INPUT4!! {}".format(self.entities)) + config = { + **self.basic_info, + CONF_DPS_STRINGS: self.dps_strings, + CONF_ENTITIES: self.entities, + } + print("NEW CONFIG!! {}".format(config)) + # self.config_entry.data[CONF_DEVICES] + + entry = async_config_entry_by_device_id(self.hass, self.unique_id) + dev_id = self.basic_info.get(CONF_DEVICE_ID) + if dev_id in self.config_entry.data[CONF_DEVICES]: + print("AGGIORNO !! {}".format(dev_id)) + self.hass.config_entries.async_update_entry(self.config_entry, data=config) + return self.async_abort( + reason="device_success", + description_placeholders={ + "dev_name": config.get(CONF_FRIENDLY_NAME), + "action": "updated" + } + ) + + print("CREO NUOVO DEVICE!! {}".format(dev_id)) + new_data = self.config_entry.data.copy() + print("PRE: {}".format(new_data)) + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) + # new_data[CONF_DEVICES]["AZZ"] = "OK" + new_data[CONF_DEVICES].update({dev_id: config}) + print("POST: {}".format(new_data)) + + self.hass.config_entries.async_update_entry( + self.config_entry, + data=new_data, + ) + return self.async_create_entry(title="", data={}) + self.async_create_entry(title="", data={}) + print("DONE! now message {}".format(new_data)) + return self.async_abort( + reason="device_success", + description_placeholders={ + "dev_name": config.get(CONF_FRIENDLY_NAME), + "action": "created" + } + ) + # return self.async_create_entry( + # title=config[CONF_FRIENDLY_NAME], data=config + # ) + print("MA ZZZIO KEN!! {} ".format(self.dps_strings)) + + self.selected_platform = user_input[PLATFORM_TO_ADD] + return await self.async_step_add_entity() + + # Add a checkbox that allows bailing out from config flow if at least one + # entity has been added + schema = PICK_ENTITY_SCHEMA + if self.selected_platform is not None: + schema = schema.extend( + {vol.Required(NO_ADDITIONAL_ENTITIES, default=True): bool} + ) + + return self.async_show_form(step_id="pick_entity_type", data_schema=schema) + + async def async_step_add_entity(self, user_input=None): + """Handle adding a new entity.""" + errors = {} + if user_input is not None: + print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID)) + already_configured = any( + switch[CONF_ID] == int(user_input[CONF_ID].split(" ")[0]) + for switch in self.entities + ) + if not already_configured: + user_input[CONF_PLATFORM] = self.selected_platform + self.entities.append(strip_dps_values(user_input, self.dps_strings)) + return await self.async_step_pick_entity_type() + + errors["base"] = "entity_already_configured" + + return self.async_show_form( + step_id="add_entity", + data_schema=platform_schema(self.selected_platform, self.dps_strings), + errors=errors, + description_placeholders={"platform": self.selected_platform}, + ) + async def async_step_yaml_import(self, user_input=None): """Manage YAML imports.""" - if user_input is not None: - return self.async_create_entry(title="", data={}) - return self.async_show_form(step_id="yaml_import") + _LOGGER.error("Configuration via YAML file is no longer supported by this integration.") + # if user_input is not None: + # return self.async_create_entry(title="", data={}) + # return self.async_show_form(step_id="yaml_import") @property def current_entity(self): diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 4148771..f50fca2 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -1,13 +1,43 @@ """Constants for localtuya integration.""" +DOMAIN = "localtuya" + +DATA_DISCOVERY = "discovery" +DATA_CLOUD = "cloud_data" + +# Platforms in this list must support config flows +PLATFORMS = [ + "binary_sensor", + "climate", + "cover", + "fan", + "light", + "number", + "select", + "sensor", + "switch", + "vacuum", +] + +TUYA_DEVICE = "tuya_device" + ATTR_CURRENT = "current" ATTR_CURRENT_CONSUMPTION = "current_consumption" ATTR_VOLTAGE = "voltage" +ATTR_UPDATED_AT = "updated_at" +# config flow CONF_LOCAL_KEY = "local_key" CONF_PROTOCOL_VERSION = "protocol_version" CONF_DPS_STRINGS = "dps_strings" CONF_PRODUCT_KEY = "product_key" +CONF_PRODUCT_NAME = "product_name" +CONF_USER_ID = "user_id" + +CONF_ACTION = "action" +CONF_ADD_DEVICE = "add_device" +CONF_EDIT_DEVICE = "edit_device" +CONF_SETUP_CLOUD = "setup_cloud" # light CONF_BRIGHTNESS_LOWER = "brightness_lower" @@ -81,23 +111,3 @@ CONF_FAULT_DP = "fault_dp" CONF_PAUSED_STATE = "paused_state" CONF_RETURN_MODE = "return_mode" CONF_STOP_STATUS = "stop_status" - -DATA_DISCOVERY = "discovery" - -DOMAIN = "localtuya" - -# Platforms in this list must support config flows -PLATFORMS = [ - "binary_sensor", - "climate", - "cover", - "fan", - "light", - "number", - "select", - "sensor", - "switch", - "vacuum", -] - -TUYA_DEVICE = "tuya_device" diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index a18d5d6..d4b5cda 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -6,7 +6,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntry -from .const import DOMAIN +from .const import DOMAIN, DATA_CLOUD async def async_get_config_entry_diagnostics( @@ -16,6 +16,8 @@ async def async_get_config_entry_diagnostics( data = {} data = {**entry.data} # print("DATA is {}".format(data)) + tuya_api = hass.data[DOMAIN][DATA_CLOUD] + data["cloud_devices"] = tuya_api._device_list # censoring private information # data["token"] = re.sub(r"[^\-]", "*", data["token"]) diff --git a/custom_components/localtuya/discovery.py b/custom_components/localtuya/discovery.py index a753c5f..d18e376 100644 --- a/custom_components/localtuya/discovery.py +++ b/custom_components/localtuya/discovery.py @@ -71,7 +71,7 @@ class TuyaDiscovery(asyncio.DatagramProtocol): def device_found(self, device): """Discover a new device.""" - if device.get("ip") not in self.devices: + if device.get("gwId") not in self.devices: self.devices[device.get("gwId")] = device _LOGGER.debug("Discovered device: %s", device) diff --git a/custom_components/localtuya/strings.json b/custom_components/localtuya/strings.json index b8bedc8..4db7e70 100644 --- a/custom_components/localtuya/strings.json +++ b/custom_components/localtuya/strings.json @@ -12,16 +12,13 @@ }, "step": { "user": { - "title": "Add Tuya device", - "description": "Fill in the basic details and pick device type. The name entered here will be used to identify the integration itself (as seen in the `Integrations` page). You will name each sub-device in the following steps.", + "title": "Main Configuration", + "description": "Input the credentials for Tuya Cloud API.", "data": { - "name": "Name", - "host": "Host", - "device_id": "Device ID", - "local_key": "Local key", - "protocol_version": "Protocol Version", - "scan_interval": "Scan interval (seconds, only when not updating automatically)", - "device_type": "Device type" + "region": "API server region", + "client_id": "Client ID", + "client_secret": "Secret", + "user_id": "User ID" } }, "power_outlet": { @@ -39,5 +36,98 @@ } } }, + "options": { + "step": { + "init": { + "title": "LocalTuya Configuration", + "description": "Please select the desired actionSSSS.", + "data": { + "add_device": "Add a new device", + "edit_device": "Edit a device", + "delete_device": "Delete a device", + "setup_cloud": "Reconfigure Cloud API account" + } + }, + "entity": { + "title": "Entity Configuration", + "description": "Editing entity with DPS `{id}` and platform `{platform}`.", + "data": { + "id": "ID", + "friendly_name": "Friendly name", + "current": "Current", + "current_consumption": "Current Consumption", + "voltage": "Voltage", + "commands_set": "Open_Close_Stop Commands Set", + "positioning_mode": "Positioning mode", + "current_position_dp": "Current Position (for *position* mode only)", + "set_position_dp": "Set Position (for *position* mode only)", + "position_inverted": "Invert 0-100 position (for *position* mode only)", + "span_time": "Full opening time, in secs. (for *timed* mode only)", + "unit_of_measurement": "Unit of Measurement", + "device_class": "Device Class", + "scaling": "Scaling Factor", + "state_on": "On Value", + "state_off": "Off Value", + "powergo_dp": "Power DP (Usually 25 or 2)", + "idle_status_value": "Idle Status (comma-separated)", + "returning_status_value": "Returning Status", + "docked_status_value": "Docked Status (comma-separated)", + "fault_dp": "Fault DP (Usually 11)", + "battery_dp": "Battery status DP (Usually 14)", + "mode_dp": "Mode DP (Usually 27)", + "modes": "Modes list", + "return_mode": "Return home mode", + "fan_speed_dp": "Fan speeds DP (Usually 30)", + "fan_speeds": "Fan speeds list (comma-separated)", + "clean_time_dp": "Clean Time DP (Usually 33)", + "clean_area_dp": "Clean Area DP (Usually 32)", + "clean_record_dp": "Clean Record DP (Usually 34)", + "locate_dp": "Locate DP (Usually 31)", + "paused_state": "Pause state (pause, paused, etc)", + "stop_status": "Stop status", + "brightness": "Brightness (only for white color)", + "brightness_lower": "Brightness Lower Value", + "brightness_upper": "Brightness Upper Value", + "color_temp": "Color Temperature", + "color_temp_reverse": "Color Temperature Reverse", + "color": "Color", + "color_mode": "Color Mode", + "color_temp_min_kelvin": "Minimum Color Temperature in K", + "color_temp_max_kelvin": "Maximum Color Temperature in K", + "music_mode": "Music mode available", + "scene": "Scene", + "fan_speed_control": "Fan Speed Control dps", + "fan_oscillating_control": "Fan Oscillating Control dps", + "fan_speed_min": "minimum fan speed integer", + "fan_speed_max": "maximum fan speed integer", + "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", + "fan_direction":"fan direction dps", + "fan_direction_forward": "forward dps string", + "fan_direction_reverse": "reverse dps string", + "current_temperature_dp": "Current Temperature", + "target_temperature_dp": "Target Temperature", + "temperature_step": "Temperature Step (optional)", + "max_temperature_dp": "Max Temperature (optional)", + "min_temperature_dp": "Min Temperature (optional)", + "precision": "Precision (optional, for DPs values)", + "target_precision": "Target Precision (optional, for DPs values)", + "temperature_unit": "Temperature Unit (optional)", + "hvac_mode_dp": "HVAC Mode DP (optional)", + "hvac_mode_set": "HVAC Mode Set (optional)", + "hvac_action_dp": "HVAC Current Action DP (optional)", + "hvac_action_set": "HVAC Current Action Set (optional)", + "preset_dp": "Presets DP (optional)", + "preset_set": "Presets Set (optional)", + "eco_dp": "Eco DP (optional)", + "eco_value": "Eco value (optional)", + "heuristic_action": "Enable heuristic action (optional)" + } + }, + "yaml_import": { + "title": "Not Supported", + "description": "Options cannot be edited when configured via YAML." + } + } + }, "title": "LocalTuya" } diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index ebd22ee..7a5b314 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -5,7 +5,9 @@ "device_updated": "Device configuration has been updated!" }, "error": { + "authentication_failed": "Failed to authenticate.\n{msg}", "cannot_connect": "Cannot connect to device. Verify that address is correct and try again.", + "device_list_failed": "Failed to retrieve device list.\n{msg}", "invalid_auth": "Failed to authenticate with device. Verify that device id and local key are correct.", "unknown": "An unknown error occurred. See log for details.", "entity_already_configured": "Entity with this ID has already been configured.", @@ -15,15 +17,57 @@ }, "step": { "user": { - "title": "Device Discovery", + "title": "Cloud API account configuration", + "description": "Input the credentials for Tuya Cloud API.", + "data": { + "region": "API server region", + "client_id": "Client ID", + "client_secret": "Secret", + "user_id": "User ID" + } + } + } + }, + "options": { + "abort": { + "already_configured": "Device has already been configured.", + "device_success": "Device {dev_name} successfully {action}." + }, + "error": { + "authentication_failed": "Failed to authenticate.\n{msg}", + "cannot_connect": "Cannot connect to device. Verify that address is correct and try again.", + "device_list_failed": "Failed to retrieve device list.\n{msg}", + "invalid_auth": "Failed to authenticate with device. Verify that device id and local key are correct.", + "unknown": "An unknown error occurred. See log for details.", + "entity_already_configured": "Entity with this ID has already been configured.", + "address_in_use": "Address used for discovery is already in use. Make sure no other application is using it (TCP port 6668).", + "discovery_failed": "Something failed when discovering devices. See log for details.", + "empty_dps": "Connection to device succeeded but no datapoints found, please try again. Create a new issue and include debug logs if problem persists." + }, + "step": { + "yaml_import": { + "title": "Not Supported", + "description": "Options cannot be edited when configured via YAML." + }, + "init": { + "title": "LocalTuya Configuration", + "description": "Please select the desired action.", + "data": { + "add_device": "AAAAA a new device", + "edit_device": "Edit a device", + "setup_cloud": "Reconfigure Cloud API account" + } + }, + "add_device": { + "title": "Add a new device", "description": "Pick one of the automatically discovered devices or `...` to manually to add a device.", "data": { - "discovered_device": "Discovered Device" + "discovered_device": "Discovered Devices" } }, "basic_info": { "title": "Add Tuya device", - "description": "Fill in the basic device details. The name entered here will be used to identify the integration itself (as seen in the `Integrations` page). You will add entities and give them names in the following steps.", + "description": "Fill in the basic device details. The name entered here will be used to identify the device itself (as seen in the `Integrations` page). You will add entities and give them names in the following steps.", "data": { "friendly_name": "Name", "host": "Host", @@ -38,7 +82,7 @@ "description": "Please pick the type of entity you want to add.", "data": { "platform_to_add": "Platform", - "no_additional_platforms": "Do not add any more entities" + "no_additional_entities": "Do not add any more entities" } }, "add_entity": { @@ -118,100 +162,5 @@ } } }, - "options": { - "step": { - "init": { - "title": "Configure Tuya Device", - "description": "Basic configuration for device id `{device_id}`.", - "data": { - "friendly_name": "Friendly Name", - "host": "Host", - "local_key": "Local key", - "protocol_version": "Protocol Version", - "scan_interval": "Scan interval (seconds, only when not updating automatically)", - "entities": "Entities (uncheck an entity to remove it)" - } - }, - "entity": { - "title": "Entity Configuration", - "description": "Editing entity with DPS `{id}` and platform `{platform}`.", - "data": { - "id": "ID", - "friendly_name": "Friendly name", - "current": "Current", - "current_consumption": "Current Consumption", - "voltage": "Voltage", - "commands_set": "Open_Close_Stop Commands Set", - "positioning_mode": "Positioning mode", - "current_position_dp": "Current Position (for *position* mode only)", - "set_position_dp": "Set Position (for *position* mode only)", - "position_inverted": "Invert 0-100 position (for *position* mode only)", - "span_time": "Full opening time, in secs. (for *timed* mode only)", - "unit_of_measurement": "Unit of Measurement", - "device_class": "Device Class", - "scaling": "Scaling Factor", - "state_on": "On Value", - "state_off": "Off Value", - "powergo_dp": "Power DP (Usually 25 or 2)", - "idle_status_value": "Idle Status (comma-separated)", - "returning_status_value": "Returning Status", - "docked_status_value": "Docked Status (comma-separated)", - "fault_dp": "Fault DP (Usually 11)", - "battery_dp": "Battery status DP (Usually 14)", - "mode_dp": "Mode DP (Usually 27)", - "modes": "Modes list", - "return_mode": "Return home mode", - "fan_speed_dp": "Fan speeds DP (Usually 30)", - "fan_speeds": "Fan speeds list (comma-separated)", - "clean_time_dp": "Clean Time DP (Usually 33)", - "clean_area_dp": "Clean Area DP (Usually 32)", - "clean_record_dp": "Clean Record DP (Usually 34)", - "locate_dp": "Locate DP (Usually 31)", - "paused_state": "Pause state (pause, paused, etc)", - "stop_status": "Stop status", - "brightness": "Brightness (only for white color)", - "brightness_lower": "Brightness Lower Value", - "brightness_upper": "Brightness Upper Value", - "color_temp": "Color Temperature", - "color_temp_reverse": "Color Temperature Reverse", - "color": "Color", - "color_mode": "Color Mode", - "color_temp_min_kelvin": "Minimum Color Temperature in K", - "color_temp_max_kelvin": "Maximum Color Temperature in K", - "music_mode": "Music mode available", - "scene": "Scene", - "fan_speed_control": "Fan Speed Control dps", - "fan_oscillating_control": "Fan Oscillating Control dps", - "fan_speed_min": "minimum fan speed integer", - "fan_speed_max": "maximum fan speed integer", - "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", - "fan_direction":"fan direction dps", - "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string", - "current_temperature_dp": "Current Temperature", - "target_temperature_dp": "Target Temperature", - "temperature_step": "Temperature Step (optional)", - "max_temperature_dp": "Max Temperature (optional)", - "min_temperature_dp": "Min Temperature (optional)", - "precision": "Precision (optional, for DPs values)", - "target_precision": "Target Precision (optional, for DPs values)", - "temperature_unit": "Temperature Unit (optional)", - "hvac_mode_dp": "HVAC Mode DP (optional)", - "hvac_mode_set": "HVAC Mode Set (optional)", - "hvac_action_dp": "HVAC Current Action DP (optional)", - "hvac_action_set": "HVAC Current Action Set (optional)", - "preset_dp": "Presets DP (optional)", - "preset_set": "Presets Set (optional)", - "eco_dp": "Eco DP (optional)", - "eco_value": "Eco value (optional)", - "heuristic_action": "Enable heuristic action (optional)" - } - }, - "yaml_import": { - "title": "Not Supported", - "description": "Options cannot be edited when configured via YAML." - } - } - }, "title": "LocalTuya" } From afc0230796ae1eca7460c73fe51449d41bb5523f Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Thu, 19 May 2022 13:36:55 +0200 Subject: [PATCH 13/52] Introduced migration; fixed update when adding device --- custom_components/localtuya/__init__.py | 150 +++++--- custom_components/localtuya/cloud_api.py | 2 +- custom_components/localtuya/common.py | 19 +- custom_components/localtuya/config_flow.py | 362 +++++++++++------- custom_components/localtuya/const.py | 2 +- custom_components/localtuya/manifest.json | 2 +- .../localtuya/translations/en.json | 27 +- 7 files changed, 348 insertions(+), 216 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index e08980a..a32ab83 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -11,12 +11,16 @@ import voluptuous as vol from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, + CONF_REGION, CONF_DEVICE_ID, CONF_DEVICES, CONF_ENTITIES, CONF_HOST, CONF_ID, CONF_PLATFORM, + CONF_USERNAME, EVENT_HOMEASSISTANT_STOP, SERVICE_RELOAD, ) @@ -31,10 +35,11 @@ from .config_flow import config_schema from .const import ( ATTR_UPDATED_AT, CONF_PRODUCT_KEY, + CONF_USER_ID, DATA_CLOUD, DATA_DISCOVERY, DOMAIN, - TUYA_DEVICE + TUYA_DEVICES, ) from .discovery import TuyaDiscovery @@ -62,6 +67,7 @@ SERVICE_SET_DP_SCHEMA = vol.Schema( async def async_setup(hass: HomeAssistant, config: dict): """Set up the LocalTuya integration component.""" hass.data.setdefault(DOMAIN, {}) + print("SETUP") device_cache = {} @@ -84,14 +90,11 @@ async def async_setup(hass: HomeAssistant, config: dict): async def _handle_set_dp(event): """Handle set_dp service call.""" - entry = async_config_entry_by_device_id(hass, event.data[CONF_DEVICE_ID]) - if not entry: + dev_id = event.data[CONF_DEVICE_ID] + if dev_id not in hass.data[DOMAIN][TUYA_DEVICES]: raise HomeAssistantError("unknown device id") - if entry.entry_id not in hass.data[DOMAIN]: - raise HomeAssistantError("device has not been discovered") - - device = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE] + device = hass.data[DOMAIN][TUYA_DEVICES][dev_id] if not device.connected: raise HomeAssistantError("not connected to device") @@ -138,21 +141,9 @@ async def async_setup(hass: HomeAssistant, config: dict): elif entry.entry_id in hass.data[DOMAIN]: _LOGGER.debug("Device %s found with IP %s", device_id, device_ip) - device = hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE] + device = hass.data[DOMAIN][TUYA_DEVICES][device_id] device.async_connect() - client_id = 'xx' - secret = 'xx' - uid = 'xx' - tuya_api = TuyaCloudApi(hass, "eu", client_id, secret, uid) - res = await tuya_api.async_get_access_token() - _LOGGER.debug("ACCESS TOKEN RES: %s", res) - res = await tuya_api.async_get_devices_list() - for dev_id, dev in tuya_api._device_list.items(): - print(f"Name: {dev['name']} \t dev_id {dev['id']} \t key {dev['local_key']} ") - hass.data[DOMAIN][DATA_CLOUD] = tuya_api - - _LOGGER.debug("\n\nSTARTING DISCOVERY") discovery = TuyaDiscovery(_device_discovered) def _shutdown(event): @@ -168,13 +159,16 @@ async def async_setup(hass: HomeAssistant, config: dict): async def _async_reconnect(now): """Try connecting to devices not already connected to.""" - for entry_id, value in hass.data[DOMAIN].items(): - if entry_id == DATA_DISCOVERY: - continue - - device = value[TUYA_DEVICE] + for device in hass.data[DOMAIN][TUYA_DEVICES]: if not device.connected: device.async_connect() + # for entry_id, value in hass.data[DOMAIN].items(): + # if entry_id == DATA_DISCOVERY: + # continue + # + # device = value[TUYA_DEVICE] + # if not device.connected: + # device.async_connect() async_track_time_interval(hass, _async_reconnect, RECONNECT_INTERVAL) @@ -191,16 +185,69 @@ async def async_setup(hass: HomeAssistant, config: dict): return True -async def async_migrate_entry(domain): - """Migrate old entry.""" - print("WHYYYYYYY") - _LOGGER.error("WHHHYYYYYY") +async def async_migrate_entry(hass, config_entry: ConfigEntry): + """Migrate old entries merging all of them in one.""" + new_version = 2 + if config_entry.version == 1: + _LOGGER.debug("Migrating config entry from version %s", config_entry.version) + stored_entries = hass.config_entries.async_entries(DOMAIN) + + if config_entry.entry_id == stored_entries[0].entry_id: + _LOGGER.debug( + "Migrating the first config entry (%s)", config_entry.entry_id + ) + new_data = {} + new_data[CONF_REGION] = "eu" + new_data[CONF_CLIENT_ID] = "xxx" + new_data[CONF_CLIENT_SECRET] = "xxx" + new_data[CONF_USER_ID] = "xxx" + new_data[CONF_USERNAME] = DOMAIN + new_data[CONF_DEVICES] = { + config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy() + } + config_entry.version = new_version + hass.config_entries.async_update_entry( + config_entry, title=DOMAIN, data=new_data + ) + else: + _LOGGER.debug( + "Merging the config entry %s into the main one", config_entry.entry_id + ) + new_data = stored_entries[0].data.copy() + new_data[CONF_DEVICES].update( + {config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy()} + ) + hass.config_entries.async_update_entry(stored_entries[0], data=new_data) + await hass.config_entries.async_remove(config_entry.entry_id) + + _LOGGER.info( + "Entry %s successfully migrated to version %s.", + config_entry.entry_id, + new_version, + ) return True async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): + print("SETUP ENTRY STARTED!") """Set up LocalTuya integration from a config entry.""" unsub_listener = entry.add_update_listener(update_listener) + hass.data[DOMAIN][UNSUB_LISTENER] = unsub_listener + hass.data[DOMAIN][TUYA_DEVICES] = {} + + region = entry.data[CONF_REGION] + client_id = entry.data[CONF_CLIENT_ID] + secret = entry.data[CONF_CLIENT_SECRET] + user_id = entry.data[CONF_USER_ID] + tuya_api = TuyaCloudApi(hass, region, client_id, secret, user_id) + res = await tuya_api.async_get_access_token() + _LOGGER.debug("ACCESS TOKEN RES: %s . CLOUD DEVICES:", res) + res = await tuya_api.async_get_devices_list() + for dev_id, dev in tuya_api._device_list.items(): + _LOGGER.debug( + "Name: %s \t dev_id %s \t key %s", dev["name"], dev["id"], dev["local_key"] + ) + hass.data[DOMAIN][DATA_CLOUD] = tuya_api # device = TuyaDevice(hass, entry.data) # @@ -212,13 +259,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): async def setup_entities(dev_id): dev_entry = entry.data[CONF_DEVICES][dev_id] device = TuyaDevice(hass, dev_entry) - hass.data[DOMAIN][dev_id] = { - UNSUB_LISTENER: unsub_listener, - TUYA_DEVICE: device, - } + hass.data[DOMAIN][TUYA_DEVICES][dev_id] = device platforms = set(entity[CONF_PLATFORM] for entity in dev_entry[CONF_ENTITIES]) - print("DEV {} platforms: {}".format(dev_id, platforms)) await asyncio.gather( *[ hass.config_entries.async_forward_entry_setup(entry, platform) @@ -228,56 +271,64 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): device.async_connect() await async_remove_orphan_entities(hass, entry) + print("SETUP_ENTITIES for {} ENDED".format(dev_id)) for dev_id in entry.data[CONF_DEVICES]: hass.async_create_task(setup_entities(dev_id)) + print("SETUP ENTRY ENDED!") + return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload a config entry.""" + # print("ASYNC_UNLOAD_ENTRY INVOKED...") + platforms = {} + for dev_id, dev_entry in entry.data[CONF_DEVICES].items(): + for entity in dev_entry[CONF_ENTITIES]: + platforms[entity[CONF_PLATFORM]] = True + unload_ok = all( await asyncio.gather( *[ hass.config_entries.async_forward_entry_unload(entry, component) - for component in set( - entity[CONF_PLATFORM] for entity in entry.data[CONF_ENTITIES] - ) + for component in platforms ] ) ) - hass.data[DOMAIN][entry.entry_id][UNSUB_LISTENER]() - await hass.data[DOMAIN][entry.entry_id][TUYA_DEVICE].close() - if unload_ok: - hass.data[DOMAIN].pop(entry.entry_id) + hass.data[DOMAIN][UNSUB_LISTENER]() + for dev_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): + await device.close() + if unload_ok: + hass.data[DOMAIN][TUYA_DEVICES] = {} + + # print("ASYNC_UNLOAD_ENTRY ENDED") return True async def update_listener(hass, config_entry): """Update listener.""" + print("UPDATE_LISTENER INVOKED") await hass.config_entries.async_reload(config_entry.entry_id) + print("UPDATE_LISTENER RELOADED {}".format(config_entry.entry_id)) async def async_remove_config_entry_device( hass: HomeAssistant, config_entry: ConfigEntry, device_entry: DeviceEntry ) -> bool: """Remove a config entry from a device.""" - print("REMOVING {} FROM {}".format(device_entry.identifiers, config_entry.data)) dev_id = list(device_entry.identifiers)[0][1].split("_")[-1] - _LOGGER.debug("Removing %s", dev_id) if dev_id not in config_entry.data[CONF_DEVICES]: _LOGGER.debug( - "Device ID %s not found in config entry: finalizing device removal", - dev_id + "Device ID %s not found in config entry: finalizing device removal", dev_id ) return True - _LOGGER.debug("Closing device connection for %s", dev_id) - await hass.data[DOMAIN][dev_id][TUYA_DEVICE].close() + await hass.data[DOMAIN][TUYA_DEVICES][dev_id].close() new_data = config_entry.data.copy() new_data[CONF_DEVICES].pop(dev_id) @@ -287,7 +338,6 @@ async def async_remove_config_entry_device( config_entry, data=new_data, ) - _LOGGER.debug("Config entry updated") ent_reg = await er.async_get_registry(hass) entities = { @@ -298,13 +348,13 @@ async def async_remove_config_entry_device( for entity_id in entities.values(): ent_reg.async_remove(entity_id) print("REMOVED {}".format(entity_id)) - _LOGGER.debug("Removed %s entities: finalizing device removal", len(entities)) return True async def async_remove_orphan_entities(hass, entry): """Remove entities associated with config entry that has been removed.""" + return ent_reg = await er.async_get_registry(hass) entities = { ent.unique_id: ent.entity_id @@ -312,7 +362,7 @@ async def async_remove_orphan_entities(hass, entry): } print("ENTITIES ORPHAN {}".format(entities)) return - res = ent_reg.async_remove('switch.aa') + res = ent_reg.async_remove("switch.aa") print("RESULT ORPHAN {}".format(res)) # del entities[101] diff --git a/custom_components/localtuya/cloud_api.py b/custom_components/localtuya/cloud_api.py index e4b4140..b6343ea 100755 --- a/custom_components/localtuya/cloud_api.py +++ b/custom_components/localtuya/cloud_api.py @@ -127,7 +127,7 @@ class TuyaCloudApi: ) return f"Error {r_json['code']}: {r_json['msg']}" - self._device_list = {dev['id']: dev for dev in r_json["result"]} + self._device_list = {dev["id"]: dev for dev in r_json["result"]} # print("DEV__LIST: {}".format(self._device_list)) return "ok" diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index 1c7f61d..0ebb5e0 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -28,7 +28,7 @@ from .const import ( CONF_PROTOCOL_VERSION, DOMAIN, DATA_CLOUD, - TUYA_DEVICE, + TUYA_DEVICES, ) _LOGGER = logging.getLogger(__name__) @@ -57,11 +57,6 @@ async def async_setup_entry( This is a generic method and each platform should lock domain and entity_class with functools.partial. """ - print("ASYNC_SETUP_ENTRY: {} {} {}".format( - config_entry.data, - entity_class, - flow_schema(None).items()) - ) entities = [] for dev_id in config_entry.data[CONF_DEVICES]: @@ -77,7 +72,7 @@ async def async_setup_entry( if len(entities_to_setup) > 0: - tuyainterface = hass.data[DOMAIN][dev_id][TUYA_DEVICE] + tuyainterface = hass.data[DOMAIN][TUYA_DEVICES][dev_id] dps_config_fields = list(get_dps_for_platform(flow_schema)) @@ -94,8 +89,6 @@ async def async_setup_entry( entity_config[CONF_ID], ) ) - print("ADDING {} entities".format(len(entities))) - async_add_entities(entities) @@ -209,11 +202,10 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): "New local key for %s: from %s to %s", dev_id, old_key, - self._local_key + self._local_key, ) self.exception( - f"Connect to {self._config_entry[CONF_HOST]} failed: %s", - type(e) + f"Connect to {self._config_entry[CONF_HOST]} failed: %s", type(e) ) if self._interface is not None: await self._interface.close() @@ -243,8 +235,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): if self._disconnect_task is not None: self._disconnect_task() self.debug( - "Closed connection with device %s.", - self._config_entry[CONF_FRIENDLY_NAME] + "Closed connection with device %s.", self._config_entry[CONF_FRIENDLY_NAME] ) async def set_dp(self, state, dp_index): diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 902efac..7c69cbb 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -21,7 +21,7 @@ from homeassistant.const import ( CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_REGION, - + CONF_USERNAME, ) from homeassistant.core import callback @@ -49,7 +49,7 @@ _LOGGER = logging.getLogger(__name__) PLATFORM_TO_ADD = "platform_to_add" NO_ADDITIONAL_ENTITIES = "no_additional_entities" -DISCOVERED_DEVICE = "discovered_device" +SELECTED_DEVICE = "selected_device" CUSTOM_DEVICE = "..." @@ -71,10 +71,11 @@ CLOUD_SETUP_SCHEMA = vol.Schema( vol.Required(CONF_CLIENT_ID): cv.string, vol.Required(CONF_CLIENT_SECRET): cv.string, vol.Required(CONF_USER_ID): cv.string, + vol.Optional(CONF_USERNAME, default=DOMAIN): cv.string, } ) -BASIC_INFO_SCHEMA = vol.Schema( +CONFIGURE_DEVICE_SCHEMA = vol.Schema( { vol.Required(CONF_FRIENDLY_NAME): str, vol.Required(CONF_LOCAL_KEY): str, @@ -101,16 +102,17 @@ PICK_ENTITY_SCHEMA = vol.Schema( ) -def devices_schema(discovered_devices, cloud_devices_list): +def devices_schema(discovered_devices, cloud_devices_list, add_custom_device=True): """Create schema for devices step.""" devices = {} - for dev_id, dev in discovered_devices.items(): + for dev_id, dev_host in discovered_devices.items(): dev_name = dev_id if dev_id in cloud_devices_list.keys(): dev_name = cloud_devices_list[dev_id][CONF_NAME] - devices[dev_id] = f"{dev_name} ({discovered_devices[dev_id]['ip']})" + devices[dev_id] = f"{dev_name} ({dev_host})" - devices.update({CUSTOM_DEVICE: CUSTOM_DEVICE}) + if add_custom_device: + devices.update({CUSTOM_DEVICE: CUSTOM_DEVICE}) # devices.update( # { @@ -118,15 +120,13 @@ def devices_schema(discovered_devices, cloud_devices_list): # for ent in entries # } # ) - return vol.Schema( - {vol.Required(DISCOVERED_DEVICE): vol.In(devices)} - ) + return vol.Schema({vol.Required(SELECTED_DEVICE): vol.In(devices)}) def options_schema(entities): """Create schema for options.""" entity_names = [ - f"{entity[CONF_ID]} {entity[CONF_FRIENDLY_NAME]}" for entity in entities + f"{entity[CONF_ID]}: {entity[CONF_FRIENDLY_NAME]}" for entity in entities ] return vol.Schema( { @@ -258,7 +258,7 @@ async def attempt_cloud_connection(hass, user_input): user_input.get(CONF_REGION), user_input.get(CONF_CLIENT_ID), user_input.get(CONF_CLIENT_SECRET), - user_input.get(CONF_USER_ID) + user_input.get(CONF_USER_ID), ) res = await tuya_api.async_get_access_token() @@ -306,17 +306,18 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): placeholders = {"msg": res["msg"]} defaults = {} - defaults[CONF_REGION] = 'eu' - defaults[CONF_CLIENT_ID] = 'xx' - defaults[CONF_CLIENT_SECRET] = 'xx' - defaults[CONF_USER_ID] = 'xx' + defaults[CONF_REGION] = "eu" + defaults[CONF_CLIENT_ID] = "xxx" + defaults[CONF_CLIENT_SECRET] = "xxx" + defaults[CONF_USER_ID] = "xxx" + defaults[CONF_USERNAME] = "xxx" defaults.update(user_input or {}) return self.async_show_form( step_id="user", data_schema=schema_defaults(CLOUD_SETUP_SCHEMA, **defaults), errors=errors, - description_placeholders=placeholders + description_placeholders=placeholders, ) async def _create_entry(self, user_input): @@ -331,13 +332,15 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): user_input[CONF_DEVICES] = {} return self.async_create_entry( - title="LocalTuya", + title=user_input.get(CONF_USERNAME), data=user_input, ) async def async_step_import(self, user_input): """Handle import from YAML.""" - _LOGGER.error("Configuration via YAML file is no longer supported by this integration.") + _LOGGER.error( + "Configuration via YAML file is no longer supported by this integration." + ) # await self.async_set_unique_id(user_input[CONF_DEVICE_ID]) # self._abort_if_unique_id_configured(updates=user_input) # return self.async_create_entry( @@ -354,12 +357,12 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): # self.dps_strings = config_entry.data.get(CONF_DPS_STRINGS, gen_dps_strings()) # self.entities = config_entry.data[CONF_ENTITIES] self.selected_device = None - self.basic_info = None + self.editing_device = False + self.device_data = None self.dps_strings = [] self.selected_platform = None - self.devices = {} + self.discovered_devices = {} self.entities = [] - self.data = None async def async_step_init(self, user_input=None): """Manage basic options.""" @@ -369,6 +372,8 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): return await self.async_step_cloud_setup() if user_input.get(CONF_ACTION) == CONF_ADD_DEVICE: return await self.async_step_add_device() + if user_input.get(CONF_ACTION) == CONF_EDIT_DEVICE: + return await self.async_step_edit_device() return self.async_show_form( step_id="init", @@ -403,27 +408,28 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): step_id="cloud_setup", data_schema=schema_defaults(CLOUD_SETUP_SCHEMA, **defaults), errors=errors, - description_placeholders=placeholders + description_placeholders=placeholders, ) async def async_step_add_device(self, user_input=None): """Handle adding a new device.""" # Use cache if available or fallback to manual discovery + self.editing_device = False errors = {} if user_input is not None: print("Selected {}".format(user_input)) - if user_input[DISCOVERED_DEVICE] != CUSTOM_DEVICE: - self.selected_device = user_input[DISCOVERED_DEVICE] - return await self.async_step_basic_info() + if user_input[SELECTED_DEVICE] != CUSTOM_DEVICE: + self.selected_device = user_input[SELECTED_DEVICE] + return await self.async_step_configure_device() - discovered_devices = {} + self.discovered_devices = {} data = self.hass.data.get(DOMAIN) if data and DATA_DISCOVERY in data: - discovered_devices = data[DATA_DISCOVERY].devices + self.discovered_devices = data[DATA_DISCOVERY].devices else: try: - discovered_devices = await discover() + self.discovered_devices = await discover() except OSError as ex: if ex.errno == errno.EADDRINUSE: errors["base"] = "address_in_use" @@ -433,37 +439,90 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): _LOGGER.exception("discovery failed") errors["base"] = "discovery_failed" - self.devices = { - ip: dev - for ip, dev in discovered_devices.items() + devices = { + dev_id: dev["ip"] + for dev_id, dev in self.discovered_devices.items() if dev["gwId"] not in self.config_entry.data[CONF_DEVICES] } + print("SCHEMA DEVS {}".format(devices)) return self.async_show_form( step_id="add_device", data_schema=devices_schema( - self.devices, - self.hass.data[DOMAIN][DATA_CLOUD]._device_list + devices, self.hass.data[DOMAIN][DATA_CLOUD]._device_list ), errors=errors, ) - async def async_step_basic_info(self, user_input=None): + async def async_step_edit_device(self, user_input=None): + """Handle editing a device.""" + self.editing_device = True + # Use cache if available or fallback to manual discovery + print("AAA") + errors = {} + if user_input is not None: + print("Selected {}".format(user_input)) + self.selected_device = user_input[SELECTED_DEVICE] + dev_conf = self.config_entry.data[CONF_DEVICES][self.selected_device] + self.dps_strings = dev_conf.get(CONF_DPS_STRINGS, gen_dps_strings()) + self.entities = dev_conf[CONF_ENTITIES] + print("Selected DPS {} ENT {}".format(self.dps_strings, self.entities)) + + return await self.async_step_configure_device() + + print("BBB: {}".format(self.config_entry.data[CONF_DEVICES])) + devices = {} + for dev_id, configured_dev in self.config_entry.data[CONF_DEVICES].items(): + devices[dev_id] = configured_dev[CONF_HOST] + print("SCHEMA DEVS {}".format(devices)) + + return self.async_show_form( + step_id="edit_device", + data_schema=devices_schema( + devices, self.hass.data[DOMAIN][DATA_CLOUD]._device_list, False + ), + errors=errors, + ) + + async def async_step_configure_device(self, user_input=None): """Handle input of basic info.""" errors = {} dev_id = self.selected_device if user_input is not None: - print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID)) + print("INPUT1!! {} {}".format(user_input, dev_id)) try: - self.basic_info = user_input + self.device_data = user_input.copy() if dev_id is not None: - # self.basic_info[CONF_PRODUCT_KEY] = self.devices[ + # self.device_data[CONF_PRODUCT_KEY] = self.devices[ # self.selected_device # ]["productKey"] cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD]._device_list if dev_id in cloud_devs: - self.basic_info[CONF_MODEL] = cloud_devs[dev_id].get(CONF_PRODUCT_NAME) + self.device_data[CONF_MODEL] = cloud_devs[dev_id].get( + CONF_PRODUCT_NAME + ) + if self.editing_device: + self.device_data.update( + { + CONF_DEVICE_ID: dev_id, + CONF_MODEL: self.device_data[CONF_MODEL], + CONF_DPS_STRINGS: self.dps_strings, + CONF_ENTITIES: [], + } + ) + if len(user_input[CONF_ENTITIES]) > 0: + entity_ids = [ + int(entity.split(":")[0]) + for entity in user_input[CONF_ENTITIES] + ] + device_config = self.config_entry.data[CONF_DEVICES][dev_id] + self.entities = [ + entity + for entity in device_config[CONF_ENTITIES] + if entity[CONF_ID] in entity_ids + ] + return await self.async_step_configure_entity() self.dps_strings = await validate_input(self.hass, user_input) print("ZIO KEN!! {} ".format(self.dps_strings)) @@ -478,21 +537,19 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" - # If selected device exists as a config entry, load config from it - if self.selected_device in self.config_entry.data[CONF_DEVICES]: - print("ALREADY EXISTING!! {}".format(self.selected_device)) - # entry = self.config_entry.data[CONF_DEVICES][self.selected_device] - # await self.async_set_unique_id(entry.data[CONF_DEVICE_ID]) - # self.basic_info = entry.data.copy() - # self.dps_strings = self.basic_info.pop(CONF_DPS_STRINGS).copy() - # self.entities = self.basic_info.pop(CONF_ENTITIES).copy() - # return await self.async_step_pick_entity_type() - - # Insert default values from discovery and cloud if present defaults = {} - defaults.update(user_input or {}) - if dev_id is not None: - device = self.devices[dev_id] + if self.editing_device: + # If selected device exists as a config entry, load config from it + print("ALREADY EXISTING!! {}".format(dev_id)) + defaults = self.config_entry.data[CONF_DEVICES][dev_id].copy() + print("ALREADY EXISTING!! {} {}".format(self.entities, defaults)) + schema = schema_defaults(options_schema(self.entities), **defaults) + placeholders = {"for_device": f" for device `{dev_id}`"} + print("SCHEMA!! {}".format(schema)) + elif dev_id is not None: + # Insert default values from discovery and cloud if present + print("NEW DEVICE!! {}".format(dev_id)) + device = self.discovered_devices[dev_id] defaults[CONF_HOST] = device.get("ip") defaults[CONF_DEVICE_ID] = device.get("gwId") defaults[CONF_PROTOCOL_VERSION] = device.get("version") @@ -500,29 +557,100 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): if dev_id in cloud_devs: defaults[CONF_LOCAL_KEY] = cloud_devs[dev_id].get(CONF_LOCAL_KEY) defaults[CONF_FRIENDLY_NAME] = cloud_devs[dev_id].get(CONF_NAME) + schema = schema_defaults(CONFIGURE_DEVICE_SCHEMA, **defaults) + placeholders = {"for_device": ""} + defaults.update(user_input or {}) return self.async_show_form( - step_id="basic_info", - data_schema=schema_defaults(BASIC_INFO_SCHEMA, **defaults), + step_id="configure_device", + data_schema=schema, errors=errors, + description_placeholders=placeholders, ) + async def async_step_pick_entity_type(self, user_input=None): + """Handle asking if user wants to add another entity.""" + if user_input is not None: + print("INPUT4!! {} {}".format(user_input, self.device_data)) + if user_input.get(NO_ADDITIONAL_ENTITIES): + config = { + **self.device_data, + CONF_DPS_STRINGS: self.dps_strings, + CONF_ENTITIES: self.entities, + } + print("NEW CONFIG!! {}".format(config)) + + # entry = async_config_entry_by_device_id(self.hass, self.unique_id) + dev_id = self.device_data.get(CONF_DEVICE_ID) + if dev_id in self.config_entry.data[CONF_DEVICES]: + print("AGGIORNO !! {}".format(dev_id)) + self.hass.config_entries.async_update_entry( + self.config_entry, data=config + ) + return self.async_abort( + reason="device_success", + description_placeholders={ + "dev_name": config.get(CONF_FRIENDLY_NAME), + "action": "updated", + }, + ) + + print("CREO NUOVO DEVICE!! {}".format(dev_id)) + new_data = self.config_entry.data.copy() + print("PRE: {}".format(new_data)) + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) + # new_data[CONF_DEVICES]["AZZ"] = "OK" + new_data[CONF_DEVICES].update({dev_id: config}) + print("POST: {}".format(new_data)) + + self.hass.config_entries.async_update_entry( + self.config_entry, + data=new_data, + ) + return self.async_create_entry(title="", data={}) + self.async_create_entry(title="", data={}) + + self.selected_platform = user_input[PLATFORM_TO_ADD] + return await self.async_step_configure_entity() + + # Add a checkbox that allows bailing out from config flow if at least one + # entity has been added + schema = PICK_ENTITY_SCHEMA + if self.selected_platform is not None: + schema = schema.extend( + {vol.Required(NO_ADDITIONAL_ENTITIES, default=True): bool} + ) + + return self.async_show_form(step_id="pick_entity_type", data_schema=schema) + + def available_dps_strings(self): + available_dps = [] + # print("FILTERING!! {} {}".format(self.dps_strings, self.entities)) + used_dps = [str(entity[CONF_ID]) for entity in self.entities] + # print("FILTERING-- {} {}".format(self.dps_strings, used_dps)) + for dp_string in self.dps_strings: + dp = dp_string.split(" ")[0] + # print("FILTERING2!! {} {}".format(dp_string, dp)) + if dp not in used_dps: + available_dps.append(dp_string) + return available_dps + async def async_step_entity(self, user_input=None): """Manage entity settings.""" errors = {} if user_input is not None: - print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID)) + print("INPUT2!! {} {}".format(user_input, CONF_DEVICE_ID)) print("ZIO KEN!! {} ".format(self.dps_strings)) entity = strip_dps_values(user_input, self.dps_strings) entity[CONF_ID] = self.current_entity[CONF_ID] entity[CONF_PLATFORM] = self.current_entity[CONF_PLATFORM] - self.data[CONF_ENTITIES].append(entity) + self.device_data[CONF_ENTITIES].append(entity) - if len(self.entities) == len(self.data[CONF_ENTITIES]): + if len(self.entities) == len(self.device_data[CONF_ENTITIES]): self.hass.config_entries.async_update_entry( self.config_entry, - title=self.data[CONF_FRIENDLY_NAME], - data=self.data, + title=self.device_data[CONF_FRIENDLY_NAME], + data=self.device_data, ) return self.async_create_entry(title="", data={}) @@ -541,102 +669,56 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): }, ) - async def async_step_pick_entity_type(self, user_input=None): - """Handle asking if user wants to add another entity.""" - if user_input is not None: - print("INPUT3!! {} {}".format(user_input, self.basic_info)) - print("AAAZIO KEN!! {} ".format(self.dps_strings)) - print("NAAA!! {} ".format(user_input.get(NO_ADDITIONAL_ENTITIES))) - if user_input.get(NO_ADDITIONAL_ENTITIES): - print("INPUT4!! {}".format(self.dps_strings)) - print("INPUT4!! {}".format(self.entities)) - config = { - **self.basic_info, - CONF_DPS_STRINGS: self.dps_strings, - CONF_ENTITIES: self.entities, - } - print("NEW CONFIG!! {}".format(config)) - # self.config_entry.data[CONF_DEVICES] - - entry = async_config_entry_by_device_id(self.hass, self.unique_id) - dev_id = self.basic_info.get(CONF_DEVICE_ID) - if dev_id in self.config_entry.data[CONF_DEVICES]: - print("AGGIORNO !! {}".format(dev_id)) - self.hass.config_entries.async_update_entry(self.config_entry, data=config) - return self.async_abort( - reason="device_success", - description_placeholders={ - "dev_name": config.get(CONF_FRIENDLY_NAME), - "action": "updated" - } - ) - - print("CREO NUOVO DEVICE!! {}".format(dev_id)) - new_data = self.config_entry.data.copy() - print("PRE: {}".format(new_data)) - new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) - # new_data[CONF_DEVICES]["AZZ"] = "OK" - new_data[CONF_DEVICES].update({dev_id: config}) - print("POST: {}".format(new_data)) - - self.hass.config_entries.async_update_entry( - self.config_entry, - data=new_data, - ) - return self.async_create_entry(title="", data={}) - self.async_create_entry(title="", data={}) - print("DONE! now message {}".format(new_data)) - return self.async_abort( - reason="device_success", - description_placeholders={ - "dev_name": config.get(CONF_FRIENDLY_NAME), - "action": "created" - } - ) - # return self.async_create_entry( - # title=config[CONF_FRIENDLY_NAME], data=config - # ) - print("MA ZZZIO KEN!! {} ".format(self.dps_strings)) - - self.selected_platform = user_input[PLATFORM_TO_ADD] - return await self.async_step_add_entity() - - # Add a checkbox that allows bailing out from config flow if at least one - # entity has been added - schema = PICK_ENTITY_SCHEMA - if self.selected_platform is not None: - schema = schema.extend( - {vol.Required(NO_ADDITIONAL_ENTITIES, default=True): bool} - ) - - return self.async_show_form(step_id="pick_entity_type", data_schema=schema) - - async def async_step_add_entity(self, user_input=None): - """Handle adding a new entity.""" + async def async_step_configure_entity(self, user_input=None): + """Manage entity settings.""" errors = {} if user_input is not None: print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID)) already_configured = any( - switch[CONF_ID] == int(user_input[CONF_ID].split(" ")[0]) - for switch in self.entities + entity[CONF_ID] == int(user_input[CONF_ID].split(" ")[0]) + for entity in self.entities ) if not already_configured: user_input[CONF_PLATFORM] = self.selected_platform self.entities.append(strip_dps_values(user_input, self.dps_strings)) - return await self.async_step_pick_entity_type() + # new entity added. Let's check if there are more left... + print("ADDED. Remaining... {}".format(self.available_dps_strings())) + user_input = None + if len(self.available_dps_strings()) == 0: + user_input = {NO_ADDITIONAL_ENTITIES: True} + return await self.async_step_pick_entity_type(user_input) errors["base"] = "entity_already_configured" + if self.editing_device: + schema = platform_schema( + self.current_entity[CONF_PLATFORM], self.dps_strings, allow_id=False + ) + schema = schema_defaults(schema, self.dps_strings, **self.current_entity) + placeholders = { + "entity": f"entity with DP {self.current_entity[CONF_ID]}", + "platform": self.current_entity[CONF_PLATFORM], + } + else: + available_dps = self.available_dps_strings() + schema = platform_schema(self.selected_platform, available_dps) + placeholders = { + "entity": "an entity", + "platform": self.selected_platform, + } + return self.async_show_form( - step_id="add_entity", - data_schema=platform_schema(self.selected_platform, self.dps_strings), + step_id="configure_entity", + data_schema=schema, errors=errors, - description_placeholders={"platform": self.selected_platform}, + description_placeholders=placeholders, ) async def async_step_yaml_import(self, user_input=None): """Manage YAML imports.""" - _LOGGER.error("Configuration via YAML file is no longer supported by this integration.") + _LOGGER.error( + "Configuration via YAML file is no longer supported by this integration." + ) # if user_input is not None: # return self.async_create_entry(title="", data={}) # return self.async_show_form(step_id="yaml_import") @@ -644,7 +726,7 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): @property def current_entity(self): """Existing configuration for entity currently being edited.""" - return self.entities[len(self.data[CONF_ENTITIES])] + return self.entities[len(self.device_data[CONF_ENTITIES])] class CannotConnect(exceptions.HomeAssistantError): diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index f50fca2..8feafad 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -19,7 +19,7 @@ PLATFORMS = [ "vacuum", ] -TUYA_DEVICE = "tuya_device" +TUYA_DEVICES = "tuya_devices" ATTR_CURRENT = "current" ATTR_CURRENT_CONSUMPTION = "current_consumption" diff --git a/custom_components/localtuya/manifest.json b/custom_components/localtuya/manifest.json index 7f19f9f..bb6f2ea 100644 --- a/custom_components/localtuya/manifest.json +++ b/custom_components/localtuya/manifest.json @@ -1,7 +1,7 @@ { "domain": "localtuya", "name": "LocalTuya integration", - "version": "3.2.1", + "version": "4.0.0", "documentation": "https://github.com/rospogrigio/localtuya/", "dependencies": [], "codeowners": [ diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 7a5b314..b80cf9e 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -23,7 +23,8 @@ "region": "API server region", "client_id": "Client ID", "client_secret": "Secret", - "user_id": "User ID" + "user_id": "User ID", + "user_name": "Username" } } } @@ -62,19 +63,27 @@ "title": "Add a new device", "description": "Pick one of the automatically discovered devices or `...` to manually to add a device.", "data": { - "discovered_device": "Discovered Devices" + "selected_device": "Discovered Devices" } }, - "basic_info": { - "title": "Add Tuya device", - "description": "Fill in the basic device details. The name entered here will be used to identify the device itself (as seen in the `Integrations` page). You will add entities and give them names in the following steps.", + "edit_device": { + "title": "Edit a new device", + "description": "Pick the configured device you wish to edit.", + "data": { + "selected_device": "Configured Devices" + } + }, + "configure_device": { + "title": "Configure Tuya device", + "description": "Fill in the device details{for_device}.", "data": { "friendly_name": "Name", "host": "Host", "device_id": "Device ID", "local_key": "Local key", "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)" } }, "pick_entity_type": { @@ -85,9 +94,9 @@ "no_additional_entities": "Do not add any more entities" } }, - "add_entity": { - "title": "Add new entity", - "description": "Please fill out the details for an entity with type `{platform}`. All settings except for `ID` can be changed from the Options page later.", + "configure_entity": { + "title": "Configure entity", + "description": "Please fill out the details for {entity} with type `{platform}`. All settings except for `ID` can be changed from the Options page later.", "data": { "id": "ID", "friendly_name": "Friendly name", From cdfe8e4906865e5ff3c43a49a697f77c1d50bf4e Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Fri, 20 May 2022 18:28:32 +0200 Subject: [PATCH 14/52] Finalized device editing, device diagnostics, auto-update of local key and IP --- custom_components/localtuya/__init__.py | 77 +++++++-------- custom_components/localtuya/cloud_api.py | 16 ++- custom_components/localtuya/common.py | 101 ++++++++++--------- custom_components/localtuya/config_flow.py | 107 ++++++++++----------- custom_components/localtuya/diagnostics.py | 11 ++- 5 files changed, 160 insertions(+), 152 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index a32ab83..e547a37 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -27,7 +27,6 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.event import async_track_time_interval -from homeassistant.helpers.reload import async_integration_yaml_config from .cloud_api import TuyaCloudApi from .common import TuyaDevice, async_config_entry_by_device_id @@ -73,13 +72,9 @@ async def async_setup(hass: HomeAssistant, config: dict): async def _handle_reload(service): """Handle reload service call.""" - config = await async_integration_yaml_config(hass, DOMAIN) - - if not config or DOMAIN not in config: - return + _LOGGER.debug("Service %s.reload called: reloading integration", DOMAIN) current_entries = hass.config_entries.async_entries(DOMAIN) - # entries_by_id = {entry.data[CONF_DEVICE_ID]: entry for entry in current_entries} reload_tasks = [ hass.config_entries.async_reload(entry.entry_id) @@ -107,42 +102,51 @@ async def async_setup(hass: HomeAssistant, config: dict): product_key = device["productKey"] # If device is not in cache, check if a config entry exists - if device_id not in device_cache: - entry = async_config_entry_by_device_id(hass, device_id) - if entry: - # Save address from config entry in cache to trigger - # potential update below - device_cache[device_id] = entry.data[CONF_HOST] - - if device_id not in device_cache: - return - entry = async_config_entry_by_device_id(hass, device_id) if entry is None: return - updates = {} + if device_id not in device_cache: + if entry and device_id in entry.data[CONF_DEVICES]: + # Save address from config entry in cache to trigger + # potential update below + host_ip = entry.data[CONF_DEVICES][device_id][CONF_HOST] + device_cache[device_id] = host_ip + + if device_id not in device_cache: + return + + dev_entry = entry.data[CONF_DEVICES][device_id] + + new_data = entry.data.copy() + updated = False if device_cache[device_id] != device_ip: - updates[CONF_HOST] = device_ip + updated = True + new_data[CONF_DEVICES][device_id][CONF_HOST] = device_ip device_cache[device_id] = device_ip - if entry.data.get(CONF_PRODUCT_KEY) != product_key: - updates[CONF_PRODUCT_KEY] = product_key + if dev_entry.get(CONF_PRODUCT_KEY) != product_key: + updated = True + new_data[CONF_DEVICES][device_id][CONF_PRODUCT_KEY] = product_key # Update settings if something changed, otherwise try to connect. Updating # settings triggers a reload of the config entry, which tears down the device # so no need to connect in that case. - if updates: - _LOGGER.debug("Update keys for device %s: %s", device_id, updates) + if updated: + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) hass.config_entries.async_update_entry( - entry, data={**entry.data, **updates} + entry, data=new_data ) - elif entry.entry_id in hass.data[DOMAIN]: - _LOGGER.debug("Device %s found with IP %s", device_id, device_ip) + device = hass.data[DOMAIN][TUYA_DEVICES][device_id] + if not device.connected: + device.async_connect() + elif device_id in hass.data[DOMAIN][TUYA_DEVICES]: + # _LOGGER.debug("Device %s found with IP %s", device_id, device_ip) device = hass.data[DOMAIN][TUYA_DEVICES][device_id] - device.async_connect() + if not device.connected: + device.async_connect() discovery = TuyaDiscovery(_device_discovered) @@ -159,18 +163,11 @@ async def async_setup(hass: HomeAssistant, config: dict): async def _async_reconnect(now): """Try connecting to devices not already connected to.""" - for device in hass.data[DOMAIN][TUYA_DEVICES]: + for device_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): if not device.connected: device.async_connect() - # for entry_id, value in hass.data[DOMAIN].items(): - # if entry_id == DATA_DISCOVERY: - # continue - # - # device = value[TUYA_DEVICE] - # if not device.connected: - # device.async_connect() - async_track_time_interval(hass, _async_reconnect, RECONNECT_INTERVAL) + # async_track_time_interval(hass, _async_reconnect, RECONNECT_INTERVAL) hass.helpers.service.async_register_admin_service( DOMAIN, @@ -205,6 +202,7 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry): new_data[CONF_DEVICES] = { config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy() } + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) config_entry.version = new_version hass.config_entries.async_update_entry( config_entry, title=DOMAIN, data=new_data @@ -217,6 +215,7 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry): new_data[CONF_DEVICES].update( {config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy()} ) + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) hass.config_entries.async_update_entry(stored_entries[0], data=new_data) await hass.config_entries.async_remove(config_entry.entry_id) @@ -241,11 +240,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): user_id = entry.data[CONF_USER_ID] tuya_api = TuyaCloudApi(hass, region, client_id, secret, user_id) res = await tuya_api.async_get_access_token() - _LOGGER.debug("ACCESS TOKEN RES: %s . CLOUD DEVICES:", res) + if res != "ok": + _LOGGER.error("Cloud API connection failed: %s", res) + _LOGGER.info("Cloud API connection succeeded.") res = await tuya_api.async_get_devices_list() for dev_id, dev in tuya_api._device_list.items(): _LOGGER.debug( - "Name: %s \t dev_id %s \t key %s", dev["name"], dev["id"], dev["local_key"] + "Cloud device: %s \t dev_id %s \t key %s", dev["name"], dev["id"], dev["local_key"] ) hass.data[DOMAIN][DATA_CLOUD] = tuya_api @@ -258,7 +259,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): # async def setup_entities(dev_id): dev_entry = entry.data[CONF_DEVICES][dev_id] - device = TuyaDevice(hass, dev_entry) + device = TuyaDevice(hass, entry, dev_id) hass.data[DOMAIN][TUYA_DEVICES][dev_id] = device platforms = set(entity[CONF_PLATFORM] for entity in dev_entry[CONF_ENTITIES]) diff --git a/custom_components/localtuya/cloud_api.py b/custom_components/localtuya/cloud_api.py index b6343ea..ac82c4e 100755 --- a/custom_components/localtuya/cloud_api.py +++ b/custom_components/localtuya/cloud_api.py @@ -67,7 +67,7 @@ class TuyaCloudApi: "sign_method": "HMAC-SHA256", } full_url = self._base_url + url - print("\n" + method + ": [{}]".format(full_url)) + # print("\n" + method + ": [{}]".format(full_url)) if method == "GET": func = functools.partial( @@ -80,7 +80,7 @@ class TuyaCloudApi: headers=dict(default_par, **headers), data=json.dumps(body), ) - print("BODY: [{}]".format(body)) + # print("BODY: [{}]".format(body)) elif method == "PUT": func = functools.partial( requests.put, @@ -104,9 +104,7 @@ class TuyaCloudApi: if not r_json["success"]: return f"Error {r_json['code']}: {r_json['msg']}" - print(r.json()) self._access_token = r.json()["result"]["access_token"] - print("GET_ACCESS_TOKEN: {}".format(self._access_token)) return "ok" async def async_get_devices_list(self): @@ -120,11 +118,11 @@ class TuyaCloudApi: r_json = r.json() if not r_json["success"]: - print( - "Request failed, reply is {}".format( - json.dumps(r_json, indent=2, ensure_ascii=False) - ) - ) + # print( + # "Request failed, reply is {}".format( + # json.dumps(r_json, indent=2, ensure_ascii=False) + # ) + # ) return f"Error {r_json['code']}: {r_json['msg']}" self._device_list = {dev["id"]: dev for dev in r_json["result"]} diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index 0ebb5e0..c7b8c0d 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -2,6 +2,7 @@ import asyncio import logging from datetime import timedelta +import time from homeassistant.const import ( CONF_DEVICE_ID, @@ -24,6 +25,7 @@ from homeassistant.helpers.restore_state import RestoreEntity from . import pytuya from .const import ( + ATTR_UPDATED_AT, CONF_LOCAL_KEY, CONF_PROTOCOL_VERSION, DOMAIN, @@ -112,7 +114,7 @@ def async_config_entry_by_device_id(hass, device_id): """Look up config entry by device id.""" current_entries = hass.config_entries.async_entries(DOMAIN) for entry in current_entries: - if entry.data[CONF_DEVICE_ID] == device_id: + if device_id in entry.data[CONF_DEVICES]: return entry return None @@ -120,11 +122,12 @@ def async_config_entry_by_device_id(hass, device_id): class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): """Cache wrapper for pytuya.TuyaInterface.""" - def __init__(self, hass, config_entry): + def __init__(self, hass, config_entry, dev_id): """Initialize the cache.""" super().__init__() self._hass = hass self._config_entry = config_entry + self._dev_config_entry = config_entry.data[CONF_DEVICES][dev_id] self._interface = None self._status = {} self.dps_to_request = {} @@ -132,11 +135,11 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._connect_task = None self._disconnect_task = None self._unsub_interval = None - self._local_key = self._config_entry[CONF_LOCAL_KEY] - self.set_logger(_LOGGER, config_entry[CONF_DEVICE_ID]) + self._local_key = self._dev_config_entry[CONF_LOCAL_KEY] + self.set_logger(_LOGGER, self._dev_config_entry[CONF_DEVICE_ID]) # This has to be done in case the device type is type_0d - for entity in config_entry[CONF_ENTITIES]: + for entity in self._dev_config_entry[CONF_ENTITIES]: self.dps_to_request[entity[CONF_ID]] = None @property @@ -151,14 +154,14 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): 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._dev_config_entry[CONF_HOST]) try: self._interface = await pytuya.connect( - self._config_entry[CONF_HOST], - self._config_entry[CONF_DEVICE_ID], + self._dev_config_entry[CONF_HOST], + self._dev_config_entry[CONF_DEVICE_ID], self._local_key, - float(self._config_entry[CONF_PROTOCOL_VERSION]), + float(self._dev_config_entry[CONF_PROTOCOL_VERSION]), self, ) self._interface.add_dps_to_request(self.dps_to_request) @@ -174,52 +177,63 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self.debug( "New entity %s was added to %s", entity_id, - self._config_entry[CONF_HOST], + self._dev_config_entry[CONF_HOST], ) self._dispatch_status() - signal = f"localtuya_entity_{self._config_entry[CONF_DEVICE_ID]}" + signal = f"localtuya_entity_{self._dev_config_entry[CONF_DEVICE_ID]}" self._disconnect_task = async_dispatcher_connect( self._hass, signal, _new_entity_handler ) if ( - CONF_SCAN_INTERVAL in self._config_entry - and self._config_entry[CONF_SCAN_INTERVAL] > 0 + CONF_SCAN_INTERVAL in self._dev_config_entry + and self._dev_config_entry[CONF_SCAN_INTERVAL] > 0 ): self._unsub_interval = async_track_time_interval( self._hass, self._async_refresh, - timedelta(seconds=self._config_entry[CONF_SCAN_INTERVAL]), + timedelta(seconds=self._dev_config_entry[CONF_SCAN_INTERVAL]), ) except UnicodeDecodeError as e: # pylint: disable=broad-except - dev_id = self._config_entry[CONF_DEVICE_ID] - cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD]._device_list - if dev_id in cloud_devs: - old_key = self._local_key - self._local_key = cloud_devs[dev_id].get(CONF_LOCAL_KEY) - self.error( - "New local key for %s: from %s to %s", - dev_id, - old_key, - self._local_key, - ) self.exception( - f"Connect to {self._config_entry[CONF_HOST]} failed: %s", type(e) + f"Connect to {self._dev_config_entry[CONF_HOST]} failed: %s", type(e) ) if self._interface is not None: await self._interface.close() self._interface = None except Exception as e: # pylint: disable=broad-except - self.exception(f"Connect to {self._config_entry[CONF_HOST]} failed") - self.error("BBBB: %s", type(e)) + self.exception(f"Connect to {self._dev_config_entry[CONF_HOST]} failed") + self.error("BBBB: %s", type(type(e))) + if 'json.decode' in str(type(e)): + self.error("BBBB2") + await self.update_local_key() if self._interface is not None: await self._interface.close() self._interface = None self._connect_task = None + async def update_local_key(self): + dev_id = self._dev_config_entry[CONF_DEVICE_ID] + await self._hass.data[DOMAIN][DATA_CLOUD].async_get_devices_list() + cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD]._device_list + if dev_id in cloud_devs: + old_key = self._local_key + self._local_key = cloud_devs[dev_id].get(CONF_LOCAL_KEY) + new_data = self._config_entry.data.copy() + new_data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] = self._local_key + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) + self._hass.config_entries.async_update_entry( + self._config_entry, + data=new_data, + ) + self.debug( + "New local key for %s: from %s to %s", + dev_id, old_key, self._local_key + ) + async def _async_refresh(self, _now): if self._interface is not None: await self._interface.update_dps() @@ -235,7 +249,8 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): if self._disconnect_task is not None: self._disconnect_task() self.debug( - "Closed connection with device %s.", self._config_entry[CONF_FRIENDLY_NAME] + "Closed connection with device %s.", + self._dev_config_entry[CONF_FRIENDLY_NAME] ) async def set_dp(self, state, dp_index): @@ -247,7 +262,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self.exception("Failed to set DP %d to %d", dp_index, state) else: self.error( - "Not connected to device %s", self._config_entry[CONF_FRIENDLY_NAME] + "Not connected to device %s", self._dev_config_entry[CONF_FRIENDLY_NAME] ) async def set_dps(self, states): @@ -259,7 +274,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self.exception("Failed to set DPs %r", states) else: self.error( - "Not connected to device %s", self._config_entry[CONF_FRIENDLY_NAME] + "Not connected to device %s", self._dev_config_entry[CONF_FRIENDLY_NAME] ) @callback @@ -269,13 +284,13 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._dispatch_status() def _dispatch_status(self): - signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}" + signal = f"localtuya_{self._dev_config_entry[CONF_DEVICE_ID]}" async_dispatcher_send(self._hass, signal, self._status) @callback def disconnected(self): """Device disconnected.""" - signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}" + signal = f"localtuya_{self._dev_config_entry[CONF_DEVICE_ID]}" async_dispatcher_send(self._hass, signal, None) if self._unsub_interval is not None: self._unsub_interval() @@ -291,11 +306,11 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger): """Initialize the Tuya entity.""" super().__init__() self._device = device - self._config_entry = config_entry + self._dev_config_entry = config_entry self._config = get_entity_config(config_entry, dp_id) self._dp_id = dp_id self._status = {} - self.set_logger(logger, self._config_entry[CONF_DEVICE_ID]) + self.set_logger(logger, self._dev_config_entry[CONF_DEVICE_ID]) async def async_added_to_hass(self): """Subscribe localtuya events.""" @@ -317,28 +332,28 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger): self.status_updated() self.schedule_update_ha_state() - signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}" + signal = f"localtuya_{self._dev_config_entry[CONF_DEVICE_ID]}" self.async_on_remove( async_dispatcher_connect(self.hass, signal, _update_handler) ) - signal = f"localtuya_entity_{self._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) @property def device_info(self): """Return device information for the device registry.""" - model = self._config_entry.get(CONF_MODEL, "Tuya generic") + model = self._dev_config_entry.get(CONF_MODEL, "Tuya generic") return { "identifiers": { # Serial numbers are unique identifiers within a specific domain - (DOMAIN, f"local_{self._config_entry[CONF_DEVICE_ID]}") + (DOMAIN, f"local_{self._dev_config_entry[CONF_DEVICE_ID]}") }, - "name": self._config_entry[CONF_FRIENDLY_NAME], + "name": self._dev_config_entry[CONF_FRIENDLY_NAME], "manufacturer": "Tuya", - "model": f"{model} ({self._config_entry[CONF_DEVICE_ID]})", - "sw_version": self._config_entry[CONF_PROTOCOL_VERSION], + "model": f"{model} ({self._dev_config_entry[CONF_DEVICE_ID]})", + "sw_version": self._dev_config_entry[CONF_PROTOCOL_VERSION], } @property @@ -354,7 +369,7 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger): @property def unique_id(self): """Return unique device identifier.""" - return f"local_{self._config_entry[CONF_DEVICE_ID]}_{self._dp_id}" + return f"local_{self._dev_config_entry[CONF_DEVICE_ID]}_{self._dp_id}" def has_config(self, attr): """Return if a config parameter has a valid value.""" diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 7c69cbb..e901289 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -5,6 +5,7 @@ import time from importlib import import_module import homeassistant.helpers.config_validation as cv +import homeassistant.helpers.entity_registry as er import voluptuous as vol from homeassistant import config_entries, core, exceptions from homeassistant.const import ( @@ -253,7 +254,7 @@ async def validate_input(hass: core.HomeAssistant, data): async def attempt_cloud_connection(hass, user_input): """Create device.""" - tuya_api = TuyaCloudApi( + cloud_api = TuyaCloudApi( hass, user_input.get(CONF_REGION), user_input.get(CONF_CLIENT_ID), @@ -261,20 +262,18 @@ async def attempt_cloud_connection(hass, user_input): user_input.get(CONF_USER_ID), ) - res = await tuya_api.async_get_access_token() - _LOGGER.debug("ACCESS TOKEN RES: %s", res) + res = await cloud_api.async_get_access_token() if res != "ok": - return {"reason": "authentication_failed", "msg": res} + _LOGGER.error("Cloud API connection failed: %s", res) + return cloud_api, {"reason": "authentication_failed", "msg": res} - res = await tuya_api.async_get_devices_list() - _LOGGER.debug("DEV LIST RES: %s", res) + res = await cloud_api.async_get_devices_list() if res != "ok": - return {"reason": "device_list_failed", "msg": res} + _LOGGER.error("Cloud API get_devices_list failed: %s", res) + return cloud_api, {"reason": "device_list_failed", "msg": res} + _LOGGER.info("Cloud API connection succeeded.") - for dev_id, dev in tuya_api._device_list.items(): - print(f"Name: {dev['name']} \t dev_id {dev['id']} \t key {dev['local_key']} ") - - return {} + return cloud_api, {} class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @@ -297,8 +296,7 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors = {} placeholders = {} if user_input is not None: - print("ECCOCI") - res = await attempt_cloud_connection(self.hass, user_input) + cloud_api, res = await attempt_cloud_connection(self.hass, user_input) if len(res) == 0: return await self._create_entry(user_input) @@ -322,9 +320,6 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): async def _create_entry(self, user_input): """Register new entry.""" - # if not self.unique_id: - # await self.async_set_unique_id(password) - # self._abort_if_unique_id_configured() if self._async_current_entries(): return self.async_abort(reason="already_configured") @@ -341,11 +336,6 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): _LOGGER.error( "Configuration via YAML file is no longer supported by this integration." ) - # await self.async_set_unique_id(user_input[CONF_DEVICE_ID]) - # self._abort_if_unique_id_configured(updates=user_input) - # return self.async_create_entry( - # title=f"{user_input[CONF_FRIENDLY_NAME]} (YAML)", data=user_input - # ) class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): @@ -385,19 +375,24 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): errors = {} placeholders = {} if user_input is not None: - res = await attempt_cloud_connection(self.hass, user_input) + cloud_api, res = await attempt_cloud_connection(self.hass, user_input) if len(res) == 0: new_data = self.config_entry.data.copy() new_data.update(user_input) - print("CURR_ENTRY {}".format(self.config_entry)) - print("NEW DATA {}".format(new_data)) + cloud_devs = cloud_api._device_list + for dev_id, dev in new_data[CONF_DEVICES].items(): + if CONF_MODEL not in dev and dev_id in cloud_devs: + new_data[CONF_DEVICES][dev_id][CONF_MODEL] = cloud_devs[dev_id].get( + CONF_PRODUCT_NAME + ) + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) self.hass.config_entries.async_update_entry( self.config_entry, data=new_data, ) - return self.async_create_entry(title="", data={}) + return self.async_create_entry(title=new_data.get(CONF_USERNAME), data={}) errors["base"] = res["reason"] placeholders = {"msg": res["msg"]} @@ -417,7 +412,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): self.editing_device = False errors = {} if user_input is not None: - print("Selected {}".format(user_input)) if user_input[SELECTED_DEVICE] != CUSTOM_DEVICE: self.selected_device = user_input[SELECTED_DEVICE] return await self.async_step_configure_device() @@ -444,7 +438,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): for dev_id, dev in self.discovered_devices.items() if dev["gwId"] not in self.config_entry.data[CONF_DEVICES] } - print("SCHEMA DEVS {}".format(devices)) return self.async_show_form( step_id="add_device", @@ -458,23 +451,18 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): """Handle editing a device.""" self.editing_device = True # Use cache if available or fallback to manual discovery - print("AAA") errors = {} if user_input is not None: - print("Selected {}".format(user_input)) self.selected_device = user_input[SELECTED_DEVICE] dev_conf = self.config_entry.data[CONF_DEVICES][self.selected_device] self.dps_strings = dev_conf.get(CONF_DPS_STRINGS, gen_dps_strings()) self.entities = dev_conf[CONF_ENTITIES] - print("Selected DPS {} ENT {}".format(self.dps_strings, self.entities)) return await self.async_step_configure_device() - print("BBB: {}".format(self.config_entry.data[CONF_DEVICES])) devices = {} for dev_id, configured_dev in self.config_entry.data[CONF_DEVICES].items(): devices[dev_id] = configured_dev[CONF_HOST] - print("SCHEMA DEVS {}".format(devices)) return self.async_show_form( step_id="edit_device", @@ -489,8 +477,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): errors = {} dev_id = self.selected_device if user_input is not None: - print("INPUT1!! {} {}".format(user_input, dev_id)) - try: self.device_data = user_input.copy() if dev_id is not None: @@ -525,7 +511,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): return await self.async_step_configure_entity() self.dps_strings = await validate_input(self.hass, user_input) - print("ZIO KEN!! {} ".format(self.dps_strings)) return await self.async_step_pick_entity_type() except CannotConnect: errors["base"] = "cannot_connect" @@ -540,15 +525,11 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): defaults = {} if self.editing_device: # If selected device exists as a config entry, load config from it - print("ALREADY EXISTING!! {}".format(dev_id)) defaults = self.config_entry.data[CONF_DEVICES][dev_id].copy() - print("ALREADY EXISTING!! {} {}".format(self.entities, defaults)) schema = schema_defaults(options_schema(self.entities), **defaults) placeholders = {"for_device": f" for device `{dev_id}`"} - print("SCHEMA!! {}".format(schema)) elif dev_id is not None: # Insert default values from discovery and cloud if present - print("NEW DEVICE!! {}".format(dev_id)) device = self.discovered_devices[dev_id] defaults[CONF_HOST] = device.get("ip") defaults[CONF_DEVICE_ID] = device.get("gwId") @@ -571,19 +552,16 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): async def async_step_pick_entity_type(self, user_input=None): """Handle asking if user wants to add another entity.""" if user_input is not None: - print("INPUT4!! {} {}".format(user_input, self.device_data)) if user_input.get(NO_ADDITIONAL_ENTITIES): config = { **self.device_data, CONF_DPS_STRINGS: self.dps_strings, CONF_ENTITIES: self.entities, } - print("NEW CONFIG!! {}".format(config)) # entry = async_config_entry_by_device_id(self.hass, self.unique_id) dev_id = self.device_data.get(CONF_DEVICE_ID) if dev_id in self.config_entry.data[CONF_DEVICES]: - print("AGGIORNO !! {}".format(dev_id)) self.hass.config_entries.async_update_entry( self.config_entry, data=config ) @@ -595,13 +573,9 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): }, ) - print("CREO NUOVO DEVICE!! {}".format(dev_id)) new_data = self.config_entry.data.copy() - print("PRE: {}".format(new_data)) new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) - # new_data[CONF_DEVICES]["AZZ"] = "OK" new_data[CONF_DEVICES].update({dev_id: config}) - print("POST: {}".format(new_data)) self.hass.config_entries.async_update_entry( self.config_entry, @@ -625,12 +599,9 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): def available_dps_strings(self): available_dps = [] - # print("FILTERING!! {} {}".format(self.dps_strings, self.entities)) used_dps = [str(entity[CONF_ID]) for entity in self.entities] - # print("FILTERING-- {} {}".format(self.dps_strings, used_dps)) for dp_string in self.dps_strings: dp = dp_string.split(" ")[0] - # print("FILTERING2!! {} {}".format(dp_string, dp)) if dp not in used_dps: available_dps.append(dp_string) return available_dps @@ -639,8 +610,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): """Manage entity settings.""" errors = {} if user_input is not None: - print("INPUT2!! {} {}".format(user_input, CONF_DEVICE_ID)) - print("ZIO KEN!! {} ".format(self.dps_strings)) entity = strip_dps_values(user_input, self.dps_strings) entity[CONF_ID] = self.current_entity[CONF_ID] entity[CONF_PLATFORM] = self.current_entity[CONF_PLATFORM] @@ -673,16 +642,38 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): """Manage entity settings.""" errors = {} if user_input is not None: - print("INPUT3!! {} {}".format(user_input, CONF_DEVICE_ID)) - already_configured = any( - entity[CONF_ID] == int(user_input[CONF_ID].split(" ")[0]) - for entity in self.entities - ) - if not already_configured: + if self.editing_device: + entity = strip_dps_values(user_input, self.dps_strings) + entity[CONF_ID] = self.current_entity[CONF_ID] + entity[CONF_PLATFORM] = self.current_entity[CONF_PLATFORM] + self.device_data[CONF_ENTITIES].append(entity) + + if len(self.entities) == len(self.device_data[CONF_ENTITIES]): + # finished editing device. Let's store the new config entry.... + dev_id = self.device_data[CONF_DEVICE_ID] + new_data = self.config_entry.data.copy() + entry_id = self.config_entry.entry_id + # removing entities from registry (they will be recreated) + ent_reg = await er.async_get_registry(self.hass) + reg_entities = { + ent.unique_id: ent.entity_id + for ent in er.async_entries_for_config_entry(ent_reg, entry_id) + if dev_id in ent.unique_id + } + for entity_id in reg_entities.values(): + ent_reg.async_remove(entity_id) + + new_data[CONF_DEVICES][dev_id] = self.device_data + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) + self.hass.config_entries.async_update_entry( + self.config_entry, + data=new_data, + ) + return self.async_create_entry(title="", data={}) + else: user_input[CONF_PLATFORM] = self.selected_platform self.entities.append(strip_dps_values(user_input, self.dps_strings)) # new entity added. Let's check if there are more left... - print("ADDED. Remaining... {}".format(self.available_dps_strings())) user_input = None if len(self.available_dps_strings()) == 0: user_input = {NO_ADDITIONAL_ENTITIES: True} diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index d4b5cda..72c3979 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_DEVICES from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntry @@ -29,9 +30,11 @@ async def async_get_device_diagnostics( hass: HomeAssistant, entry: ConfigEntry, device: DeviceEntry ) -> dict[str, Any]: """Return diagnostics for a device entry.""" - # dev_id = next(iter(device.identifiers))[1] - data = {} - # data["device"] = device - # data["log"] = hass.data[DOMAIN][AIRBNK_DEVICES][dev_id].logger.retrieve_log() + dev_id = list(device.identifiers)[0][1].split("_")[-1] + data["device_config"] = entry.data[CONF_DEVICES][dev_id] + tuya_api = hass.data[DOMAIN][DATA_CLOUD] + if dev_id in tuya_api._device_list: + data["device_cloud_info"] = tuya_api._device_list[dev_id] + # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() return data From ef50285c33bcfbb1f8a8e0942c8f5caf751e85a7 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Mon, 23 May 2022 12:31:15 +0200 Subject: [PATCH 15/52] Obfuscated diagnostics; fixed IP auto-update; cleaned logs --- custom_components/localtuya/__init__.py | 73 ++++++++-------------- custom_components/localtuya/common.py | 14 ++--- custom_components/localtuya/config_flow.py | 17 ++--- custom_components/localtuya/diagnostics.py | 46 +++++++++++--- 4 files changed, 75 insertions(+), 75 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index e547a37..4a6f1bf 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -66,7 +66,6 @@ SERVICE_SET_DP_SCHEMA = vol.Schema( async def async_setup(hass: HomeAssistant, config: dict): """Set up the LocalTuya integration component.""" hass.data.setdefault(DOMAIN, {}) - print("SETUP") device_cache = {} @@ -134,6 +133,9 @@ async def async_setup(hass: HomeAssistant, config: dict): # settings triggers a reload of the config entry, which tears down the device # so no need to connect in that case. if updated: + _LOGGER.debug("Updating keys for device %s: %s %s", + device_id, device_ip, product_key + ) new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) hass.config_entries.async_update_entry( entry, data=new_data @@ -148,26 +150,17 @@ async def async_setup(hass: HomeAssistant, config: dict): if not device.connected: device.async_connect() - discovery = TuyaDiscovery(_device_discovered) - def _shutdown(event): """Clean up resources when shutting down.""" discovery.close() - try: - await discovery.start() - hass.data[DOMAIN][DATA_DISCOVERY] = discovery - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) - except Exception: # pylint: disable=broad-except - _LOGGER.exception("failed to set up discovery") - async def _async_reconnect(now): """Try connecting to devices not already connected to.""" for device_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): if not device.connected: device.async_connect() - # async_track_time_interval(hass, _async_reconnect, RECONNECT_INTERVAL) + async_track_time_interval(hass, _async_reconnect, RECONNECT_INTERVAL) hass.helpers.service.async_register_admin_service( DOMAIN, @@ -179,6 +172,14 @@ async def async_setup(hass: HomeAssistant, config: dict): DOMAIN, SERVICE_SET_DP, _handle_set_dp, schema=SERVICE_SET_DP_SCHEMA ) + discovery = TuyaDiscovery(_device_discovered) + try: + await discovery.start() + hass.data[DOMAIN][DATA_DISCOVERY] = discovery + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) + except Exception: # pylint: disable=broad-except + _LOGGER.exception("failed to set up discovery") + return True @@ -228,7 +229,6 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): - print("SETUP ENTRY STARTED!") """Set up LocalTuya integration from a config entry.""" unsub_listener = entry.add_update_listener(update_listener) hass.data[DOMAIN][UNSUB_LISTENER] = unsub_listener @@ -244,19 +244,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): _LOGGER.error("Cloud API connection failed: %s", res) _LOGGER.info("Cloud API connection succeeded.") res = await tuya_api.async_get_devices_list() - for dev_id, dev in tuya_api._device_list.items(): - _LOGGER.debug( - "Cloud device: %s \t dev_id %s \t key %s", dev["name"], dev["id"], dev["local_key"] - ) hass.data[DOMAIN][DATA_CLOUD] = tuya_api - # device = TuyaDevice(hass, entry.data) - # - # hass.data[DOMAIN][entry.entry_id] = { - # UNSUB_LISTENER: unsub_listener, - # TUYA_DEVICE: device, - # } - # async def setup_entities(dev_id): dev_entry = entry.data[CONF_DEVICES][dev_id] device = TuyaDevice(hass, entry, dev_id) @@ -272,19 +261,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): device.async_connect() await async_remove_orphan_entities(hass, entry) - print("SETUP_ENTITIES for {} ENDED".format(dev_id)) for dev_id in entry.data[CONF_DEVICES]: hass.async_create_task(setup_entities(dev_id)) - print("SETUP ENTRY ENDED!") - return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload a config entry.""" - # print("ASYNC_UNLOAD_ENTRY INVOKED...") platforms = {} for dev_id, dev_entry in entry.data[CONF_DEVICES].items(): for entity in dev_entry[CONF_ENTITIES]: @@ -301,20 +286,18 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): hass.data[DOMAIN][UNSUB_LISTENER]() for dev_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): - await device.close() + if device.connected: + await device.close() if unload_ok: hass.data[DOMAIN][TUYA_DEVICES] = {} - # print("ASYNC_UNLOAD_ENTRY ENDED") return True async def update_listener(hass, config_entry): """Update listener.""" - print("UPDATE_LISTENER INVOKED") await hass.config_entries.async_reload(config_entry.entry_id) - print("UPDATE_LISTENER RELOADED {}".format(config_entry.entry_id)) async def async_remove_config_entry_device( @@ -323,9 +306,18 @@ async def async_remove_config_entry_device( """Remove a config entry from a device.""" dev_id = list(device_entry.identifiers)[0][1].split("_")[-1] + ent_reg = await er.async_get_registry(hass) + entities = { + ent.unique_id: ent.entity_id + for ent in er.async_entries_for_config_entry(ent_reg, config_entry.entry_id) + if dev_id in ent.unique_id + } + for entity_id in entities.values(): + ent_reg.async_remove(entity_id) + if dev_id not in config_entry.data[CONF_DEVICES]: - _LOGGER.debug( - "Device ID %s not found in config entry: finalizing device removal", dev_id + _LOGGER.info( + "Device %s not found in config entry: finalizing device removal", dev_id ) return True @@ -340,15 +332,7 @@ async def async_remove_config_entry_device( data=new_data, ) - ent_reg = await er.async_get_registry(hass) - entities = { - ent.unique_id: ent.entity_id - for ent in er.async_entries_for_config_entry(ent_reg, config_entry.entry_id) - if dev_id in ent.unique_id - } - for entity_id in entities.values(): - ent_reg.async_remove(entity_id) - print("REMOVED {}".format(entity_id)) + _LOGGER.info("Device %s removed.", dev_id) return True @@ -361,11 +345,8 @@ async def async_remove_orphan_entities(hass, entry): ent.unique_id: ent.entity_id for ent in er.async_entries_for_config_entry(ent_reg, entry.entry_id) } - print("ENTITIES ORPHAN {}".format(entities)) + _LOGGER.info("ENTITIES ORPHAN %s", entities) return - res = ent_reg.async_remove("switch.aa") - print("RESULT ORPHAN {}".format(res)) - # del entities[101] for entity in entry.data[CONF_ENTITIES]: if entity[CONF_ID] in entities: diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index c7b8c0d..7921383 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -74,6 +74,10 @@ async def async_setup_entry( if len(entities_to_setup) > 0: + if dev_id not in hass.data[DOMAIN][TUYA_DEVICES]: + print("STRANO: {}".format(hass.data[DOMAIN][TUYA_DEVICES])) + return + tuyainterface = hass.data[DOMAIN][TUYA_DEVICES][dev_id] dps_config_fields = list(get_dps_for_platform(flow_schema)) @@ -127,7 +131,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): super().__init__() self._hass = hass self._config_entry = config_entry - self._dev_config_entry = config_entry.data[CONF_DEVICES][dev_id] + self._dev_config_entry = config_entry.data[CONF_DEVICES][dev_id].copy() self._interface = None self._status = {} self.dps_to_request = {} @@ -205,9 +209,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): except Exception as e: # pylint: disable=broad-except self.exception(f"Connect to {self._dev_config_entry[CONF_HOST]} failed") - self.error("BBBB: %s", type(type(e))) if 'json.decode' in str(type(e)): - self.error("BBBB2") await self.update_local_key() if self._interface is not None: @@ -220,7 +222,6 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): await self._hass.data[DOMAIN][DATA_CLOUD].async_get_devices_list() cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD]._device_list if dev_id in cloud_devs: - old_key = self._local_key self._local_key = cloud_devs[dev_id].get(CONF_LOCAL_KEY) new_data = self._config_entry.data.copy() new_data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] = self._local_key @@ -229,10 +230,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._config_entry, data=new_data, ) - self.debug( - "New local key for %s: from %s to %s", - dev_id, old_key, self._local_key - ) + self.info("local_key updated for device %s.", dev_id) async def _async_refresh(self, _now): if self._interface is not None: diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index e901289..28c0b65 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -27,7 +27,7 @@ from homeassistant.const import ( from homeassistant.core import callback from .cloud_api import TuyaCloudApi -from .common import async_config_entry_by_device_id, pytuya +from .common import pytuya from .const import ( CONF_ACTION, CONF_ADD_DEVICE, @@ -304,11 +304,6 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): placeholders = {"msg": res["msg"]} defaults = {} - defaults[CONF_REGION] = "eu" - defaults[CONF_CLIENT_ID] = "xxx" - defaults[CONF_CLIENT_SECRET] = "xxx" - defaults[CONF_USER_ID] = "xxx" - defaults[CONF_USERNAME] = "xxx" defaults.update(user_input or {}) return self.async_show_form( @@ -383,16 +378,17 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): cloud_devs = cloud_api._device_list for dev_id, dev in new_data[CONF_DEVICES].items(): if CONF_MODEL not in dev and dev_id in cloud_devs: - new_data[CONF_DEVICES][dev_id][CONF_MODEL] = cloud_devs[dev_id].get( - CONF_PRODUCT_NAME - ) + model = cloud_devs[dev_id].get(CONF_PRODUCT_NAME) + new_data[CONF_DEVICES][dev_id][CONF_MODEL] = model new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) self.hass.config_entries.async_update_entry( self.config_entry, data=new_data, ) - return self.async_create_entry(title=new_data.get(CONF_USERNAME), data={}) + return self.async_create_entry( + title=new_data.get(CONF_USERNAME), data={} + ) errors["base"] = res["reason"] placeholders = {"msg": res["msg"]} @@ -559,7 +555,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): CONF_ENTITIES: self.entities, } - # entry = async_config_entry_by_device_id(self.hass, self.unique_id) dev_id = self.device_data.get(CONF_DEVICE_ID) if dev_id in self.config_entry.data[CONF_DEVICES]: self.hass.config_entries.async_update_entry( diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index 72c3979..3d3cb95 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -3,11 +3,26 @@ from __future__ import annotations from typing import Any from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_DEVICES from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntry -from .const import DOMAIN, DATA_CLOUD + +from homeassistant.const import ( + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, + CONF_DEVICES, +) + +from .const import ( + DOMAIN, + DATA_CLOUD, + CONF_USER_ID, + CONF_LOCAL_KEY, +) + +CLOUD_DEVICES = "cloud_devices" +DEVICE_CONFIG = "device_config" +DEVICE_CLOUD_INFO = "device_cloud_info" async def async_get_config_entry_diagnostics( @@ -15,14 +30,20 @@ async def async_get_config_entry_diagnostics( ) -> dict[str, Any]: """Return diagnostics for a config entry.""" data = {} - data = {**entry.data} - # print("DATA is {}".format(data)) + data = entry.data.copy() tuya_api = hass.data[DOMAIN][DATA_CLOUD] - data["cloud_devices"] = tuya_api._device_list - # censoring private information - # data["token"] = re.sub(r"[^\-]", "*", data["token"]) - # data["userId"] = re.sub(r"[^\-]", "*", data["userId"]) + for field in [CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_USER_ID]: + data[field] = f"{data[field][0:3]}...{data[field][-3:]}" + for dev_id, dev in data[CONF_DEVICES].items(): + local_key = data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] + local_key_obfuscated = f"{local_key[0:3]}...{local_key[-3:]}" + data[CONF_DEVICES][dev_id][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 = f"{local_key[0:3]}...{local_key[-3:]}" + data[CLOUD_DEVICES][dev_id][CONF_LOCAL_KEY] = local_key_obfuscated return data @@ -32,9 +53,14 @@ async def async_get_device_diagnostics( """Return diagnostics for a device entry.""" data = {} dev_id = list(device.identifiers)[0][1].split("_")[-1] - data["device_config"] = entry.data[CONF_DEVICES][dev_id] + data[DEVICE_CONFIG] = entry.data[CONF_DEVICES][dev_id] + local_key = data[DEVICE_CONFIG][CONF_LOCAL_KEY] + data[DEVICE_CONFIG][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" + tuya_api = hass.data[DOMAIN][DATA_CLOUD] if dev_id in tuya_api._device_list: - data["device_cloud_info"] = tuya_api._device_list[dev_id] + data[DEVICE_CLOUD_INFO] = tuya_api._device_list[dev_id] + local_key = data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] + data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() return data From 6be13c5ba07d9b7778ae68b8939580b3658600be Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Mon, 23 May 2022 12:31:15 +0200 Subject: [PATCH 16/52] Obfuscated diagnostics; fixed IP auto-update; cleaned logs --- custom_components/localtuya/__init__.py | 82 ++++++++-------------- custom_components/localtuya/climate.py | 32 ++++----- custom_components/localtuya/cloud_api.py | 3 +- custom_components/localtuya/common.py | 24 +++---- custom_components/localtuya/config_flow.py | 31 ++++---- custom_components/localtuya/diagnostics.py | 36 +++++++--- custom_components/localtuya/vacuum.py | 29 ++++---- 7 files changed, 112 insertions(+), 125 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index e547a37..9701b4a 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -5,27 +5,26 @@ import time from datetime import timedelta import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.device_registry import DeviceEntry import homeassistant.helpers.entity_registry as er import voluptuous as vol - from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( CONF_CLIENT_ID, CONF_CLIENT_SECRET, - CONF_REGION, CONF_DEVICE_ID, CONF_DEVICES, CONF_ENTITIES, CONF_HOST, CONF_ID, CONF_PLATFORM, + CONF_REGION, CONF_USERNAME, EVENT_HOMEASSISTANT_STOP, SERVICE_RELOAD, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.event import async_track_time_interval from .cloud_api import TuyaCloudApi @@ -66,7 +65,6 @@ SERVICE_SET_DP_SCHEMA = vol.Schema( async def async_setup(hass: HomeAssistant, config: dict): """Set up the LocalTuya integration component.""" hass.data.setdefault(DOMAIN, {}) - print("SETUP") device_cache = {} @@ -134,10 +132,11 @@ async def async_setup(hass: HomeAssistant, config: dict): # settings triggers a reload of the config entry, which tears down the device # so no need to connect in that case. if updated: - new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) - hass.config_entries.async_update_entry( - entry, data=new_data + _LOGGER.debug( + "Updating keys for device %s: %s %s", device_id, device_ip, product_key ) + new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) + hass.config_entries.async_update_entry(entry, data=new_data) device = hass.data[DOMAIN][TUYA_DEVICES][device_id] if not device.connected: device.async_connect() @@ -148,26 +147,17 @@ async def async_setup(hass: HomeAssistant, config: dict): if not device.connected: device.async_connect() - discovery = TuyaDiscovery(_device_discovered) - def _shutdown(event): """Clean up resources when shutting down.""" discovery.close() - try: - await discovery.start() - hass.data[DOMAIN][DATA_DISCOVERY] = discovery - hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) - except Exception: # pylint: disable=broad-except - _LOGGER.exception("failed to set up discovery") - async def _async_reconnect(now): """Try connecting to devices not already connected to.""" for device_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): if not device.connected: device.async_connect() - # async_track_time_interval(hass, _async_reconnect, RECONNECT_INTERVAL) + async_track_time_interval(hass, _async_reconnect, RECONNECT_INTERVAL) hass.helpers.service.async_register_admin_service( DOMAIN, @@ -179,6 +169,14 @@ async def async_setup(hass: HomeAssistant, config: dict): DOMAIN, SERVICE_SET_DP, _handle_set_dp, schema=SERVICE_SET_DP_SCHEMA ) + discovery = TuyaDiscovery(_device_discovered) + try: + await discovery.start() + hass.data[DOMAIN][DATA_DISCOVERY] = discovery + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) + except Exception: # pylint: disable=broad-except + _LOGGER.exception("failed to set up discovery") + return True @@ -228,7 +226,6 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): - print("SETUP ENTRY STARTED!") """Set up LocalTuya integration from a config entry.""" unsub_listener = entry.add_update_listener(update_listener) hass.data[DOMAIN][UNSUB_LISTENER] = unsub_listener @@ -244,19 +241,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): _LOGGER.error("Cloud API connection failed: %s", res) _LOGGER.info("Cloud API connection succeeded.") res = await tuya_api.async_get_devices_list() - for dev_id, dev in tuya_api._device_list.items(): - _LOGGER.debug( - "Cloud device: %s \t dev_id %s \t key %s", dev["name"], dev["id"], dev["local_key"] - ) hass.data[DOMAIN][DATA_CLOUD] = tuya_api - # device = TuyaDevice(hass, entry.data) - # - # hass.data[DOMAIN][entry.entry_id] = { - # UNSUB_LISTENER: unsub_listener, - # TUYA_DEVICE: device, - # } - # async def setup_entities(dev_id): dev_entry = entry.data[CONF_DEVICES][dev_id] device = TuyaDevice(hass, entry, dev_id) @@ -272,19 +258,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): device.async_connect() await async_remove_orphan_entities(hass, entry) - print("SETUP_ENTITIES for {} ENDED".format(dev_id)) for dev_id in entry.data[CONF_DEVICES]: hass.async_create_task(setup_entities(dev_id)) - print("SETUP ENTRY ENDED!") - return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload a config entry.""" - # print("ASYNC_UNLOAD_ENTRY INVOKED...") platforms = {} for dev_id, dev_entry in entry.data[CONF_DEVICES].items(): for entity in dev_entry[CONF_ENTITIES]: @@ -301,20 +283,18 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): hass.data[DOMAIN][UNSUB_LISTENER]() for dev_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): - await device.close() + if device.connected: + await device.close() if unload_ok: hass.data[DOMAIN][TUYA_DEVICES] = {} - # print("ASYNC_UNLOAD_ENTRY ENDED") return True async def update_listener(hass, config_entry): """Update listener.""" - print("UPDATE_LISTENER INVOKED") await hass.config_entries.async_reload(config_entry.entry_id) - print("UPDATE_LISTENER RELOADED {}".format(config_entry.entry_id)) async def async_remove_config_entry_device( @@ -323,9 +303,18 @@ async def async_remove_config_entry_device( """Remove a config entry from a device.""" dev_id = list(device_entry.identifiers)[0][1].split("_")[-1] + ent_reg = await er.async_get_registry(hass) + entities = { + ent.unique_id: ent.entity_id + for ent in er.async_entries_for_config_entry(ent_reg, config_entry.entry_id) + if dev_id in ent.unique_id + } + for entity_id in entities.values(): + ent_reg.async_remove(entity_id) + if dev_id not in config_entry.data[CONF_DEVICES]: - _LOGGER.debug( - "Device ID %s not found in config entry: finalizing device removal", dev_id + _LOGGER.info( + "Device %s not found in config entry: finalizing device removal", dev_id ) return True @@ -340,15 +329,7 @@ async def async_remove_config_entry_device( data=new_data, ) - ent_reg = await er.async_get_registry(hass) - entities = { - ent.unique_id: ent.entity_id - for ent in er.async_entries_for_config_entry(ent_reg, config_entry.entry_id) - if dev_id in ent.unique_id - } - for entity_id in entities.values(): - ent_reg.async_remove(entity_id) - print("REMOVED {}".format(entity_id)) + _LOGGER.info("Device %s removed.", dev_id) return True @@ -361,11 +342,8 @@ async def async_remove_orphan_entities(hass, entry): ent.unique_id: ent.entity_id for ent in er.async_entries_for_config_entry(ent_reg, entry.entry_id) } - print("ENTITIES ORPHAN {}".format(entities)) + _LOGGER.info("ENTITIES ORPHAN %s", entities) return - res = ent_reg.async_remove("switch.aa") - print("RESULT ORPHAN {}".format(res)) - # del entities[101] for entity in entry.data[CONF_ENTITIES]: if entity[CONF_ID] in entities: diff --git a/custom_components/localtuya/climate.py b/custom_components/localtuya/climate.py index 9d9358f..8474a21 100644 --- a/custom_components/localtuya/climate.py +++ b/custom_components/localtuya/climate.py @@ -11,18 +11,18 @@ from homeassistant.components.climate import ( ClimateEntity, ) from homeassistant.components.climate.const import ( + CURRENT_HVAC_HEAT, + CURRENT_HVAC_IDLE, HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF, + PRESET_AWAY, + PRESET_ECO, + PRESET_HOME, + PRESET_NONE, SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE_RANGE, - CURRENT_HVAC_IDLE, - CURRENT_HVAC_HEAT, - PRESET_NONE, - PRESET_ECO, - PRESET_AWAY, - PRESET_HOME, ) from homeassistant.const import ( ATTR_TEMPERATURE, @@ -37,21 +37,21 @@ from homeassistant.const import ( from .common import LocalTuyaEntity, async_setup_entry from .const import ( CONF_CURRENT_TEMPERATURE_DP, - CONF_MAX_TEMP_DP, - CONF_MIN_TEMP_DP, - CONF_PRECISION, - CONF_TARGET_PRECISION, - CONF_TARGET_TEMPERATURE_DP, - CONF_TEMPERATURE_STEP, - CONF_HVAC_MODE_DP, - CONF_HVAC_MODE_SET, + CONF_ECO_DP, + CONF_ECO_VALUE, CONF_HEURISTIC_ACTION, CONF_HVAC_ACTION_DP, CONF_HVAC_ACTION_SET, - CONF_ECO_DP, - CONF_ECO_VALUE, + CONF_HVAC_MODE_DP, + CONF_HVAC_MODE_SET, + CONF_MAX_TEMP_DP, + CONF_MIN_TEMP_DP, + CONF_PRECISION, CONF_PRESET_DP, CONF_PRESET_SET, + CONF_TARGET_PRECISION, + CONF_TARGET_TEMPERATURE_DP, + CONF_TEMPERATURE_STEP, ) _LOGGER = logging.getLogger(__name__) diff --git a/custom_components/localtuya/cloud_api.py b/custom_components/localtuya/cloud_api.py index ac82c4e..b55c46f 100755 --- a/custom_components/localtuya/cloud_api.py +++ b/custom_components/localtuya/cloud_api.py @@ -3,9 +3,10 @@ import functools import hashlib import hmac import json -import requests import time +import requests + # Signature algorithm. def calc_sign(msg, key): diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index c7b8c0d..d3e691b 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -1,8 +1,8 @@ """Code shared between all platforms.""" import asyncio import logging -from datetime import timedelta import time +from datetime import timedelta from homeassistant.const import ( CONF_DEVICE_ID, @@ -16,11 +16,11 @@ from homeassistant.const import ( CONF_SCAN_INTERVAL, ) from homeassistant.core import callback -from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, ) +from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.restore_state import RestoreEntity from . import pytuya @@ -28,8 +28,8 @@ from .const import ( ATTR_UPDATED_AT, CONF_LOCAL_KEY, CONF_PROTOCOL_VERSION, - DOMAIN, DATA_CLOUD, + DOMAIN, TUYA_DEVICES, ) @@ -74,6 +74,10 @@ async def async_setup_entry( if len(entities_to_setup) > 0: + if dev_id not in hass.data[DOMAIN][TUYA_DEVICES]: + print("STRANO: {}".format(hass.data[DOMAIN][TUYA_DEVICES])) + return + tuyainterface = hass.data[DOMAIN][TUYA_DEVICES][dev_id] dps_config_fields = list(get_dps_for_platform(flow_schema)) @@ -127,7 +131,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): super().__init__() self._hass = hass self._config_entry = config_entry - self._dev_config_entry = config_entry.data[CONF_DEVICES][dev_id] + self._dev_config_entry = config_entry.data[CONF_DEVICES][dev_id].copy() self._interface = None self._status = {} self.dps_to_request = {} @@ -205,9 +209,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): except Exception as e: # pylint: disable=broad-except self.exception(f"Connect to {self._dev_config_entry[CONF_HOST]} failed") - self.error("BBBB: %s", type(type(e))) - if 'json.decode' in str(type(e)): - self.error("BBBB2") + if "json.decode" in str(type(e)): await self.update_local_key() if self._interface is not None: @@ -220,7 +222,6 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): await self._hass.data[DOMAIN][DATA_CLOUD].async_get_devices_list() cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD]._device_list if dev_id in cloud_devs: - old_key = self._local_key self._local_key = cloud_devs[dev_id].get(CONF_LOCAL_KEY) new_data = self._config_entry.data.copy() new_data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] = self._local_key @@ -229,10 +230,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._config_entry, data=new_data, ) - self.debug( - "New local key for %s: from %s to %s", - dev_id, old_key, self._local_key - ) + self.info("local_key updated for device %s.", dev_id) async def _async_refresh(self, _now): if self._interface is not None: @@ -250,7 +248,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._disconnect_task() self.debug( "Closed connection with device %s.", - self._dev_config_entry[CONF_FRIENDLY_NAME] + self._dev_config_entry[CONF_FRIENDLY_NAME], ) async def set_dp(self, state, dp_index): diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index e901289..3de2284 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -9,6 +9,8 @@ import homeassistant.helpers.entity_registry as er import voluptuous as vol from homeassistant import config_entries, core, exceptions from homeassistant.const import ( + CONF_CLIENT_ID, + CONF_CLIENT_SECRET, CONF_DEVICE_ID, CONF_DEVICES, CONF_ENTITIES, @@ -18,29 +20,27 @@ from homeassistant.const import ( CONF_MODEL, CONF_NAME, CONF_PLATFORM, - CONF_SCAN_INTERVAL, - CONF_CLIENT_ID, - CONF_CLIENT_SECRET, CONF_REGION, + CONF_SCAN_INTERVAL, CONF_USERNAME, ) from homeassistant.core import callback from .cloud_api import TuyaCloudApi -from .common import async_config_entry_by_device_id, pytuya +from .common import pytuya from .const import ( + ATTR_UPDATED_AT, CONF_ACTION, CONF_ADD_DEVICE, + CONF_DPS_STRINGS, CONF_EDIT_DEVICE, - CONF_SETUP_CLOUD, CONF_LOCAL_KEY, CONF_PRODUCT_NAME, CONF_PROTOCOL_VERSION, + CONF_SETUP_CLOUD, CONF_USER_ID, - CONF_DPS_STRINGS, - ATTR_UPDATED_AT, - DATA_DISCOVERY, DATA_CLOUD, + DATA_DISCOVERY, DOMAIN, PLATFORMS, ) @@ -304,11 +304,6 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): placeholders = {"msg": res["msg"]} defaults = {} - defaults[CONF_REGION] = "eu" - defaults[CONF_CLIENT_ID] = "xxx" - defaults[CONF_CLIENT_SECRET] = "xxx" - defaults[CONF_USER_ID] = "xxx" - defaults[CONF_USERNAME] = "xxx" defaults.update(user_input or {}) return self.async_show_form( @@ -383,16 +378,17 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): cloud_devs = cloud_api._device_list for dev_id, dev in new_data[CONF_DEVICES].items(): if CONF_MODEL not in dev and dev_id in cloud_devs: - new_data[CONF_DEVICES][dev_id][CONF_MODEL] = cloud_devs[dev_id].get( - CONF_PRODUCT_NAME - ) + model = cloud_devs[dev_id].get(CONF_PRODUCT_NAME) + new_data[CONF_DEVICES][dev_id][CONF_MODEL] = model new_data[ATTR_UPDATED_AT] = str(int(time.time() * 1000)) self.hass.config_entries.async_update_entry( self.config_entry, data=new_data, ) - return self.async_create_entry(title=new_data.get(CONF_USERNAME), data={}) + return self.async_create_entry( + title=new_data.get(CONF_USERNAME), data={} + ) errors["base"] = res["reason"] placeholders = {"msg": res["msg"]} @@ -559,7 +555,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): CONF_ENTITIES: self.entities, } - # entry = async_config_entry_by_device_id(self.hass, self.unique_id) dev_id = self.device_data.get(CONF_DEVICE_ID) if dev_id in self.config_entry.data[CONF_DEVICES]: self.hass.config_entries.async_update_entry( diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index 72c3979..9ea0f6c 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -1,13 +1,18 @@ """Diagnostics support for LocalTuya.""" from __future__ import annotations + from typing import Any from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_DEVICES +from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DEVICES from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntry -from .const import DOMAIN, DATA_CLOUD +from .const import CONF_LOCAL_KEY, CONF_USER_ID, DATA_CLOUD, DOMAIN + +CLOUD_DEVICES = "cloud_devices" +DEVICE_CONFIG = "device_config" +DEVICE_CLOUD_INFO = "device_cloud_info" async def async_get_config_entry_diagnostics( @@ -15,14 +20,20 @@ async def async_get_config_entry_diagnostics( ) -> dict[str, Any]: """Return diagnostics for a config entry.""" data = {} - data = {**entry.data} - # print("DATA is {}".format(data)) + data = entry.data.copy() tuya_api = hass.data[DOMAIN][DATA_CLOUD] - data["cloud_devices"] = tuya_api._device_list - # censoring private information - # data["token"] = re.sub(r"[^\-]", "*", data["token"]) - # data["userId"] = re.sub(r"[^\-]", "*", data["userId"]) + for field in [CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_USER_ID]: + data[field] = f"{data[field][0:3]}...{data[field][-3:]}" + for dev_id, dev in data[CONF_DEVICES].items(): + local_key = data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] + local_key_obfuscated = f"{local_key[0:3]}...{local_key[-3:]}" + data[CONF_DEVICES][dev_id][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 = f"{local_key[0:3]}...{local_key[-3:]}" + data[CLOUD_DEVICES][dev_id][CONF_LOCAL_KEY] = local_key_obfuscated return data @@ -32,9 +43,14 @@ async def async_get_device_diagnostics( """Return diagnostics for a device entry.""" data = {} dev_id = list(device.identifiers)[0][1].split("_")[-1] - data["device_config"] = entry.data[CONF_DEVICES][dev_id] + data[DEVICE_CONFIG] = entry.data[CONF_DEVICES][dev_id] + local_key = data[DEVICE_CONFIG][CONF_LOCAL_KEY] + data[DEVICE_CONFIG][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" + tuya_api = hass.data[DOMAIN][DATA_CLOUD] if dev_id in tuya_api._device_list: - data["device_cloud_info"] = tuya_api._device_list[dev_id] + data[DEVICE_CLOUD_INFO] = tuya_api._device_list[dev_id] + local_key = data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] + data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() return data diff --git a/custom_components/localtuya/vacuum.py b/custom_components/localtuya/vacuum.py index 9a14399..dc75044 100644 --- a/custom_components/localtuya/vacuum.py +++ b/custom_components/localtuya/vacuum.py @@ -7,41 +7,40 @@ from homeassistant.components.vacuum import ( DOMAIN, STATE_CLEANING, STATE_DOCKED, - STATE_IDLE, - STATE_RETURNING, - STATE_PAUSED, STATE_ERROR, + STATE_IDLE, + STATE_PAUSED, + STATE_RETURNING, SUPPORT_BATTERY, SUPPORT_FAN_SPEED, + SUPPORT_LOCATE, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, SUPPORT_START, SUPPORT_STATE, SUPPORT_STATUS, SUPPORT_STOP, - SUPPORT_LOCATE, StateVacuumEntity, ) from .common import LocalTuyaEntity, async_setup_entry - from .const import ( - CONF_POWERGO_DP, - CONF_IDLE_STATUS_VALUE, - CONF_RETURNING_STATUS_VALUE, - CONF_DOCKED_STATUS_VALUE, CONF_BATTERY_DP, - CONF_MODE_DP, - CONF_MODES, - CONF_FAN_SPEED_DP, - CONF_FAN_SPEEDS, - CONF_CLEAN_TIME_DP, CONF_CLEAN_AREA_DP, CONF_CLEAN_RECORD_DP, - CONF_LOCATE_DP, + CONF_CLEAN_TIME_DP, + CONF_DOCKED_STATUS_VALUE, + CONF_FAN_SPEED_DP, + CONF_FAN_SPEEDS, CONF_FAULT_DP, + CONF_IDLE_STATUS_VALUE, + CONF_LOCATE_DP, + CONF_MODE_DP, + CONF_MODES, CONF_PAUSED_STATE, + CONF_POWERGO_DP, CONF_RETURN_MODE, + CONF_RETURNING_STATUS_VALUE, CONF_STOP_STATUS, ) From f85e579a6878e089146dafa24edc3c20d015e070 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Mon, 23 May 2022 14:38:03 +0200 Subject: [PATCH 17/52] Fixed device diagnostic corrupting config entry --- custom_components/localtuya/__init__.py | 2 +- custom_components/localtuya/diagnostics.py | 2 +- requirements_test.txt | 3 ++- tox.ini | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 9701b4a..eae9853 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -70,7 +70,7 @@ async def async_setup(hass: HomeAssistant, config: dict): async def _handle_reload(service): """Handle reload service call.""" - _LOGGER.debug("Service %s.reload called: reloading integration", DOMAIN) + _LOGGER.info("Service %s.reload called: reloading integration", DOMAIN) current_entries = hass.config_entries.async_entries(DOMAIN) diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index 9ea0f6c..cce5670 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -43,7 +43,7 @@ async def async_get_device_diagnostics( """Return diagnostics for a device entry.""" data = {} dev_id = list(device.identifiers)[0][1].split("_")[-1] - data[DEVICE_CONFIG] = entry.data[CONF_DEVICES][dev_id] + data[DEVICE_CONFIG] = entry.data[CONF_DEVICES][dev_id].copy() local_key = data[DEVICE_CONFIG][CONF_LOCAL_KEY] data[DEVICE_CONFIG][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" diff --git a/requirements_test.txt b/requirements_test.txt index a4e209a..ca811a0 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -6,4 +6,5 @@ pydocstyle==6.1.1 cryptography==3.3.2 pylint==2.8.2 pylint-strict-informational==0.1 -homeassistant==2021.7.1 +homeassistant==2022.5.1 +tox==3.25.0 diff --git a/tox.ini b/tox.ini index c7d9e69..033e723 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,7 @@ whitelist_externals = true setenv = LANG=en_US.UTF-8 - PYTHONPATH = {toxinidir}/localtuya-homeassistant + PYTHONPATH = {toxinidir}/localtuya deps = -r{toxinidir}/requirements_test.txt commands = From 6c6b9550482aa3dd9522a68f98edbc665c4644b3 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Mon, 23 May 2022 15:31:08 +0200 Subject: [PATCH 18/52] First tox fixes --- custom_components/localtuya/__init__.py | 2 +- custom_components/localtuya/cloud_api.py | 2 ++ custom_components/localtuya/common.py | 1 + custom_components/localtuya/config_flow.py | 1 + requirements_test.txt | 3 +-- tox.ini | 2 +- 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index eae9853..d4549a1 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -1,4 +1,4 @@ -"""The LocalTuya integration. """ +"""The LocalTuya integration.""" import asyncio import logging import time diff --git a/custom_components/localtuya/cloud_api.py b/custom_components/localtuya/cloud_api.py index b55c46f..76687c2 100755 --- a/custom_components/localtuya/cloud_api.py +++ b/custom_components/localtuya/cloud_api.py @@ -10,6 +10,7 @@ import requests # Signature algorithm. def calc_sign(msg, key): + """Calculate signature for request.""" sign = ( hmac.new( msg=bytes(msg, "latin-1"), @@ -36,6 +37,7 @@ class TuyaCloudApi: self._device_list = {} def generate_payload(self, method, t, url, headers, body=None): + """Generate signed payload for requests.""" payload = self._client_id + self._access_token + t payload += method + "\n" diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index d3e691b..372b61e 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -218,6 +218,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._connect_task = None async def update_local_key(self): + """Retrieve updated local_key from Cloud API and update the config_entry.""" dev_id = self._dev_config_entry[CONF_DEVICE_ID] await self._hass.data[DOMAIN][DATA_CLOUD].async_get_devices_list() cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD]._device_list diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 3de2284..605ea03 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -593,6 +593,7 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): return self.async_show_form(step_id="pick_entity_type", data_schema=schema) def available_dps_strings(self): + """Return list of DPs use by the device's entities.""" available_dps = [] used_dps = [str(entity[CONF_ID]) for entity in self.entities] for dp_string in self.dps_strings: diff --git a/requirements_test.txt b/requirements_test.txt index ca811a0..d803b74 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -black==21.4b0 +black==22.3.0 codespell==2.0.0 flake8==3.9.2 mypy==0.901 @@ -7,4 +7,3 @@ cryptography==3.3.2 pylint==2.8.2 pylint-strict-informational==0.1 homeassistant==2022.5.1 -tox==3.25.0 diff --git a/tox.ini b/tox.ini index 033e723..0a642b7 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,7 @@ ignore_errors = True deps = {[testenv]deps} commands = - codespell -q 4 -L {[tox]cs_exclude_words} --skip="*.pyc,*.pyi,*~" custom_components + codespell -q 4 -L {[tox]cs_exclude_words} --skip="*.pyc,*.pyi,*~,*.json" custom_components flake8 custom_components black --fast --check . pydocstyle -v custom_components From b2e89808a1c8b2d8de216dd608a1f64bd1191713 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Mon, 23 May 2022 15:33:11 +0200 Subject: [PATCH 19/52] tox fixes --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index d803b74..7fca101 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -6,4 +6,4 @@ pydocstyle==6.1.1 cryptography==3.3.2 pylint==2.8.2 pylint-strict-informational==0.1 -homeassistant==2022.5.1 +homeassistant==2021.12.10 From f5ad40219b9f5245847f91eb2289fa8df01f58b2 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Mon, 23 May 2022 16:44:44 +0200 Subject: [PATCH 20/52] tox fixes --- requirements_test.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 7fca101..8cfbf7c 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -3,7 +3,6 @@ codespell==2.0.0 flake8==3.9.2 mypy==0.901 pydocstyle==6.1.1 -cryptography==3.3.2 pylint==2.8.2 pylint-strict-informational==0.1 homeassistant==2021.12.10 From cd39a3a74d86f0090df43a6aac6a79b537fb2c6d Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Mon, 23 May 2022 17:26:19 +0200 Subject: [PATCH 21/52] tox fixes --- custom_components/localtuya/cloud_api.py | 38 +- custom_components/localtuya/common.py | 4 +- custom_components/localtuya/config_flow.py | 13 +- custom_components/localtuya/const.py | 1 + custom_components/localtuya/diagnostics.py | 6 +- pylint.rc | 646 +++++++++++++++++++++ tox.ini | 4 +- 7 files changed, 679 insertions(+), 33 deletions(-) create mode 100644 pylint.rc diff --git a/custom_components/localtuya/cloud_api.py b/custom_components/localtuya/cloud_api.py index 76687c2..707b2da 100755 --- a/custom_components/localtuya/cloud_api.py +++ b/custom_components/localtuya/cloud_api.py @@ -34,11 +34,11 @@ class TuyaCloudApi: self._secret = secret self._user_id = user_id self._access_token = "" - self._device_list = {} + self.device_list = {} - def generate_payload(self, method, t, url, headers, body=None): + def generate_payload(self, method, timestamp, url, headers, body=None): """Generate signed payload for requests.""" - payload = self._client_id + self._access_token + t + payload = self._client_id + self._access_token + timestamp payload += method + "\n" # Content-SHA256 @@ -60,13 +60,13 @@ class TuyaCloudApi: async def async_make_request(self, method, url, body=None, headers={}): """Perform requests.""" - t = str(int(time.time() * 1000)) - payload = self.generate_payload(method, t, url, headers, body) + timestamp = str(int(time.time() * 1000)) + payload = self.generate_payload(method, timestamp, url, headers, body) default_par = { "client_id": self._client_id, "access_token": self._access_token, "sign": calc_sign(payload, self._secret), - "t": t, + "t": timestamp, "sign_method": "HMAC-SHA256", } full_url = self._base_url + url @@ -92,34 +92,34 @@ class TuyaCloudApi: data=json.dumps(body), ) - r = await self._hass.async_add_executor_job(func) + resp = await self._hass.async_add_executor_job(func) # r = json.dumps(r.json(), indent=2, ensure_ascii=False) # Beautify the format - return r + return resp async def async_get_access_token(self): """Obtain a valid access token.""" - r = await self.async_make_request("GET", "/v1.0/token?grant_type=1") + resp = await self.async_make_request("GET", "/v1.0/token?grant_type=1") - if not r.ok: - return "Request failed, status " + str(r.status) + if not resp.ok: + return "Request failed, status " + str(resp.status) - r_json = r.json() + r_json = resp.json() if not r_json["success"]: return f"Error {r_json['code']}: {r_json['msg']}" - self._access_token = r.json()["result"]["access_token"] + self._access_token = resp.json()["result"]["access_token"] return "ok" async def async_get_devices_list(self): """Obtain the list of devices associated to a user.""" - r = await self.async_make_request( + resp = await self.async_make_request( "GET", url=f"/v1.0/users/{self._user_id}/devices" ) - if not r.ok: - return "Request failed, status " + str(r.status) + if not resp.ok: + return "Request failed, status " + str(resp.status) - r_json = r.json() + r_json = resp.json() if not r_json["success"]: # print( # "Request failed, reply is {}".format( @@ -128,7 +128,7 @@ class TuyaCloudApi: # ) return f"Error {r_json['code']}: {r_json['msg']}" - self._device_list = {dev["id"]: dev for dev in r_json["result"]} - # print("DEV__LIST: {}".format(self._device_list)) + self.device_list = {dev["id"]: dev for dev in r_json["result"]} + # print("DEV__LIST: {}".format(self.device_list)) return "ok" diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index 372b61e..e694869 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -11,7 +11,6 @@ from homeassistant.const import ( CONF_FRIENDLY_NAME, CONF_HOST, CONF_ID, - CONF_MODEL, CONF_PLATFORM, CONF_SCAN_INTERVAL, ) @@ -27,6 +26,7 @@ from . import pytuya from .const import ( ATTR_UPDATED_AT, CONF_LOCAL_KEY, + CONF_MODEL, CONF_PROTOCOL_VERSION, DATA_CLOUD, DOMAIN, @@ -221,7 +221,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): """Retrieve updated local_key from Cloud API and update the config_entry.""" dev_id = self._dev_config_entry[CONF_DEVICE_ID] await self._hass.data[DOMAIN][DATA_CLOUD].async_get_devices_list() - cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD]._device_list + cloud_devs = self._hass.data[DOMAIN][DATA_CLOUD].device_list if dev_id in cloud_devs: self._local_key = cloud_devs[dev_id].get(CONF_LOCAL_KEY) new_data = self._config_entry.data.copy() diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 605ea03..18f9fac 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -17,7 +17,6 @@ from homeassistant.const import ( CONF_FRIENDLY_NAME, CONF_HOST, CONF_ID, - CONF_MODEL, CONF_NAME, CONF_PLATFORM, CONF_REGION, @@ -35,6 +34,7 @@ from .const import ( CONF_DPS_STRINGS, CONF_EDIT_DEVICE, CONF_LOCAL_KEY, + CONF_MODEL, CONF_PRODUCT_NAME, CONF_PROTOCOL_VERSION, CONF_SETUP_CLOUD, @@ -375,7 +375,7 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): if len(res) == 0: new_data = self.config_entry.data.copy() new_data.update(user_input) - cloud_devs = cloud_api._device_list + cloud_devs = cloud_api.device_list for dev_id, dev in new_data[CONF_DEVICES].items(): if CONF_MODEL not in dev and dev_id in cloud_devs: model = cloud_devs[dev_id].get(CONF_PRODUCT_NAME) @@ -438,7 +438,7 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): return self.async_show_form( step_id="add_device", data_schema=devices_schema( - devices, self.hass.data[DOMAIN][DATA_CLOUD]._device_list + devices, self.hass.data[DOMAIN][DATA_CLOUD].device_list ), errors=errors, ) @@ -463,7 +463,7 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): return self.async_show_form( step_id="edit_device", data_schema=devices_schema( - devices, self.hass.data[DOMAIN][DATA_CLOUD]._device_list, False + devices, self.hass.data[DOMAIN][DATA_CLOUD].device_list, False ), errors=errors, ) @@ -479,7 +479,7 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): # self.device_data[CONF_PRODUCT_KEY] = self.devices[ # self.selected_device # ]["productKey"] - cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD]._device_list + cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD].device_list if dev_id in cloud_devs: self.device_data[CONF_MODEL] = cloud_devs[dev_id].get( CONF_PRODUCT_NAME @@ -530,7 +530,7 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): defaults[CONF_HOST] = device.get("ip") defaults[CONF_DEVICE_ID] = device.get("gwId") defaults[CONF_PROTOCOL_VERSION] = device.get("version") - cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD]._device_list + cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD].device_list if dev_id in cloud_devs: defaults[CONF_LOCAL_KEY] = cloud_devs[dev_id].get(CONF_LOCAL_KEY) defaults[CONF_FRIENDLY_NAME] = cloud_devs[dev_id].get(CONF_NAME) @@ -577,7 +577,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): data=new_data, ) return self.async_create_entry(title="", data={}) - self.async_create_entry(title="", data={}) self.selected_platform = user_input[PLATFORM_TO_ADD] return await self.async_step_configure_entity() diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 8feafad..c1d4248 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -30,6 +30,7 @@ ATTR_UPDATED_AT = "updated_at" CONF_LOCAL_KEY = "local_key" CONF_PROTOCOL_VERSION = "protocol_version" CONF_DPS_STRINGS = "dps_strings" +CONF_MODEL = "model" CONF_PRODUCT_KEY = "product_key" CONF_PRODUCT_NAME = "product_name" CONF_USER_ID = "user_id" diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index cce5670..21a4304 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -29,7 +29,7 @@ async def async_get_config_entry_diagnostics( local_key = data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] local_key_obfuscated = f"{local_key[0:3]}...{local_key[-3:]}" data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] = local_key_obfuscated - data[CLOUD_DEVICES] = tuya_api._device_list + 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 = f"{local_key[0:3]}...{local_key[-3:]}" @@ -48,8 +48,8 @@ async def async_get_device_diagnostics( data[DEVICE_CONFIG][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" tuya_api = hass.data[DOMAIN][DATA_CLOUD] - if dev_id in tuya_api._device_list: - data[DEVICE_CLOUD_INFO] = tuya_api._device_list[dev_id] + if dev_id in tuya_api.device_list: + data[DEVICE_CLOUD_INFO] = tuya_api.device_list[dev_id] local_key = data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() diff --git a/pylint.rc b/pylint.rc new file mode 100644 index 0000000..4ec670e --- /dev/null +++ b/pylint.rc @@ -0,0 +1,646 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Specify a score threshold to be exceeded before program exits with error. +fail-under=10.0 + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Files or directories matching the regex patterns are skipped. The regex +# matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=line-too-long, + too-many-lines, + trailing-whitespace, + missing-final-newline, + trailing-newlines, + multiple-statements, + superfluous-parens, + mixed-line-endings, + unexpected-line-ending-format, + wrong-import-order, + not-context-manager, + print-statement, + parameter-unpacking, + unpacking-in-except, + old-raise-syntax, + backtick, + long-suffix, + old-ne-operator, + old-octal-literal, + import-star-module-level, + non-ascii-bytes-literal, + raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + cyclic-import, + duplicate-code, + too-many-ancestors, + too-many-instance-attributes, + too-few-public-methods, + too-many-public-methods, + too-many-return-statements, + too-many-branches, + too-many-arguments, + too-many-locals, + too-many-statements, + too-many-boolean-expressions, + inconsistent-return-statements, + abstract-method, + unnecessary-semicolon, + bad-indentation, + unused-argument, + apply-builtin, + basestring-builtin, + buffer-builtin, + cmp-builtin, + coerce-builtin, + execfile-builtin, + file-builtin, + long-builtin, + raw_input-builtin, + reduce-builtin, + standarderror-builtin, + unicode-builtin, + xrange-builtin, + coerce-method, + delslice-method, + getslice-method, + setslice-method, + no-absolute-import, + old-division, + dict-iter-method, + dict-view-method, + next-method-called, + metaclass-assignment, + indexing-exception, + raising-string, + reload-builtin, + oct-method, + hex-method, + nonzero-method, + cmp-method, + input-builtin, + round-builtin, + intern-builtin, + unichr-builtin, + map-builtin-not-iterating, + zip-builtin-not-iterating, + range-builtin-not-iterating, + filter-builtin-not-iterating, + using-cmp-argument, + eq-without-hash, + div-method, + idiv-method, + rdiv-method, + exception-message-attribute, + invalid-str-codec, + sys-max-int, + bad-python3-import, + deprecated-string-function, + deprecated-str-translate-call, + deprecated-itertools-function, + deprecated-types-field, + next-method-defined, + dict-items-not-iterating, + dict-keys-not-iterating, + dict-values-not-iterating, + deprecated-operator-function, + deprecated-urllib-function, + xreadlines-attribute, + deprecated-sys-function, + exception-escape, + comprehension-escape, + unused-variable, + invalid-name, + dangerous-default-value, + unreachable + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=use-symbolic-message-instead, + c-extension-no-member + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'error', 'warning', 'refactor', and 'convention' +# which contain the number of messages in each category, as well as 'statement' +# which is the total number of statements analyzed. This score is used by the +# global evaluation report (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=no + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=XXX + +# Regular expression of note tags to take in consideration. +#notes-rgx= + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the 'python-enchant' package. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear and the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format=LF + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +# allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=_, + ev, + ex, + fp, + i, + id, + j, + k, + Run, + T, + hs + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. +#variable-rgx= + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=optparse,tkinter.tix + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[DESIGN] + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "BaseException, Exception". +overgeneral-exceptions=BaseException, + Exception diff --git a/tox.ini b/tox.ini index 0a642b7..f965d9f 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,7 @@ whitelist_externals = true setenv = LANG=en_US.UTF-8 - PYTHONPATH = {toxinidir}/localtuya + PYTHONPATH = {toxinidir}/localtuya-homeassistant deps = -r{toxinidir}/requirements_test.txt commands = @@ -31,7 +31,7 @@ commands = flake8 custom_components black --fast --check . pydocstyle -v custom_components - pylint custom_components/localtuya + pylint custom_components/localtuya --rcfile=pylint.rc [testenv:typing] commands = From 824c0127c1548a26d4c13659e5fae6a54813b63e Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Tue, 24 May 2022 10:30:27 +0200 Subject: [PATCH 22/52] Removed local_key censoring in device diagnostics --- custom_components/localtuya/diagnostics.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index 21a4304..db0f6a0 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -22,7 +22,7 @@ async def async_get_config_entry_diagnostics( data = {} data = entry.data.copy() tuya_api = hass.data[DOMAIN][DATA_CLOUD] - # censoring private information + # censoring private information on integration diagnostic data for field in [CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_USER_ID]: data[field] = f"{data[field][0:3]}...{data[field][-3:]}" for dev_id, dev in data[CONF_DEVICES].items(): @@ -44,13 +44,14 @@ async def async_get_device_diagnostics( data = {} dev_id = list(device.identifiers)[0][1].split("_")[-1] data[DEVICE_CONFIG] = entry.data[CONF_DEVICES][dev_id].copy() + # NOT censoring private information on device diagnostic data local_key = data[DEVICE_CONFIG][CONF_LOCAL_KEY] - data[DEVICE_CONFIG][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" + # data[DEVICE_CONFIG][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" tuya_api = hass.data[DOMAIN][DATA_CLOUD] if dev_id in tuya_api.device_list: data[DEVICE_CLOUD_INFO] = tuya_api.device_list[dev_id] local_key = data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] - data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" + # data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() return data From 42aafa048a01d8d15ea5df58525846bb8b93f735 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Tue, 24 May 2022 10:35:13 +0200 Subject: [PATCH 23/52] Removed local_key censoring in device diagnostics --- custom_components/localtuya/diagnostics.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index db0f6a0..0e38469 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -52,6 +52,7 @@ async def async_get_device_diagnostics( if dev_id in tuya_api.device_list: data[DEVICE_CLOUD_INFO] = tuya_api.device_list[dev_id] local_key = data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] - # data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" - # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() + local_key_obfuscated = "{local_key[0:3]}...{local_key[-3:]}" + # data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = local_key_obfuscated + # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() return data From 3896064c73564fa7dab35a45ba78f1604034ce16 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Tue, 24 May 2022 10:44:17 +0200 Subject: [PATCH 24/52] Removed local_key censoring in device diagnostics --- custom_components/localtuya/diagnostics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index 0e38469..76310ca 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -52,7 +52,7 @@ async def async_get_device_diagnostics( if dev_id in tuya_api.device_list: data[DEVICE_CLOUD_INFO] = tuya_api.device_list[dev_id] local_key = data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] - local_key_obfuscated = "{local_key[0:3]}...{local_key[-3:]}" + # local_key_obfuscated = "{local_key[0:3]}...{local_key[-3:]}" # data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = local_key_obfuscated - # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() + # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() return data From b76b323f90bf0d88f3999fedaa3ad6db4fd210ba Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Tue, 24 May 2022 10:48:20 +0200 Subject: [PATCH 25/52] Removed local_key censoring in device diagnostics --- custom_components/localtuya/diagnostics.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index 76310ca..7ad3d9d 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -44,15 +44,17 @@ async def async_get_device_diagnostics( data = {} dev_id = list(device.identifiers)[0][1].split("_")[-1] data[DEVICE_CONFIG] = entry.data[CONF_DEVICES][dev_id].copy() - # NOT censoring private information on device diagnostic data local_key = data[DEVICE_CONFIG][CONF_LOCAL_KEY] + # NOT censoring private information on device diagnostic data # data[DEVICE_CONFIG][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" tuya_api = hass.data[DOMAIN][DATA_CLOUD] if dev_id in tuya_api.device_list: data[DEVICE_CLOUD_INFO] = tuya_api.device_list[dev_id] - local_key = data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] + # 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:]}" # data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = local_key_obfuscated + # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() return data From cbac83f36280bddd908802aadb6c8484c69f1838 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Tue, 24 May 2022 10:48:20 +0200 Subject: [PATCH 26/52] Removed local_key censoring in device diagnostics --- custom_components/localtuya/diagnostics.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index 76310ca..9cc2692 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -45,14 +45,16 @@ async def async_get_device_diagnostics( dev_id = list(device.identifiers)[0][1].split("_")[-1] data[DEVICE_CONFIG] = entry.data[CONF_DEVICES][dev_id].copy() # NOT censoring private information on device diagnostic data - local_key = data[DEVICE_CONFIG][CONF_LOCAL_KEY] + # local_key = data[DEVICE_CONFIG][CONF_LOCAL_KEY] # data[DEVICE_CONFIG][CONF_LOCAL_KEY] = f"{local_key[0:3]}...{local_key[-3:]}" tuya_api = hass.data[DOMAIN][DATA_CLOUD] if dev_id in tuya_api.device_list: data[DEVICE_CLOUD_INFO] = tuya_api.device_list[dev_id] - local_key = data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] + # 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:]}" # data[DEVICE_CLOUD_INFO][CONF_LOCAL_KEY] = local_key_obfuscated + # data["log"] = hass.data[DOMAIN][CONF_DEVICES][dev_id].logger.retrieve_log() return data From c12c4111d88b9b353ec055e2276bc0bca78cd48a Mon Sep 17 00:00:00 2001 From: rospogrigio <49229287+rospogrigio@users.noreply.github.com> Date: Wed, 25 May 2022 11:27:56 +0200 Subject: [PATCH 27/52] Added images for README --- img/6-project_date.png | Bin 0 -> 59139 bytes img/7-auth_keys.png | Bin 0 -> 66636 bytes img/8-user_id.png | Bin 0 -> 73998 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/6-project_date.png create mode 100644 img/7-auth_keys.png create mode 100644 img/8-user_id.png diff --git a/img/6-project_date.png b/img/6-project_date.png new file mode 100644 index 0000000000000000000000000000000000000000..de324ac34addf10066c34b62a7c75e6ecd2582a8 GIT binary patch literal 59139 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 G{Q6%Kvi_C; literal 0 HcmV?d00001 diff --git a/img/7-auth_keys.png b/img/7-auth_keys.png new file mode 100644 index 0000000000000000000000000000000000000000..b8cc331aafef085e4c73ce2a32f973ab2c4628af GIT binary patch literal 66636 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
  • |MKO(zl1!F+V=v3Smm>&3v}J}Da%#P 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⪻W2N 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 literal 0 HcmV?d00001 From b1a2a6dc6bb28552a541acbbc20dea009a6d7556 Mon Sep 17 00:00:00 2001 From: rospogrigio <49229287+rospogrigio@users.noreply.github.com> Date: Wed, 25 May 2022 11:30:40 +0200 Subject: [PATCH 28/52] Adding images for README --- img/9-cloud_setup.png | Bin 0 -> 22177 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/9-cloud_setup.png diff --git a/img/9-cloud_setup.png b/img/9-cloud_setup.png new file mode 100644 index 0000000000000000000000000000000000000000..c51927d3fc52ba4b66f2c14fd8ec61eda373d08d GIT binary patch literal 22177 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 mY_yILDe$0Dl9bqG(WHx7cm5w@;K>I7 literal 0 HcmV?d00001 From e890e592be5cb5164e022104a7261965ea866988 Mon Sep 17 00:00:00 2001 From: rospogrigio <49229287+rospogrigio@users.noreply.github.com> Date: Wed, 25 May 2022 12:48:47 +0200 Subject: [PATCH 29/52] Update README.md --- README.md | 135 ++++++++++++++++++++---------------------------------- 1 file changed, 50 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index 7404fe9..16347b6 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,19 @@ A Home Assistant custom Integration for local handling of Tuya-based devices. This custom integration updates device status via push updates instead of polling, so status updates are fast (even when manually operated). +The integration also supports the Tuya IoT Cloud APIs, for the retrieval of info and of the local_keys of the devices. + + +**NOTE: The Cloud API account configuration is not mandatory (LocalTuya can work also without it) but is strongly suggested for easy retrieval (and auto-update after re-pairing a device) of local_keys. Cloud API calls are performed only at startup, and when a local_key update is needed.** + The following Tuya device types are currently supported: -* 1 and multiple gang switches -* Wi-Fi smart plugs (including those with additional USB plugs) +* Switches * Lights * Covers * Fans -* Climates (soon) +* Climates +* Vacuums Energy monitoring (voltage, current, watts, etc.) is supported for compatible devices. @@ -19,112 +24,68 @@ This repository's development began as code from [@NameLessJedi](https://github. # Installation: -Copy the localtuya folder and all of its contents into your Home Assistant's custom_components folder. This folder is usually inside your `/config` folder. If you are running Hass.io, use SAMBA to copy the folder over. If you are running Home Assistant Supervised, the custom_components folder might be located at `/usr/share/hassio/homeassistant`. You may need to create the `custom_components` folder and then copy the localtuya folder and all of its contents into it +The easiest way, if you are using [HACS](https://hacs.xyz/), is to install localtuya through HACS. -Alternatively, you can install localtuya through [HACS](https://hacs.xyz/) by adding this repository. +For manual installation, copy the localtuya folder and all of its contents into your Home Assistant's custom_components folder. This folder is usually inside your `/config` folder. If you are running Hass.io, use SAMBA to copy the folder over. If you are running Home Assistant Supervised, the custom_components folder might be located at `/usr/share/hassio/homeassistant`. You may need to create the `custom_components` folder and then copy the localtuya folder and all of its contents into it. # Usage: -**NOTE: You must have your Tuya device's Key and ID in order to use localtuya. There are several ways to obtain the localKey depending on your environment and the devices you own. A good place to start getting info is https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md .** +**NOTE: You must have your Tuya device's Key and ID in order to use localtuya. The easiest way is to configure the Cloud API account in the integration. If you choose not to do it, there are several ways to obtain the local_keys depending on your environment and the devices you own. A good place to start getting info is https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md .** **NOTE - Nov 2020: If you plan to integrate these devices on a network that has internet and blocking their internet access, you must also block DNS requests (to the local DNS server, e.g. 192.168.1.1). If you only block outbound internet, then the device will sit in a zombie state; it will refuse / not respond to any connections with the localkey. Therefore, you must first connect the devices with an active internet connection, grab each device localkey, and implement the block.** -Devices can be configured in two ways: -# Option one: YAML config files - -Add the proper entry to your configuration.yaml file. Several example configurations for different device types are provided below. Make sure to save when you are finished editing configuration.yaml. - -```yaml -localtuya: - - host: 192.168.1.x - device_id: xxxxx - local_key: xxxxx - friendly_name: Tuya Device - protocol_version: "3.3" - scan_interval: # optional, only needed if energy monitoring values are not updating - seconds: 30 # Values less than 10 seconds may cause stability issues - entities: - - platform: binary_sensor - friendly_name: Plug Status - id: 1 - device_class: power - state_on: "true" # Optional - state_off: "false" # Optional - - - platform: cover - friendly_name: Device Cover - id: 2 - open_close_cmds: ["on_off","open_close"] # Optional, default: "on_off" - positioning_mode: ["none","position","timed"] # Optional, default: "none" - currpos_dps: 3 # Optional, required only for "position" mode - setpos_dps: 4 # Optional, required only for "position" mode - span_time: 25 # Full movement time: Optional, required only for "timed" mode - - - platform: fan - friendly_name: Device Fan - id: 3 # dps for on/off state - fan_direction: 4 # Optional, dps for fan direction - fan_direction_fwd: forward # String for the forward direction - fan_direction_rev: reverse # String for the reverse direction - fan_ordered_list: low,medium,high,auto # Optional, If this is used it will not use the min and max integers. - fan_oscilating_control: 4 # Optional, dps for fan osciallation - fan_speed_control: 3 # Optional, if ordered list not used, dps for speed control - fan_speed_min: 1 # Optional, if ordered list not used, minimum integer for speed range - fan_speed_max: 10 # Optional, if ordered list not used, maximum integer for speed range +# Adding the Integration - - platform: light - friendly_name: Device Light - 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" +**NOTE: starting from v.4.0.0, configuration using YAML files is no longer supported. The integration can be configured only using the config flow.** - - platform: sensor - friendly_name: Plug Voltage - id: 20 - scaling: 0.1 # Optional - device_class: voltage # Optional - unit_of_measurement: "V" # Optional - - platform: switch - friendly_name: Plug - id: 1 - current: 18 # Optional - current_consumption: 19 # Optional - voltage: 20 # Optional -``` +To start configuring the integration, just press the "+ADD INTEGRATION" button in the Settings - Integrations page, and select localtuya from the drop-down menu. +The Cloud API configuration page will appear, requesting to input your Tuya IoT Platform account credentials: -Note that a single device can contain several different entities. Some examples: -- a cover device might have 1 (or many) cover entities, plus a switch to control backlight -- a multi-gang switch will contain several switch entities, one for each gang controlled +![cloud_setup](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/9-cloud_setup.png) -Restart Home Assistant when finished editing. +To setup a Tuya IoT Platform account and setup a project in it, refer to the instructions for the official Tuya integration: +https://www.home-assistant.io/integrations/tuya/ -# Option two: Using config flow +> **Note: as stated in the above page, if you already have an account and an IoT project, make sure that it was created after May 25, 2021 (due to changes introduced in the cloud for Tuya 2.0). Otherwise, you need to create a new project. See the following screenshot for where to check your project creation date:** -Start by going to Configuration - Integration and pressing the "+" button to create a new Integration, then select LocalTuya in the drop-down menu. -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. +![project_date](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/6-project_date.png) + +After pressing the Submit button, the first setup is complete and the Integration will be added. + +> **Note: it is not mandatory to configure the Cloud API account: you can choose to leave the default "xxx" values, and the Integration will be added anyway.** + +After the Integration has been set up, devices can be added and configured pressing the Configure button in the Integrations page: + +![integration_configure](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/10-integration_configure.png) + + +# Integration Configuration menu + +The configuration menu is the following: + +![config_menu](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/11-config_menu.png) + +From this menu, you can select the "Reconfigure Cloud API account" to edit your Tuya Cloud credentials and settings, in case they have changed or if the integration was migrated from v.3.x.x versions. + +You can then proceed Adding or Editing your Tuya devices. + +# Adding/editing a device + +If you select to "Add or Edit a device", a drop-down menu will appear containing the list of detected devices (using auto-discovery if adding was selected, or the list of already configured devices if editing was selected): you can select one of these, or manually input all the parameters selecting the "..." option. > **Note: The tuya app on your device must be closed for the following steps to work reliably.** + ![discovery](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/1-discovery.png) -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 localKey. These values will be automatically retrieved if you have configured your Cloud API account, otherwise you will need to input them manually. -Setting the scan interval is optional, only needed if energy/power values are not updating frequently enough by default. Values less than 10 seconds may cause stability issues. +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. Once you press "Submit", the connection is tested to check that everything works. @@ -147,6 +108,10 @@ Once you configure the entities, the procedure is complete. You can now associat ![success](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/5-success.png) +# Migration from LocalTuya v.3.x.x + +Automatically migrates... you can delete... + # Energy monitoring values You can obtain Energy monitoring (voltage, current) in two different ways: From ef911a84c1598aeff8fe7f3cb724f82be7c41edf Mon Sep 17 00:00:00 2001 From: rospogrigio <49229287+rospogrigio@users.noreply.github.com> Date: Wed, 25 May 2022 14:30:55 +0200 Subject: [PATCH 30/52] Update README.md --- README.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 16347b6..1caf3c0 100644 --- a/README.md +++ b/README.md @@ -19,22 +19,24 @@ The following Tuya device types are currently supported: Energy monitoring (voltage, current, watts, etc.) is supported for compatible devices. +> **Currently, only Tuya protocols 3.1 and 3.3 are supported (3.4 is not).** + This repository's development began as code from [@NameLessJedi](https://github.com/NameLessJedi), [@mileperhour](https://github.com/mileperhour) and [@TradeFace](https://github.com/TradeFace). Their code was then deeply refactored to provide proper integration with Home Assistant environment, adding config flow and other features. Refer to the "Thanks to" section below. # Installation: -The easiest way, if you are using [HACS](https://hacs.xyz/), is to install localtuya through HACS. +The easiest way, if you are using [HACS](https://hacs.xyz/), is to install LocalTuya through HACS. For manual installation, copy the localtuya folder and all of its contents into your Home Assistant's custom_components folder. This folder is usually inside your `/config` folder. If you are running Hass.io, use SAMBA to copy the folder over. If you are running Home Assistant Supervised, the custom_components folder might be located at `/usr/share/hassio/homeassistant`. You may need to create the `custom_components` folder and then copy the localtuya folder and all of its contents into it. # Usage: -**NOTE: You must have your Tuya device's Key and ID in order to use localtuya. The easiest way is to configure the Cloud API account in the integration. If you choose not to do it, there are several ways to obtain the local_keys depending on your environment and the devices you own. A good place to start getting info is https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md .** +**NOTE: You must have your Tuya device's Key and ID in order to use LocalTuya. The easiest way is to configure the Cloud API account in the integration. If you choose not to do it, there are several ways to obtain the local_keys depending on your environment and the devices you own. A good place to start getting info is https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md .** -**NOTE - Nov 2020: If you plan to integrate these devices on a network that has internet and blocking their internet access, you must also block DNS requests (to the local DNS server, e.g. 192.168.1.1). If you only block outbound internet, then the device will sit in a zombie state; it will refuse / not respond to any connections with the localkey. Therefore, you must first connect the devices with an active internet connection, grab each device localkey, and implement the block.** +**NOTE 2: If you plan to integrate these devices on a network that has internet and blocking their internet access, you must also block DNS requests (to the local DNS server, e.g. 192.168.1.1). If you only block outbound internet, then the device will sit in a zombie state; it will refuse / not respond to any connections with the localkey. Therefore, you must first connect the devices with an active internet connection, grab each device localkey, and implement the block.** # Adding the Integration @@ -43,15 +45,18 @@ For manual installation, copy the localtuya folder and all of its contents into **NOTE: starting from v.4.0.0, configuration using YAML files is no longer supported. The integration can be configured only using the config flow.** -To start configuring the integration, just press the "+ADD INTEGRATION" button in the Settings - Integrations page, and select localtuya from the drop-down menu. +To start configuring the integration, just press the "+ADD INTEGRATION" button in the Settings - Integrations page, and select LocalTuya from the drop-down menu. The Cloud API configuration page will appear, requesting to input your Tuya IoT Platform account credentials: ![cloud_setup](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/9-cloud_setup.png) To setup a Tuya IoT Platform account and setup a project in it, refer to the instructions for the official Tuya integration: https://www.home-assistant.io/integrations/tuya/ +The place to find the Client ID and Secret is described in this link (in the ["Get Authorization Key"](https://www.home-assistant.io/integrations/tuya/#get-authorization-key) paragraph), while the User ID can be found in the "Link Tuya App Account" subtab within the Cloud project: -> **Note: as stated in the above page, if you already have an account and an IoT project, make sure that it was created after May 25, 2021 (due to changes introduced in the cloud for Tuya 2.0). Otherwise, you need to create a new project. See the following screenshot for where to check your project creation date:** +![user_id.png](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/8-user_id.png.png) + +> **Note: as stated in the above link, if you already have an account and an IoT project, make sure that it was created after May 25, 2021 (due to changes introduced in the cloud for Tuya 2.0). Otherwise, you need to create a new project. See the following screenshot for where to check your project creation date:** ![project_date](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/6-project_date.png) @@ -110,7 +115,10 @@ Once you configure the entities, the procedure is complete. You can now associat # Migration from LocalTuya v.3.x.x -Automatically migrates... you can delete... +If you upgrade LocalTuya from v3.x.x or older, the config entry will automatically be migrated to the new setup. Everything should work as it did before the upgrade, apart from the fact that in the Integration tab you will see just one LocalTuya integration (showing the number of devices and entities configured) instead of several Integrations grouped within the LocalTuya Box. This will happen both if the old configuration was done using YAML files and with the config flow. Once migrated, you can just input your Tuya IoT account credentials to enable the support for the Cloud API (and benefit from the local_key retrieval and auto-update): see https://github.com/rospogrigio/localtuya/edit/localtuya_4.0/README.md#integration-configuration-menu. + +If you had configured LocalTuya using YAML files, you can delete all its references from within the YAML files because they will no longer be considered so they might bring confusion (only the logger configuration part needs to be kept, of course, see https://github.com/rospogrigio/localtuya/edit/localtuya_4.0/README.md#debugging). + # Energy monitoring values @@ -162,6 +170,8 @@ logger: * Everything listed in https://github.com/rospogrigio/localtuya-homeassistant/issues/15 +* Support devices that use Tuya protocol v.3.4 + # Thanks to: NameLessJedi https://github.com/NameLessJedi/localtuya-homeassistant and mileperhour https://github.com/mileperhour/localtuya-homeassistant being the major sources of inspiration, and whose code for switches is substantially unchanged. From f5af7e09a1efcad3ae7843751b64a4fe5e83b1ab Mon Sep 17 00:00:00 2001 From: rospogrigio <49229287+rospogrigio@users.noreply.github.com> Date: Wed, 25 May 2022 14:33:12 +0200 Subject: [PATCH 31/52] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1caf3c0..9213e3a 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ To setup a Tuya IoT Platform account and setup a project in it, refer to the ins https://www.home-assistant.io/integrations/tuya/ The place to find the Client ID and Secret is described in this link (in the ["Get Authorization Key"](https://www.home-assistant.io/integrations/tuya/#get-authorization-key) paragraph), while the User ID can be found in the "Link Tuya App Account" subtab within the Cloud project: -![user_id.png](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/8-user_id.png.png) +![user_id.png](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/8-user_id.png) > **Note: as stated in the above link, if you already have an account and an IoT project, make sure that it was created after May 25, 2021 (due to changes introduced in the cloud for Tuya 2.0). Otherwise, you need to create a new project. See the following screenshot for where to check your project creation date:** From 81d1149dc28a23fce9549e1adafe43dc44fd5e7c Mon Sep 17 00:00:00 2001 From: rospogrigio <49229287+rospogrigio@users.noreply.github.com> Date: Wed, 25 May 2022 14:38:34 +0200 Subject: [PATCH 32/52] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9213e3a..6c94a6b 100644 --- a/README.md +++ b/README.md @@ -115,9 +115,9 @@ Once you configure the entities, the procedure is complete. You can now associat # Migration from LocalTuya v.3.x.x -If you upgrade LocalTuya from v3.x.x or older, the config entry will automatically be migrated to the new setup. Everything should work as it did before the upgrade, apart from the fact that in the Integration tab you will see just one LocalTuya integration (showing the number of devices and entities configured) instead of several Integrations grouped within the LocalTuya Box. This will happen both if the old configuration was done using YAML files and with the config flow. Once migrated, you can just input your Tuya IoT account credentials to enable the support for the Cloud API (and benefit from the local_key retrieval and auto-update): see https://github.com/rospogrigio/localtuya/edit/localtuya_4.0/README.md#integration-configuration-menu. +If you upgrade LocalTuya from v3.x.x or older, the config entry will automatically be migrated to the new setup. Everything should work as it did before the upgrade, apart from the fact that in the Integration tab you will see just one LocalTuya integration (showing the number of devices and entities configured) instead of several Integrations grouped within the LocalTuya Box. This will happen both if the old configuration was done using YAML files and with the config flow. Once migrated, you can just input your Tuya IoT account credentials to enable the support for the Cloud API (and benefit from the local_key retrieval and auto-update): see [Configuration menu](https://github.com/rospogrigio/localtuya#integration-configuration-menu). -If you had configured LocalTuya using YAML files, you can delete all its references from within the YAML files because they will no longer be considered so they might bring confusion (only the logger configuration part needs to be kept, of course, see https://github.com/rospogrigio/localtuya/edit/localtuya_4.0/README.md#debugging). +If you had configured LocalTuya using YAML files, you can delete all its references from within the YAML files because they will no longer be considered so they might bring confusion (only the logger configuration part needs to be kept, of course, see [Debugging](https://github.com/rospogrigio/localtuya#debugging) ). # Energy monitoring values From fe56c247d3d87624c029a84c531040f9890f8c4a Mon Sep 17 00:00:00 2001 From: rospogrigio <49229287+rospogrigio@users.noreply.github.com> Date: Wed, 25 May 2022 14:39:33 +0200 Subject: [PATCH 33/52] Update info.md --- info.md | 201 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 105 insertions(+), 96 deletions(-) diff --git a/info.md b/info.md index 0489319..31287ee 100644 --- a/info.md +++ b/info.md @@ -5,159 +5,163 @@ ![logo](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/logo-small.png) A Home Assistant custom Integration for local handling of Tuya-based devices. -Device status is updated receiving push updates from the device instead of polling, so status updates are extremely fast (even if manually operated). + +This custom integration updates device status via push updates instead of polling, so status updates are fast (even when manually operated). +The integration also supports the Tuya IoT Cloud APIs, for the retrieval of info and of the local_keys of the devices. + + +**NOTE: The Cloud API account configuration is not mandatory (LocalTuya can work also without it) but is strongly suggested for easy retrieval (and auto-update after re-pairing a device) of local_keys. Cloud API calls are performed only at startup, and when a local_key update is needed.** + The following Tuya device types are currently supported: -* 1 and multiple gang switches -* Wi-Fi smart plugs (including those with additional USB plugs) +* Switches * Lights * Covers * Fans -* Climates (soon) +* Climates +* Vacuums -Energy monitoring (voltage, current, watts, etc.) is supported for compatible devices. +Energy monitoring (voltage, current, watts, etc.) is supported for compatible devices. + +> **Currently, only Tuya protocols 3.1 and 3.3 are supported (3.4 is not).** + +This repository's development began as code from [@NameLessJedi](https://github.com/NameLessJedi), [@mileperhour](https://github.com/mileperhour) and [@TradeFace](https://github.com/TradeFace). Their code was then deeply refactored to provide proper integration with Home Assistant environment, adding config flow and other features. Refer to the "Thanks to" section below. -This repository's development has substantially started by utilizing and merging code from NameLessJedi, mileperhour and TradeFace, and then was deeply refactored to provide proper integration with Home Assistant environment, adding config flow and other features. Refer to the "Thanks to" section below. # Installation: -Copy the localtuya folder and all of its contents into your Home Assistant's custom_components folder. This is often located inside of your /config folder. If you are running Hass.io, use SAMBA to copy the folder over. If you are running Home Assistant Supervised, the custom_components folder might be located at /usr/share/hassio/homeassistant. It is possible that your custom_components folder does not exist. If that is the case, create the folder in the proper location, and then copy the localtuya folder and all of its contents inside the newly created custom_components folder. +The easiest way, if you are using [HACS](https://hacs.xyz/), is to install LocalTuya through HACS. -Alternatively, you can install localtuya through HACS by adding this repository. +For manual installation, copy the localtuya folder and all of its contents into your Home Assistant's custom_components folder. This folder is usually inside your `/config` folder. If you are running Hass.io, use SAMBA to copy the folder over. If you are running Home Assistant Supervised, the custom_components folder might be located at `/usr/share/hassio/homeassistant`. You may need to create the `custom_components` folder and then copy the localtuya folder and all of its contents into it. # Usage: -**NOTE: You must have your Tuya device's Key and ID in order to use localtuya. There are several ways to obtain the localKey depending on your environment and the devices you own. A good place to start getting info is https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md .** +**NOTE: You must have your Tuya device's Key and ID in order to use LocalTuya. The easiest way is to configure the Cloud API account in the integration. If you choose not to do it, there are several ways to obtain the local_keys depending on your environment and the devices you own. A good place to start getting info is https://github.com/codetheweb/tuyapi/blob/master/docs/SETUP.md .** -Devices can be configured in two ways: -# 1. YAML config files +**NOTE 2: If you plan to integrate these devices on a network that has internet and blocking their internet access, you must also block DNS requests (to the local DNS server, e.g. 192.168.1.1). If you only block outbound internet, then the device will sit in a zombie state; it will refuse / not respond to any connections with the localkey. Therefore, you must first connect the devices with an active internet connection, grab each device localkey, and implement the block.** -Add the proper entry to your configuration.yaml file. Several example configurations for different device types are provided below. Make sure to save when you are finished editing configuration.yaml. -``` -localtuya: - - host: 192.168.1.x - device_id: xxxxx - local_key: xxxxx - friendly_name: Tuya Device - protocol_version: "3.3" - scan_interval: # optional, only needed if energy monitoring values are not updating - seconds: 30 # Values less than 10 seconds may cause stability issues - entities: - - platform: binary_sensor - friendly_name: Plug Status - id: 1 - device_class: power - state_on: "true" # Optional - state_off: "false" # Optional +# Adding the Integration - - platform: cover - friendly_name: Device Cover - id: 2 - open_close_cmds: ["on_off","open_close"] # Optional, default: "on_off" - positioning_mode: ["none","position","timed"] # Optional, default: "none" - currpos_dps: 3 # Optional, required only for "position" mode - setpos_dps: 4 # Optional, required only for "position" mode - span_time: 25 # Full movement time: Optional, required only for "timed" mode - - - platform: fan - friendly_name: Device Fan - id: 3 - - platform: light - friendly_name: Device Light - 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" +**NOTE: starting from v.4.0.0, configuration using YAML files is no longer supported. The integration can be configured only using the config flow.** - - platform: sensor - friendly_name: Plug Voltage - id: 20 - scaling: 0.1 # Optional - device_class: voltage # Optional - unit_of_measurement: "V" # Optional - - platform: switch - friendly_name: Plug - id: 1 - current: 18 # Optional - current_consumption: 19 # Optional - voltage: 20 # Optional -``` - -Note that a single device can contain several different entities. Some examples: -- a cover device might have 1 (or many) cover entities, plus a switch to control backlight -- a multi-gang switch will contain several switch entities, one for each gang controlled +To start configuring the integration, just press the "+ADD INTEGRATION" button in the Settings - Integrations page, and select LocalTuya from the drop-down menu. +The Cloud API configuration page will appear, requesting to input your Tuya IoT Platform account credentials: -Restart Home Assistant when finished editing. +![cloud_setup](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/9-cloud_setup.png) -# 2. Using config flow +To setup a Tuya IoT Platform account and setup a project in it, refer to the instructions for the official Tuya integration: +https://www.home-assistant.io/integrations/tuya/ +The place to find the Client ID and Secret is described in this link (in the ["Get Authorization Key"](https://www.home-assistant.io/integrations/tuya/#get-authorization-key) paragraph), while the User ID can be found in the "Link Tuya App Account" subtab within the Cloud project: + +![user_id.png](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/8-user_id.png) + +> **Note: as stated in the above link, if you already have an account and an IoT project, make sure that it was created after May 25, 2021 (due to changes introduced in the cloud for Tuya 2.0). Otherwise, you need to create a new project. See the following screenshot for where to check your project creation date:** + +![project_date](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/6-project_date.png) + +After pressing the Submit button, the first setup is complete and the Integration will be added. + +> **Note: it is not mandatory to configure the Cloud API account: you can choose to leave the default "xxx" values, and the Integration will be added anyway.** + +After the Integration has been set up, devices can be added and configured pressing the Configure button in the Integrations page: + +![integration_configure](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/10-integration_configure.png) + + +# Integration Configuration menu + +The configuration menu is the following: + +![config_menu](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/11-config_menu.png) + +From this menu, you can select the "Reconfigure Cloud API account" to edit your Tuya Cloud credentials and settings, in case they have changed or if the integration was migrated from v.3.x.x versions. + +You can then proceed Adding or Editing your Tuya devices. + +# Adding/editing a device + +If you select to "Add or Edit a device", a drop-down menu will appear containing the list of detected devices (using auto-discovery if adding was selected, or the list of already configured devices if editing was selected): you can select one of these, or manually input all the parameters selecting the "..." option. + +> **Note: The tuya app on your device must be closed for the following steps to work reliably.** -Start by going to Configuration - Integration and pressing the "+" button to create a new Integration, then select LocalTuya in the drop-down menu. -Wait for 6 seconds for the scanning of the devices in your LAN. Then, a drop-down menu will appear containing the list of detectes devices: you can -select one of these, or manually input all the parameters. ![discovery](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/1-discovery.png) -If you have selected one entry, you just have to input the Friendly Name of the Device, and the localKey. +If you have selected one entry, you only need to input the device's Friendly Name and localKey. These values will be automatically retrieved if you have configured your Cloud API account, otherwise you will need to input them manually. -Setting the scan interval is optional, only needed if energy/power values are not updating frequently enough by default. Values less than 10 seconds may cause stability issues. +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. Once you press "Submit", the connection is tested to check that everything works. ![image](https://user-images.githubusercontent.com/1082213/146664103-ac40319e-f934-4933-90cf-2beaff1e6bac.png) -Then, it's time to add the entities: this step will take place several times. Select the entity type from the drop-down menu to set it up. -After you have defined all the needed entities leave the "Do not add more entities" checkbox checked: this will complete the procedure. + +Then, it's time to add the entities: this step will take place several times. First, select the entity type from the drop-down menu to set it up. +After you have defined all the needed entities, leave the "Do not add more entities" checkbox checked: this will complete the procedure. ![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 avaliable DPs found on the device (with their current status!!) for an easy identification. Each entity type has different options -to be configured, here is an example for the "switch" entity: +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: ![entity](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/4-entity.png) -After all the entities have been configured, the procedure is complete, and the Device can be associated to the Area desired. +Once you configure the entities, the procedure is complete. You can now associate the device with an Area in Home Assistant ![success](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/5-success.png) +# Migration from LocalTuya v.3.x.x + +If you upgrade LocalTuya from v3.x.x or older, the config entry will automatically be migrated to the new setup. Everything should work as it did before the upgrade, apart from the fact that in the Integration tab you will see just one LocalTuya integration (showing the number of devices and entities configured) instead of several Integrations grouped within the LocalTuya Box. This will happen both if the old configuration was done using YAML files and with the config flow. Once migrated, you can just input your Tuya IoT account credentials to enable the support for the Cloud API (and benefit from the local_key retrieval and auto-update): see [Configuration menu](https://github.com/rospogrigio/localtuya#integration-configuration-menu). + +If you had configured LocalTuya using YAML files, you can delete all its references from within the YAML files because they will no longer be considered so they might bring confusion (only the logger configuration part needs to be kept, of course, see [Debugging](https://github.com/rospogrigio/localtuya#debugging) ). + + # Energy monitoring values -Energy monitoring (voltage, current...) values can be obtained in two different ways: -1) creating individual sensors, each one with the desired name. Note: Voltage and Consumption usually include the first decimal, so 0.1 as "scaling" parameter shall be used in order to get the correct values. -2) accessing the voltage/current/current_consumption attributes of a switch, and then defining template sensors like this (please note that in this case the values are already divided by 10 for Voltage and Consumption) -3) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended. +You can obtain Energy monitoring (voltage, current) in two different ways: -``` +1) Creating individual sensors, each one with the desired name. + Note: Voltage and Consumption usually include the first decimal. You will need to scale the parament by 0.1 to get the correct values. +1) Access the voltage/current/current_consumption attributes of a switch, and define template sensors + Note: these values are already divided by 10 for Voltage and Consumption +1) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended. + +``` sensor: - platform: template sensors: tuya-sw01_voltage: value_template: >- {{ states.switch.sw01.attributes.voltage }} - unit_of_measurement: 'V' + unit_of_measurement: 'V' tuya-sw01_current: - value_template: >- + value_template: >- {{ states.switch.sw01.attributes.current }} - unit_of_measurement: 'mA' + unit_of_measurement: 'mA' tuya-sw01_current_consumption: value_template: >- {{ states.switch.sw01.attributes.current_consumption }} - unit_of_measurement: 'W' -``` + unit_of_measurement: 'W' +``` + +# Debugging + +Whenever you write a bug report, it helps tremendously if you include debug logs directly (otherwise we will just ask for them and it will take longer). So please enable debug logs like this and include them in your issue: + +```yaml +logger: + default: warning + logs: + custom_components.localtuya: debug +``` # Notes: @@ -165,11 +169,13 @@ Energy monitoring (voltage, current...) values can be obtained in two different # To-do list: -* Create a (good and precise) sensor (counter) for Energy (kWh) -not just Power, but based on it-. +* Create a (good and precise) sensor (counter) for Energy (kWh) -not just Power, but based on it-. Ideas: Use: https://www.home-assistant.io/components/integration/ and https://www.home-assistant.io/components/utility_meter/ - + * Everything listed in https://github.com/rospogrigio/localtuya-homeassistant/issues/15 +* Support devices that use Tuya protocol v.3.4 + # Thanks to: NameLessJedi https://github.com/NameLessJedi/localtuya-homeassistant and mileperhour https://github.com/mileperhour/localtuya-homeassistant being the major sources of inspiration, and whose code for switches is substantially unchanged. @@ -179,3 +185,6 @@ TradeFace, for being the only one to provide the correct code for communication sean6541, for the working (standard) Python Handler for Tuya devices. postlund, for the ideas, for coding 95% of the refactoring and boosting the quality of this repo to levels hard to imagine (by me, at least) and teaching me A LOT of how things work in Home Assistant. + +Buy Me A Coffee +PayPal Logo From 56759c5225e6634837c703e8db5ed965d5c51a64 Mon Sep 17 00:00:00 2001 From: rospogrigio <49229287+rospogrigio@users.noreply.github.com> Date: Wed, 25 May 2022 14:44:28 +0200 Subject: [PATCH 34/52] Add files via upload --- img/9-cloud_setup.png | Bin 22177 -> 21521 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/img/9-cloud_setup.png b/img/9-cloud_setup.png index c51927d3fc52ba4b66f2c14fd8ec61eda373d08d..cc70a5183a8b8963953238ae493dbce5155afb10 100644 GIT binary patch literal 21521 zcma&OcRbd8|2N)5rKpHxWF#RwD^z4O&@dt+GfLTeon=Omgb+eegizTdTQ*6Oz4t16 z+|N(fb=~*%``zC^?(^|DALr@dIF8SFzh1BBdVd~WQIg-gn{M}(EnD_pP&j{W%a*MS z_;-ek1g}JX$UBZ7yi8?eu3V6jIci~LZft61v}FrNkZX{nLg`uha^35fWH}jro-1RP zvCUxNjEhuBCp&cUW~?e{@THJh$qF=l5UOZcLe5{9)>><<903{_qo@;%Z z&!qitPz0>bPIdK9F&q`*ynM>JVykNAV-*Q@W9#I&O=>Am$}(ExW5#2Te(RHHSGGw? zxD@!$y`LK1pkKe(W%!RgTeLy0-hOjRWphpOVb?xBt_!sRFcOhpu?-uAcSS{4#yNu4KS7lFD~$ zAx*~yEy-Hm+06SL-Ibp^CAQ`_6o-6YP(M;!dwlfI=R;!K9(EL$+ujUb{HKQDR?men z^6H>`Aus8z4rk)cziy4a`O3U*GUZcX3+YiA?UUVGNPUVGf->6j^7FDXv)mg#uB>jI zt+4vZnmvPFz!WUUQ0J^;}v_;P|@tjCoJ`C3lgT z%&TpG&YiO!b$7q!t99Z%+hfn{^^(Dhy_P4_;*-XV0*{bBEH)Gf3JN->sY%Bkd||

    1`Ip}ueY?HrDSHT|zNpsEwD|EApO65Xncd9ZEpNp7CtLPQ z`}Xc*Jj=^Z$nK0;lZtXm|F&Bl|m@Vy=JAE{mRiY-^Db{g2*i`>t;`Pq+B|Htn? zf0{Zw*Y!qzm=4~u$;Kl7s0&)!QV zGWNzOkW!jl{Y^Z_?%Y&^iLtS{E-N1VQEXaTU}|bA#p!3A_eB^3<^O)g(&}ozRn8UF zKkh!CDRGGR^+$6zid@B;(^x%er;4}c)M%0QmNquox;j*}Sc=mBn6$IeuDr$9Gd&%c zlw@OXKVQWfIMXOY!SBA|qGD^JX zv{WHd_@%9I;gtC4(*=R{HZ~vh%XdF}_N?mbSN(yiAa2c%zC{~<(z@E#hD3_0BTn6w z3d!;ljZ)gjD4}rmYLIg1uvN~FY)jTRZ{A3(jKr5!RLr#%ZG;?;b||^6uCThY@*~Tf zDN#9QVXFVwrBI8tCCiv7t>enk1B*qQlH=b~`bV1Lg-)His;*ucE92uZl{0)J&+g>j zy?bNYDH50qYVwnl{fDES_xY55`QkykYxnNHncmX1m9`>w0f9L`k0M>CKeZ-8`h2wc z#~;UWbfrg7EaQ&acrqa|wws)GxP1S9JtHrM94b@FKy zTSOh_*v_o=QtTopXXE9i@GtMvP7G`o+MCs}uIH4@DHq?gYC1LRkblkyUqJmLbv(QC z{xm}tiN}}K6(-k$2aDn}n#P%jWv}LM-V+GVPrEHdH|rq56q+W-nv~Ss(o%{Grl6>J zjEigBPqnAmZfzuYBsZXDaL%o$#86FLDu^q`L)g%U1@KddwE-=n*< z?NTUr&O`x+-@~LNlL+r7lk+V?LW+52V)(ki%6@Hm?KaYa=}T=CuQD@(X>?tJG%}6( zoqn50OH2RAwH<%OIee$`*PChkTaMeuty31JyBk7J-n7Wk^7Qc$#Hz)yVPWUb*Po`M zrna0LxDXK;sgYxK?CRC4rEbqea6?p7%3g|D^PDEJ(H<`G+Vk=AX92wuPrVd5-Xkwu z-zL3WUtOH<@t`=s#H4)d*3q1t9B!@brvU*0HTSbj?%e4cO^h-AJ zVBdt@X=RDo9YrgxY55K7vw`llyza}Fw0LN6vn4i``fsGDF**+H=j7r_%T0f&bezd$ zX6yAtrNLewrlFLquG|T(ZEDHXXU?42wrv|$CySt9@ceLtyP0jjjd**G^>JLV-e~8k z-n+W1bXbjDGuof-P`9_YzfMS)tI^$f78-iUMmsSn$#2?ybM?HOoPa?Ex$wQ8o~6CT zy%rygY7WiK&p-0>>r1a>?EjTxJ(63WZczC`$n=1sqT-g9;r=f_9&^-9q$a~JHUyXt;^y6ZSwPJ`>~vF{%o;(RI7#?z`Qg#_i8 z`idt<@VbKpV*sP%*RLU5k9>V^jW)lb7qi-t)loOwUs+I-*B@}2s6G_$?}dfAj_!?6 zkuC=hhSPR4OZ|Z@?d{cjc=L;cm8C}SS-saOhk4)#!?_2D0=C$v371>;C z*u1QvaX2a}YHdXROx%YLPib@<9@vh5x18;hHLi;oOj)3#qg(ou=C0`EBrYZ0xIQXxsK2PNFgmN-t;5-L2{(uN^yx?}1r=4J<+(wu z4IH1Xt?k6GoR;HRM_b$4GViwWbh&S?*95GOjos04=-r9B9nUQ6WFA-eZX4xK!`o*g z{^-%8St&+(`r1;8GqsNo3zGclMML_Yan`kE9kn<0(i5WN zr+b3Z{cP4Qt*E${nwpyERZNlfot>Q%T?LU3A8s?Qeevbd!Q*e=z01+x<}ATj|58lG z)bs?a+~e#!UfZwe=+tL6NY$XJYU?--QfFjlF84ESD(LDmO&6~6m>2vxWS&1u;^~cm)Y8F$!#6o#>>y_Q+ z4&J$Yx1h%V$rGcF&-Wyj2ZG;gWO}Ej@)V-WTTd4(K8uNIpm3Ynjw;Kij!Ye=mvnlr zuIn1|>2BNH_nYSXsi|e%-Ou>Nz2ew?$ls{Znf|R3mF$;Mn`gnnrC69}t*r%%Ry(I1 z)@Cam`=5x=p1MPote#eJi*JS~gA2jz=~jamr}Czy9?P+wJ9m!5PZ}SWmOr-}WtP9W zv6{a;6lwP3!-K3Yr<$8VcOoi0D5R=-OMOr&J#`}8`9ldtdV0(S1qEG&U-%6wuH3j$ zg$vyfv+?^ldP#p};9_B-L1lop9rr$;T?g4exGYlXi&+h*dSvG3zueexv$D3nprnKe^^8Jlsd&}J z<4dtYpkzh1Wq(z5b?>{x=su#uJ32Ztt-4h{Ia0}$mM?wh4ku0u?fJTnjuKWwd3pK3 z(9pB+@Cz<365SZn#FdBl`a{5h7n2n8(k4{b=-LBSSzgkg@LhiR->7QQgsx4 zUjF*ks}CysnMFluGus~*6&2ZYcx}bmO|)nF$oe1R*DoWxcK!O_E1qS(=-rLesjjLz z%EJ@z@+Cb+gr#^?871T{1?exaEV zS4`4*;fktiIk8IQ0_dx;rX1>4^%PcYNcI&+rr1%*&bq~EX_h!jFpiCl;j}!Ys?pZ& z-Mi=M=}DgCmz$SonWM$jp@rhfOZfbuV{&LRtGnWUR;Js!(-zE5+M{wA*GE7msst?hdX#@9w@?`8inEY4jLSTXkLCAgZ&==<~;qPs77UB}WAW1h%|= z`}S2{UdPQ(FD|pAjeE3=zJBw@3%z~18$eh{aehIN{1cW|ZfjVYbBwzj9YcUL3*-7rQ9oI=-b%az}wFQr^p4z99b1Qr#= zEG*cSl$MgJUYFs28dcdJTUfZ^NBt2k1WPA2D=Ta}={|=x)vM2IHjG6*ZugZvy1{+u zd#bj7>bT+U+twI;OD^@NZC|m@7@vMhCCe-<9NO4;Wje&;f$;A@x8JM_lN}->1G~t` zj*5x3clf87(mi?d1kKFxv^nWM&j-QgJ%*Q+l!n%p=ev{xlRGP}-n?0}I$flULMMxnGdnvQlYJ0)1h=oJF;+%m;fI0Ca*eJrE*M}{ zyBRw_e~`B~iTSPPVPV!wGkR-lYk&81sNKjLPCfdMC%C@$rPMkRLt*x!4wRLdsTZ}Ae85wg2M*{eDk(P`%pJf

    xHYZ}U|3rlJ~PavySy8qV706;=mGm36fRDut3T<}r;r~%ZqypjD|)TA z2FPf)RW~#o7Z;CEO;&6A^~F_?9;4TA%D!(|wj%O>+Yy|oGlBuW;Rt2$!o$|qIz?k>++`;I+W@&CDWN~;6@ES;N z#@k-!?Aa~A0uvosfdDVUW(y;^G|U>qNp&+w-KSr-RSzr=lvunJ6eL-(Fpq zZ2PvsxKw`?2n3AOBlXr!QqoNHG#$5PBQ*AtCr`fD{N$Gy;}-G$J$GM!Kk;1$k1Gve zoMN>L7}wJBXk}X%9Q)K%wKUuBxI7r9k#(N|cQGGL8VpWF^%%Mbx_-vJ4&lDOz8ghu z>#81Q-Q7%yE!ABG&IKJtJp*88s5U=;{}WtXtfHbnR_(#Oez`7o=C6z;cWrzD7@GZI z5V$v~#QgVqd0Cm3-3hH0A$G-x^QNYw)LgOqS>=`{UZ1g_qQ}f*2iPeqJC7^(PqiTn zFR%HJ(%Y{(WUo?@qw}8;rX{b9mRwDsG2^496*MMC{pN41jMIzT_~K247@-G_9PzIi z>??k-{qYfz0I&mq_N@dv0=jkSKhk{*7zNz|J^5X7vH>O&t^?k4NkM_^dctL9F|osd zlt;|-AE2%=7eQe89On$DtABKK)SyB!vDoap91|A{_PWGP;VxGPAGZb*5D*kha}ew9 zpS3^*9XN0R2Uq**pd>rH_tU48-_vxJ!AAve*AMiS`{M*6aW_C0^SfIduoiE8G}(`F zgx54RH612DwV9Zi4K&F!9b;wnn3=K2$jJEdL2oG@Z z`zst}Douo*g@nl7ycxO3*$^Knb@d%G?%|nxShpttgEXd zJ{HrfCX|MX>imTZyONTuDjDnnXz&f$T0DTdvMMT6@p4c4KDFueVkSh19Flfn z8A{r=gU6C%%I;8s62>O-fDAwG5Ey`h0B*8 zXHooVPFT0#;1sX+YMD+q?Kt-wgD^3-Wpv=t@#v?^wdEhIMCkz+oX&K1JB^>*nDIF? zGgre)q4CL;E9$g;K$wn`X(NrQt@bcZH7++dVs38k9Q7684+4XJ4`!ILqYGp>9opbJ zW!>d3o||gjSGEhM-+7`s8u8aUOpc_2Yqj>pX>L1+obcfl#bLv6V zvdqVilP0%1Y3R0muw^;2S3v9COcZe8Ou@8kAf|qgRnAhI`?@;8UxE)ds55-puFFf1 z1VI0{Nnf#A#NX<&o;uaK`e~Q0!o`a$Cr*qejlT>D8Elzk3kjHUoB_ia`t0gX;_<+7 zeu&?0;zj_|86~hRTx#^NpwQ5v^@W0>qRrb2w0vKqTBOM>4@#>(dh+DWt5?O&;SN1t z>xzpS1p15l_8pLtCU;%(Et>7R|K`@(`zPbfp4qlqt_1>e8I&b7RBI*8w6WhV$rC?> z$5T>;d@ZB>Q|g@NNoZU(M|GshW-kz`*#U8B-;OXFwmr-)XXV=@vPmoc3T<`tQ3=hi zFKvA;&0i$zQ1%JG;lL-5b@RP=HFhIbOMpgt zDX!$|BU;I*WexAx6dUh5I=?Sz-L3(+4xeRjY9)1=?=Ire3xH6i! z4E0IOWJOQ0#bGG?_KQ<@525$JPD~^XthBC?JM%s@b>qk36KQt(z=B1?1m4AlU=Mv^4&c2M{TL;pbhD?F@s;eN*}KN*MMz z2uw=-Cgm@(%$T$dAWC%L_ zqRv=Iu^;1`7$G2ZXx&?~Eqb>ltEE6|O1J2hM$dx+MD(9;-@ftbmF$4rBrvk$w}S*h z^5WuH^bHJvOe%XVEJhlcS5{UCndRikC(tF)XD(d2)L(yky39}jGrBrj(wU5$oL*%R zZDVes!+bj*R3%J!QH!1}_p>4)P+q%s?S+^%`*Ee0Jw0DMpdUSZ@xnKb1B9Wcbosme z)UOL*&u9+$6NZZp63_sGLHPmt!%y7}wXYY@ZX;Se-hC942M{O>(+AhICdYc101QC1 zppIyu>+9=4Mnm;c{ur`oylL+1$>4!YC^2zGofo((1DPuOXTjPAqVm>$kG5cjK}Tg|#|N1dGODVAaTtAleu9dM zijtbzE5VeChUWT}D`$c42=O8}H51|&x-fJo0_wKDImZO z)kWfZ5{h#0aph~;+I2D%lEk__!NZe~ob2DG>mEg@koSLyii?ZqhqM!H%eW!B?%TWT z8bxlBxKs3J++IS7nkw3K!!@eMY$pUZLgj-Zzz#%=U*5TM2di}A=O_PPzqAgW)GWK3 zRv@RW-0RXG-~9ae=TA*2L#Ia;?SEO467T;)%EZRP@{s5(fS-&`Bh2+N zXQrdZa^ zOG)QYFg5d#_U7h?n6ZOly6mS;h2-Z;fc>R8Xhn-zdykBmOck!)-@RuK?_K?^Kyg@_ z5C*FpuE)eRZYAo5pP!#-9G^c61O38>ftEo@NB0$K4eEYC^~?owlI=-JI>K+pi~F~4 z*G*3P`f%qSG75_0xIMT!PR`C^PQN+O+a@~mX!1JY^pw8xc>DUb=jrME;D7$vGHp|w zn;IM%S^>Jw7k(K_(*p{r-Q-VMC^)#v=zs53!z7&h$kIri`_U%M{ z)M=lZ!IJLon9-KRgLHHk_4Qd&Qc~1i&4J6%vaVo(5Tp)*8;HHf=c|y3q45CVWID|1 zqdV|)NRfDKjc@YBH3FL>dTVjGwH=|DYL69hUr6C3LLDYJPe9s~7qZ}N*LNoT+C}3Qxl49kxS$WD-D_%Ua`yIFA{K;_3$@nqPpi7AiOH>B z*_PJ^UFagznIxCezFCUQ%onpQ$h5xCv(dnzrr&rWV$KLT3jmcSvt453s8wEPm$E~0 z>qS6YK+Z0{BTLU?B%OsZH$YXZ4dW6Lyt($8Ok0T3o^g{vzuAKFbO?LL;b-3|HPzBG zXqA4yQ{1@zvr125@9XGnBgQ_~k*2~kKIxRbfL;<&_Bb#)*v2})5Hm6QS&e|)^bZDVJ5@glh* zZ^j~zb&R`m<)mH9&!2vk{k>rIY{!qsCvoPcwtW9Chb9m1<{>>EuWkmR&``kDY&fO$ z&M{HZ7m$V=6BQ@N#^`>(1m{6P`{Vt<+b=~e^&2mk2+>_oR0LAo=3g$YtV{ti3d(t1 zU0ojb8GiVfm9?D`ON{b9-)} zTH?>Q>kthcxqOzE90=_cC$>hC-1-&910+$#Z=Sg}0qG~MC(JZo;@yAb$ha$oab`Fg z$o%gXWz4sL)bTDZ>+RzqXiT~rb2J2p!d2r^PtBj$k!N$}7o3XxxvyOL>kA!am6h{e zye?nz(xiA?F0%(OE}~7M%epP*b?;|jPy`>ekF2by5WqkJZQD{+RYf2XLZ~?|A+eT` z)Mz4+mMSVLxBhwtgnYO*s_d>0QHK!Y&D!rU(p>&e7C-=Z z805Iei$-V0?Zv>r036?9Lauh-Ty%HN>UcA)*dhd@XDmf}tG>Se$jHbHqq%y=q3s>9 zm>F%`N1l%_cl!j;zs#a(kLk+|ppgy9tM#Ik+D#vS*n=hW z92v9AxvP(AtIA(pd;JZE-gUFvu7=Lrd*)L$BnP%?TFxlcCeqC8pecE5mQRc9||a}wm_+zIloBXl;-eO3}2zv17;3!U&86C4n&dV5k>E>;(j`Ng(@f%Yb>RtK0qR zojqwn=j8PCq8gP_;=#PmfX@4sXgM)wX6Uxh`v7K~b04gD{eUJHj!rJ-#Hv+@L^=<;}+M4N@k zUBzLOnncK{sT~Rl2>~aqSUn91KPH9=C{rJ^Q$<}pI4H=nyeH}Xd&BjWKd6qDA3v%f z&>4Zww1e@2lNI~4w0GIIX%iX&oJm2WuT+r2L@{r5*T>Xzz!nH6W^;~rvjjCsh}wtz zccOa1N-7}_5sF$cyP|$moGf(lEf$!?g`4Zs#K%DBQ@C=4GOcJs9FiG6Q zCNYpmv>6cj7v}D(9dJL1bqelz`dVBa9)*Q=ArjO9mWAwiWDs?LIg$O!ahG3TwS)l*xi6-Gcq|z*kYJ%z$emn zcEa#^2=Ki*@4i6+g^uvb5)#;{Wc8+g<@~~h469R=yz`9%6P8ff@Kyr=?Z-@KqT}TQ zGn^L*UQ+X5JE_C(Ci(Om+#o@2H{>?%DOm+pmLkp&E_{y;&NdLH076P+`CR6&!fyTFRJ(1^cJ##4nq^>GG!aC39RP;+&aMCsw?(*9Fh zdl|YP*{)q*fG}jQe$Y(u*wASa0+S7wa1?;QdH66v`alG_{$OhRmkYdI<|v!IuOO3{ zni>Gn>cF8x-fNLT-fIJ8j1JW931L=Cfms>}nFoRuQ zgR=#07A|14`@w?;9-&;ltWtN$u}IE*7kUCgwd-5{H>9G3BN6HHTx|KJpH6w;fHdZL zY-}v9mCko^;*5jHwe;1fyfC*_RNr7eL7IaU3`Lw!p9>46z$ic@j{#s{u|R&vS7cXJ zJq%M4`~d3$Hjtp;LB*C4n2CVu(CZ97X=1&x9zEKVQ4B+nnT2JWs_LW1j}6Ow_apMN zABzDBBE~=DVnn3)XvrbL2*61wDJg+%Ly&oB#*nv7@7!TIcC0+_iHwX45*j7OnR=P+ zzKyAy5$2%K5R=O=_R;1OXi z?Msn@_V@Yo=iVHrKN@Ej#tJF=rR*3XZ6PuqS2Pxv7E! zf2SNFzy0@=`#S<5s{QX@_EoHF|01#c&{SH!Z*SmrdH%Ei6kSyM_<$q%@$2KBp7oSX z4_fXqy16!2SMx-XQA?{>j96UV9=TgrNhDpO&hx?;KQo)n{qI3?6qhJYzo2Vx0=qrky5LetnK}WN?LlDq+0k(#hvgZb+TPiN8Z`|68CWuI#lK=$~ZTE zyYr>>82RPmXeM;N*Xu0@xh1DkMxUMdKc8hx66@oaB%+4wvEsd@BKVJ zEti^{JYWB6)J8l9iUMzB^>s4KNS}cA*H4+=W=uYetfds>l8gNDbE4NFFyP$Qy?h)q z>nf~n>|VKyPqs^nZ;H&Wl(JxeEzb|nx2Cy;pl3nN|NJ==ahYHDML?4ULeeO+oaZ@f zlRmI?6^C+)+!jC8?4kO-_QTwt^Q^&jPnq6lIK_nB+c7BMhX0NLlY;j^p0iTmfZj8hSevPK-%e*$ag^l#>+9JatsSl-D z3mqD&Q*m)OOXFqot`A2!iEQeO^rY(_=Y7JT$yd#xOHL)t85J2!_kM2d@}9J4yV*Vs znr>dphonL~r4@JElW<6gl$Vs850AKn1;PA`>ebx(i9?JN-$tfd8h^cdd+RgDV=}c} z0vB9~#muL$M~(T${bM+av-LLr2Uq@E`Th$q|1Z!<{4(Z11ys+MN{NrDxjkuxK?xCS zjZNaLfY?F2`sz4dMS{%T-JOt5R78>dv@@+&hzX!9N&L^J6OfknK$cDRwX6jqq?Rh9s&de*LG`Zmy6+b#_!d*gd_I?P?M8* zDE}SD2&2U(#kkCJLst9nfe&(zeP_)WWc1Y3egA+eW75Nng46_U7(#WJbG_$`Z16?Z z&Bd5B8i+9?qod3`JW~3+tUq5;jR$! zA$}{y;yDw0vpMMf_{yp7TTX%^ah!fu?cK>qez+1y5fQpQq+qe#o-;5EgHk7@b0DYjUM(i5F0&!--+^IcL>Tz2-7fkz@V zPwhOrAed}cR&%zzTu6fBbgo~!Rsu~8b!h}HP2ee*uY&edT9{;m9F!$K_bIc^7l6b?_cxx^P6s3@RS?!YoJB z&=boUWEV(@$ZSG+L1e&?7k?{g^W`AuJQN(*08GD6AI{Xkvei4r%vyHagS_vU=aQKk zVLrXAyf9zxPq;`-ji(~GC^ECyuYkDu_9l)p-br=}Kjr!!^bJ39kYr4~eA3#$4gOa` z6C1Dpsh#hw9|mSg7Ey6haV>VnMZ}LZj`1zd>5msLYBvXQV;v2d2&o=w`Zf^+dP#mp z_7tC0h0L0JCV6u{kpcg?Z1J9(C{^VPpq~G!UCZkv6}5!rMSey*ez|s6lJg4xXWQRB z!{#|}#tA738q)o&BdV(Dnkk-MUeFhfZtt>;oDp5MG!?J#>$8#@x}1HC3ryu4d<97R zi{1fo9LNK(u(F1WTC$9ej%H+eWcieW!dEmRuFjOR^&PA>j`uS=^t7TjKSy6 zpMOAj4Dwy|>kGlCR^p8e(#P4Po;82wN!9c2W=^pXzW)oUy-lNe&2}?kKz5*^A*ctp zu3qiy-?!qYRfNNd}Lmfzl5|!WcABt7X2 z7dQrNUH~Qpn}$6PZMy5uC9>JWyBIJTpc8R$a>nK5tsacj2jKX)99GZ0kdZ(OmOl0 zny4@P`8Am?HZ~6@J3~{7Q8kVa&~CZXL`|O|iG3LgSE(LK-}|_jcQwlH$(R(w30u7Q zLVSmJMYCh zoh7`B*@aqsIBK`>VRbpOUe=1=7Tc8)9~9NyBQ}8?5kJKGHkS=;M!t@#eTVe_2enH{ z+F0b-4jdtx*^U6E+mVsa82^0tMy-L*`T6<1egFP-UfvO;_&~4G$&U*NTr)PK?ECzR zlGg0joouUdY3z%+c=00P2SP7L1PHzff;&VKxHVZlI3%PTZT`ZQD^Di`nWZ|zJ?^OR z(PrDmS`Sq1N7mUDN(Fco)-v3sCZWAPrRPv0m;rAvUNBB-YAL0EvERQSKL;==26q)E z#%%=Nh>Y;?=WD9ek#!Xab2-j@AvHBM)kBU`MD^+}W~tIxw8XN(su{7m zKHS<0bql7g&SD05d3&MjX{E&L3m++CS{p|(+~MzF@EZt(OU*6=zMy`QeRk`t*$v+d z?lbKZIcN|Fy~Gt1F!;#Gc^&Hep1qTy8kR4H3W7kXqsyps==5*{oPZa`#|{3BwXXG_ z* z?CcqcY7;1_e3;lG1W%l>D%{+pzV0QzXOh{Drd_F@PT1s#Rzs99+*n&iiMFpw|ME-t zo^s=DxxOBRHG-)kKrU!CpimLOa0x6O8We;8EX6H1&>H|kGZG=Y<}CNX9(#rR1-S;J zWTc>15C$r2LOvWB`1yY(dT?~K67~m0Bsi|xtEiTn2wg1lheBK0uUQz*?U^m9aN>VZ zasM+@iCX&~(Ka#s|DS(Z_K9|`dcj6}vHsf$&!ykjsCLR;-6)iSX^nVJqmbgl3(Bl> zBUoFH<@%p`H#`+iW3%wgc7;*aG08X=TJzL9U`GIDmhQ}&w$Fc&ZQ>XRy_q;b-$;EM zQF@eY`vGZRJuH1R>9-(zE+*#tVU6h>lY}jgsw4zy$YyzVlX1uvt>4oOj*78EvRU`|rq3o)zuJZNk2Wfb9UVr@fyeVG(k~}0xnfWA0r6>|DE&8M+ zq}R$2XD8{D>qayU+y$QnXSb!RO9{gjVoE$R74Y5nF-cv%af23;HMGoV;3L3Nn3FIb zUCd-(kBdWbgl__?_(_5(dQD?Xiwx{%s91Bh+lZ6LX}}yepD(|~_w?B_D~&7I5QWzI z`t^>#vFbDEJKzi$a|hWk6G0Jl-^L$5e35K7&fHH)X;DY*SFZo%epb9`guN@g-p8Hy z!&uMzcDzB~7<+ZD2>bd5t$+l1ou`WV-a_n#77QI2nW6Z+Jm)LNtl~ZPnnnyUJZmx& z7Bp>U18m;sBeP=TNjqj;gl+?3A48GAh#VonB`D_>z#>q%P%bqZ%mM%@)VhL#!cie1 zs`w^{a$Cl1I=hs3+FUIjbP9Rcx&Ly&!9K7b5coq(+LLzMJ|B;N>23TZq@-BNt{ac0 z5P_7FBbhGRlwuFw1#Wu(K96wZy(=%sRhWqsEcgU^2}m5|A^wXm-fSE8iME`gYnU*!*6<{pBi)^WN|n8J zM2FBd3Q#is@-X(Ej`qDV8s@S@5f1N})+?+T-aIydVn;;oKX2UMC%h+$NZ1@Xa@tVw zJ@Xz0Y}t^ZkYsbdKsKBHTKQkTR`$swcSoug3E~n|X2tz6n|J`NxGnafTlU;GxEe5rPW*@VQ_c;pa#l)03ZnyXh;$9TKvk_mh=`%AX-f3=< zyN#nc5nnUbe3NE_amwdoxX{X}!>KUru3>)<@+2_UuuB1Zw;FUY=@B|{pg5a}=>w#RY z)Vpr?LH3E=1DN{Im9A@Q`n8O*LC(Q;1K>%_7ldDEJ-(cUWrD~zL6wGweaiJ?;g!II z`uX!G+1|Z`QV+We`Z?ec=5Zr;jB zV*FiEr4Fk>lnOh$`0jq^#<{{Q>VuDlDYg~%7Q`kbxDh@IgZ@SY17R}aU;tK-(?5Iu z{0^i-h%q=YV2B(R2NWvApb?9bdrbQPNj_|xfueyiggxt7Y=d637aJe^N@WrZ7d`qodcJE2^_c)@{G}lGNNm;hyw|z3j9W6~dn@qjRjM zNB7vVml{YPrU;l{RXEh&A1(-`G-h+SwqQ6^jxt#-z2OUkZ_r56^0wR@gU**9GKv~%i?2=mleO;n~NTn0|A#QNX zu>^6{G1b-AXCQQHY;4R|VUiZYTY%Nk>}kpGtJQY0PFwTttVEj1F6#3B*$#nniw@~Z z+smBgEI!JWGPcfx`?9 z5F?UY*u25>XHaw>6n?FIhai9_m+s2N^=ll|>O$P9i<;^n}h>pt2Jc9B}IH_g^bND>o^!?y=-RoZmykV*yfz;OmR< za6N?ak%8kA7r$t3&I>t^<-nt-7vFsNK1YN}MMV|34-v^fz&1i!VhMT$x)&SE48T(h z%MB@`HwbP%dbA6lc5z;uWzUm-(KpLA_=|*u1cZ@?apFkyd&4KT>M7oeyRRTbX9T|x z7xK@Z&EAyX=QA=9ZyR5g!gh(m!J>_JvxS5yCU0gvTK$xA+sUK9(BccIDpL-}_S+=~?!q_d((vzS+HU zi14`0%pSM5Ys58i;7-82uJ!jnZlW(^xK&Ledbp{%*{ZclU+T)Pqb#k-6)K~?3ZDjU zZ+GJLOtLxudbdnrW3xNw(P$f0{KB?ERriv!(Dx%=0lq5C*7QEIwmcQB=QDg&tY4fu znAE&Kh;vq^i{bO%cTxv@dMT>i^WyLCy<|A4dzH0`_kAfgtcq3Mre#1a&LXg=#-<&i z=0w|Oq9lJlsZ{fW%94<8Uz`R@#+8S0b76OqZj9_WR}858@v&#swmnhzIz9&}B@XM3 z&>A@OJv#oI^HJ5_cij8;%5`^$g- z>5xKso5JVs_0@L_SgyHRr_-6p${Z)#=F)wfT&6&iWzSIyiH6eod_NLpSs(S?^g&*w znwMg)yj`k#6Bip_ykEoNi(s0J{NGRgAHU<@2S6N-#;aKC!SLh|iQrH9X?>&1AyTrs z=kG_`=S4f<&OoCeb|s<_1qRL&kCl*Tbo9hSUM}fjM{pTzvD(k~I*4#z(Q=)+iHXV4 zD5XrOdx!-EG_S9&u0E>lx3RG?VK^G(MKAtc^Wo9#iB;*?EoY@-67et%GvQ0voJwqr z)I*x;DCn{gwwiNmWO$~h^Ru(FLtyJ6Hi^J*Pby@n?8WAvL2Ufiv$1*p?_KPW_!>Ye zu=7}1f$hL$MMX;(eEp8m3%i57#Pf4?-Vi!It@ppJepCxBE$z|Z>JLtT?jSaIn1Nxi zMfuENW30^UYwwPz$(Qd=Kp7&X@9pi)S3zv|om-X&4h}{-(BN?sBlDc?1F+TJm6jU}bnz6k;P#1tBT$OG>7QF$H-^ zhN_!$aZ;0%ewNbc>`(^?#|YFTteVFdIOrwJi0 zIfp(dP5O<&W9H!M=ScyV*=6qCJDEBzz{f}Il=uobjy;P{1A=8c@QBjCoY9efq>kF{p(D*bo4 z{NozanP=ZUcU**MHa^$NZ9^C;PwWjzZDaIi{{HjYP5v9HFa1xXj%(&Od|EjW#LIrj zzjMxqn|Q`RaP_UnIR)0@0sUI@XRHerxVHB@9V{9o-KLYqyFW)#OPgN#{E#O9?f0J? z5V%|_OzYVnBqn(JbPeX?n_g9xptnoZYv<2@m5sl8;L(l>+9)F)mew~|?f+vb7RKe9 z2`95YlQmQtv=Yl`=T05(qu#yKrMpcH9~-Q|_BJi8h)O(U;zpR`evKQPr?zqG{)yF( zJ-A~lk{T^er&#uI+n^5Q#G_Jg#8B^HFd)anDLi$K?ayHjIrg8r{!h`zFo_!&82miI za;=~-{Kbn#Q%aU=oxLFeY=?r9yp#mOzlBl;jd=mB1b_DQRpGS}`gkBH$vfb`o5u3B z*#nQx%bq4Ly+TUo$GxRIWO8yF!*ZqA?HM<#ty!MM%(6R(6$j*I9ml{@H zP_=t9ARYS(-Qno_Pdm>2GwhhfC@ws(*0Y*#C-ipWCD4V_oAZZm%&5dY{qSADt&h## zwlnRK;r@5^S5CHfeQlCqI)SE3_pZM=H^YC@J_nuU-Pf~wFTXUh?@Vk&dp)moIn1#3}oM#jh);1=<8~3hOYKu8N>T^{;bag!^shOzHMRg6api5 z@tlT%O-53wK~*P$ zl9VxwRTIzA3P6tqSoS^7@^@1(PVjY$DYi)9q$(`w8J#EiB^p@6YG#S2KA?pVKs)vd z#~-7VlPOIu1ztYa-cfBK>>CpkVuKJ+WUIRFpnA~?HCV`y&zZH@1@a&-;E4X`(j}Jk zF5SF!Y0n#$Z`=)k`Er1GumnsSHo&PCZ0zuMrCH^cqQH_Q8a4K_5Q!_K(QpM5(T}HH zr=5w6?oY1shvsHD5BX!t63Ml8vrsGyV*dy1;}`f?gwifwzWl+wTS_4Ovgw6yeJ-^j zTh*>#ml2LE*SsXbNZfdwiqzUnX|pTNo!DPQJWqm_z^$~UWo0|S)B?Uc9@6j)QNq%g z?JK7>_yJX$cxn-mf36_{xrlP}Rm@=G%po-KtYMhg8l;h~zcWyBftI+5|Awfd0YrB` zN7<9hgI!a7Z^Cp?j5GL<$l)0GXyQRZm@3zGbt%r`Om#O`+KA84qs_p>m567R5ZEtC zH5u5iHJVvKJw6hA%5Y`>9=&d*&@lM zO3lS(cDQib8V^Xqww*7;Cb4FNnwZ2j-JrDG8{FRfL!m2#xfFhx#GrPPK_{dr-M_M3 zYuUIcqd-){#i%3X46mE68#zTWv2BM)4&%HX+WEx>T{$hzAL^MIv-A3eXs{#p4B6z+ z@Ap#r^H4tIan)q}K`B`R%Ip!f(UH{XiA(GC_EkAy^S!miPzNu!_EI0nQuV5sT{q1!)#16hT`@u1CN!z_%wrm+m8pFX`&Q6zxF|j!{KGRHvu7!g z)iGM@pwV@IW~P!%S4XKep#gJ|@~WLP4b$3V)+Rsa`qCwqdzwq!_-Pmmxk^|kL!RtX z6=#{RmF=FJmY=ubwp#Yz%O4QnMK5}==Vr~96aGxw4he|W%rNl|TJhXAHM3YAud{ra zUCOn6=Ne_k&YC?fp}1^24)F|~+!Us76g+(328U;8x3b0@vzf`Mwa!pcHf^=fbQ=j= z^&%}HX_wHTYXvJF7*fe4oV2azWooPR&9&gq-e^5Y|El8b-CxZ6E%}Vj+*BK%>vE`C zK3vn3{`3*$p*;15K{;)c0P%0xSIl1>8@e@g>#KT5K$XPxaj~^QSAM#^5`VsPZ`uec z7eC=WX!KzF?d==APY!pTVY1Nm{~~(pB%7qkTg$@{WR8~E#UB1zaWV7tF*W|B*{^oC z$_};8aGEc#hi`MYGk@;hPB$$5eXd((=i~lcoek1@?EkD(-43ZQH7)HnSDaCg+P^XX zT6rokBxoz6uKrS6pexU1FSGl?S%1Xs0r!WFkap!z&IHFhZrDp0{>l=nWYFy2cXwPX zT1=y7GH6>v%$^_3`WKmn1n!DG+vQ*?ddsNf%SpHMlf{xxypuf+Ao4_@PpHFrtsmd?`5gVv+;x1< zTC$?%F@E;Rc2k;!gp*N!c-%8s{#OA10Ra9&L)5_qIuj0eHyc*hIBZyKXpXtulYov! zmx$|dPXtyp8WP^6kV-nRBI(jv>sG8HI(+yrv$M0fuB(Y;k@;wG++p#Zk*nI9;|>pB zqkV0gfi`nhdwbHQJ$b2^*zmQ_-tOtzKlWi@$MVbj+mxQ$SWsyrB2}f(5fO!sh$wVK zL@Ec0{CF;iF1NM5@Yl97mOoh|GC~qd6TBse15~LdS6& z!Z6fU5s^6$p3?B@^y$;uA|g_8V`F3Fa=Fs?E`aO096EG}GiT1I;t`RG22O8pFEcYU zrFk8%cq5a^@bb$qlSm{=!>u3)u-0O&B@DyTZQ!zSDsL}$y|#QlPb3og!oRrh`*@zG zr8neDtGM4sqtP$?nBx8C@`Il1^?j}O^2y-h?eZ_zbxZ#iZ|8D3a=F}P{dwh2kk99t zot-5N!&2xJZx@57JbbPUo9o>!ZwtflrZ;|}P{3MCEEdz=D_=^!?-K;UO^1##=BEBY zS1NqUzrXVKwZf)6aEvjfw(_49`|%vd!CH&!x`bhPRcGaaQEV?(IiD9i*Q$DQxg3#5 z| zFz+TK0NxS#i^%qizDWD2Q|t_!m1+r+=#i#)`68TlzfuXn3J26 zk(%LNowm3{Hu1%}nZv1&9{WOmQ(8h4f8)OeksNpYwb@cq*@i^oI!OE>b9^goj~6Ly zrDVk@`Zn&}%y>rJgT4qa?X?wCvK6r~Gt)D-C5c$;Y1``Q?sqV66!iFd_1UiLqK8C|ojP>#!&}d!km&kReN|&u zMUMDHKjoa^=B95{+4k1kQhfwpvh6*(Zg~y#272Uamh2^#Xb{d*{cYS;gvxnnf%rhoG>#GBNjhLdN>ckbCU)#*uXJ^YhrH@Ak2 zmR9GkrQx4HmD*kB)K@3`+%3oQ=HgCCdhXo0b2|e=MRU4|QDYM0!#&(zN=va zV9SuRk&)4Izwny&Xls_PzJA;*zB&Knp_MKZpZAoMl)Trkq!+LsmYf@HE3d5VeH)~4 zv+rAQnBc9+kobhJ_~hhk*QHrUXJ;J) z1Bc4G#Hgr)w{PEeymhN4P9a`DtL^^%`?-sQ@z@wO+otYPzf{MGn_b=AGkV6XvL{;8 zZ2B+vRfS8(Tn?Yd4M`~}(UE)JPDwct;l6q+e{qn*JbQqRP3DEAtU?1W`LuP3xuS;A$CjWMFUk-sW*S>JZ`S_)@&c~=*YL2+<;#!L9keG_ z<~nrvLbHaFazgZy51%_1Q69kFJv^*7ma=QtE>4Zy7d255-6JEeuG6eCY_#z=61z(7 z)5=5$2Dj%qtCpT=Yi*@FsZTjOJBy988LW$S^0wc7Or->YmRNkMB8a>HTky#}d4p+` z3t?wjudAtj3p-=8iHeGilk<^7higzuN{aMgphbIb@b~XmBHR{@9z1w3KbkX!t=^`x z_JzTsCx2;leP@59`$zld+;6poroNrLd+*+_u`xMYTiy=WImT0`PVot49#j2z+-=$M z&Ye4(wr=eXHi*dT+>(%)`Jz$7+1JNslZo=?&6{7yunnCss0ur9;K1$QzYUjHR_^=w zbhbHkqz?$`85o?j>^y(-=FP)gTp#lzLP8!C6bRSG$RU|NWw++dwM!ct9}QrWWjTD< zr>so!MuO7qa@`cFyvmSMTRm_md)(dKugS{~B?Nd4mo5}XAdO@~`Fxj`m$%MF<&VU_ zd#ANHJ+SWx!rB3Jnf^$3&C3_kjD+QrsvO-1D~mJTkI#;FIutB1N=Zrav6nuKO-RU1 z5U4CK*G3p|YUYRCUQaPuxSrawr;KKGzEn5`c|ByU>9%l)T5z0n)_(Yw2Rw)VXH;n%d@vhFM-i*T8g%3q$8{-%EtIpNTL%iE|n z+6teG%)cKHK>Ojt2f@YPQ9F0-Qg3iW*?H*A(2*qsXO*Ok@($|Y7o8@`t<3iG^5(GZ{N})3L_l5Ha8?|x;J$s zAC!1J-V-2)kFqYZs;;Wi|8#dVB?I3heB#u2F?B#>q%E~&9Uzr@q=&KQvBeE^W zA>hdqtD2cg{gX?xBRVy&#Yocwwf(Q#zD?z{W@Tjw&eX~0PJCeyTw9)6UKw+z&>Cq@ zM@u3CAdpk}Vkn=P(nr(1$kqhqRIgHI_prLU>qEPm2J7PyLfSvxNF}Id5Z&b5+Omy+ z-CzlRX1DoQvaX35c_A-e(D9kKmf=m@@n)IX*(cmrU2Y_*d`7t@_W4~> z65FX$yA%}_qh*3WjsGep``wbkjbuk(D`-fePhmcE=+L)FVP+3%;bs53cQ+94t>7+@ zv9jV>Tb(N~OL{caa*l$6Vl=CtX*7SyoOH+2bD=_W_1e(=a_d)96Q#^3g-3?Muf!`R z_o2cjr?&Bn+S#3=Ws}h=eawi!-*@)xS)%rU7w@a$Ulc!A|O6waM= z|5tR!k4iz;MP3Ge^9|ToaHj1$Qch3r5Czq~{Tv(*QGl~;2iAGi7_tfogkZ<^ zQBzanPJU+Du%mOdx-E?~1Uyep=Jpeu+*IsMozXBBD<5MzSn>JuJ*)07zXk`LYF>TH zo?lp?!9^8m6>1$<>bK#{IH7Tg!Klnl*T5hkFz^cy$UuGk9uk^si+PT3zUz`o|8sih zB~HaxOJsZHM^gq)Q!Ee{|^h$h@H z$0Tk8&uJ!QVYtIZ0l25g zVsBO08IhYeS)>CwwE?B@y_)t5mA&(kXE(d@1eOnEe|q_mNy=u+0~Fs*$u__QOgSFJ3eb@5!%A2@$!XD2JccdCn)3RUgspRiSuw!Zb{COkV`Qv?Otcx}@O>~zgUa0#XZ)IR$Fr?I*<&egE zT|bNI^`ZxvI+4V)JCR6w{%f)FUfIlMjd>%bw<>aJf zXJ-%PD#*)Q*{HruV8vTg1oF1BwvJ0Tmt~{fyLYc?Q_8)sUoV+-hHGk~=>ku84h$@W zeyMkl&u%WuT0%uvGYG$~pkTE;Z-xHt85*h@V5)<#08;JhEbiMyKVPPRbJtq*? zcT-2_{(}cBCr(VJM5=WT54R5c6?`VL1<{g6Tj)_a_a;jq!{KME+f4iK1KN8jEb8NS zp|AJFM7pKqE24iGM7oNjd?7VU+uBy{%rG)C0y@3S&JM{0y)K_aG`h^xEB*YI~%llE5>hFS{}#xFHcr*H}n!^ zl<3O&Ypc#-;o(FLKw(3{`XZovMM1yxCX{c}FEW~_2K@W^S)PLCN;*U@nPg5E7zNi7d?w+v~_h`#C3woqRNewCKH zO{Qod&l6QMejubfc!jS-qUh1=+xHEKz3IKSr=2E%t!dxpu&}V-tyw2GZrs?=h?aQP zVKnVb{!D!Uq8GTh!RasuNBkUJonpp$Pd|5g<1&kNoPM1?etyaPKFJMZTj>R^S&i;1 z`8?{B&E*wJBFY4+?E=9ffPR7du%-dw;gtimF@TN-*w~`0W+Wc(lLAyFdit?rz6|D> zw?(|EtUxNiJlZQ#;I`tC+C2hfrc>;-?GgQ{EvUqkQ&VPNhL8CkL2=&2apSmg-QnEa z+=Slxm^W|k&I~nn4-S4sD7zFbC90%%^@Umx6v?7sB``@nhfm7E7MvnMCH(=>tP2az zALZcSrb{&gH3eGkij9f5`RxVI26FOi>guvUqX>kC4~sMh#os&47^CBV0_91tQ$>&w z4Hg?bffz2LU?x&At)UUgcgC`ll+kLv^`Q78Qq|+D)O5!^GCJHsr2^P$vTO`4UAmNT z+v<#f!|0>DJRxS+X&Tj!rrM}f4O0C!r*wrW$=CY{ubu|7Jq*tGQZ^hlH-v)8@#WS2 ztyNWa3dz zD41RR2{-|6nd-jgHapxT0-)&Q>r1fLxVegfuC7mOD;;Y*HhnUk!!HU#Lm2>BbLYDr zHe8yTp6+Qfh}7#WyuEYxZtMARZ6@bo9yZw!+Y7f+4XX7gd&-{$2BzR++2kS!hBesc z2SoP4dCnX$b2~ZNB2m2rUrg*I=pompm@g-srVUqtWx9KNKZD89a; zB=Vdq0nY$ci7IBNF~O%ROj?{9tA2V&I&+~f6as_(+Um+9I-WbIJ1i_LA=4H0*qhy4 zYLXe|Z9j7yxY7KBG~Ea?8QLrEOaB>JX64|J!PLRu?ih7Jv9PMDN^oVm7GHhuq(Rkt ztQhfIxGB4$E2<(xA77QO8PBnDItZK+SFSv;AN7RW$$3~|@0OGN)9l48)>5Ds`l(HQ z%9nv{IuWOD#2)T;884zpv*^&+rQnSYw$gVt0Qp0|lNBFOrP&wm(Ebas3i5$wJ=49_ zh`W-sxRE|z{`U9hR^LWPd-e3_rlh70wCCki9{XYa3SeD>SJXJCW&GOLOnR^?mzox} z>anXJC5nDWWLn8@I&&Vtc|y`JghCe+rcQX<(pNd_0FI z`L?cd_Q>2$hf~)wnj0OIc8`nz-At=}_3ub+I>e+rF$Vy013 z^F#GnVEzjiF5oM=J3GH0)*~k;e;O8MDx@ql-|6`+{WoR0yWw!x@68YWPsYSdEP&#oaFeqpWO3?P*yTzfMJ=}ea z=$&?>CI~k|@%W1%(^L)CZG(+anqwCd3g^-J(m6I(JwMTHmeg6z_yE_S+mxagcFwiA z@8rV=4;*Kk3=-{v{Qb4^LLNSR*!_8rX4(;*-mlMJRQ3rB9Xfk9v;%~%;lMG3#e-cO zbON@AvA{ARy!TOF>(9ERLFq#sf}o|3_6ofCrs|cSFjO9wm6=A{vGz8ndzwhrkW(gf zkazIbWn>Rg!}v<4Z&NU7{rYqdYL`F_3Q+Khqg^9} zZLhb~FM$lv3A+gr*B&mAUR5oiC?&Ny$6<_EdICkE<|nlGGVa^=yD^!$`-`tmTeckl z=D={AeY3SH&USw+{Eg$vazM;lv07A8wxd5MhZtlz`SeMR?lM5>)ZV0#WyX#pw`nlq%_P zeX#Dm1ZlkO-AH{?Q!u1O&~g2|m-7%21{x9>2=aqwm^=PyE1|j)*RU|vKZVXy*QCF7 z>sB5*N>JOD-1fK9dsfHRR{enZUNyXT9Z7FQ-D5j)O&4pXH z&R|m-<}7kX_=c85yeMDcXK&uT;a#v_n4dp>Yf`tht!)5c7nyO0gJY{lYpWVC!Dr+i z$jxi@9Q#(tq;F8pEKgCt2QooeKtb3k9z_H3kWj`#dHQokvmauKe}FOqxJ^tA<55=np$XjJM=eSJ{h|tgyxnlTOh%8 zqDr9%`jV|D|NdHRDoMsD1= zaqY^LLI}$*P8vL}w8m;h1L>i#*FwQri_Q*bw6|ZYoW)3nC{Kol?&-KUUW{8J<1xQVUxAZnh2|v*_O z6`b7p^EU(3^FEiB5@eVd^kFUpRsI(g4wJmRR0z9|gq&ynpFJyqX7Ga7Xb0;5qHI8_ z;v&~eY|nA6LK2FlVa@A}__#N?#D-T%1*gv+k+SQ`L46cL{0dgN}ElLRRlucvzSrb_3A( zT`Ch4N9h|kc9;4wTYTr!c#LuY;6kura!<0!6% zeoIKbP!CEFAyU%P2YGmE`riCVQa?+@C?JZ8nW)*|#c6X6dJ@8zfb2vXp+>nZPm~hn zC*7?Ew|_xQY~!CJs@buQYE%w0Hq~l9UivIdP>9Aj)Ry-bD;p;TpQSx=cqWiAl}q=qRU3nh!=emm>tFOiejE(6c?|%J#(X0o~^_{dpc> z_1?XEoNAfh*`ArwKMQ9J<+tb~^fcn)Fh0TJp*(-cuuv)`x)RWc>Re7-+ym4RYinz| z<67sV(z70>b5!^Tp!@;!^959)9w7%Y(=EDB%RUcKp`2l^9v)f$(MD}-j(#I3Cq-Ure+YPw9|HBJ_F#qH{mWMH|P(lTQ-n#`GREB&8F1?(rkvBh_TKyZP*G}EDs1Q?+ zDn(7#&yc%J@@|@yok&+75XL|Q+%E#{MY&%JG%8=kTNa{RF-LOlCqmPOR7HrGL?7`| zS67Ui>Z{aQU0xtcH{|ALOQSi}0@N7fh+V(F6Ah9dwUeT%iXzN;ogcJ;-!)N~LY*}H zz8}?!j?Yxdb1?6~4AJ?Zczp9eAoAjb-WK$PXaqs}<#q;2awf-4GNA3fXRKKfLt;+V{f=4JJS3d8~y_`_Ek*j8k-{?i61i&>FrJgTW!td}rYr4A&82v$6wQ&)?J&`6*jOyN zTR$}zq!jyd5d&w_b{jjpLo6(JAUMoE?Mtm1d&{Zx-UHK~3kK~bK1HoBx_1QO>WB)X zUlkU6kfriLlj$GPjBXWV>mBIhw1cHF3tWK+PQ+l+Aa}YL*k$GSPxrRog7^r~m|JO9 z#v>lTtv^9``THM zsyIp-qwBM>%Jb0MFkDZ-lnmnpNVR6|V_12t<^jl=(a7=XXDhKNn)uU@4=gX2VCii(Rj=Q8ivQ;Mt$7INmBYqg;Ux6uY6Df#?axl!0< z{y5NaFTQ!*%iP>B&>7G7?~en;D5V+FV2V$+AM6Z5p2xkddv@&JeZz$MU6S4)M`KDP zDm^<$Appn;i*~_ixyZ{z85T;y+#z(b-LZQc!79f}zkHbliark7LZl1m8B7ADgjm|y>4C5( zXcn~hU7B@4@`Z)%L;HgWQe0H@>vg2Ox;h~-FSL!hd3$=U2RG~n;iLfiA)$d^ftK>_ zYLq1~@&5fD@7^5_r5%P+`nl}ML8G?pQ}*`uckkY{gaD1q^Fm^x_;f~xnt#G@@9!vr zHKrycIBy((zw*XRBhH2yI@--%tZ)#@{Fld!5222#XWRaQIV3(+!`jBCDbJZ76G&o~ zhZcuDufi|G8kEk(k6H=*>!45E{c9i+eswr^>@bAt7K)-Tlg&X(`T6+SugmZ>=6N z^6}x7k_5YY`s|q~^aV2w>TTQJqUX_3k{8-{En3t0iWAx}#K>|GOr=~$p5%g60W>3m ziFiP2n(TVW&G*h=ok71iIn};0h&_aT3$CI!HvLr%a};nlv{)4Q=Hxg>H7LtJH@PY= zufuqemL|)%ShlyfmzWyAl@3Y)LK$*Lwhv?ZFj3=eHeeKX3o~o^2(J`}_#-;PgtBSV zrcNv%=_OtUWFW>^p#g83Op`KN{r$HC+eu1FzEn)s0Mj6S$J-D%U(lksVsjs6DP;jL z>=Slt6?kLEW7eGZ3;dE#L?STD%f@*R3yY#4tJ~^=0kpi|Ki)p996|@cor8zg8%8SJ zxbX%06mkSZB`+8-{DXoF^61C@4l(8RI>9Q{-(Q+-Hw0ZIa>nj&L0o~+3+x&6HWXA$ zvSJbv9>K(N586a-)g(MXh#d-0_ai9Cpv-p(^n($5XMcap4Q>`iZdN{vb{R>@O_;Bw z+-k!#3M4Wg973F74kF!2NEW|945C0j9x)=8lQ`fzhHVj?D4_)b-UjUqtp*CJ4yaMwKANj?7x$k$89?wrEXMa(d3GXS zQ7%5D%2z%7!)2MYPTdP4&tD9ee`%Tj;>rB4oAZ;t&-qyvoljehZ|n~FY2PY(;d4`g zU(bl?%j#|7;`YZ+lvis=TorwDpSPtzCWAHO!JfJ6Pc${w`t0m^#xq8b?cB}6vc_?M zM`(r8V(r?-LRqs%tz#y-mZ}F=%?%_pkO;Px?Ow9-{o7$N^DGU z*3cunEa8o(E#(;wF7)aL_S_i47pln}_>eL_ykUAJ|D%wP;#@=9Nk==2=dymEQj*?s z($HjGzId}`B9(o%$ZU;a$Jj`YqhDvM9_NtCS7T1X>I0YBhK(C1en@j))zF|ij}aus zR}H-cD_K1TSFIsyAoEvcbDNFxuYPy2FWCfrNH94P&jUXe4cx3cenR5VZona2Cd=84 z&k#1;GB`V-`?3lO?vs#^z??=5ya#pxB7`K>jicMencaxSK4%^!Q;&!wZ>c1>GI*RhX=EoV0@PpqzaePVVQ zzV+IqP56=X>-hEV;|ms&N)74H>gk|7&0_)%J{W~kLtF!R{~h2nC@;jA3Y_LGY6Mz- z`eED*?>#BESzCLa_vvLNpTgGKXX#r~H!@3{KJmTQCyMcObJ2OZgZSr;mZ<{2PNf&} zpBq?c!__H$7vC2UV>cj)i*e?HvaYiDpha?UY~0>uvKXu(i5! z9v03^TOf1uz09XIt8{VJrp76J(d8%#>C-MB&S0jL0{&;H}b{V^3}6vM3QCbqM$kDdxd z9%IvnoUojnv#$PQF`$a{n{m^(+Yn9w3*_?ru+SZweADVLiRQPyQTpk%)@R zz$f$1RA)h`y?_7SDF|$bormY@g##=M~)O#RZ)2sz1)2W zZ;U2vY}j5nFy6$(XihE;3=F^~2_V+~j(Q3Bh>_sjI$s`XgY019q3oRsALn^6e<;VV z4XPsu)3DznR+pl3ut^r~oCLM39jBL+9(%103P7mUs|X}JZrvj(L~{dGe@IHni59Uz z;#B>Za6yRRQYPm?;5qi=6B;A#-ecOug{6KMwEEj0x$^pg_A6n{qeQWzd7}U2vcmqq zJ8ke^r_lcf$@KA&oYL=T6Wcqk8p6TFNB#20@ijqI!TDOk6-&p((>Sp zfQ`*IZ>TVs{|u>DwY7yq13StoD!Qt8@g`US;+m0UVPOHw)%e5&fyF(*SFllVzrK0- zlH>tA4FD2-@F0chb*`G^R;><`>}Re%y5Wd z864aI+9X6L!BL5W19+8CKK2PYT~EA(10%kblOks-%#wosjAO#bs^L1Lpy9Tl2RRC! zFI~*|5JV(mwgh{y2qt>?3|O;n;QdJqvvGG8dwY5S1tpM=0B%r_LU90Mc(|(11dhcHb` zGpxCT@}*^9Pzo2k0uO#Rd!Rh>G?IY~EZ^Vu=9(alwiK-%t#cB{z_h zuk#NMzV)9A#NS5zkhVJui|-p{p`0VAI%uow445lwzH6!@T~kb(PLRYO?~6rk&uVwt z5!f#3=y;k034O|gFnuo zy$P<-IrDFiqWw#A`0t!w`{$efhc=skoRoVLoMQ3dA`GnXUc#oL#TTkj!NMX7t2Q>l z8`eDWsEOuHn*+*46%=+MxCHLtr)8#mIwyh?_H@hA?DRd?sylGO$EWN$lm!txJG+2q zW{o=;IKb)89ZtIM|9#(G{H*bMzeZad>;F!h^u3AGJ1~lpd*HYnT#&JGaX)Bw(HM!& zR$4{D=tV)zq>K7XOcuMkE@CRQSJ3e|{G)p~-MdOQ!#}!!6AZ-6(7o0`Q9p)6-MsC3cug0mU}bef10! z30<550SftG)h&J_Nv#}VO&se|UL_7fp^oOnwFp{uiNYQ$jfBD21u)~$*|FRJ4DCq< zYpcTx?i$~9PiXVf*KZLMJy3H6^JK!Ti8c*A?@p1{OSshlD^qmKcVI|8J~vm;IVcF} z9AlOsptzfSp$qUGC>OX1;od>K!iZsfik|diCZVripK~Btso@NI`To8C1y6Dw+uv6% zhn+5hUkc-HE!^&KONQTz7cZo@gnN#4!_)^&n;0~p$BBbnVlcphqYWNd zG)tWRAr22+)6lq#!+gSP%O_vGdPVr_XQ=p3T}Ru4>16BY&!3?uZQr@G1UpVx%Lxs{ zzQwFT!^=m~rhb?$DLy`)C$w^pT3$<2Q_Tk*3G>WfUQUh4adF7ja2PXGGR%D;f8w(- z1c45|E{rka!J|hdP~!;qFuJC;wsv=Pa-I+-XpjdCROMn6VQgjT}o?gQ6O7vv{YFpM{d z3C|x3mHCXr(zD32Xe0qp9@KVtWQ1ElLd(LU8Us8g%TGrM z4`<8b>U-_tx<%19O(yomvWQ~By^gtjXrr{W^sTj(IrwY~#nyj<`h;^Pp&n<}s$NG2 zLJfs3Y1o#Xt`dw`rGE$+m2eLaH6$V}u0j}w%>Fa?)=5I-Y-w3~Jv4`*8h9S}7y)MYSOSU=24$RP1mOPu^1eOIlfF3RDk&6T>qd2l4{j?? z(5>zQyA!T$Y;$fW|BiIRv=2tzTMgbWYg`DJ*^pgo#? zzrPygcdiS6QdVD>w`N+UufDh}+M{ywIRE5nnOnEAj1@$Cjv79AvQ?-P4!r-JLin#| zegAl6k3q-Jja+J(SJDA~cYy^))l~2TQK!b;5EV6!`o;ENoiYArjsDAMiBQG<^;`b_ z>i^jddidY}-(RDB3pGH4e%OMDG~vhyCgr5lXU_aWc6n1V5;wW_@E?N?4vq?Xs(u@5 zYnIceU%>ZK3*xSy!%c2;Q1~vWrP?tN69coK?{7n8lvXi-HwNR#d4BhgaQa~l-<*j{ zNYDj90se2!9YvyH7DCt;NZ!=U`mo3M^z}7!_74qJ{AppjCaLxf!7hmr3*r`Y2#mHe zM?SWUW4^3)T&-rkrh>v}m;_N(V=OxI_n7v>GhYnB{XPmyKFH{%&_+~MVrWa>+D#Z0 zFtErPerfo?-_YJ>2c~9lwr#PuP-ck0@V1`D+zp4Dp>`pIiKBQp?eh=&a(7>!e%>cJ85!M) zuFn`O>l~*eD!U;soWn#(24P@5bZ8={V}%L>LAT`z3Ao|D?$LB9f{PzBtzVd%=T23f zC5&*egIP`XNMU^c73KoM76A?c|Kwf5DU_({jl;LcZ0lsckVreVi01;Reyrmc?{O#G zCku;1$=r@B7`1=FktRs&VbD@QPj06c%i?4S+;9j4lOMIUW({W{5WpJb=;)|6`SJAW z(`su6w2#vf);b<~Uz~&11~h?Ly}H^D4OHtm9dYyUN^HY`ri(ZkR#Wo??m6u9aXL!u zMJMd@@DsyDOPu1vCz`9}rBn5x2u=HO`@lqC+bVu;u3 zGF2HSVE2G<0mA&C!%J@jxQe>o3&RISx}HRzW@L0#rce+~GQ$aumv7$m+-V%k?50AI zUx43++c6pA1N7csJw5o=$&F(;+Xq=?OrK^)s zZMOVDhuL1=+(0e5bGsCYCS#vk*me;vgMg4XeC5~AoId;b`LWawGI|Dlp?h?KL)1={ z-!M}6R!6TX8eAf^g)*M}>%j{Y{71J-rS}l%n4(o{uM| zpiq#PkAe*_E<0NVSZcXrUFX?_kE%sFM$Jw#ZgMvdXwqxhpx`fIqev@9~gS@ z`0;AIa2fnTM-CjgfQkB~qoA?5xqjT{7akzVZ()Pgm~*tTNrh+pxxfF*!NI`;)i_Oj zcszt5F+kF#WqYZN~}+;C?W{ z36Vx2hp2(njazqbSaq|DB*_RuQvdqx+jV8-CjV05g&ta@?q!Hr^)ME`gt~l7Frv`c zWFVBGeN9teKbml_m-rGVvjm)`-@#&ezpyl`)jR{UVGNuFf}>+GQ9s4YYlwWlO>^5Q zB&EsJI!i6vy7$52(o(Ojz#*x&wl>_z55%YUHc@N|mqsp~Pf0z^hREyd#?>Wg)6>&M z#L0oyj~+XwU~m7C_>!T(y`woh0Dzd~qc#~)wg>n;ejKPxhh31jut;8ZD;OLavP1IY zCgr$-q;cmc#MfcFlc^;l1HM_I^o$G>m}r2m>HrveW}ND4YoTMZoZ{mnzhiwYsNyYD zD71#y{CtgQqqwA`%ZQ@Ek&)-czplWUmz|xB_ni8q$w6kxV42efwCuuW&O!WL6sTyQNaSABDH9FChHOpFG8 zyu}iwa7yEisFj%C_w(XJ3mzfhdRtC-W_C92%^Ok7`C{P-pN?W)k%XVw`t&|LK0^_4 zgfaE{mQ+q@j4)s>^~buV0f+bY^%)~npT6<(A|GkXQ3Qr`!XX&(-mM%Yb86w9M(gEt z?lxEEWfdwC9R-(Wegb?9uB!|Eh`-|eFTK{F>I(2e9h}{|>a&6L=p;-}*ARyIO-`wh z6M7Gz`<^>ma5PMkL^3yGoevrykwn*#&a?jezxMASkyyw`fA^Qi{ZgJE9IfymNbDl5 zk^}$pb`9}zR-~LxBYFHf-nQXy-}#?DmGp!}+P<0eZ~wwAc@S5AM~C;u;M`L0)PinE zG-pNE;q`@wk5h{9h00QSi`eJ5x`=)no2wX_`N}Knc87HQ0cXY2jxp-oz0AH>9?jd< zB4SCM4aa5W&qZ2XZ!lx<4c|y=UA*$>p`<1CI+A%_P4?{M0NcO6lz&|9Kd$Jne|_0p zs?_qTy)_wVnZWH6ka6M_nf zWPQq-+{oeG&Fx*6!l@j1DAbpXfy81ZeM^w(`Lk!gfn1)xL5;;=`I32t>eK6*n#ril zz#2y7PY*eD-lo9(Z5LfEY^s{X<52KW5P*XGDSI=%&{4Drfgg`37@T7vC3CHPD z;nl0ijmZTUV-u5FFqAZx1#3Nhed}m4x}Yy3s;vOt?{acf!QN_MI;%t8B3_jP-vT_i z!tx03O)Y~(E>ZY*?cN<16C?4ouCXx&!M_6cnBt{NU#XkjBqe#S)! zgn@&fUvXt+Wy`j03T|%s#9;tsau3N?V^>$#5FQ;bSa^2r*zp?174W=rUfJ+7HsbI> zp=bxTnd1m+SlH#5=H_Nrsmn3)u+-E6mYo?Ex;OT<*E%*iSE`P_0OL?d3F0X zS$bMp*>TFM6Qu5j%y4r&DB%g%vhA-=8!-era_*c4d}NbOd4t2l*R{1@p-!LF4Ngc% zuvm>^d>s0q*w@yG`6&yy}8sm;kXmD-3L!&xC=MXx+Q+Rx9T{lyZJ?^`3Xvq_3PJXyDUul zmo6Ct(qaRs4aVpn=69C_ht0=YIqB%(M58ik9{Zz`Y55Aue*KRhJLcb9fo2YcV;2(>mz03(Vj2!Zv-p)BOm2)v(5fJo z>~qxrtlX5MQHhDNIQ$UN+|pv;8|04b_Aw zZ2xire|_;kE{DH}hn{0*X$5(ALRLOd&*~YO)=oOF1EG~{-o*!U+RmM0`Gx8@V@Ku((V1z~H8JBukSQ(MKYy`Hv2L^!-wWs^@z6ix_Jr1sN%$ z-B8_m!It*_%N6~P#T!452MC{d$8)jySP}CGuRV#u?q(`hir4dju2mOH2e!z+dn@DEi3|l!ukY#)2D8PT5Ru^V%aF1)4-sw!)bE4ly#cx-`~^ z<2~!E)#Km4zXJRDBAmH^;4z;*U9_{eAH<-O;Y11spmoyRd2d!%-OM`j)h+TD^tD_Q z6BAVm-15-Qd*_zYL5qY|=esY-$u+`L6Ti6VRCSspdXqI=*j)pmoedNagZYaP6k`bWXoI1U(+u@ zOBOw$Q&8NQXr%KZ>Rq^y^9{u$wI{6VxsF$%B0ac&A2L%Z9*UHJ#}vuJD6a?v)$X=r zHZwP8(?GU=$H7w%FweY-S22Oo%VnR7mv50;N`hojD1Rx1v z4{|h`0QY+}dHDdGlc=k&Cw>4q|LEbvl1L=p%CU56#SG_zfZtEw;3&ByWGI}R4`Kx# z9Ryxgu(h?-+Bt1Dw2s6wA=D9@jCM zN3)r=Wh)>=t#kfB+7SEdTUz4b0ylwn#^0)e;=hl|Tj1T8=R>!@C4Y45*R03GV#KnypKXr&afdd@dC=PR@G-99u=eUu0 z?0%&uhx2a3sfK4~4Jj5ZK8F?MP)18)*-x!v|46I?6hL z=g8H7PRx`Y^dr8Spn9-8{+!y8F#>ALF(%HA2l))GsO6ue*LRISw zWcl}K_CGM`e+fn((LR2n@e3Zr({ZG6{`WY(N2A&Av&PU!@@S!`mHoFc`QLuBs~Oc#S=E2@>{_`R86k9v&T+-kFY(NY{iW^gg^_!i zX9t9bTyFQDB|r9->32}hOT{sbsZ3VaMPWCw(INS13D@lJI#fBno|SH5F5l1UGAOR8 z{tHYVDzi*3g3=}A6%-+G)l*UnfC`NWM zsMgbI_1nv9>@ClD7r4htT?5l%YGT|U*_CC|$BGvuIUd{C;>(b|b;y8U=j_kIOAJOysk3D{GWOY|IXz`djQ(E1Wd({20qh)NL z@6tc8rT-e)|6MqI-}Nbvw{J`|XqnCB#bRRPF3U>=m>JI~3H#^Ca>3S$j_zz+CbML6R7f*Ett=e7n*O0P0`fZ|^tMHukQ8S+2 z<8KB=Ukzo}njJ3lKW8)}{q$>#tR1CF$X?S&<=rBzBpJhrUwk9#qYb(-)4vVYhs?%k z2st<%x0LHzdiR_6)QFs`^`7a$aFyf;U+QaJJ+FeAw&y(Kt53og_VT=?4slW0ZZ5Aj z|1)n)(GDoBbDXRi@ku+l85cj z*+*hO+?C@ed-1*DTiVcwZIH%0y1F0h&oyl`>J7Kg`%t%c-e=Igb)@b~HO0)gekR1l z@OF`wF)QN*MiB{8y`s!p#^E4V*)1p1ZfX0wSvQUcHAS}SHI6z(GQBVg;_>qH(^d}r mY_yILDe$0Dl9bqG(WHx7cm5w@;K>I7 From 98643a401226149fb7849d91f175a6e5093f18b5 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Thu, 26 May 2022 13:49:04 +0200 Subject: [PATCH 35/52] Fixed diagnostics currupting stored config_entry --- custom_components/localtuya/__init__.py | 12 ++++++------ custom_components/localtuya/diagnostics.py | 11 ++++++++--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index d4549a1..b6db69d 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -268,6 +268,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload a config entry.""" platforms = {} + + hass.data[DOMAIN][UNSUB_LISTENER]() + for dev_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): + if device.connected: + await device.close() + for dev_id, dev_entry in entry.data[CONF_DEVICES].items(): for entity in dev_entry[CONF_ENTITIES]: platforms[entity[CONF_PLATFORM]] = True @@ -280,12 +286,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): ] ) ) - - hass.data[DOMAIN][UNSUB_LISTENER]() - for dev_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): - if device.connected: - await device.close() - if unload_ok: hass.data[DOMAIN][TUYA_DEVICES] = {} diff --git a/custom_components/localtuya/diagnostics.py b/custom_components/localtuya/diagnostics.py index 9cc2692..49216db 100644 --- a/custom_components/localtuya/diagnostics.py +++ b/custom_components/localtuya/diagnostics.py @@ -1,5 +1,7 @@ """Diagnostics support for LocalTuya.""" from __future__ import annotations +import logging +import copy from typing import Any @@ -14,21 +16,24 @@ CLOUD_DEVICES = "cloud_devices" DEVICE_CONFIG = "device_config" DEVICE_CLOUD_INFO = "device_cloud_info" +_LOGGER = logging.getLogger(__name__) + async def async_get_config_entry_diagnostics( hass: HomeAssistant, entry: ConfigEntry ) -> dict[str, Any]: """Return diagnostics for a config entry.""" data = {} - data = entry.data.copy() + data = dict(entry.data) tuya_api = hass.data[DOMAIN][DATA_CLOUD] # censoring private information on integration diagnostic data for field in [CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_USER_ID]: data[field] = f"{data[field][0:3]}...{data[field][-3:]}" + data[CONF_DEVICES] = copy.deepcopy(entry.data[CONF_DEVICES]) for dev_id, dev in data[CONF_DEVICES].items(): - local_key = data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] + local_key = dev[CONF_LOCAL_KEY] local_key_obfuscated = f"{local_key[0:3]}...{local_key[-3:]}" - data[CONF_DEVICES][dev_id][CONF_LOCAL_KEY] = local_key_obfuscated + 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] From d8b64eb819e1fdb652a08a60f99b4c14a66d4165 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Fri, 27 May 2022 12:35:32 +0200 Subject: [PATCH 36/52] Changed permissions for cloud_api.py --- custom_components/localtuya/cloud_api.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 custom_components/localtuya/cloud_api.py diff --git a/custom_components/localtuya/cloud_api.py b/custom_components/localtuya/cloud_api.py old mode 100755 new mode 100644 From f9b88c79a0d89cb03a348becfa489efd49ab6619 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Fri, 27 May 2022 12:54:20 +0200 Subject: [PATCH 37/52] Fixed wrong 'already_configured' message when editing entities --- custom_components/localtuya/config_flow.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 18f9fac..1453321 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -674,8 +674,6 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): user_input = {NO_ADDITIONAL_ENTITIES: True} return await self.async_step_pick_entity_type(user_input) - errors["base"] = "entity_already_configured" - if self.editing_device: schema = platform_schema( self.current_entity[CONF_PLATFORM], self.dps_strings, allow_id=False From 73be0c8a457aa6bbb8b5cfe09e604f1ffcc6985d Mon Sep 17 00:00:00 2001 From: sibowler Date: Sun, 29 May 2022 06:57:37 +1000 Subject: [PATCH 38/52] Revert "Rebase from upstream" --- README.md | 25 +- custom_components/localtuya/__init__.py | 14 - custom_components/localtuya/climate.py | 394 ------------------ custom_components/localtuya/common.py | 36 +- custom_components/localtuya/config_flow.py | 4 - custom_components/localtuya/const.py | 49 +-- custom_components/localtuya/fan.py | 211 +++------- custom_components/localtuya/light.py | 38 +- custom_components/localtuya/number.py | 5 +- .../localtuya/pytuya/__init__.py | 36 +- custom_components/localtuya/select.py | 5 +- custom_components/localtuya/strings.json | 3 +- custom_components/localtuya/switch.py | 2 +- .../localtuya/translations/en.json | 108 +---- .../localtuya/translations/pt-BR.json | 217 ---------- custom_components/localtuya/vacuum.py | 258 ------------ img/10-integration_configure.png | Bin 10229 -> 0 bytes img/11-config_menu.png | Bin 14953 -> 0 bytes img/6-project_date.png | Bin 59139 -> 0 bytes img/7-auth_keys.png | Bin 66636 -> 0 bytes img/8-user_id.png | Bin 73998 -> 0 bytes img/9-cloud_setup.png | Bin 21521 -> 0 bytes info.md | 25 +- 23 files changed, 115 insertions(+), 1315 deletions(-) delete mode 100644 custom_components/localtuya/climate.py delete mode 100644 custom_components/localtuya/translations/pt-BR.json delete mode 100644 custom_components/localtuya/vacuum.py delete mode 100644 img/10-integration_configure.png delete mode 100644 img/11-config_menu.png delete mode 100644 img/6-project_date.png delete mode 100644 img/7-auth_keys.png delete mode 100644 img/8-user_id.png delete mode 100644 img/9-cloud_setup.png diff --git a/README.md b/README.md index 7404fe9..a4ccf04 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,6 @@ localtuya: local_key: xxxxx friendly_name: Tuya Device protocol_version: "3.3" - scan_interval: # optional, only needed if energy monitoring values are not updating - seconds: 30 # Values less than 10 seconds may cause stability issues entities: - platform: binary_sensor friendly_name: Plug Status @@ -65,16 +63,7 @@ localtuya: - platform: fan friendly_name: Device Fan - id: 3 # dps for on/off state - fan_direction: 4 # Optional, dps for fan direction - fan_direction_fwd: forward # String for the forward direction - fan_direction_rev: reverse # String for the reverse direction - fan_ordered_list: low,medium,high,auto # Optional, If this is used it will not use the min and max integers. - fan_oscilating_control: 4 # Optional, dps for fan osciallation - fan_speed_control: 3 # Optional, if ordered list not used, dps for speed control - fan_speed_min: 1 # Optional, if ordered list not used, minimum integer for speed range - fan_speed_max: 10 # Optional, if ordered list not used, maximum integer for speed range - + id: 3 - platform: light friendly_name: Device Light @@ -82,7 +71,6 @@ localtuya: 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 @@ -91,6 +79,7 @@ localtuya: 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 friendly_name: Plug Voltage id: 20 @@ -123,13 +112,9 @@ select one of these, or manually input all the parameters. ![discovery](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/1-discovery.png) If you have selected one entry, you only need to input the device's Friendly Name and the localKey. - -Setting the scan interval is optional, only needed if energy/power values are not updating frequently enough by default. Values less than 10 seconds may cause stability issues. - Once you press "Submit", the connection is tested to check that everything works. -![image](https://user-images.githubusercontent.com/1082213/146664103-ac40319e-f934-4933-90cf-2beaff1e6bac.png) - +![device](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/2-device.png) Then, it's time to add the entities: this step will take place several times. First, select the entity type from the drop-down menu to set it up. After you have defined all the needed entities, leave the "Do not add more entities" checkbox checked: this will complete the procedure. @@ -155,7 +140,6 @@ You can obtain Energy monitoring (voltage, current) in two different ways: Note: Voltage and Consumption usually include the first decimal. You will need to scale the parament by 0.1 to get the correct values. 1) Access the voltage/current/current_consumption attributes of a switch, and define template sensors Note: these values are already divided by 10 for Voltage and Consumption -1) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended. ``` sensor: @@ -206,6 +190,3 @@ TradeFace, for being the only one to provide the correct code for communication sean6541, for the working (standard) Python Handler for Tuya devices. postlund, for the ideas, for coding 95% of the refactoring and boosting the quality of this repo to levels hard to imagine (by me, at least) and teaching me A LOT of how things work in Home Assistant. - -Buy Me A Coffee -PayPal Logo diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index ad879b0..0b762f2 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -53,20 +53,6 @@ localtuya: current: 18 # Optional current_consumption: 19 # Optional voltage: 20 # Optional - - - platform: vacuum - friendly_name: Vacuum - id: 28 - idle_status_value: "standby,sleep" - returning_status_value: "docking" - docked_status_value: "charging,chargecompleted" - battery_dp: 14 - mode_dp: 27 - modes: "smart,standby,chargego,wall_follow,spiral,single" - fan_speed_dp: 30 - fan_speeds: "low,normal,high" - clean_time_dp: 33 - clean_area_dp: 32 """ import asyncio import logging diff --git a/custom_components/localtuya/climate.py b/custom_components/localtuya/climate.py deleted file mode 100644 index 9d9358f..0000000 --- a/custom_components/localtuya/climate.py +++ /dev/null @@ -1,394 +0,0 @@ -"""Platform to locally control Tuya-based climate devices.""" -import asyncio -import logging -from functools import partial - -import voluptuous as vol -from homeassistant.components.climate import ( - DEFAULT_MAX_TEMP, - DEFAULT_MIN_TEMP, - DOMAIN, - ClimateEntity, -) -from homeassistant.components.climate.const import ( - HVAC_MODE_AUTO, - HVAC_MODE_HEAT, - HVAC_MODE_OFF, - SUPPORT_PRESET_MODE, - SUPPORT_TARGET_TEMPERATURE, - SUPPORT_TARGET_TEMPERATURE_RANGE, - CURRENT_HVAC_IDLE, - CURRENT_HVAC_HEAT, - PRESET_NONE, - PRESET_ECO, - PRESET_AWAY, - PRESET_HOME, -) -from homeassistant.const import ( - ATTR_TEMPERATURE, - CONF_TEMPERATURE_UNIT, - PRECISION_HALVES, - PRECISION_TENTHS, - PRECISION_WHOLE, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, -) - -from .common import LocalTuyaEntity, async_setup_entry -from .const import ( - CONF_CURRENT_TEMPERATURE_DP, - CONF_MAX_TEMP_DP, - CONF_MIN_TEMP_DP, - CONF_PRECISION, - CONF_TARGET_PRECISION, - CONF_TARGET_TEMPERATURE_DP, - CONF_TEMPERATURE_STEP, - CONF_HVAC_MODE_DP, - CONF_HVAC_MODE_SET, - CONF_HEURISTIC_ACTION, - CONF_HVAC_ACTION_DP, - CONF_HVAC_ACTION_SET, - CONF_ECO_DP, - CONF_ECO_VALUE, - CONF_PRESET_DP, - CONF_PRESET_SET, -) - -_LOGGER = logging.getLogger(__name__) - -HVAC_MODE_SETS = { - "manual/auto": { - HVAC_MODE_HEAT: "manual", - HVAC_MODE_AUTO: "auto", - }, - "Manual/Auto": { - HVAC_MODE_HEAT: "Manual", - HVAC_MODE_AUTO: "Auto", - }, - "Manual/Program": { - HVAC_MODE_HEAT: "Manual", - HVAC_MODE_AUTO: "Program", - }, - "True/False": { - HVAC_MODE_HEAT: True, - }, -} -HVAC_ACTION_SETS = { - "True/False": { - CURRENT_HVAC_HEAT: True, - CURRENT_HVAC_IDLE: False, - }, - "open/close": { - CURRENT_HVAC_HEAT: "open", - CURRENT_HVAC_IDLE: "close", - }, - "heating/no_heating": { - CURRENT_HVAC_HEAT: "heating", - CURRENT_HVAC_IDLE: "no_heating", - }, - "Heat/Warming": { - CURRENT_HVAC_HEAT: "Heat", - CURRENT_HVAC_IDLE: "Warming", - }, -} -PRESET_SETS = { - "Manual/Holiday/Program": { - PRESET_AWAY: "Holiday", - PRESET_HOME: "Program", - PRESET_NONE: "Manual", - }, -} - -TEMPERATURE_CELSIUS = "celsius" -TEMPERATURE_FAHRENHEIT = "fahrenheit" -DEFAULT_TEMPERATURE_UNIT = TEMPERATURE_CELSIUS -DEFAULT_PRECISION = PRECISION_TENTHS -DEFAULT_TEMPERATURE_STEP = PRECISION_HALVES -# Empirically tested to work for AVATTO thermostat -MODE_WAIT = 0.1 - - -def flow_schema(dps): - """Return schema used in config flow.""" - return { - vol.Optional(CONF_TARGET_TEMPERATURE_DP): vol.In(dps), - vol.Optional(CONF_CURRENT_TEMPERATURE_DP): vol.In(dps), - vol.Optional(CONF_TEMPERATURE_STEP): vol.In( - [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS] - ), - vol.Optional(CONF_MAX_TEMP_DP): vol.In(dps), - vol.Optional(CONF_MIN_TEMP_DP): vol.In(dps), - vol.Optional(CONF_PRECISION): vol.In( - [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS] - ), - vol.Optional(CONF_HVAC_MODE_DP): vol.In(dps), - vol.Optional(CONF_HVAC_MODE_SET): vol.In(list(HVAC_MODE_SETS.keys())), - vol.Optional(CONF_HVAC_ACTION_DP): vol.In(dps), - vol.Optional(CONF_HVAC_ACTION_SET): vol.In(list(HVAC_ACTION_SETS.keys())), - vol.Optional(CONF_ECO_DP): vol.In(dps), - vol.Optional(CONF_ECO_VALUE): str, - vol.Optional(CONF_PRESET_DP): vol.In(dps), - vol.Optional(CONF_PRESET_SET): vol.In(list(PRESET_SETS.keys())), - vol.Optional(CONF_TEMPERATURE_UNIT): vol.In( - [TEMPERATURE_CELSIUS, TEMPERATURE_FAHRENHEIT] - ), - vol.Optional(CONF_TARGET_PRECISION): vol.In( - [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS] - ), - vol.Optional(CONF_HEURISTIC_ACTION): bool, - } - - -class LocaltuyaClimate(LocalTuyaEntity, ClimateEntity): - """Tuya climate device.""" - - def __init__( - self, - device, - config_entry, - switchid, - **kwargs, - ): - """Initialize a new LocaltuyaClimate.""" - super().__init__(device, config_entry, switchid, _LOGGER, **kwargs) - self._state = None - self._target_temperature = None - self._current_temperature = None - self._hvac_mode = None - self._preset_mode = None - self._hvac_action = None - self._precision = self._config.get(CONF_PRECISION, DEFAULT_PRECISION) - self._target_precision = self._config.get( - CONF_TARGET_PRECISION, self._precision - ) - self._conf_hvac_mode_dp = self._config.get(CONF_HVAC_MODE_DP) - self._conf_hvac_mode_set = HVAC_MODE_SETS.get( - self._config.get(CONF_HVAC_MODE_SET), {} - ) - self._conf_preset_dp = self._config.get(CONF_PRESET_DP) - self._conf_preset_set = PRESET_SETS.get(self._config.get(CONF_PRESET_SET), {}) - self._conf_hvac_action_dp = self._config.get(CONF_HVAC_ACTION_DP) - self._conf_hvac_action_set = HVAC_ACTION_SETS.get( - self._config.get(CONF_HVAC_ACTION_SET), {} - ) - self._conf_eco_dp = self._config.get(CONF_ECO_DP) - self._conf_eco_value = self._config.get(CONF_ECO_VALUE, "ECO") - self._has_presets = self.has_config(CONF_ECO_DP) or self.has_config( - CONF_PRESET_DP - ) - print("Initialized climate [{}]".format(self.name)) - - @property - def supported_features(self): - """Flag supported features.""" - supported_features = 0 - if self.has_config(CONF_TARGET_TEMPERATURE_DP): - supported_features = supported_features | SUPPORT_TARGET_TEMPERATURE - if self.has_config(CONF_MAX_TEMP_DP): - supported_features = supported_features | SUPPORT_TARGET_TEMPERATURE_RANGE - if self.has_config(CONF_PRESET_DP) or self.has_config(CONF_ECO_DP): - supported_features = supported_features | SUPPORT_PRESET_MODE - return supported_features - - @property - def precision(self): - """Return the precision of the system.""" - return self._precision - - @property - def target_precision(self): - """Return the precision of the target.""" - return self._target_precision - - @property - def temperature_unit(self): - """Return the unit of measurement used by the platform.""" - if ( - self._config.get(CONF_TEMPERATURE_UNIT, DEFAULT_TEMPERATURE_UNIT) - == TEMPERATURE_FAHRENHEIT - ): - return TEMP_FAHRENHEIT - return TEMP_CELSIUS - - @property - def hvac_mode(self): - """Return current operation ie. heat, cool, idle.""" - return self._hvac_mode - - @property - def hvac_modes(self): - """Return the list of available operation modes.""" - if not self.has_config(CONF_HVAC_MODE_DP): - return None - return list(self._conf_hvac_mode_set) + [HVAC_MODE_OFF] - - @property - def hvac_action(self): - """Return the current running hvac operation if supported. - - Need to be one of CURRENT_HVAC_*. - """ - if self._config.get(CONF_HEURISTIC_ACTION, False): - if self._hvac_mode == HVAC_MODE_HEAT: - if self._current_temperature < ( - self._target_temperature - self._precision - ): - self._hvac_action = CURRENT_HVAC_HEAT - if self._current_temperature == ( - self._target_temperature - self._precision - ): - if self._hvac_action == CURRENT_HVAC_HEAT: - self._hvac_action = CURRENT_HVAC_HEAT - if self._hvac_action == CURRENT_HVAC_IDLE: - self._hvac_action = CURRENT_HVAC_IDLE - if ( - self._current_temperature + self._precision - ) > self._target_temperature: - self._hvac_action = CURRENT_HVAC_IDLE - return self._hvac_action - return self._hvac_action - - @property - def preset_mode(self): - """Return current preset.""" - return self._preset_mode - - @property - def preset_modes(self): - """Return the list of available presets modes.""" - if not self._has_presets: - return None - presets = list(self._conf_preset_set) - if self._conf_eco_dp: - presets.append(PRESET_ECO) - return presets - - @property - def current_temperature(self): - """Return the current temperature.""" - return self._current_temperature - - @property - def target_temperature(self): - """Return the temperature we try to reach.""" - return self._target_temperature - - @property - def target_temperature_step(self): - """Return the supported step of target temperature.""" - return self._config.get(CONF_TEMPERATURE_STEP, DEFAULT_TEMPERATURE_STEP) - - @property - def fan_mode(self): - """Return the fan setting.""" - return NotImplementedError() - - @property - def fan_modes(self): - """Return the list of available fan modes.""" - return NotImplementedError() - - async def async_set_temperature(self, **kwargs): - """Set new target temperature.""" - if ATTR_TEMPERATURE in kwargs and self.has_config(CONF_TARGET_TEMPERATURE_DP): - temperature = round(kwargs[ATTR_TEMPERATURE] / self._target_precision) - await self._device.set_dp( - temperature, self._config[CONF_TARGET_TEMPERATURE_DP] - ) - - def set_fan_mode(self, fan_mode): - """Set new target fan mode.""" - return NotImplementedError() - - async def async_set_hvac_mode(self, hvac_mode): - """Set new target operation mode.""" - if hvac_mode == HVAC_MODE_OFF: - await self._device.set_dp(False, self._dp_id) - return - if not self._state and self._conf_hvac_mode_dp != self._dp_id: - await self._device.set_dp(True, self._dp_id) - # Some thermostats need a small wait before sending another update - await asyncio.sleep(MODE_WAIT) - await self._device.set_dp( - self._conf_hvac_mode_set[hvac_mode], self._conf_hvac_mode_dp - ) - - async def async_turn_on(self) -> None: - """Turn the entity on.""" - await self._device.set_dp(True, self._dp_id) - - async def async_turn_off(self) -> None: - """Turn the entity off.""" - await self._device.set_dp(False, self._dp_id) - - async def async_set_preset_mode(self, preset_mode): - """Set new target preset mode.""" - if preset_mode == PRESET_ECO: - await self._device.set_dp(self._conf_eco_value, self._conf_eco_dp) - return - await self._device.set_dp( - self._conf_preset_set[preset_mode], self._conf_preset_dp - ) - - @property - def min_temp(self): - """Return the minimum temperature.""" - if self.has_config(CONF_MIN_TEMP_DP): - return self.dps_conf(CONF_MIN_TEMP_DP) - return DEFAULT_MIN_TEMP - - @property - def max_temp(self): - """Return the maximum temperature.""" - if self.has_config(CONF_MAX_TEMP_DP): - return self.dps_conf(CONF_MAX_TEMP_DP) - return DEFAULT_MAX_TEMP - - def status_updated(self): - """Device status was updated.""" - self._state = self.dps(self._dp_id) - - if self.has_config(CONF_TARGET_TEMPERATURE_DP): - self._target_temperature = ( - self.dps_conf(CONF_TARGET_TEMPERATURE_DP) * self._target_precision - ) - - if self.has_config(CONF_CURRENT_TEMPERATURE_DP): - self._current_temperature = ( - self.dps_conf(CONF_CURRENT_TEMPERATURE_DP) * self._precision - ) - - if self._has_presets: - if ( - self.has_config(CONF_ECO_DP) - and self.dps_conf(CONF_ECO_DP) == self._conf_eco_value - ): - self._preset_mode = PRESET_ECO - else: - for preset, value in self._conf_preset_set.items(): # todo remove - if self.dps_conf(CONF_PRESET_DP) == value: - self._preset_mode = preset - break - else: - self._preset_mode = PRESET_NONE - - # Update the HVAC status - if self.has_config(CONF_HVAC_MODE_DP): - if not self._state: - self._hvac_mode = HVAC_MODE_OFF - else: - for mode, value in self._conf_hvac_mode_set.items(): - if self.dps_conf(CONF_HVAC_MODE_DP) == value: - self._hvac_mode = mode - break - else: - # in case hvac mode and preset share the same dp - self._hvac_mode = HVAC_MODE_AUTO - - # Update the current action - for action, value in self._conf_hvac_action_set.items(): - if self.dps_conf(CONF_HVAC_ACTION_DP) == value: - self._hvac_action = action - - -async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaClimate, flow_schema) diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index ea821a5..68b0d28 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -1,7 +1,6 @@ """Code shared between all platforms.""" import asyncio import logging -from datetime import timedelta from homeassistant.const import ( CONF_DEVICE_ID, @@ -10,10 +9,8 @@ from homeassistant.const import ( CONF_HOST, CONF_ID, CONF_PLATFORM, - CONF_SCAN_INTERVAL, ) from homeassistant.core import callback -from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, @@ -120,7 +117,6 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._is_closing = False self._connect_task = None self._disconnect_task = None - self._unsub_interval = None self.set_logger(_LOGGER, config_entry[CONF_DEVICE_ID]) # This has to be done in case the device type is type_0d @@ -170,16 +166,6 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._disconnect_task = async_dispatcher_connect( self._hass, signal, _new_entity_handler ) - - if ( - CONF_SCAN_INTERVAL in self._config_entry - and self._config_entry[CONF_SCAN_INTERVAL] > 0 - ): - self._unsub_interval = async_track_time_interval( - self._hass, - self._async_refresh, - timedelta(seconds=self._config_entry[CONF_SCAN_INTERVAL]), - ) except Exception: # pylint: disable=broad-except self.exception(f"Connect to {self._config_entry[CONF_HOST]} failed") if self._interface is not None: @@ -187,10 +173,6 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._interface = None self._connect_task = None - async def _async_refresh(self, _now): - if self._interface is not None: - await self._interface.update_dps() - async def close(self): """Close connection and stop re-connect loop.""" self._is_closing = True @@ -241,9 +223,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): """Device disconnected.""" signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}" async_dispatcher_send(self._hass, signal, None) - if self._unsub_interval is not None: - self._unsub_interval() - self._unsub_interval = None + self._interface = None self.debug("Disconnected - waiting for discovery broadcast") @@ -273,13 +253,13 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger): def _update_handler(status): """Update entity state when status was updated.""" - if status is None: - status = {} - if self._status != status: - self._status = status.copy() - if status: - self.status_updated() - self.schedule_update_ha_state() + if status is not None: + self._status = status + self.status_updated() + else: + self._status = {} + + self.schedule_update_ha_state() signal = f"localtuya_{self._config_entry.data[CONF_DEVICE_ID]}" diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 03bf80d..731b60a 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -14,7 +14,6 @@ from homeassistant.const import ( CONF_HOST, CONF_ID, CONF_PLATFORM, - CONF_SCAN_INTERVAL, ) from homeassistant.core import callback @@ -45,7 +44,6 @@ BASIC_INFO_SCHEMA = vol.Schema( vol.Required(CONF_HOST): str, vol.Required(CONF_DEVICE_ID): str, vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]), - vol.Optional(CONF_SCAN_INTERVAL): int, } ) @@ -57,7 +55,6 @@ DEVICE_SCHEMA = vol.Schema( vol.Required(CONF_LOCAL_KEY): cv.string, vol.Required(CONF_FRIENDLY_NAME): cv.string, vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]), - vol.Optional(CONF_SCAN_INTERVAL): int, } ) @@ -93,7 +90,6 @@ def options_schema(entities): vol.Required(CONF_HOST): str, vol.Required(CONF_LOCAL_KEY): str, vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]), - vol.Optional(CONF_SCAN_INTERVAL): int, vol.Required( CONF_ENTITIES, description={"suggested_value": entity_names} ): cv.multi_select(entity_names), diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 4148771..12fa66c 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -16,7 +16,6 @@ CONF_COLOR = "color" CONF_COLOR_MODE = "color_mode" CONF_COLOR_TEMP_MIN_KELVIN = "color_temp_min_kelvin" CONF_COLOR_TEMP_MAX_KELVIN = "color_temp_max_kelvin" -CONF_COLOR_TEMP_REVERSE = "color_temp_reverse" CONF_MUSIC_MODE = "music_mode" # switch @@ -35,53 +34,13 @@ CONF_SPAN_TIME = "span_time" # fan CONF_FAN_SPEED_CONTROL = "fan_speed_control" CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control" -CONF_FAN_SPEED_MIN = "fan_speed_min" -CONF_FAN_SPEED_MAX = "fan_speed_max" -CONF_FAN_ORDERED_LIST = "fan_speed_ordered_list" -CONF_FAN_DIRECTION = "fan_direction" -CONF_FAN_DIRECTION_FWD = "fan_direction_forward" -CONF_FAN_DIRECTION_REV = "fan_direction_reverse" +CONF_FAN_SPEED_LOW = "fan_speed_low" +CONF_FAN_SPEED_MEDIUM = "fan_speed_medium" +CONF_FAN_SPEED_HIGH = "fan_speed_high" # sensor CONF_SCALING = "scaling" -# climate -CONF_TARGET_TEMPERATURE_DP = "target_temperature_dp" -CONF_CURRENT_TEMPERATURE_DP = "current_temperature_dp" -CONF_TEMPERATURE_STEP = "temperature_step" -CONF_MAX_TEMP_DP = "max_temperature_dp" -CONF_MIN_TEMP_DP = "min_temperature_dp" -CONF_PRECISION = "precision" -CONF_TARGET_PRECISION = "target_precision" -CONF_HVAC_MODE_DP = "hvac_mode_dp" -CONF_HVAC_MODE_SET = "hvac_mode_set" -CONF_PRESET_DP = "preset_dp" -CONF_PRESET_SET = "preset_set" -CONF_HEURISTIC_ACTION = "heuristic_action" -CONF_HVAC_ACTION_DP = "hvac_action_dp" -CONF_HVAC_ACTION_SET = "hvac_action_set" -CONF_ECO_DP = "eco_dp" -CONF_ECO_VALUE = "eco_value" - -# vacuum -CONF_POWERGO_DP = "powergo_dp" -CONF_IDLE_STATUS_VALUE = "idle_status_value" -CONF_RETURNING_STATUS_VALUE = "returning_status_value" -CONF_DOCKED_STATUS_VALUE = "docked_status_value" -CONF_BATTERY_DP = "battery_dp" -CONF_MODE_DP = "mode_dp" -CONF_MODES = "modes" -CONF_FAN_SPEED_DP = "fan_speed_dp" -CONF_FAN_SPEEDS = "fan_speeds" -CONF_CLEAN_TIME_DP = "clean_time_dp" -CONF_CLEAN_AREA_DP = "clean_area_dp" -CONF_CLEAN_RECORD_DP = "clean_record_dp" -CONF_LOCATE_DP = "locate_dp" -CONF_FAULT_DP = "fault_dp" -CONF_PAUSED_STATE = "paused_state" -CONF_RETURN_MODE = "return_mode" -CONF_STOP_STATUS = "stop_status" - DATA_DISCOVERY = "discovery" DOMAIN = "localtuya" @@ -89,7 +48,6 @@ DOMAIN = "localtuya" # Platforms in this list must support config flows PLATFORMS = [ "binary_sensor", - "climate", "cover", "fan", "light", @@ -97,7 +55,6 @@ PLATFORMS = [ "select", "sensor", "switch", - "vacuum", ] TUYA_DEVICE = "tuya_device" diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index d2b4583..0c65dfa 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,37 +1,26 @@ """Platform to locally control Tuya-based fan devices.""" import logging -import math from functools import partial -import homeassistant.helpers.config_validation as cv import voluptuous as vol from homeassistant.components.fan import ( - DIRECTION_FORWARD, - DIRECTION_REVERSE, DOMAIN, - SUPPORT_DIRECTION, + SPEED_HIGH, + SPEED_LOW, + SPEED_MEDIUM, + SPEED_OFF, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity, ) -from homeassistant.util.percentage import ( - int_states_in_range, - ordered_list_item_to_percentage, - percentage_to_ordered_list_item, - percentage_to_ranged_value, - ranged_value_to_percentage, -) from .common import LocalTuyaEntity, async_setup_entry from .const import ( - CONF_FAN_DIRECTION, - CONF_FAN_DIRECTION_FWD, - CONF_FAN_DIRECTION_REV, - CONF_FAN_ORDERED_LIST, CONF_FAN_OSCILLATING_CONTROL, CONF_FAN_SPEED_CONTROL, - CONF_FAN_SPEED_MAX, - CONF_FAN_SPEED_MIN, + CONF_FAN_SPEED_HIGH, + CONF_FAN_SPEED_LOW, + CONF_FAN_SPEED_MEDIUM, ) _LOGGER = logging.getLogger(__name__) @@ -42,12 +31,15 @@ def flow_schema(dps): return { vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps), - vol.Optional(CONF_FAN_DIRECTION): vol.In(dps), - vol.Optional(CONF_FAN_DIRECTION_FWD, default="forward"): cv.string, - vol.Optional(CONF_FAN_DIRECTION_REV, default="reverse"): cv.string, - vol.Optional(CONF_FAN_SPEED_MIN, default=1): cv.positive_int, - vol.Optional(CONF_FAN_SPEED_MAX, default=9): cv.positive_int, - vol.Optional(CONF_FAN_ORDERED_LIST, default="disabled"): cv.string, + vol.Optional(CONF_FAN_SPEED_LOW, default=SPEED_LOW): vol.In( + [SPEED_LOW, "1", "2", "small"] + ), + vol.Optional(CONF_FAN_SPEED_MEDIUM, default=SPEED_MEDIUM): vol.In( + [SPEED_MEDIUM, "mid", "2", "3"] + ), + vol.Optional(CONF_FAN_SPEED_HIGH, default=SPEED_HIGH): vol.In( + [SPEED_HIGH, "auto", "3", "4", "large", "big"] + ), } @@ -64,46 +56,28 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Initialize the entity.""" super().__init__(device, config_entry, fanid, _LOGGER, **kwargs) self._is_on = False + self._speed = None self._oscillating = None - self._direction = None - self._percentage = None - self._speed_range = ( - self._config.get(CONF_FAN_SPEED_MIN), - self._config.get(CONF_FAN_SPEED_MAX), - ) - self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).split(",") - self._ordered_list_mode = None - - if isinstance(self._ordered_list, list) and len(self._ordered_list) > 1: - self._use_ordered_list = True - _LOGGER.debug( - "Fan _use_ordered_list: %s > %s", - self._use_ordered_list, - self._ordered_list, - ) - else: - self._use_ordered_list = False - _LOGGER.debug("Fan _use_ordered_list: %s", self._use_ordered_list) @property def oscillating(self): """Return current oscillating status.""" return self._oscillating - @property - def current_direction(self): - """Return the current direction of the fan.""" - return self._direction - @property def is_on(self): """Check if Tuya fan is on.""" return self._is_on @property - def percentage(self): - """Return the current percentage.""" - return self._percentage + def speed(self) -> str: + """Return the current speed.""" + return self._speed + + @property + def speed_list(self) -> list: + """Get the list of available speeds.""" + return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] async def async_turn_on( self, @@ -113,143 +87,76 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): **kwargs, ) -> None: """Turn on the entity.""" - _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) - if percentage is not None: - await self.async_set_percentage(percentage) + if speed is not None: + await self.async_set_speed(speed) else: self.schedule_update_ha_state() async def async_turn_off(self, **kwargs) -> None: """Turn off the entity.""" - _LOGGER.debug("Fan async_turn_off") - await self._device.set_dp(False, self._dp_id) self.schedule_update_ha_state() - async def async_set_percentage(self, percentage): + async def async_set_speed(self, speed: str) -> None: """Set the speed of the fan.""" - _LOGGER.debug("Fan async_set_percentage: %s", percentage) + mapping = { + SPEED_LOW: self._config.get(CONF_FAN_SPEED_LOW), + SPEED_MEDIUM: self._config.get(CONF_FAN_SPEED_MEDIUM), + SPEED_HIGH: self._config.get(CONF_FAN_SPEED_HIGH), + } - if percentage is not None: - if percentage == 0: - return await self.async_turn_off() - if not self.is_on: - await self.async_turn_on() - if self._use_ordered_list: - await self._device.set_dp( - str( - percentage_to_ordered_list_item(self._ordered_list, percentage) - ), - self._config.get(CONF_FAN_SPEED_CONTROL), - ) - _LOGGER.debug( - "Fan async_set_percentage: %s > %s", - percentage, - percentage_to_ordered_list_item(self._ordered_list, percentage), - ) + if speed == SPEED_OFF: + await self._device.set_dp(False, self._dp_id) + else: + await self._device.set_dp( + mapping.get(speed), self._config.get(CONF_FAN_SPEED_CONTROL) + ) - else: - await self._device.set_dp( - str( - math.ceil( - percentage_to_ranged_value(self._speed_range, percentage) - ) - ), - self._config.get(CONF_FAN_SPEED_CONTROL), - ) - _LOGGER.debug( - "Fan async_set_percentage: %s > %s", - percentage, - percentage_to_ranged_value(self._speed_range, percentage), - ) - self.schedule_update_ha_state() + self.schedule_update_ha_state() async def async_oscillate(self, oscillating: bool) -> None: """Set oscillation.""" - _LOGGER.debug("Fan async_oscillate: %s", oscillating) await self._device.set_dp( oscillating, self._config.get(CONF_FAN_OSCILLATING_CONTROL) ) self.schedule_update_ha_state() - async def async_set_direction(self, direction): - """Set the direction of the fan.""" - _LOGGER.debug("Fan async_set_direction: %s", direction) - - if direction == DIRECTION_FORWARD: - value = self._config.get(CONF_FAN_DIRECTION_FWD) - - if direction == DIRECTION_REVERSE: - value = self._config.get(CONF_FAN_DIRECTION_REV) - await self._device.set_dp(value, self._config.get(CONF_FAN_DIRECTION)) - self.schedule_update_ha_state() - @property def supported_features(self) -> int: """Flag supported features.""" - features = 0 + supports = 0 if self.has_config(CONF_FAN_OSCILLATING_CONTROL): - features |= SUPPORT_OSCILLATE - + supports |= SUPPORT_OSCILLATE if self.has_config(CONF_FAN_SPEED_CONTROL): - features |= SUPPORT_SET_SPEED + supports |= SUPPORT_SET_SPEED - if self.has_config(CONF_FAN_DIRECTION): - features |= SUPPORT_DIRECTION - - return features - - @property - def speed_count(self) -> int: - """Speed count for the fan.""" - speed_count = int_states_in_range(self._speed_range) - _LOGGER.debug("Fan speed_count: %s", speed_count) - return speed_count + return supports def status_updated(self): """Get state of Tuya fan.""" + mappings = { + self._config.get(CONF_FAN_SPEED_LOW): SPEED_LOW, + self._config.get(CONF_FAN_SPEED_MEDIUM): SPEED_MEDIUM, + self._config.get(CONF_FAN_SPEED_HIGH): SPEED_HIGH, + } + self._is_on = self.dps(self._dp_id) - current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) - if self._use_ordered_list: - _LOGGER.debug( - "Fan current_speed ordered_list_item_to_percentage: %s from %s", - current_speed, - self._ordered_list, - ) - if current_speed is not None: - self._percentage = ordered_list_item_to_percentage( - self._ordered_list, current_speed + if self.has_config(CONF_FAN_SPEED_CONTROL): + self._speed = mappings.get(self.dps_conf(CONF_FAN_SPEED_CONTROL)) + if self.speed is None: + self.warning( + "%s/%s: Ignoring unknown fan controller state: %s", + self.name, + self.entity_id, + self.dps_conf(CONF_FAN_SPEED_CONTROL), ) - - else: - _LOGGER.debug( - "Fan current_speed ranged_value_to_percentage: %s from %s", - current_speed, - self._speed_range, - ) - if current_speed is not None: - self._percentage = ranged_value_to_percentage( - self._speed_range, int(current_speed) - ) - - _LOGGER.debug("Fan current_percentage: %s", self._percentage) + self._speed = None if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) - _LOGGER.debug("Fan current_oscillating : %s", self._oscillating) - - if self.has_config(CONF_FAN_DIRECTION): - value = self.dps_conf(CONF_FAN_DIRECTION) - if value is not None: - if value == self._config.get(CONF_FAN_DIRECTION_FWD): - self._direction = DIRECTION_FORWARD - - if value == self._config.get(CONF_FAN_DIRECTION_REV): - self._direction = DIRECTION_REVERSE - _LOGGER.debug("Fan current_direction : %s > %s", value, self._direction) async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) diff --git a/custom_components/localtuya/light.py b/custom_components/localtuya/light.py index 7c74e49..49fade8 100644 --- a/custom_components/localtuya/light.py +++ b/custom_components/localtuya/light.py @@ -27,17 +27,15 @@ from .const import ( CONF_COLOR_MODE, CONF_COLOR_TEMP_MAX_KELVIN, CONF_COLOR_TEMP_MIN_KELVIN, - CONF_COLOR_TEMP_REVERSE, CONF_MUSIC_MODE, ) _LOGGER = logging.getLogger(__name__) +MIRED_TO_KELVIN_CONST = 1000000 DEFAULT_MIN_KELVIN = 2700 # MIRED 370 DEFAULT_MAX_KELVIN = 6500 # MIRED 153 -DEFAULT_COLOR_TEMP_REVERSE = False - DEFAULT_LOWER_BRIGHTNESS = 29 DEFAULT_UPPER_BRIGHTNESS = 1000 @@ -119,11 +117,6 @@ def flow_schema(dps): vol.Optional(CONF_COLOR_TEMP_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All( 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_MUSIC_MODE, default=False, description={"suggested_value": False} @@ -153,14 +146,13 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): CONF_BRIGHTNESS_UPPER, DEFAULT_UPPER_BRIGHTNESS ) self._upper_color_temp = self._upper_brightness - self._max_mired = color_util.color_temperature_kelvin_to_mired( - self._config.get(CONF_COLOR_TEMP_MIN_KELVIN, DEFAULT_MIN_KELVIN) + self._max_mired = round( + MIRED_TO_KELVIN_CONST + / self._config.get(CONF_COLOR_TEMP_MIN_KELVIN, DEFAULT_MIN_KELVIN) ) - self._min_mired = color_util.color_temperature_kelvin_to_mired( - 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._min_mired = round( + MIRED_TO_KELVIN_CONST + / self._config.get(CONF_COLOR_TEMP_MAX_KELVIN, DEFAULT_MAX_KELVIN) ) self._hs = None self._effect = None @@ -207,16 +199,11 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): def color_temp(self): """Return the color_temp of the light.""" 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( self._max_mired - ( ((self._max_mired - self._min_mired) / self._upper_color_temp) - * color_temp_value + * self._color_temp ) ) return None @@ -377,17 +364,10 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): if ATTR_COLOR_TEMP in kwargs and (features & SUPPORT_COLOR_TEMP): if brightness is None: brightness = self._brightness - mired = int(kwargs[ATTR_COLOR_TEMP]) - if self._color_temp_reverse: - mired = self._max_mired - (mired - self._min_mired) - if mired < self._min_mired: - mired = self._min_mired - elif mired > self._max_mired: - mired = self._max_mired color_temp = int( self._upper_color_temp - (self._upper_color_temp / (self._max_mired - self._min_mired)) - * (mired - self._min_mired) + * (int(kwargs[ATTR_COLOR_TEMP]) - self._min_mired) ) states[self._config.get(CONF_COLOR_MODE)] = MODE_WHITE states[self._config.get(CONF_BRIGHTNESS)] = brightness diff --git a/custom_components/localtuya/number.py b/custom_components/localtuya/number.py index 596eb01..328f6a2 100644 --- a/custom_components/localtuya/number.py +++ b/custom_components/localtuya/number.py @@ -4,7 +4,10 @@ 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 homeassistant.const import ( + CONF_DEVICE_CLASS, + STATE_UNKNOWN, +) from .common import LocalTuyaEntity, async_setup_entry diff --git a/custom_components/localtuya/pytuya/__init__.py b/custom_components/localtuya/pytuya/__init__.py index b7645ec..789091c 100644 --- a/custom_components/localtuya/pytuya/__init__.py +++ b/custom_components/localtuya/pytuya/__init__.py @@ -21,7 +21,6 @@ Functions json = status() # returns json payload set_version(version) # 3.1 [default] or 3.3 detect_available_dps() # returns a list of available dps provided by the device - update_dps(dps) # sends update dps command add_dps_to_request(dp_index) # adds dp_index to the list of dps used by the # device (to be queried in the payload) set_dp(on, dp_index) # Set value of any dps index. @@ -62,7 +61,6 @@ TuyaMessage = namedtuple("TuyaMessage", "seqno cmd retcode payload crc") SET = "set" STATUS = "status" HEARTBEAT = "heartbeat" -UPDATEDPS = "updatedps" # Request refresh of DPS PROTOCOL_VERSION_BYTES_31 = b"3.1" PROTOCOL_VERSION_BYTES_33 = b"3.3" @@ -78,9 +76,6 @@ SUFFIX_VALUE = 0x0000AA55 HEARTBEAT_INTERVAL = 10 -# DPS that are known to be safe to use with update_dps (0x12) command -UPDATE_DPS_WHITELIST = [18, 19, 20] # Socket (Wi-Fi) - # This is intended to match requests.json payload at # https://github.com/codetheweb/tuyapi : # type_0a devices require the 0a command as the status request @@ -95,13 +90,11 @@ PAYLOAD_DICT = { STATUS: {"hexByte": 0x0A, "command": {"gwId": "", "devId": ""}}, SET: {"hexByte": 0x07, "command": {"devId": "", "uid": "", "t": ""}}, HEARTBEAT: {"hexByte": 0x09, "command": {}}, - UPDATEDPS: {"hexByte": 0x12, "command": {"dpId": [18, 19, 20]}}, }, "type_0d": { STATUS: {"hexByte": 0x0D, "command": {"devId": "", "uid": "", "t": ""}}, SET: {"hexByte": 0x07, "command": {"devId": "", "uid": "", "t": ""}}, HEARTBEAT: {"hexByte": 0x09, "command": {}}, - UPDATEDPS: {"hexByte": 0x12, "command": {"dpId": [18, 19, 20]}}, }, } @@ -299,8 +292,6 @@ class MessageDispatcher(ContextualLogger): sem = self.listeners[self.HEARTBEAT_SEQNO] self.listeners[self.HEARTBEAT_SEQNO] = msg sem.release() - elif msg.cmd == 0x12: - self.debug("Got normal updatedps response") elif msg.cmd == 0x08: self.debug("Got status update") self.listener(msg) @@ -487,26 +478,6 @@ class TuyaProtocol(asyncio.Protocol, ContextualLogger): """Send a heartbeat message.""" return await self.exchange(HEARTBEAT) - async def update_dps(self, dps=None): - """ - Request device to update index. - - Args: - dps([int]): list of dps to update, default=detected&whitelisted - """ - if self.version == 3.3: - if dps is None: - if not self.dps_cache: - await self.detect_available_dps() - if self.dps_cache: - dps = [int(dp) for dp in self.dps_cache] - # filter non whitelisted dps - dps = list(set(dps).intersection(set(UPDATE_DPS_WHITELIST))) - self.debug("updatedps() entry (dps %s, dps_cache %s)", dps, self.dps_cache) - payload = self._generate_payload(UPDATEDPS, dps) - self.transport.write(payload) - return True - async def set_dp(self, value, dp_index): """ Set value (may be any type: bool, int or string) of any dps index. @@ -611,10 +582,7 @@ class TuyaProtocol(asyncio.Protocol, ContextualLogger): json_data["t"] = str(int(time.time())) if data is not None: - if "dpId" in json_data: - json_data["dpId"] = data - else: - json_data["dps"] = data + json_data["dps"] = data elif command_hb == 0x0D: json_data["dps"] = self.dps_to_request @@ -623,7 +591,7 @@ class TuyaProtocol(asyncio.Protocol, ContextualLogger): if self.version == 3.3: payload = self.cipher.encrypt(payload, False) - if command_hb not in [0x0A, 0x12]: + if command_hb != 0x0A: # add the 3.3 header payload = PROTOCOL_33_HEADER + payload elif command == SET: diff --git a/custom_components/localtuya/select.py b/custom_components/localtuya/select.py index 29d11c9..43b32c9 100644 --- a/custom_components/localtuya/select.py +++ b/custom_components/localtuya/select.py @@ -4,7 +4,10 @@ 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 homeassistant.const import ( + CONF_DEVICE_CLASS, + STATE_UNKNOWN, +) from .common import LocalTuyaEntity, async_setup_entry diff --git a/custom_components/localtuya/strings.json b/custom_components/localtuya/strings.json index b8bedc8..898b99d 100644 --- a/custom_components/localtuya/strings.json +++ b/custom_components/localtuya/strings.json @@ -20,7 +20,6 @@ "device_id": "Device ID", "local_key": "Local key", "protocol_version": "Protocol Version", - "scan_interval": "Scan interval (seconds, only when not updating automatically)", "device_type": "Device type" } }, @@ -40,4 +39,4 @@ } }, "title": "LocalTuya" -} +} \ No newline at end of file diff --git a/custom_components/localtuya/switch.py b/custom_components/localtuya/switch.py index f43d910..d3e08f5 100644 --- a/custom_components/localtuya/switch.py +++ b/custom_components/localtuya/switch.py @@ -48,7 +48,7 @@ class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity): return self._state @property - def extra_state_attributes(self): + def device_state_attributes(self): """Return device state attributes.""" attrs = {} if self.has_config(CONF_CURRENT): diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 178e999..f12c4ab 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -29,8 +29,7 @@ "host": "Host", "device_id": "Device ID", "local_key": "Local key", - "protocol_version": "Protocol Version", - "scan_interval": "Scan interval (seconds, only when not updating automatically)" + "protocol_version": "Protocol Version" } }, "pick_entity_type": { @@ -61,61 +60,25 @@ "scaling": "Scaling Factor", "state_on": "On Value", "state_off": "Off Value", - "powergo_dp": "Power DP (Usually 25 or 2)", - "idle_status_value": "Idle Status (comma-separated)", - "returning_status_value": "Returning Status", - "docked_status_value": "Docked Status (comma-separated)", - "fault_dp": "Fault DP (Usually 11)", - "battery_dp": "Battery status DP (Usually 14)", - "mode_dp": "Mode DP (Usually 27)", - "modes": "Modes list", - "return_mode": "Return home mode", - "fan_speed_dp": "Fan speeds DP (Usually 30)", - "fan_speeds": "Fan speeds list (comma-separated)", - "clean_time_dp": "Clean Time DP (Usually 33)", - "clean_area_dp": "Clean Area DP (Usually 32)", - "clean_record_dp": "Clean Record DP (Usually 34)", - "locate_dp": "Locate DP (Usually 31)", - "paused_state": "Pause state (pause, paused, etc)", - "stop_status": "Stop status", "brightness": "Brightness (only for white color)", "brightness_lower": "Brightness Lower Value", "brightness_upper": "Brightness Upper Value", "color_temp": "Color Temperature", - "color_temp_reverse": "Color Temperature Reverse", "color": "Color", "color_mode": "Color Mode", "color_temp_min_kelvin": "Minimum Color Temperature in K", "color_temp_max_kelvin": "Maximum Color Temperature in K", "music_mode": "Music mode available", "scene": "Scene", + "fan_speed_control": "Fan Speed Control", + "fan_oscillating_control": "Fan Oscillating Control", + "fan_speed_low": "Fan Low Speed Setting", + "fan_speed_medium": "Fan Medium 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 ;", - "fan_speed_control": "Fan Speed Control dps", - "fan_oscillating_control": "Fan Oscillating Control dps", - "fan_speed_min": "minimum fan speed integer", - "fan_speed_max": "maximum fan speed integer", - "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", - "fan_direction":"fan direction dps", - "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string", - "current_temperature_dp": "Current Temperature", - "target_temperature_dp": "Target Temperature", - "temperature_step": "Temperature Step (optional)", - "max_temperature_dp": "Max Temperature (optional)", - "min_temperature_dp": "Min Temperature (optional)", - "precision": "Precision (optional, for DPs values)", - "target_precision": "Target Precision (optional, for DPs values)", - "temperature_unit": "Temperature Unit (optional)", - "hvac_mode_dp": "HVAC Mode DP (optional)", - "hvac_mode_set": "HVAC Mode Set (optional)", - "hvac_action_dp": "HVAC Current Action DP (optional)", - "hvac_action_set": "HVAC Current Action Set (optional)", - "preset_dp": "Presets DP (optional)", - "preset_set": "Presets Set (optional)", - "eco_dp": "Eco DP (optional)", - "eco_value": "Eco value (optional)", - "heuristic_action": "Enable heuristic action (optional)" + "select_options_friendly": "User Friendly options, separate entries by a ;" } } } @@ -130,7 +93,6 @@ "host": "Host", "local_key": "Local key", "protocol_version": "Protocol Version", - "scan_interval": "Scan interval (seconds, only when not updating automatically)", "entities": "Entities (uncheck an entity to remove it)" } }, @@ -154,61 +116,25 @@ "scaling": "Scaling Factor", "state_on": "On Value", "state_off": "Off Value", - "powergo_dp": "Power DP (Usually 25 or 2)", - "idle_status_value": "Idle Status (comma-separated)", - "returning_status_value": "Returning Status", - "docked_status_value": "Docked Status (comma-separated)", - "fault_dp": "Fault DP (Usually 11)", - "battery_dp": "Battery status DP (Usually 14)", - "mode_dp": "Mode DP (Usually 27)", - "modes": "Modes list", - "return_mode": "Return home mode", - "fan_speed_dp": "Fan speeds DP (Usually 30)", - "fan_speeds": "Fan speeds list (comma-separated)", - "clean_time_dp": "Clean Time DP (Usually 33)", - "clean_area_dp": "Clean Area DP (Usually 32)", - "clean_record_dp": "Clean Record DP (Usually 34)", - "locate_dp": "Locate DP (Usually 31)", - "paused_state": "Pause state (pause, paused, etc)", - "stop_status": "Stop status", "brightness": "Brightness (only for white color)", "brightness_lower": "Brightness Lower Value", "brightness_upper": "Brightness Upper Value", "color_temp": "Color Temperature", - "color_temp_reverse": "Color Temperature Reverse", "color": "Color", "color_mode": "Color Mode", "color_temp_min_kelvin": "Minimum Color Temperature in K", "color_temp_max_kelvin": "Maximum Color Temperature in K", "music_mode": "Music mode available", "scene": "Scene", + "fan_speed_control": "Fan Speed Control", + "fan_oscillating_control": "Fan Oscillating Control", + "fan_speed_low": "Fan Low Speed Setting", + "fan_speed_medium": "Fan Medium 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 ;", - "fan_speed_control": "Fan Speed Control dps", - "fan_oscillating_control": "Fan Oscillating Control dps", - "fan_speed_min": "minimum fan speed integer", - "fan_speed_max": "maximum fan speed integer", - "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", - "fan_direction":"fan direction dps", - "fan_direction_forward": "forward dps string", - "fan_direction_reverse": "reverse dps string", - "current_temperature_dp": "Current Temperature", - "target_temperature_dp": "Target Temperature", - "temperature_step": "Temperature Step (optional)", - "max_temperature_dp": "Max Temperature (optional)", - "min_temperature_dp": "Min Temperature (optional)", - "precision": "Precision (optional, for DPs values)", - "target_precision": "Target Precision (optional, for DPs values)", - "temperature_unit": "Temperature Unit (optional)", - "hvac_mode_dp": "HVAC Mode DP (optional)", - "hvac_mode_set": "HVAC Mode Set (optional)", - "hvac_action_dp": "HVAC Current Action DP (optional)", - "hvac_action_set": "HVAC Current Action Set (optional)", - "preset_dp": "Presets DP (optional)", - "preset_set": "Presets Set (optional)", - "eco_dp": "Eco DP (optional)", - "eco_value": "Eco value (optional)", - "heuristic_action": "Enable heuristic action (optional)" + "select_options_friendly": "User Friendly options, separate entries by a ;" } }, "yaml_import": { diff --git a/custom_components/localtuya/translations/pt-BR.json b/custom_components/localtuya/translations/pt-BR.json deleted file mode 100644 index c27dc51..0000000 --- a/custom_components/localtuya/translations/pt-BR.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "config": { - "abort": { - "already_configured": "O dispositivo já foi configurado.", - "device_updated": "A configuração do dispositivo foi atualizada!" - }, - "error": { - "cannot_connect": "Não é possível se conectar ao dispositivo. Verifique se o endereço está correto e tente novamente.", - "invalid_auth": "Falha ao autenticar com o dispositivo. Verifique se o ID do dispositivo e a chave local estão corretos.", - "unknown": "Ocorreu um erro desconhecido. Consulte o registro para obter detalhes.", - "entity_already_configured": "A entidade com este ID já foi configurada.", - "address_in_use": "O endereço usado para descoberta já está em uso. Certifique-se de que nenhum outro aplicativo o esteja usando (porta TCP 6668).", - "discovery_failed": "Algo falhou ao descobrir dispositivos. Consulte o registro para obter detalhes.", - "empty_dps": "A conexão com o dispositivo foi bem-sucedida, mas nenhum ponto de dados foi encontrado. Tente novamente. Crie um novo issue e inclua os logs de depuração se o problema persistir." - }, - "step": { - "user": { - "title": "Descoberta de dispositivo", - "description": "Escolha um dos dispositivos descobertos automaticamente ou clique em `...` para adicionar um dispositivo manualmente.", - "data": { - "discovered_device": "Dispositivo descoberto" - } - }, - "basic_info": { - "title": "Adicionar dispositivo Tuya", - "description": "Preencha os detalhes básicos do dispositivo. O nome inserido aqui será usado para identificar a própria integração (como visto na página `Integrations`). Você adicionará entidades e dará nomes a elas nas etapas a seguir.", - "data": { - "friendly_name": "Nome", - "host": "Host", - "device_id": "ID do dispositivo", - "local_key": "Local key", - "protocol_version": "Versão do protocolo", - "scan_interval": "Intervalo do escaneamento (segundos, somente quando não estiver atualizando automaticamente)" - } - }, - "pick_entity_type": { - "title": "Seleção do tipo de entidade", - "description": "Escolha o tipo de entidade que deseja adicionar.", - "data": { - "platform_to_add": "Platforma", - "no_additional_platforms": "Não adicione mais entidades" - } - }, - "add_entity": { - "title": "Adicionar nova entidade", - "description": "Por favor, preencha os detalhes de uma entidade com o tipo `{platform}`. Todas as configurações, exceto `ID`, podem ser alteradas na página Opções posteriormente.", - "data": { - "id": "ID", - "friendly_name": "Name fantasia", - "current": "Atual", - "current_consumption": "Consumo atual", - "voltage": "Voltagem", - "commands_set": "Conjunto de comandos Open_Close_Stop", - "positioning_mode": "Modo de posicão", - "current_position_dp": "Posição atual (somente para o modo de posição)", - "set_position_dp": "Definir posição (somente para o modo de posição)", - "position_inverted": "Inverter posição 0-100 (somente para o modo de posição)", - "span_time": "Tempo de abertura completo, em segundos. (somente para o modo temporizado)", - "unit_of_measurement": "Unidade de medida", - "device_class": "Classe do dispositivo", - "scaling": "Fator de escala", - "state_on": "Valor On", - "state_off": "Valor Off", - "powergo_dp": "Potência DP (Geralmente 25 ou 2)", - "idle_status_value": "Status ocioso (separado por vírgula)", - "returning_status_value": "Status de retorno", - "docked_status_value": "Status docked (separado por vírgula)", - "fault_dp": "Falha DP (Geralmente 11)", - "battery_dp": "Status da bateria DP (normalmente 14)", - "mode_dp": "Modo DP (Geralmente 27)", - "modes": "Lista de modos", - "return_mode": "Modo de retorno para base", - "fan_speed_dp": "Velocidades do ventilador DP (normalmente 30)", - "fan_speeds": "Lista de velocidades do ventilador (separadas por vírgulas)", - "clean_time_dp": "Tempo de Limpeza DP (Geralmente 33)", - "clean_area_dp": "Área Limpa DP (Geralmente 32)", - "clean_record_dp": "Limpar Registro DP (Geralmente 34)", - "locate_dp": "Localize DP (Geralmente 31)", - "paused_state": "Estado de pausa (pausa, pausado, etc)", - "stop_status": "Status de parada", - "brightness": "Brilho (somente para cor branca)", - "brightness_lower": "Valor mais baixo do brilho", - "brightness_upper": "Valor mais alto do brilho", - "color_temp": "Temperatura da cor", - "color_temp_reverse": "Temperatura da cor reversa", - "color": "Cor", - "color_mode": "Modo de cor", - "color_temp_min_kelvin": "Minima temperatura de cor em K", - "color_temp_max_kelvin": "Máxima temperatura de cor em K", - "music_mode": "Modo de música disponível", - "scene": "Cena", - "fan_speed_control": "dps de controle de velocidade do ventilador", - "fan_oscillating_control": "dps de controle oscilante do ventilador", - "fan_speed_min": "velocidade mínima do ventilador inteiro", - "fan_speed_max": "velocidade máxima do ventilador inteiro", - "fan_speed_ordered_list": "Lista de modos de velocidade do ventilador (substitui a velocidade min/max)", - "fan_direction":"direção do ventilador dps", - "fan_direction_forward": "string de dps para frente", - "fan_direction_reverse": "string dps reversa", - "current_temperature_dp": "Temperatura atual", - "target_temperature_dp": "Temperatura alvo", - "temperature_step": "Etapa de temperatura (opcional)", - "max_temperature_dp": "Max Temperatura (opcional)", - "min_temperature_dp": "Min Temperatura (opcional)", - "precision": "Precisão (opcional, para valores de DPs)", - "target_precision": "Precisão do alvo (opcional, para valores de DPs)", - "temperature_unit": "Unidade de Temperatura (opcional)", - "hvac_mode_dp": "Modo HVAC DP (opcional)", - "hvac_mode_set": "Conjunto de modo HVAC (opcional)", - "hvac_action_dp": "Ação atual de HVAC DP (opcional)", - "hvac_action_set": "Conjunto de ação atual de HVAC (opcional)", - "preset_dp": "Predefinições DP (opcional)", - "preset_set": "Conjunto de predefinições (opcional)", - "eco_dp": "Eco DP (opcional)", - "eco_value": "Valor ECO (opcional)", - "heuristic_action": "Ativar ação heurística (opcional)" - } - } - } - }, - "options": { - "step": { - "init": { - "title": "Configurar dispositivo Tuya", - "description": "Configuração básica para o ID do dispositivo `{device_id}`.", - "data": { - "friendly_name": "Nome fantasia", - "host": "Host", - "local_key": "Local key", - "protocol_version": "Versão do protocolo", - "scan_interval": "Intervalo de escaneamento (segundos, somente quando não estiver atualizando automaticamente)", - "entities": "Entidades (desmarque uma entidade para removê-la)" - } - }, - "entity": { - "title": "Adicionar nova entidade", - "description": "Por favor, preencha os detalhes de uma entidade com o tipo `{platform}`. Todas as configurações, exceto `ID`, podem ser alteradas na página Opções posteriormente.", - "data": { - "id": "ID", - "friendly_name": "Name fantasia", - "current": "Atual", - "current_consumption": "Consumo atual", - "voltage": "Voltagem", - "commands_set": "Conjunto de comandos Open_Close_Stop", - "positioning_mode": "Modo de posicão", - "current_position_dp": "Posição atual (somente para o modo de posição)", - "set_position_dp": "Definir posição (somente para o modo de posição)", - "position_inverted": "Inverter posição 0-100 (somente para o modo de posição)", - "span_time": "Tempo de abertura completo, em segundos. (somente para o modo temporizado)", - "unit_of_measurement": "Unidade de medida", - "device_class": "Classe do dispositivo", - "scaling": "Fator de escala", - "state_on": "Valor On", - "state_off": "Valor Off", - "powergo_dp": "Potência DP (Geralmente 25 ou 2)", - "idle_status_value": "Status ocioso (separado por vírgula)", - "returning_status_value": "Status de retorno", - "docked_status_value": "Status docked (separado por vírgula)", - "fault_dp": "Falha DP (Geralmente 11)", - "battery_dp": "Status da bateria DP (normalmente 14)", - "mode_dp": "Modo DP (Geralmente 27)", - "modes": "Lista de modos", - "return_mode": "Modo de retorno para base", - "fan_speed_dp": "Velocidades do ventilador DP (normalmente 30)", - "fan_speeds": "Lista de velocidades do ventilador (separadas por vírgulas)", - "clean_time_dp": "Tempo de Limpeza DP (Geralmente 33)", - "clean_area_dp": "Área Limpa DP (Geralmente 32)", - "clean_record_dp": "Limpar Registro DP (Geralmente 34)", - "locate_dp": "Localize DP (Geralmente 31)", - "paused_state": "Estado de pausa (pausa, pausado, etc)", - "stop_status": "Status de parada", - "brightness": "Brilho (somente para cor branca)", - "brightness_lower": "Valor mais baixo do brilho", - "brightness_upper": "Valor mais alto do brilho", - "color_temp": "Temperatura da cor", - "color_temp_reverse": "Temperatura da cor reversa", - "color": "Cor", - "color_mode": "Modo de cor", - "color_temp_min_kelvin": "Minima temperatura de cor em K", - "color_temp_max_kelvin": "Máxima temperatura de cor em K", - "music_mode": "Modo de música disponível", - "scene": "Cena", - "fan_speed_control": "dps de controle de velocidade do ventilador", - "fan_oscillating_control": "dps de controle oscilante do ventilador", - "fan_speed_min": "velocidade mínima do ventilador inteiro", - "fan_speed_max": "velocidade máxima do ventilador inteiro", - "fan_speed_ordered_list": "Lista de modos de velocidade do ventilador (substitui a velocidade min/max)", - "fan_direction":"direção do ventilador dps", - "fan_direction_forward": "string de dps para frente", - "fan_direction_reverse": "string dps reversa", - "current_temperature_dp": "Temperatura atual", - "target_temperature_dp": "Temperatura alvo", - "temperature_step": "Etapa de temperatura (opcional)", - "max_temperature_dp": "Max Temperatura (opcional)", - "min_temperature_dp": "Min Temperatura (opcional)", - "precision": "Precisão (opcional, para valores de DPs)", - "target_precision": "Precisão do alvo (opcional, para valores de DPs)", - "temperature_unit": "Unidade de Temperatura (opcional)", - "hvac_mode_dp": "Modo HVAC DP (opcional)", - "hvac_mode_set": "Conjunto de modo HVAC (opcional)", - "hvac_action_dp": "Ação atual de HVAC DP (opcional)", - "hvac_action_set": "Conjunto de ação atual de HVAC (opcional)", - "preset_dp": "Predefinições DP (opcional)", - "preset_set": "Conjunto de predefinições (opcional)", - "eco_dp": "Eco DP (opcional)", - "eco_value": "Valor ECO (opcional)", - "heuristic_action": "Ativar ação heurística (opcional)" - } - }, - "yaml_import": { - "title": "Não suportado", - "description": "As opções não podem ser editadas quando configuradas via YAML." - } - } - }, - "title": "LocalTuya" -} diff --git a/custom_components/localtuya/vacuum.py b/custom_components/localtuya/vacuum.py deleted file mode 100644 index 9a14399..0000000 --- a/custom_components/localtuya/vacuum.py +++ /dev/null @@ -1,258 +0,0 @@ -"""Platform to locally control Tuya-based vacuum devices.""" -import logging -from functools import partial - -import voluptuous as vol -from homeassistant.components.vacuum import ( - DOMAIN, - STATE_CLEANING, - STATE_DOCKED, - STATE_IDLE, - STATE_RETURNING, - STATE_PAUSED, - STATE_ERROR, - SUPPORT_BATTERY, - SUPPORT_FAN_SPEED, - SUPPORT_PAUSE, - SUPPORT_RETURN_HOME, - SUPPORT_START, - SUPPORT_STATE, - SUPPORT_STATUS, - SUPPORT_STOP, - SUPPORT_LOCATE, - StateVacuumEntity, -) - -from .common import LocalTuyaEntity, async_setup_entry - -from .const import ( - CONF_POWERGO_DP, - CONF_IDLE_STATUS_VALUE, - CONF_RETURNING_STATUS_VALUE, - CONF_DOCKED_STATUS_VALUE, - CONF_BATTERY_DP, - CONF_MODE_DP, - CONF_MODES, - CONF_FAN_SPEED_DP, - CONF_FAN_SPEEDS, - CONF_CLEAN_TIME_DP, - CONF_CLEAN_AREA_DP, - CONF_CLEAN_RECORD_DP, - CONF_LOCATE_DP, - CONF_FAULT_DP, - CONF_PAUSED_STATE, - CONF_RETURN_MODE, - CONF_STOP_STATUS, -) - -_LOGGER = logging.getLogger(__name__) - -CLEAN_TIME = "clean_time" -CLEAN_AREA = "clean_area" -CLEAN_RECORD = "clean_record" -MODES_LIST = "cleaning_mode_list" -MODE = "cleaning_mode" -FAULT = "fault" - -DEFAULT_IDLE_STATUS = "standby,sleep" -DEFAULT_RETURNING_STATUS = "docking" -DEFAULT_DOCKED_STATUS = "charging,chargecompleted" -DEFAULT_MODES = "smart,wall_follow,spiral,single" -DEFAULT_FAN_SPEEDS = "low,normal,high" -DEFAULT_PAUSED_STATE = "paused" -DEFAULT_RETURN_MODE = "chargego" -DEFAULT_STOP_STATUS = "standby" - - -def flow_schema(dps): - """Return schema used in config flow.""" - return { - vol.Required(CONF_IDLE_STATUS_VALUE, default=DEFAULT_IDLE_STATUS): str, - vol.Required(CONF_POWERGO_DP): vol.In(dps), - vol.Required(CONF_DOCKED_STATUS_VALUE, default=DEFAULT_DOCKED_STATUS): str, - vol.Optional( - CONF_RETURNING_STATUS_VALUE, default=DEFAULT_RETURNING_STATUS - ): str, - vol.Optional(CONF_BATTERY_DP): vol.In(dps), - vol.Optional(CONF_MODE_DP): vol.In(dps), - vol.Optional(CONF_MODES, default=DEFAULT_MODES): str, - vol.Optional(CONF_RETURN_MODE, default=DEFAULT_RETURN_MODE): str, - vol.Optional(CONF_FAN_SPEED_DP): vol.In(dps), - vol.Optional(CONF_FAN_SPEEDS, default=DEFAULT_FAN_SPEEDS): str, - vol.Optional(CONF_CLEAN_TIME_DP): vol.In(dps), - vol.Optional(CONF_CLEAN_AREA_DP): vol.In(dps), - vol.Optional(CONF_CLEAN_RECORD_DP): vol.In(dps), - vol.Optional(CONF_LOCATE_DP): vol.In(dps), - vol.Optional(CONF_FAULT_DP): vol.In(dps), - vol.Optional(CONF_PAUSED_STATE, default=DEFAULT_PAUSED_STATE): str, - vol.Optional(CONF_STOP_STATUS, default=DEFAULT_STOP_STATUS): str, - } - - -class LocaltuyaVacuum(LocalTuyaEntity, StateVacuumEntity): - """Tuya vacuum device.""" - - def __init__(self, device, config_entry, switchid, **kwargs): - """Initialize a new LocaltuyaVacuum.""" - super().__init__(device, config_entry, switchid, _LOGGER, **kwargs) - self._state = None - self._battery_level = None - self._attrs = {} - - self._idle_status_list = [] - if self.has_config(CONF_IDLE_STATUS_VALUE): - self._idle_status_list = self._config[CONF_IDLE_STATUS_VALUE].split(",") - - self._modes_list = [] - if self.has_config(CONF_MODES): - self._modes_list = self._config[CONF_MODES].split(",") - self._attrs[MODES_LIST] = self._modes_list - - self._docked_status_list = [] - if self.has_config(CONF_DOCKED_STATUS_VALUE): - self._docked_status_list = self._config[CONF_DOCKED_STATUS_VALUE].split(",") - - self._fan_speed_list = [] - if self.has_config(CONF_FAN_SPEEDS): - self._fan_speed_list = self._config[CONF_FAN_SPEEDS].split(",") - - self._fan_speed = "" - self._cleaning_mode = "" - - print("Initialized vacuum [{}]".format(self.name)) - - @property - def supported_features(self): - """Flag supported features.""" - supported_features = ( - SUPPORT_START - | SUPPORT_PAUSE - | SUPPORT_STOP - | SUPPORT_STATUS - | SUPPORT_STATE - ) - - if self.has_config(CONF_RETURN_MODE): - supported_features = supported_features | SUPPORT_RETURN_HOME - if self.has_config(CONF_FAN_SPEED_DP): - supported_features = supported_features | SUPPORT_FAN_SPEED - if self.has_config(CONF_BATTERY_DP): - supported_features = supported_features | SUPPORT_BATTERY - if self.has_config(CONF_LOCATE_DP): - supported_features = supported_features | SUPPORT_LOCATE - - return supported_features - - @property - def state(self): - """Return the vacuum state.""" - return self._state - - @property - def battery_level(self): - """Return the current battery level.""" - return self._battery_level - - @property - def extra_state_attributes(self): - """Return the specific state attributes of this vacuum cleaner.""" - return self._attrs - - @property - def fan_speed(self): - """Return the current fan speed.""" - return self._fan_speed - - @property - def fan_speed_list(self) -> list: - """Return the list of available fan speeds.""" - return self._fan_speed_list - - async def async_start(self, **kwargs): - """Turn the vacuum on and start cleaning.""" - await self._device.set_dp(True, self._config[CONF_POWERGO_DP]) - - async def async_pause(self, **kwargs): - """Stop the vacuum cleaner, do not return to base.""" - await self._device.set_dp(False, self._config[CONF_POWERGO_DP]) - - async def async_return_to_base(self, **kwargs): - """Set the vacuum cleaner to return to the dock.""" - if self.has_config(CONF_RETURN_MODE): - await self._device.set_dp( - self._config[CONF_RETURN_MODE], self._config[CONF_MODE_DP] - ) - else: - _LOGGER.error("Missing command for return home in commands set.") - - async def async_stop(self, **kwargs): - """Turn the vacuum off stopping the cleaning.""" - if self.has_config(CONF_STOP_STATUS): - await self._device.set_dp( - self._config[CONF_STOP_STATUS], self._config[CONF_MODE_DP] - ) - else: - _LOGGER.error("Missing command for stop in commands set.") - - async def async_clean_spot(self, **kwargs): - """Perform a spot clean-up.""" - return None - - async def async_locate(self, **kwargs): - """Locate the vacuum cleaner.""" - if self.has_config(CONF_LOCATE_DP): - await self._device.set_dp("", self._config[CONF_LOCATE_DP]) - - async def async_set_fan_speed(self, fan_speed, **kwargs): - """Set the fan speed.""" - await self._device.set_dp(fan_speed, self._config[CONF_FAN_SPEED_DP]) - - async def async_send_command(self, command, params=None, **kwargs): - """Send a command to a vacuum cleaner.""" - if command == "set_mode" and "mode" in params: - mode = params["mode"] - await self._device.set_dp(mode, self._config[CONF_MODE_DP]) - - def status_updated(self): - """Device status was updated.""" - state_value = str(self.dps(self._dp_id)) - - if state_value in self._idle_status_list: - self._state = STATE_IDLE - elif state_value in self._docked_status_list: - self._state = STATE_DOCKED - elif state_value == self._config[CONF_RETURNING_STATUS_VALUE]: - self._state = STATE_RETURNING - elif state_value == self._config[CONF_PAUSED_STATE]: - self._state = STATE_PAUSED - else: - self._state = STATE_CLEANING - - if self.has_config(CONF_BATTERY_DP): - self._battery_level = self.dps_conf(CONF_BATTERY_DP) - - self._cleaning_mode = "" - if self.has_config(CONF_MODES): - self._cleaning_mode = self.dps_conf(CONF_MODE_DP) - self._attrs[MODE] = self._cleaning_mode - - self._fan_speed = "" - if self.has_config(CONF_FAN_SPEEDS): - self._fan_speed = self.dps_conf(CONF_FAN_SPEED_DP) - - if self.has_config(CONF_CLEAN_TIME_DP): - self._attrs[CLEAN_TIME] = self.dps_conf(CONF_CLEAN_TIME_DP) - - if self.has_config(CONF_CLEAN_AREA_DP): - self._attrs[CLEAN_AREA] = self.dps_conf(CONF_CLEAN_AREA_DP) - - if self.has_config(CONF_CLEAN_RECORD_DP): - self._attrs[CLEAN_RECORD] = self.dps_conf(CONF_CLEAN_RECORD_DP) - - if self.has_config(CONF_FAULT_DP): - self._attrs[FAULT] = self.dps_conf(CONF_FAULT_DP) - if self._attrs[FAULT] != 0: - self._state = STATE_ERROR - - -async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaVacuum, flow_schema) diff --git a/img/10-integration_configure.png b/img/10-integration_configure.png deleted file mode 100644 index f023c22595dc0bc6661a457233dc74183e5415aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10229 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 diff --git a/img/11-config_menu.png b/img/11-config_menu.png deleted file mode 100644 index 22f29ff8e72ffca6a9e17d4a0e938879d8d4633b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14953 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 G{Q6%Kvi_C; diff --git a/img/7-auth_keys.png b/img/7-auth_keys.png deleted file mode 100644 index b8cc331aafef085e4c73ce2a32f973ab2c4628af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66636 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

  • |MKO(zl1!F+V=v3Smm>&3v}J}Da%#P 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⪻W2N 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 diff --git a/img/9-cloud_setup.png b/img/9-cloud_setup.png deleted file mode 100644 index cc70a5183a8b8963953238ae493dbce5155afb10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21521 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 z Date: Sun, 29 May 2022 07:20:19 +1000 Subject: [PATCH 39/52] Revert "Revert "Rebase from upstream"" --- README.md | 25 +- custom_components/localtuya/__init__.py | 14 + custom_components/localtuya/climate.py | 394 ++++++++++++++++++ custom_components/localtuya/common.py | 36 +- custom_components/localtuya/config_flow.py | 4 + custom_components/localtuya/const.py | 49 ++- custom_components/localtuya/fan.py | 213 +++++++--- custom_components/localtuya/light.py | 38 +- custom_components/localtuya/number.py | 5 +- .../localtuya/pytuya/__init__.py | 36 +- custom_components/localtuya/select.py | 5 +- custom_components/localtuya/strings.json | 3 +- custom_components/localtuya/switch.py | 2 +- .../localtuya/translations/en.json | 108 ++++- .../localtuya/translations/pt-BR.json | 217 ++++++++++ custom_components/localtuya/vacuum.py | 258 ++++++++++++ img/10-integration_configure.png | Bin 0 -> 10229 bytes img/11-config_menu.png | Bin 0 -> 14953 bytes img/6-project_date.png | Bin 0 -> 59139 bytes img/7-auth_keys.png | Bin 0 -> 66636 bytes img/8-user_id.png | Bin 0 -> 73998 bytes img/9-cloud_setup.png | Bin 0 -> 21521 bytes info.md | 25 +- 23 files changed, 1316 insertions(+), 116 deletions(-) create mode 100644 custom_components/localtuya/climate.py create mode 100644 custom_components/localtuya/translations/pt-BR.json create mode 100644 custom_components/localtuya/vacuum.py create mode 100644 img/10-integration_configure.png create mode 100644 img/11-config_menu.png create mode 100644 img/6-project_date.png create mode 100644 img/7-auth_keys.png create mode 100644 img/8-user_id.png create mode 100644 img/9-cloud_setup.png diff --git a/README.md b/README.md index a4ccf04..7404fe9 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ localtuya: local_key: xxxxx friendly_name: Tuya Device protocol_version: "3.3" + scan_interval: # optional, only needed if energy monitoring values are not updating + seconds: 30 # Values less than 10 seconds may cause stability issues entities: - platform: binary_sensor friendly_name: Plug Status @@ -63,7 +65,16 @@ localtuya: - platform: fan friendly_name: Device Fan - id: 3 + id: 3 # dps for on/off state + fan_direction: 4 # Optional, dps for fan direction + fan_direction_fwd: forward # String for the forward direction + fan_direction_rev: reverse # String for the reverse direction + fan_ordered_list: low,medium,high,auto # Optional, If this is used it will not use the min and max integers. + fan_oscilating_control: 4 # Optional, dps for fan osciallation + fan_speed_control: 3 # Optional, if ordered list not used, dps for speed control + fan_speed_min: 1 # Optional, if ordered list not used, minimum integer for speed range + fan_speed_max: 10 # Optional, if ordered list not used, maximum integer for speed range + - platform: light friendly_name: Device Light @@ -71,6 +82,7 @@ localtuya: 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 @@ -79,7 +91,6 @@ localtuya: 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 friendly_name: Plug Voltage id: 20 @@ -112,9 +123,13 @@ select one of these, or manually input all the parameters. ![discovery](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/1-discovery.png) If you have selected one entry, you only need to input the device's Friendly Name and the localKey. + +Setting the scan interval is optional, only needed if energy/power values are not updating frequently enough by default. Values less than 10 seconds may cause stability issues. + Once you press "Submit", the connection is tested to check that everything works. -![device](https://github.com/rospogrigio/localtuya-homeassistant/blob/master/img/2-device.png) +![image](https://user-images.githubusercontent.com/1082213/146664103-ac40319e-f934-4933-90cf-2beaff1e6bac.png) + Then, it's time to add the entities: this step will take place several times. First, select the entity type from the drop-down menu to set it up. After you have defined all the needed entities, leave the "Do not add more entities" checkbox checked: this will complete the procedure. @@ -140,6 +155,7 @@ You can obtain Energy monitoring (voltage, current) in two different ways: Note: Voltage and Consumption usually include the first decimal. You will need to scale the parament by 0.1 to get the correct values. 1) Access the voltage/current/current_consumption attributes of a switch, and define template sensors Note: these values are already divided by 10 for Voltage and Consumption +1) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended. ``` sensor: @@ -190,3 +206,6 @@ TradeFace, for being the only one to provide the correct code for communication sean6541, for the working (standard) Python Handler for Tuya devices. postlund, for the ideas, for coding 95% of the refactoring and boosting the quality of this repo to levels hard to imagine (by me, at least) and teaching me A LOT of how things work in Home Assistant. + +Buy Me A Coffee +PayPal Logo diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 0b762f2..ad879b0 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -53,6 +53,20 @@ localtuya: current: 18 # Optional current_consumption: 19 # Optional voltage: 20 # Optional + + - platform: vacuum + friendly_name: Vacuum + id: 28 + idle_status_value: "standby,sleep" + returning_status_value: "docking" + docked_status_value: "charging,chargecompleted" + battery_dp: 14 + mode_dp: 27 + modes: "smart,standby,chargego,wall_follow,spiral,single" + fan_speed_dp: 30 + fan_speeds: "low,normal,high" + clean_time_dp: 33 + clean_area_dp: 32 """ import asyncio import logging diff --git a/custom_components/localtuya/climate.py b/custom_components/localtuya/climate.py new file mode 100644 index 0000000..9d9358f --- /dev/null +++ b/custom_components/localtuya/climate.py @@ -0,0 +1,394 @@ +"""Platform to locally control Tuya-based climate devices.""" +import asyncio +import logging +from functools import partial + +import voluptuous as vol +from homeassistant.components.climate import ( + DEFAULT_MAX_TEMP, + DEFAULT_MIN_TEMP, + DOMAIN, + ClimateEntity, +) +from homeassistant.components.climate.const import ( + HVAC_MODE_AUTO, + HVAC_MODE_HEAT, + HVAC_MODE_OFF, + SUPPORT_PRESET_MODE, + SUPPORT_TARGET_TEMPERATURE, + SUPPORT_TARGET_TEMPERATURE_RANGE, + CURRENT_HVAC_IDLE, + CURRENT_HVAC_HEAT, + PRESET_NONE, + PRESET_ECO, + PRESET_AWAY, + PRESET_HOME, +) +from homeassistant.const import ( + ATTR_TEMPERATURE, + CONF_TEMPERATURE_UNIT, + PRECISION_HALVES, + PRECISION_TENTHS, + PRECISION_WHOLE, + TEMP_CELSIUS, + TEMP_FAHRENHEIT, +) + +from .common import LocalTuyaEntity, async_setup_entry +from .const import ( + CONF_CURRENT_TEMPERATURE_DP, + CONF_MAX_TEMP_DP, + CONF_MIN_TEMP_DP, + CONF_PRECISION, + CONF_TARGET_PRECISION, + CONF_TARGET_TEMPERATURE_DP, + CONF_TEMPERATURE_STEP, + CONF_HVAC_MODE_DP, + CONF_HVAC_MODE_SET, + CONF_HEURISTIC_ACTION, + CONF_HVAC_ACTION_DP, + CONF_HVAC_ACTION_SET, + CONF_ECO_DP, + CONF_ECO_VALUE, + CONF_PRESET_DP, + CONF_PRESET_SET, +) + +_LOGGER = logging.getLogger(__name__) + +HVAC_MODE_SETS = { + "manual/auto": { + HVAC_MODE_HEAT: "manual", + HVAC_MODE_AUTO: "auto", + }, + "Manual/Auto": { + HVAC_MODE_HEAT: "Manual", + HVAC_MODE_AUTO: "Auto", + }, + "Manual/Program": { + HVAC_MODE_HEAT: "Manual", + HVAC_MODE_AUTO: "Program", + }, + "True/False": { + HVAC_MODE_HEAT: True, + }, +} +HVAC_ACTION_SETS = { + "True/False": { + CURRENT_HVAC_HEAT: True, + CURRENT_HVAC_IDLE: False, + }, + "open/close": { + CURRENT_HVAC_HEAT: "open", + CURRENT_HVAC_IDLE: "close", + }, + "heating/no_heating": { + CURRENT_HVAC_HEAT: "heating", + CURRENT_HVAC_IDLE: "no_heating", + }, + "Heat/Warming": { + CURRENT_HVAC_HEAT: "Heat", + CURRENT_HVAC_IDLE: "Warming", + }, +} +PRESET_SETS = { + "Manual/Holiday/Program": { + PRESET_AWAY: "Holiday", + PRESET_HOME: "Program", + PRESET_NONE: "Manual", + }, +} + +TEMPERATURE_CELSIUS = "celsius" +TEMPERATURE_FAHRENHEIT = "fahrenheit" +DEFAULT_TEMPERATURE_UNIT = TEMPERATURE_CELSIUS +DEFAULT_PRECISION = PRECISION_TENTHS +DEFAULT_TEMPERATURE_STEP = PRECISION_HALVES +# Empirically tested to work for AVATTO thermostat +MODE_WAIT = 0.1 + + +def flow_schema(dps): + """Return schema used in config flow.""" + return { + vol.Optional(CONF_TARGET_TEMPERATURE_DP): vol.In(dps), + vol.Optional(CONF_CURRENT_TEMPERATURE_DP): vol.In(dps), + vol.Optional(CONF_TEMPERATURE_STEP): vol.In( + [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS] + ), + vol.Optional(CONF_MAX_TEMP_DP): vol.In(dps), + vol.Optional(CONF_MIN_TEMP_DP): vol.In(dps), + vol.Optional(CONF_PRECISION): vol.In( + [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS] + ), + vol.Optional(CONF_HVAC_MODE_DP): vol.In(dps), + vol.Optional(CONF_HVAC_MODE_SET): vol.In(list(HVAC_MODE_SETS.keys())), + vol.Optional(CONF_HVAC_ACTION_DP): vol.In(dps), + vol.Optional(CONF_HVAC_ACTION_SET): vol.In(list(HVAC_ACTION_SETS.keys())), + vol.Optional(CONF_ECO_DP): vol.In(dps), + vol.Optional(CONF_ECO_VALUE): str, + vol.Optional(CONF_PRESET_DP): vol.In(dps), + vol.Optional(CONF_PRESET_SET): vol.In(list(PRESET_SETS.keys())), + vol.Optional(CONF_TEMPERATURE_UNIT): vol.In( + [TEMPERATURE_CELSIUS, TEMPERATURE_FAHRENHEIT] + ), + vol.Optional(CONF_TARGET_PRECISION): vol.In( + [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS] + ), + vol.Optional(CONF_HEURISTIC_ACTION): bool, + } + + +class LocaltuyaClimate(LocalTuyaEntity, ClimateEntity): + """Tuya climate device.""" + + def __init__( + self, + device, + config_entry, + switchid, + **kwargs, + ): + """Initialize a new LocaltuyaClimate.""" + super().__init__(device, config_entry, switchid, _LOGGER, **kwargs) + self._state = None + self._target_temperature = None + self._current_temperature = None + self._hvac_mode = None + self._preset_mode = None + self._hvac_action = None + self._precision = self._config.get(CONF_PRECISION, DEFAULT_PRECISION) + self._target_precision = self._config.get( + CONF_TARGET_PRECISION, self._precision + ) + self._conf_hvac_mode_dp = self._config.get(CONF_HVAC_MODE_DP) + self._conf_hvac_mode_set = HVAC_MODE_SETS.get( + self._config.get(CONF_HVAC_MODE_SET), {} + ) + self._conf_preset_dp = self._config.get(CONF_PRESET_DP) + self._conf_preset_set = PRESET_SETS.get(self._config.get(CONF_PRESET_SET), {}) + self._conf_hvac_action_dp = self._config.get(CONF_HVAC_ACTION_DP) + self._conf_hvac_action_set = HVAC_ACTION_SETS.get( + self._config.get(CONF_HVAC_ACTION_SET), {} + ) + self._conf_eco_dp = self._config.get(CONF_ECO_DP) + self._conf_eco_value = self._config.get(CONF_ECO_VALUE, "ECO") + self._has_presets = self.has_config(CONF_ECO_DP) or self.has_config( + CONF_PRESET_DP + ) + print("Initialized climate [{}]".format(self.name)) + + @property + def supported_features(self): + """Flag supported features.""" + supported_features = 0 + if self.has_config(CONF_TARGET_TEMPERATURE_DP): + supported_features = supported_features | SUPPORT_TARGET_TEMPERATURE + if self.has_config(CONF_MAX_TEMP_DP): + supported_features = supported_features | SUPPORT_TARGET_TEMPERATURE_RANGE + if self.has_config(CONF_PRESET_DP) or self.has_config(CONF_ECO_DP): + supported_features = supported_features | SUPPORT_PRESET_MODE + return supported_features + + @property + def precision(self): + """Return the precision of the system.""" + return self._precision + + @property + def target_precision(self): + """Return the precision of the target.""" + return self._target_precision + + @property + def temperature_unit(self): + """Return the unit of measurement used by the platform.""" + if ( + self._config.get(CONF_TEMPERATURE_UNIT, DEFAULT_TEMPERATURE_UNIT) + == TEMPERATURE_FAHRENHEIT + ): + return TEMP_FAHRENHEIT + return TEMP_CELSIUS + + @property + def hvac_mode(self): + """Return current operation ie. heat, cool, idle.""" + return self._hvac_mode + + @property + def hvac_modes(self): + """Return the list of available operation modes.""" + if not self.has_config(CONF_HVAC_MODE_DP): + return None + return list(self._conf_hvac_mode_set) + [HVAC_MODE_OFF] + + @property + def hvac_action(self): + """Return the current running hvac operation if supported. + + Need to be one of CURRENT_HVAC_*. + """ + if self._config.get(CONF_HEURISTIC_ACTION, False): + if self._hvac_mode == HVAC_MODE_HEAT: + if self._current_temperature < ( + self._target_temperature - self._precision + ): + self._hvac_action = CURRENT_HVAC_HEAT + if self._current_temperature == ( + self._target_temperature - self._precision + ): + if self._hvac_action == CURRENT_HVAC_HEAT: + self._hvac_action = CURRENT_HVAC_HEAT + if self._hvac_action == CURRENT_HVAC_IDLE: + self._hvac_action = CURRENT_HVAC_IDLE + if ( + self._current_temperature + self._precision + ) > self._target_temperature: + self._hvac_action = CURRENT_HVAC_IDLE + return self._hvac_action + return self._hvac_action + + @property + def preset_mode(self): + """Return current preset.""" + return self._preset_mode + + @property + def preset_modes(self): + """Return the list of available presets modes.""" + if not self._has_presets: + return None + presets = list(self._conf_preset_set) + if self._conf_eco_dp: + presets.append(PRESET_ECO) + return presets + + @property + def current_temperature(self): + """Return the current temperature.""" + return self._current_temperature + + @property + def target_temperature(self): + """Return the temperature we try to reach.""" + return self._target_temperature + + @property + def target_temperature_step(self): + """Return the supported step of target temperature.""" + return self._config.get(CONF_TEMPERATURE_STEP, DEFAULT_TEMPERATURE_STEP) + + @property + def fan_mode(self): + """Return the fan setting.""" + return NotImplementedError() + + @property + def fan_modes(self): + """Return the list of available fan modes.""" + return NotImplementedError() + + async def async_set_temperature(self, **kwargs): + """Set new target temperature.""" + if ATTR_TEMPERATURE in kwargs and self.has_config(CONF_TARGET_TEMPERATURE_DP): + temperature = round(kwargs[ATTR_TEMPERATURE] / self._target_precision) + await self._device.set_dp( + temperature, self._config[CONF_TARGET_TEMPERATURE_DP] + ) + + def set_fan_mode(self, fan_mode): + """Set new target fan mode.""" + return NotImplementedError() + + async def async_set_hvac_mode(self, hvac_mode): + """Set new target operation mode.""" + if hvac_mode == HVAC_MODE_OFF: + await self._device.set_dp(False, self._dp_id) + return + if not self._state and self._conf_hvac_mode_dp != self._dp_id: + await self._device.set_dp(True, self._dp_id) + # Some thermostats need a small wait before sending another update + await asyncio.sleep(MODE_WAIT) + await self._device.set_dp( + self._conf_hvac_mode_set[hvac_mode], self._conf_hvac_mode_dp + ) + + async def async_turn_on(self) -> None: + """Turn the entity on.""" + await self._device.set_dp(True, self._dp_id) + + async def async_turn_off(self) -> None: + """Turn the entity off.""" + await self._device.set_dp(False, self._dp_id) + + async def async_set_preset_mode(self, preset_mode): + """Set new target preset mode.""" + if preset_mode == PRESET_ECO: + await self._device.set_dp(self._conf_eco_value, self._conf_eco_dp) + return + await self._device.set_dp( + self._conf_preset_set[preset_mode], self._conf_preset_dp + ) + + @property + def min_temp(self): + """Return the minimum temperature.""" + if self.has_config(CONF_MIN_TEMP_DP): + return self.dps_conf(CONF_MIN_TEMP_DP) + return DEFAULT_MIN_TEMP + + @property + def max_temp(self): + """Return the maximum temperature.""" + if self.has_config(CONF_MAX_TEMP_DP): + return self.dps_conf(CONF_MAX_TEMP_DP) + return DEFAULT_MAX_TEMP + + def status_updated(self): + """Device status was updated.""" + self._state = self.dps(self._dp_id) + + if self.has_config(CONF_TARGET_TEMPERATURE_DP): + self._target_temperature = ( + self.dps_conf(CONF_TARGET_TEMPERATURE_DP) * self._target_precision + ) + + if self.has_config(CONF_CURRENT_TEMPERATURE_DP): + self._current_temperature = ( + self.dps_conf(CONF_CURRENT_TEMPERATURE_DP) * self._precision + ) + + if self._has_presets: + if ( + self.has_config(CONF_ECO_DP) + and self.dps_conf(CONF_ECO_DP) == self._conf_eco_value + ): + self._preset_mode = PRESET_ECO + else: + for preset, value in self._conf_preset_set.items(): # todo remove + if self.dps_conf(CONF_PRESET_DP) == value: + self._preset_mode = preset + break + else: + self._preset_mode = PRESET_NONE + + # Update the HVAC status + if self.has_config(CONF_HVAC_MODE_DP): + if not self._state: + self._hvac_mode = HVAC_MODE_OFF + else: + for mode, value in self._conf_hvac_mode_set.items(): + if self.dps_conf(CONF_HVAC_MODE_DP) == value: + self._hvac_mode = mode + break + else: + # in case hvac mode and preset share the same dp + self._hvac_mode = HVAC_MODE_AUTO + + # Update the current action + for action, value in self._conf_hvac_action_set.items(): + if self.dps_conf(CONF_HVAC_ACTION_DP) == value: + self._hvac_action = action + + +async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaClimate, flow_schema) diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index 68b0d28..ea821a5 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -1,6 +1,7 @@ """Code shared between all platforms.""" import asyncio import logging +from datetime import timedelta from homeassistant.const import ( CONF_DEVICE_ID, @@ -9,8 +10,10 @@ from homeassistant.const import ( CONF_HOST, CONF_ID, CONF_PLATFORM, + CONF_SCAN_INTERVAL, ) from homeassistant.core import callback +from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, @@ -117,6 +120,7 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._is_closing = False self._connect_task = None self._disconnect_task = None + self._unsub_interval = None self.set_logger(_LOGGER, config_entry[CONF_DEVICE_ID]) # This has to be done in case the device type is type_0d @@ -166,6 +170,16 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._disconnect_task = async_dispatcher_connect( self._hass, signal, _new_entity_handler ) + + if ( + CONF_SCAN_INTERVAL in self._config_entry + and self._config_entry[CONF_SCAN_INTERVAL] > 0 + ): + self._unsub_interval = async_track_time_interval( + self._hass, + self._async_refresh, + timedelta(seconds=self._config_entry[CONF_SCAN_INTERVAL]), + ) except Exception: # pylint: disable=broad-except self.exception(f"Connect to {self._config_entry[CONF_HOST]} failed") if self._interface is not None: @@ -173,6 +187,10 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): self._interface = None self._connect_task = None + async def _async_refresh(self, _now): + if self._interface is not None: + await self._interface.update_dps() + async def close(self): """Close connection and stop re-connect loop.""" self._is_closing = True @@ -223,7 +241,9 @@ class TuyaDevice(pytuya.TuyaListener, pytuya.ContextualLogger): """Device disconnected.""" signal = f"localtuya_{self._config_entry[CONF_DEVICE_ID]}" async_dispatcher_send(self._hass, signal, None) - + if self._unsub_interval is not None: + self._unsub_interval() + self._unsub_interval = None self._interface = None self.debug("Disconnected - waiting for discovery broadcast") @@ -253,13 +273,13 @@ class LocalTuyaEntity(RestoreEntity, pytuya.ContextualLogger): def _update_handler(status): """Update entity state when status was updated.""" - if status is not None: - self._status = status - self.status_updated() - else: - self._status = {} - - self.schedule_update_ha_state() + if status is None: + status = {} + if self._status != status: + self._status = status.copy() + if status: + self.status_updated() + self.schedule_update_ha_state() signal = f"localtuya_{self._config_entry.data[CONF_DEVICE_ID]}" diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 731b60a..03bf80d 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -14,6 +14,7 @@ from homeassistant.const import ( CONF_HOST, CONF_ID, CONF_PLATFORM, + CONF_SCAN_INTERVAL, ) from homeassistant.core import callback @@ -44,6 +45,7 @@ BASIC_INFO_SCHEMA = vol.Schema( vol.Required(CONF_HOST): str, vol.Required(CONF_DEVICE_ID): str, vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]), + vol.Optional(CONF_SCAN_INTERVAL): int, } ) @@ -55,6 +57,7 @@ DEVICE_SCHEMA = vol.Schema( vol.Required(CONF_LOCAL_KEY): cv.string, vol.Required(CONF_FRIENDLY_NAME): cv.string, vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]), + vol.Optional(CONF_SCAN_INTERVAL): int, } ) @@ -90,6 +93,7 @@ def options_schema(entities): vol.Required(CONF_HOST): str, vol.Required(CONF_LOCAL_KEY): str, vol.Required(CONF_PROTOCOL_VERSION, default="3.3"): vol.In(["3.1", "3.3"]), + vol.Optional(CONF_SCAN_INTERVAL): int, vol.Required( CONF_ENTITIES, description={"suggested_value": entity_names} ): cv.multi_select(entity_names), diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 12fa66c..4148771 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -16,6 +16,7 @@ CONF_COLOR = "color" CONF_COLOR_MODE = "color_mode" CONF_COLOR_TEMP_MIN_KELVIN = "color_temp_min_kelvin" CONF_COLOR_TEMP_MAX_KELVIN = "color_temp_max_kelvin" +CONF_COLOR_TEMP_REVERSE = "color_temp_reverse" CONF_MUSIC_MODE = "music_mode" # switch @@ -34,13 +35,53 @@ CONF_SPAN_TIME = "span_time" # fan CONF_FAN_SPEED_CONTROL = "fan_speed_control" CONF_FAN_OSCILLATING_CONTROL = "fan_oscillating_control" -CONF_FAN_SPEED_LOW = "fan_speed_low" -CONF_FAN_SPEED_MEDIUM = "fan_speed_medium" -CONF_FAN_SPEED_HIGH = "fan_speed_high" +CONF_FAN_SPEED_MIN = "fan_speed_min" +CONF_FAN_SPEED_MAX = "fan_speed_max" +CONF_FAN_ORDERED_LIST = "fan_speed_ordered_list" +CONF_FAN_DIRECTION = "fan_direction" +CONF_FAN_DIRECTION_FWD = "fan_direction_forward" +CONF_FAN_DIRECTION_REV = "fan_direction_reverse" # sensor CONF_SCALING = "scaling" +# climate +CONF_TARGET_TEMPERATURE_DP = "target_temperature_dp" +CONF_CURRENT_TEMPERATURE_DP = "current_temperature_dp" +CONF_TEMPERATURE_STEP = "temperature_step" +CONF_MAX_TEMP_DP = "max_temperature_dp" +CONF_MIN_TEMP_DP = "min_temperature_dp" +CONF_PRECISION = "precision" +CONF_TARGET_PRECISION = "target_precision" +CONF_HVAC_MODE_DP = "hvac_mode_dp" +CONF_HVAC_MODE_SET = "hvac_mode_set" +CONF_PRESET_DP = "preset_dp" +CONF_PRESET_SET = "preset_set" +CONF_HEURISTIC_ACTION = "heuristic_action" +CONF_HVAC_ACTION_DP = "hvac_action_dp" +CONF_HVAC_ACTION_SET = "hvac_action_set" +CONF_ECO_DP = "eco_dp" +CONF_ECO_VALUE = "eco_value" + +# vacuum +CONF_POWERGO_DP = "powergo_dp" +CONF_IDLE_STATUS_VALUE = "idle_status_value" +CONF_RETURNING_STATUS_VALUE = "returning_status_value" +CONF_DOCKED_STATUS_VALUE = "docked_status_value" +CONF_BATTERY_DP = "battery_dp" +CONF_MODE_DP = "mode_dp" +CONF_MODES = "modes" +CONF_FAN_SPEED_DP = "fan_speed_dp" +CONF_FAN_SPEEDS = "fan_speeds" +CONF_CLEAN_TIME_DP = "clean_time_dp" +CONF_CLEAN_AREA_DP = "clean_area_dp" +CONF_CLEAN_RECORD_DP = "clean_record_dp" +CONF_LOCATE_DP = "locate_dp" +CONF_FAULT_DP = "fault_dp" +CONF_PAUSED_STATE = "paused_state" +CONF_RETURN_MODE = "return_mode" +CONF_STOP_STATUS = "stop_status" + DATA_DISCOVERY = "discovery" DOMAIN = "localtuya" @@ -48,6 +89,7 @@ DOMAIN = "localtuya" # Platforms in this list must support config flows PLATFORMS = [ "binary_sensor", + "climate", "cover", "fan", "light", @@ -55,6 +97,7 @@ PLATFORMS = [ "select", "sensor", "switch", + "vacuum", ] TUYA_DEVICE = "tuya_device" diff --git a/custom_components/localtuya/fan.py b/custom_components/localtuya/fan.py index 0c65dfa..d2b4583 100644 --- a/custom_components/localtuya/fan.py +++ b/custom_components/localtuya/fan.py @@ -1,26 +1,37 @@ """Platform to locally control Tuya-based fan devices.""" import logging +import math from functools import partial +import homeassistant.helpers.config_validation as cv import voluptuous as vol from homeassistant.components.fan import ( + DIRECTION_FORWARD, + DIRECTION_REVERSE, DOMAIN, - SPEED_HIGH, - SPEED_LOW, - SPEED_MEDIUM, - SPEED_OFF, + SUPPORT_DIRECTION, SUPPORT_OSCILLATE, SUPPORT_SET_SPEED, FanEntity, ) +from homeassistant.util.percentage import ( + int_states_in_range, + ordered_list_item_to_percentage, + percentage_to_ordered_list_item, + percentage_to_ranged_value, + ranged_value_to_percentage, +) from .common import LocalTuyaEntity, async_setup_entry from .const import ( + CONF_FAN_DIRECTION, + CONF_FAN_DIRECTION_FWD, + CONF_FAN_DIRECTION_REV, + CONF_FAN_ORDERED_LIST, CONF_FAN_OSCILLATING_CONTROL, CONF_FAN_SPEED_CONTROL, - CONF_FAN_SPEED_HIGH, - CONF_FAN_SPEED_LOW, - CONF_FAN_SPEED_MEDIUM, + CONF_FAN_SPEED_MAX, + CONF_FAN_SPEED_MIN, ) _LOGGER = logging.getLogger(__name__) @@ -31,15 +42,12 @@ def flow_schema(dps): return { vol.Optional(CONF_FAN_SPEED_CONTROL): vol.In(dps), vol.Optional(CONF_FAN_OSCILLATING_CONTROL): vol.In(dps), - vol.Optional(CONF_FAN_SPEED_LOW, default=SPEED_LOW): vol.In( - [SPEED_LOW, "1", "2", "small"] - ), - vol.Optional(CONF_FAN_SPEED_MEDIUM, default=SPEED_MEDIUM): vol.In( - [SPEED_MEDIUM, "mid", "2", "3"] - ), - vol.Optional(CONF_FAN_SPEED_HIGH, default=SPEED_HIGH): vol.In( - [SPEED_HIGH, "auto", "3", "4", "large", "big"] - ), + vol.Optional(CONF_FAN_DIRECTION): vol.In(dps), + vol.Optional(CONF_FAN_DIRECTION_FWD, default="forward"): cv.string, + vol.Optional(CONF_FAN_DIRECTION_REV, default="reverse"): cv.string, + vol.Optional(CONF_FAN_SPEED_MIN, default=1): cv.positive_int, + vol.Optional(CONF_FAN_SPEED_MAX, default=9): cv.positive_int, + vol.Optional(CONF_FAN_ORDERED_LIST, default="disabled"): cv.string, } @@ -56,28 +64,46 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): """Initialize the entity.""" super().__init__(device, config_entry, fanid, _LOGGER, **kwargs) self._is_on = False - self._speed = None self._oscillating = None + self._direction = None + self._percentage = None + self._speed_range = ( + self._config.get(CONF_FAN_SPEED_MIN), + self._config.get(CONF_FAN_SPEED_MAX), + ) + self._ordered_list = self._config.get(CONF_FAN_ORDERED_LIST).split(",") + self._ordered_list_mode = None + + if isinstance(self._ordered_list, list) and len(self._ordered_list) > 1: + self._use_ordered_list = True + _LOGGER.debug( + "Fan _use_ordered_list: %s > %s", + self._use_ordered_list, + self._ordered_list, + ) + else: + self._use_ordered_list = False + _LOGGER.debug("Fan _use_ordered_list: %s", self._use_ordered_list) @property def oscillating(self): """Return current oscillating status.""" return self._oscillating + @property + def current_direction(self): + """Return the current direction of the fan.""" + return self._direction + @property def is_on(self): """Check if Tuya fan is on.""" return self._is_on @property - def speed(self) -> str: - """Return the current speed.""" - return self._speed - - @property - def speed_list(self) -> list: - """Get the list of available speeds.""" - return [SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH] + def percentage(self): + """Return the current percentage.""" + return self._percentage async def async_turn_on( self, @@ -87,76 +113,143 @@ class LocaltuyaFan(LocalTuyaEntity, FanEntity): **kwargs, ) -> None: """Turn on the entity.""" + _LOGGER.debug("Fan async_turn_on") await self._device.set_dp(True, self._dp_id) - if speed is not None: - await self.async_set_speed(speed) + if percentage is not None: + await self.async_set_percentage(percentage) else: self.schedule_update_ha_state() async def async_turn_off(self, **kwargs) -> None: """Turn off the entity.""" + _LOGGER.debug("Fan async_turn_off") + await self._device.set_dp(False, self._dp_id) self.schedule_update_ha_state() - async def async_set_speed(self, speed: str) -> None: + async def async_set_percentage(self, percentage): """Set the speed of the fan.""" - mapping = { - SPEED_LOW: self._config.get(CONF_FAN_SPEED_LOW), - SPEED_MEDIUM: self._config.get(CONF_FAN_SPEED_MEDIUM), - SPEED_HIGH: self._config.get(CONF_FAN_SPEED_HIGH), - } + _LOGGER.debug("Fan async_set_percentage: %s", percentage) - if speed == SPEED_OFF: - await self._device.set_dp(False, self._dp_id) - else: - await self._device.set_dp( - mapping.get(speed), self._config.get(CONF_FAN_SPEED_CONTROL) - ) + if percentage is not None: + if percentage == 0: + return await self.async_turn_off() + if not self.is_on: + await self.async_turn_on() + if self._use_ordered_list: + await self._device.set_dp( + str( + percentage_to_ordered_list_item(self._ordered_list, percentage) + ), + self._config.get(CONF_FAN_SPEED_CONTROL), + ) + _LOGGER.debug( + "Fan async_set_percentage: %s > %s", + percentage, + percentage_to_ordered_list_item(self._ordered_list, percentage), + ) - self.schedule_update_ha_state() + else: + await self._device.set_dp( + str( + math.ceil( + percentage_to_ranged_value(self._speed_range, percentage) + ) + ), + self._config.get(CONF_FAN_SPEED_CONTROL), + ) + _LOGGER.debug( + "Fan async_set_percentage: %s > %s", + percentage, + percentage_to_ranged_value(self._speed_range, percentage), + ) + self.schedule_update_ha_state() async def async_oscillate(self, oscillating: bool) -> None: """Set oscillation.""" + _LOGGER.debug("Fan async_oscillate: %s", oscillating) await self._device.set_dp( oscillating, self._config.get(CONF_FAN_OSCILLATING_CONTROL) ) self.schedule_update_ha_state() + async def async_set_direction(self, direction): + """Set the direction of the fan.""" + _LOGGER.debug("Fan async_set_direction: %s", direction) + + if direction == DIRECTION_FORWARD: + value = self._config.get(CONF_FAN_DIRECTION_FWD) + + if direction == DIRECTION_REVERSE: + value = self._config.get(CONF_FAN_DIRECTION_REV) + await self._device.set_dp(value, self._config.get(CONF_FAN_DIRECTION)) + self.schedule_update_ha_state() + @property def supported_features(self) -> int: """Flag supported features.""" - supports = 0 + features = 0 if self.has_config(CONF_FAN_OSCILLATING_CONTROL): - supports |= SUPPORT_OSCILLATE - if self.has_config(CONF_FAN_SPEED_CONTROL): - supports |= SUPPORT_SET_SPEED + features |= SUPPORT_OSCILLATE - return supports + if self.has_config(CONF_FAN_SPEED_CONTROL): + features |= SUPPORT_SET_SPEED + + if self.has_config(CONF_FAN_DIRECTION): + features |= SUPPORT_DIRECTION + + return features + + @property + def speed_count(self) -> int: + """Speed count for the fan.""" + speed_count = int_states_in_range(self._speed_range) + _LOGGER.debug("Fan speed_count: %s", speed_count) + return speed_count def status_updated(self): """Get state of Tuya fan.""" - mappings = { - self._config.get(CONF_FAN_SPEED_LOW): SPEED_LOW, - self._config.get(CONF_FAN_SPEED_MEDIUM): SPEED_MEDIUM, - self._config.get(CONF_FAN_SPEED_HIGH): SPEED_HIGH, - } - self._is_on = self.dps(self._dp_id) - if self.has_config(CONF_FAN_SPEED_CONTROL): - self._speed = mappings.get(self.dps_conf(CONF_FAN_SPEED_CONTROL)) - if self.speed is None: - self.warning( - "%s/%s: Ignoring unknown fan controller state: %s", - self.name, - self.entity_id, - self.dps_conf(CONF_FAN_SPEED_CONTROL), + current_speed = self.dps_conf(CONF_FAN_SPEED_CONTROL) + if self._use_ordered_list: + _LOGGER.debug( + "Fan current_speed ordered_list_item_to_percentage: %s from %s", + current_speed, + self._ordered_list, + ) + if current_speed is not None: + self._percentage = ordered_list_item_to_percentage( + self._ordered_list, current_speed ) - self._speed = None + + else: + _LOGGER.debug( + "Fan current_speed ranged_value_to_percentage: %s from %s", + current_speed, + self._speed_range, + ) + if current_speed is not None: + self._percentage = ranged_value_to_percentage( + self._speed_range, int(current_speed) + ) + + _LOGGER.debug("Fan current_percentage: %s", self._percentage) if self.has_config(CONF_FAN_OSCILLATING_CONTROL): self._oscillating = self.dps_conf(CONF_FAN_OSCILLATING_CONTROL) + _LOGGER.debug("Fan current_oscillating : %s", self._oscillating) + + if self.has_config(CONF_FAN_DIRECTION): + value = self.dps_conf(CONF_FAN_DIRECTION) + if value is not None: + if value == self._config.get(CONF_FAN_DIRECTION_FWD): + self._direction = DIRECTION_FORWARD + + if value == self._config.get(CONF_FAN_DIRECTION_REV): + self._direction = DIRECTION_REVERSE + _LOGGER.debug("Fan current_direction : %s > %s", value, self._direction) async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaFan, flow_schema) diff --git a/custom_components/localtuya/light.py b/custom_components/localtuya/light.py index 49fade8..7c74e49 100644 --- a/custom_components/localtuya/light.py +++ b/custom_components/localtuya/light.py @@ -27,15 +27,17 @@ from .const import ( CONF_COLOR_MODE, CONF_COLOR_TEMP_MAX_KELVIN, CONF_COLOR_TEMP_MIN_KELVIN, + CONF_COLOR_TEMP_REVERSE, CONF_MUSIC_MODE, ) _LOGGER = logging.getLogger(__name__) -MIRED_TO_KELVIN_CONST = 1000000 DEFAULT_MIN_KELVIN = 2700 # MIRED 370 DEFAULT_MAX_KELVIN = 6500 # MIRED 153 +DEFAULT_COLOR_TEMP_REVERSE = False + DEFAULT_LOWER_BRIGHTNESS = 29 DEFAULT_UPPER_BRIGHTNESS = 1000 @@ -117,6 +119,11 @@ def flow_schema(dps): vol.Optional(CONF_COLOR_TEMP_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All( 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_MUSIC_MODE, default=False, description={"suggested_value": False} @@ -146,13 +153,14 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): CONF_BRIGHTNESS_UPPER, DEFAULT_UPPER_BRIGHTNESS ) self._upper_color_temp = self._upper_brightness - self._max_mired = round( - MIRED_TO_KELVIN_CONST - / self._config.get(CONF_COLOR_TEMP_MIN_KELVIN, DEFAULT_MIN_KELVIN) + self._max_mired = color_util.color_temperature_kelvin_to_mired( + self._config.get(CONF_COLOR_TEMP_MIN_KELVIN, DEFAULT_MIN_KELVIN) ) - self._min_mired = round( - MIRED_TO_KELVIN_CONST - / self._config.get(CONF_COLOR_TEMP_MAX_KELVIN, DEFAULT_MAX_KELVIN) + self._min_mired = color_util.color_temperature_kelvin_to_mired( + 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._effect = None @@ -199,11 +207,16 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): def color_temp(self): """Return the color_temp of the light.""" 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( self._max_mired - ( ((self._max_mired - self._min_mired) / self._upper_color_temp) - * self._color_temp + * color_temp_value ) ) return None @@ -364,10 +377,17 @@ class LocaltuyaLight(LocalTuyaEntity, LightEntity): if ATTR_COLOR_TEMP in kwargs and (features & SUPPORT_COLOR_TEMP): if brightness is None: brightness = self._brightness + mired = int(kwargs[ATTR_COLOR_TEMP]) + if self._color_temp_reverse: + mired = self._max_mired - (mired - self._min_mired) + if mired < self._min_mired: + mired = self._min_mired + elif mired > self._max_mired: + mired = self._max_mired color_temp = int( self._upper_color_temp - (self._upper_color_temp / (self._max_mired - self._min_mired)) - * (int(kwargs[ATTR_COLOR_TEMP]) - self._min_mired) + * (mired - self._min_mired) ) states[self._config.get(CONF_COLOR_MODE)] = MODE_WHITE states[self._config.get(CONF_BRIGHTNESS)] = brightness diff --git a/custom_components/localtuya/number.py b/custom_components/localtuya/number.py index 328f6a2..596eb01 100644 --- a/custom_components/localtuya/number.py +++ b/custom_components/localtuya/number.py @@ -4,10 +4,7 @@ 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 homeassistant.const import CONF_DEVICE_CLASS, STATE_UNKNOWN from .common import LocalTuyaEntity, async_setup_entry diff --git a/custom_components/localtuya/pytuya/__init__.py b/custom_components/localtuya/pytuya/__init__.py index 789091c..b7645ec 100644 --- a/custom_components/localtuya/pytuya/__init__.py +++ b/custom_components/localtuya/pytuya/__init__.py @@ -21,6 +21,7 @@ Functions json = status() # returns json payload set_version(version) # 3.1 [default] or 3.3 detect_available_dps() # returns a list of available dps provided by the device + update_dps(dps) # sends update dps command add_dps_to_request(dp_index) # adds dp_index to the list of dps used by the # device (to be queried in the payload) set_dp(on, dp_index) # Set value of any dps index. @@ -61,6 +62,7 @@ TuyaMessage = namedtuple("TuyaMessage", "seqno cmd retcode payload crc") SET = "set" STATUS = "status" HEARTBEAT = "heartbeat" +UPDATEDPS = "updatedps" # Request refresh of DPS PROTOCOL_VERSION_BYTES_31 = b"3.1" PROTOCOL_VERSION_BYTES_33 = b"3.3" @@ -76,6 +78,9 @@ SUFFIX_VALUE = 0x0000AA55 HEARTBEAT_INTERVAL = 10 +# DPS that are known to be safe to use with update_dps (0x12) command +UPDATE_DPS_WHITELIST = [18, 19, 20] # Socket (Wi-Fi) + # This is intended to match requests.json payload at # https://github.com/codetheweb/tuyapi : # type_0a devices require the 0a command as the status request @@ -90,11 +95,13 @@ PAYLOAD_DICT = { STATUS: {"hexByte": 0x0A, "command": {"gwId": "", "devId": ""}}, SET: {"hexByte": 0x07, "command": {"devId": "", "uid": "", "t": ""}}, HEARTBEAT: {"hexByte": 0x09, "command": {}}, + UPDATEDPS: {"hexByte": 0x12, "command": {"dpId": [18, 19, 20]}}, }, "type_0d": { STATUS: {"hexByte": 0x0D, "command": {"devId": "", "uid": "", "t": ""}}, SET: {"hexByte": 0x07, "command": {"devId": "", "uid": "", "t": ""}}, HEARTBEAT: {"hexByte": 0x09, "command": {}}, + UPDATEDPS: {"hexByte": 0x12, "command": {"dpId": [18, 19, 20]}}, }, } @@ -292,6 +299,8 @@ class MessageDispatcher(ContextualLogger): sem = self.listeners[self.HEARTBEAT_SEQNO] self.listeners[self.HEARTBEAT_SEQNO] = msg sem.release() + elif msg.cmd == 0x12: + self.debug("Got normal updatedps response") elif msg.cmd == 0x08: self.debug("Got status update") self.listener(msg) @@ -478,6 +487,26 @@ class TuyaProtocol(asyncio.Protocol, ContextualLogger): """Send a heartbeat message.""" return await self.exchange(HEARTBEAT) + async def update_dps(self, dps=None): + """ + Request device to update index. + + Args: + dps([int]): list of dps to update, default=detected&whitelisted + """ + if self.version == 3.3: + if dps is None: + if not self.dps_cache: + await self.detect_available_dps() + if self.dps_cache: + dps = [int(dp) for dp in self.dps_cache] + # filter non whitelisted dps + dps = list(set(dps).intersection(set(UPDATE_DPS_WHITELIST))) + self.debug("updatedps() entry (dps %s, dps_cache %s)", dps, self.dps_cache) + payload = self._generate_payload(UPDATEDPS, dps) + self.transport.write(payload) + return True + async def set_dp(self, value, dp_index): """ Set value (may be any type: bool, int or string) of any dps index. @@ -582,7 +611,10 @@ class TuyaProtocol(asyncio.Protocol, ContextualLogger): json_data["t"] = str(int(time.time())) if data is not None: - json_data["dps"] = data + if "dpId" in json_data: + json_data["dpId"] = data + else: + json_data["dps"] = data elif command_hb == 0x0D: json_data["dps"] = self.dps_to_request @@ -591,7 +623,7 @@ class TuyaProtocol(asyncio.Protocol, ContextualLogger): if self.version == 3.3: payload = self.cipher.encrypt(payload, False) - if command_hb != 0x0A: + if command_hb not in [0x0A, 0x12]: # add the 3.3 header payload = PROTOCOL_33_HEADER + payload elif command == SET: diff --git a/custom_components/localtuya/select.py b/custom_components/localtuya/select.py index 43b32c9..29d11c9 100644 --- a/custom_components/localtuya/select.py +++ b/custom_components/localtuya/select.py @@ -4,10 +4,7 @@ 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 homeassistant.const import CONF_DEVICE_CLASS, STATE_UNKNOWN from .common import LocalTuyaEntity, async_setup_entry diff --git a/custom_components/localtuya/strings.json b/custom_components/localtuya/strings.json index 898b99d..b8bedc8 100644 --- a/custom_components/localtuya/strings.json +++ b/custom_components/localtuya/strings.json @@ -20,6 +20,7 @@ "device_id": "Device ID", "local_key": "Local key", "protocol_version": "Protocol Version", + "scan_interval": "Scan interval (seconds, only when not updating automatically)", "device_type": "Device type" } }, @@ -39,4 +40,4 @@ } }, "title": "LocalTuya" -} \ No newline at end of file +} diff --git a/custom_components/localtuya/switch.py b/custom_components/localtuya/switch.py index d3e08f5..f43d910 100644 --- a/custom_components/localtuya/switch.py +++ b/custom_components/localtuya/switch.py @@ -48,7 +48,7 @@ class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity): return self._state @property - def device_state_attributes(self): + def extra_state_attributes(self): """Return device state attributes.""" attrs = {} if self.has_config(CONF_CURRENT): diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index f12c4ab..178e999 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -29,7 +29,8 @@ "host": "Host", "device_id": "Device ID", "local_key": "Local key", - "protocol_version": "Protocol Version" + "protocol_version": "Protocol Version", + "scan_interval": "Scan interval (seconds, only when not updating automatically)" } }, "pick_entity_type": { @@ -60,25 +61,61 @@ "scaling": "Scaling Factor", "state_on": "On Value", "state_off": "Off Value", + "powergo_dp": "Power DP (Usually 25 or 2)", + "idle_status_value": "Idle Status (comma-separated)", + "returning_status_value": "Returning Status", + "docked_status_value": "Docked Status (comma-separated)", + "fault_dp": "Fault DP (Usually 11)", + "battery_dp": "Battery status DP (Usually 14)", + "mode_dp": "Mode DP (Usually 27)", + "modes": "Modes list", + "return_mode": "Return home mode", + "fan_speed_dp": "Fan speeds DP (Usually 30)", + "fan_speeds": "Fan speeds list (comma-separated)", + "clean_time_dp": "Clean Time DP (Usually 33)", + "clean_area_dp": "Clean Area DP (Usually 32)", + "clean_record_dp": "Clean Record DP (Usually 34)", + "locate_dp": "Locate DP (Usually 31)", + "paused_state": "Pause state (pause, paused, etc)", + "stop_status": "Stop status", "brightness": "Brightness (only for white color)", "brightness_lower": "Brightness Lower Value", "brightness_upper": "Brightness Upper Value", "color_temp": "Color Temperature", + "color_temp_reverse": "Color Temperature Reverse", "color": "Color", "color_mode": "Color Mode", "color_temp_min_kelvin": "Minimum Color Temperature in K", "color_temp_max_kelvin": "Maximum Color Temperature in K", "music_mode": "Music mode available", "scene": "Scene", - "fan_speed_control": "Fan Speed Control", - "fan_oscillating_control": "Fan Oscillating Control", - "fan_speed_low": "Fan Low Speed Setting", - "fan_speed_medium": "Fan Medium 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 ;" + "select_options_friendly": "User Friendly options, separate entries by a ;", + "fan_speed_control": "Fan Speed Control dps", + "fan_oscillating_control": "Fan Oscillating Control dps", + "fan_speed_min": "minimum fan speed integer", + "fan_speed_max": "maximum fan speed integer", + "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", + "fan_direction":"fan direction dps", + "fan_direction_forward": "forward dps string", + "fan_direction_reverse": "reverse dps string", + "current_temperature_dp": "Current Temperature", + "target_temperature_dp": "Target Temperature", + "temperature_step": "Temperature Step (optional)", + "max_temperature_dp": "Max Temperature (optional)", + "min_temperature_dp": "Min Temperature (optional)", + "precision": "Precision (optional, for DPs values)", + "target_precision": "Target Precision (optional, for DPs values)", + "temperature_unit": "Temperature Unit (optional)", + "hvac_mode_dp": "HVAC Mode DP (optional)", + "hvac_mode_set": "HVAC Mode Set (optional)", + "hvac_action_dp": "HVAC Current Action DP (optional)", + "hvac_action_set": "HVAC Current Action Set (optional)", + "preset_dp": "Presets DP (optional)", + "preset_set": "Presets Set (optional)", + "eco_dp": "Eco DP (optional)", + "eco_value": "Eco value (optional)", + "heuristic_action": "Enable heuristic action (optional)" } } } @@ -93,6 +130,7 @@ "host": "Host", "local_key": "Local key", "protocol_version": "Protocol Version", + "scan_interval": "Scan interval (seconds, only when not updating automatically)", "entities": "Entities (uncheck an entity to remove it)" } }, @@ -116,25 +154,61 @@ "scaling": "Scaling Factor", "state_on": "On Value", "state_off": "Off Value", + "powergo_dp": "Power DP (Usually 25 or 2)", + "idle_status_value": "Idle Status (comma-separated)", + "returning_status_value": "Returning Status", + "docked_status_value": "Docked Status (comma-separated)", + "fault_dp": "Fault DP (Usually 11)", + "battery_dp": "Battery status DP (Usually 14)", + "mode_dp": "Mode DP (Usually 27)", + "modes": "Modes list", + "return_mode": "Return home mode", + "fan_speed_dp": "Fan speeds DP (Usually 30)", + "fan_speeds": "Fan speeds list (comma-separated)", + "clean_time_dp": "Clean Time DP (Usually 33)", + "clean_area_dp": "Clean Area DP (Usually 32)", + "clean_record_dp": "Clean Record DP (Usually 34)", + "locate_dp": "Locate DP (Usually 31)", + "paused_state": "Pause state (pause, paused, etc)", + "stop_status": "Stop status", "brightness": "Brightness (only for white color)", "brightness_lower": "Brightness Lower Value", "brightness_upper": "Brightness Upper Value", "color_temp": "Color Temperature", + "color_temp_reverse": "Color Temperature Reverse", "color": "Color", "color_mode": "Color Mode", "color_temp_min_kelvin": "Minimum Color Temperature in K", "color_temp_max_kelvin": "Maximum Color Temperature in K", "music_mode": "Music mode available", "scene": "Scene", - "fan_speed_control": "Fan Speed Control", - "fan_oscillating_control": "Fan Oscillating Control", - "fan_speed_low": "Fan Low Speed Setting", - "fan_speed_medium": "Fan Medium 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 ;" + "select_options_friendly": "User Friendly options, separate entries by a ;", + "fan_speed_control": "Fan Speed Control dps", + "fan_oscillating_control": "Fan Oscillating Control dps", + "fan_speed_min": "minimum fan speed integer", + "fan_speed_max": "maximum fan speed integer", + "fan_speed_ordered_list": "Fan speed modes list (overrides speed min/max)", + "fan_direction":"fan direction dps", + "fan_direction_forward": "forward dps string", + "fan_direction_reverse": "reverse dps string", + "current_temperature_dp": "Current Temperature", + "target_temperature_dp": "Target Temperature", + "temperature_step": "Temperature Step (optional)", + "max_temperature_dp": "Max Temperature (optional)", + "min_temperature_dp": "Min Temperature (optional)", + "precision": "Precision (optional, for DPs values)", + "target_precision": "Target Precision (optional, for DPs values)", + "temperature_unit": "Temperature Unit (optional)", + "hvac_mode_dp": "HVAC Mode DP (optional)", + "hvac_mode_set": "HVAC Mode Set (optional)", + "hvac_action_dp": "HVAC Current Action DP (optional)", + "hvac_action_set": "HVAC Current Action Set (optional)", + "preset_dp": "Presets DP (optional)", + "preset_set": "Presets Set (optional)", + "eco_dp": "Eco DP (optional)", + "eco_value": "Eco value (optional)", + "heuristic_action": "Enable heuristic action (optional)" } }, "yaml_import": { diff --git a/custom_components/localtuya/translations/pt-BR.json b/custom_components/localtuya/translations/pt-BR.json new file mode 100644 index 0000000..c27dc51 --- /dev/null +++ b/custom_components/localtuya/translations/pt-BR.json @@ -0,0 +1,217 @@ +{ + "config": { + "abort": { + "already_configured": "O dispositivo já foi configurado.", + "device_updated": "A configuração do dispositivo foi atualizada!" + }, + "error": { + "cannot_connect": "Não é possível se conectar ao dispositivo. Verifique se o endereço está correto e tente novamente.", + "invalid_auth": "Falha ao autenticar com o dispositivo. Verifique se o ID do dispositivo e a chave local estão corretos.", + "unknown": "Ocorreu um erro desconhecido. Consulte o registro para obter detalhes.", + "entity_already_configured": "A entidade com este ID já foi configurada.", + "address_in_use": "O endereço usado para descoberta já está em uso. Certifique-se de que nenhum outro aplicativo o esteja usando (porta TCP 6668).", + "discovery_failed": "Algo falhou ao descobrir dispositivos. Consulte o registro para obter detalhes.", + "empty_dps": "A conexão com o dispositivo foi bem-sucedida, mas nenhum ponto de dados foi encontrado. Tente novamente. Crie um novo issue e inclua os logs de depuração se o problema persistir." + }, + "step": { + "user": { + "title": "Descoberta de dispositivo", + "description": "Escolha um dos dispositivos descobertos automaticamente ou clique em `...` para adicionar um dispositivo manualmente.", + "data": { + "discovered_device": "Dispositivo descoberto" + } + }, + "basic_info": { + "title": "Adicionar dispositivo Tuya", + "description": "Preencha os detalhes básicos do dispositivo. O nome inserido aqui será usado para identificar a própria integração (como visto na página `Integrations`). Você adicionará entidades e dará nomes a elas nas etapas a seguir.", + "data": { + "friendly_name": "Nome", + "host": "Host", + "device_id": "ID do dispositivo", + "local_key": "Local key", + "protocol_version": "Versão do protocolo", + "scan_interval": "Intervalo do escaneamento (segundos, somente quando não estiver atualizando automaticamente)" + } + }, + "pick_entity_type": { + "title": "Seleção do tipo de entidade", + "description": "Escolha o tipo de entidade que deseja adicionar.", + "data": { + "platform_to_add": "Platforma", + "no_additional_platforms": "Não adicione mais entidades" + } + }, + "add_entity": { + "title": "Adicionar nova entidade", + "description": "Por favor, preencha os detalhes de uma entidade com o tipo `{platform}`. Todas as configurações, exceto `ID`, podem ser alteradas na página Opções posteriormente.", + "data": { + "id": "ID", + "friendly_name": "Name fantasia", + "current": "Atual", + "current_consumption": "Consumo atual", + "voltage": "Voltagem", + "commands_set": "Conjunto de comandos Open_Close_Stop", + "positioning_mode": "Modo de posicão", + "current_position_dp": "Posição atual (somente para o modo de posição)", + "set_position_dp": "Definir posição (somente para o modo de posição)", + "position_inverted": "Inverter posição 0-100 (somente para o modo de posição)", + "span_time": "Tempo de abertura completo, em segundos. (somente para o modo temporizado)", + "unit_of_measurement": "Unidade de medida", + "device_class": "Classe do dispositivo", + "scaling": "Fator de escala", + "state_on": "Valor On", + "state_off": "Valor Off", + "powergo_dp": "Potência DP (Geralmente 25 ou 2)", + "idle_status_value": "Status ocioso (separado por vírgula)", + "returning_status_value": "Status de retorno", + "docked_status_value": "Status docked (separado por vírgula)", + "fault_dp": "Falha DP (Geralmente 11)", + "battery_dp": "Status da bateria DP (normalmente 14)", + "mode_dp": "Modo DP (Geralmente 27)", + "modes": "Lista de modos", + "return_mode": "Modo de retorno para base", + "fan_speed_dp": "Velocidades do ventilador DP (normalmente 30)", + "fan_speeds": "Lista de velocidades do ventilador (separadas por vírgulas)", + "clean_time_dp": "Tempo de Limpeza DP (Geralmente 33)", + "clean_area_dp": "Área Limpa DP (Geralmente 32)", + "clean_record_dp": "Limpar Registro DP (Geralmente 34)", + "locate_dp": "Localize DP (Geralmente 31)", + "paused_state": "Estado de pausa (pausa, pausado, etc)", + "stop_status": "Status de parada", + "brightness": "Brilho (somente para cor branca)", + "brightness_lower": "Valor mais baixo do brilho", + "brightness_upper": "Valor mais alto do brilho", + "color_temp": "Temperatura da cor", + "color_temp_reverse": "Temperatura da cor reversa", + "color": "Cor", + "color_mode": "Modo de cor", + "color_temp_min_kelvin": "Minima temperatura de cor em K", + "color_temp_max_kelvin": "Máxima temperatura de cor em K", + "music_mode": "Modo de música disponível", + "scene": "Cena", + "fan_speed_control": "dps de controle de velocidade do ventilador", + "fan_oscillating_control": "dps de controle oscilante do ventilador", + "fan_speed_min": "velocidade mínima do ventilador inteiro", + "fan_speed_max": "velocidade máxima do ventilador inteiro", + "fan_speed_ordered_list": "Lista de modos de velocidade do ventilador (substitui a velocidade min/max)", + "fan_direction":"direção do ventilador dps", + "fan_direction_forward": "string de dps para frente", + "fan_direction_reverse": "string dps reversa", + "current_temperature_dp": "Temperatura atual", + "target_temperature_dp": "Temperatura alvo", + "temperature_step": "Etapa de temperatura (opcional)", + "max_temperature_dp": "Max Temperatura (opcional)", + "min_temperature_dp": "Min Temperatura (opcional)", + "precision": "Precisão (opcional, para valores de DPs)", + "target_precision": "Precisão do alvo (opcional, para valores de DPs)", + "temperature_unit": "Unidade de Temperatura (opcional)", + "hvac_mode_dp": "Modo HVAC DP (opcional)", + "hvac_mode_set": "Conjunto de modo HVAC (opcional)", + "hvac_action_dp": "Ação atual de HVAC DP (opcional)", + "hvac_action_set": "Conjunto de ação atual de HVAC (opcional)", + "preset_dp": "Predefinições DP (opcional)", + "preset_set": "Conjunto de predefinições (opcional)", + "eco_dp": "Eco DP (opcional)", + "eco_value": "Valor ECO (opcional)", + "heuristic_action": "Ativar ação heurística (opcional)" + } + } + } + }, + "options": { + "step": { + "init": { + "title": "Configurar dispositivo Tuya", + "description": "Configuração básica para o ID do dispositivo `{device_id}`.", + "data": { + "friendly_name": "Nome fantasia", + "host": "Host", + "local_key": "Local key", + "protocol_version": "Versão do protocolo", + "scan_interval": "Intervalo de escaneamento (segundos, somente quando não estiver atualizando automaticamente)", + "entities": "Entidades (desmarque uma entidade para removê-la)" + } + }, + "entity": { + "title": "Adicionar nova entidade", + "description": "Por favor, preencha os detalhes de uma entidade com o tipo `{platform}`. Todas as configurações, exceto `ID`, podem ser alteradas na página Opções posteriormente.", + "data": { + "id": "ID", + "friendly_name": "Name fantasia", + "current": "Atual", + "current_consumption": "Consumo atual", + "voltage": "Voltagem", + "commands_set": "Conjunto de comandos Open_Close_Stop", + "positioning_mode": "Modo de posicão", + "current_position_dp": "Posição atual (somente para o modo de posição)", + "set_position_dp": "Definir posição (somente para o modo de posição)", + "position_inverted": "Inverter posição 0-100 (somente para o modo de posição)", + "span_time": "Tempo de abertura completo, em segundos. (somente para o modo temporizado)", + "unit_of_measurement": "Unidade de medida", + "device_class": "Classe do dispositivo", + "scaling": "Fator de escala", + "state_on": "Valor On", + "state_off": "Valor Off", + "powergo_dp": "Potência DP (Geralmente 25 ou 2)", + "idle_status_value": "Status ocioso (separado por vírgula)", + "returning_status_value": "Status de retorno", + "docked_status_value": "Status docked (separado por vírgula)", + "fault_dp": "Falha DP (Geralmente 11)", + "battery_dp": "Status da bateria DP (normalmente 14)", + "mode_dp": "Modo DP (Geralmente 27)", + "modes": "Lista de modos", + "return_mode": "Modo de retorno para base", + "fan_speed_dp": "Velocidades do ventilador DP (normalmente 30)", + "fan_speeds": "Lista de velocidades do ventilador (separadas por vírgulas)", + "clean_time_dp": "Tempo de Limpeza DP (Geralmente 33)", + "clean_area_dp": "Área Limpa DP (Geralmente 32)", + "clean_record_dp": "Limpar Registro DP (Geralmente 34)", + "locate_dp": "Localize DP (Geralmente 31)", + "paused_state": "Estado de pausa (pausa, pausado, etc)", + "stop_status": "Status de parada", + "brightness": "Brilho (somente para cor branca)", + "brightness_lower": "Valor mais baixo do brilho", + "brightness_upper": "Valor mais alto do brilho", + "color_temp": "Temperatura da cor", + "color_temp_reverse": "Temperatura da cor reversa", + "color": "Cor", + "color_mode": "Modo de cor", + "color_temp_min_kelvin": "Minima temperatura de cor em K", + "color_temp_max_kelvin": "Máxima temperatura de cor em K", + "music_mode": "Modo de música disponível", + "scene": "Cena", + "fan_speed_control": "dps de controle de velocidade do ventilador", + "fan_oscillating_control": "dps de controle oscilante do ventilador", + "fan_speed_min": "velocidade mínima do ventilador inteiro", + "fan_speed_max": "velocidade máxima do ventilador inteiro", + "fan_speed_ordered_list": "Lista de modos de velocidade do ventilador (substitui a velocidade min/max)", + "fan_direction":"direção do ventilador dps", + "fan_direction_forward": "string de dps para frente", + "fan_direction_reverse": "string dps reversa", + "current_temperature_dp": "Temperatura atual", + "target_temperature_dp": "Temperatura alvo", + "temperature_step": "Etapa de temperatura (opcional)", + "max_temperature_dp": "Max Temperatura (opcional)", + "min_temperature_dp": "Min Temperatura (opcional)", + "precision": "Precisão (opcional, para valores de DPs)", + "target_precision": "Precisão do alvo (opcional, para valores de DPs)", + "temperature_unit": "Unidade de Temperatura (opcional)", + "hvac_mode_dp": "Modo HVAC DP (opcional)", + "hvac_mode_set": "Conjunto de modo HVAC (opcional)", + "hvac_action_dp": "Ação atual de HVAC DP (opcional)", + "hvac_action_set": "Conjunto de ação atual de HVAC (opcional)", + "preset_dp": "Predefinições DP (opcional)", + "preset_set": "Conjunto de predefinições (opcional)", + "eco_dp": "Eco DP (opcional)", + "eco_value": "Valor ECO (opcional)", + "heuristic_action": "Ativar ação heurística (opcional)" + } + }, + "yaml_import": { + "title": "Não suportado", + "description": "As opções não podem ser editadas quando configuradas via YAML." + } + } + }, + "title": "LocalTuya" +} diff --git a/custom_components/localtuya/vacuum.py b/custom_components/localtuya/vacuum.py new file mode 100644 index 0000000..9a14399 --- /dev/null +++ b/custom_components/localtuya/vacuum.py @@ -0,0 +1,258 @@ +"""Platform to locally control Tuya-based vacuum devices.""" +import logging +from functools import partial + +import voluptuous as vol +from homeassistant.components.vacuum import ( + DOMAIN, + STATE_CLEANING, + STATE_DOCKED, + STATE_IDLE, + STATE_RETURNING, + STATE_PAUSED, + STATE_ERROR, + SUPPORT_BATTERY, + SUPPORT_FAN_SPEED, + SUPPORT_PAUSE, + SUPPORT_RETURN_HOME, + SUPPORT_START, + SUPPORT_STATE, + SUPPORT_STATUS, + SUPPORT_STOP, + SUPPORT_LOCATE, + StateVacuumEntity, +) + +from .common import LocalTuyaEntity, async_setup_entry + +from .const import ( + CONF_POWERGO_DP, + CONF_IDLE_STATUS_VALUE, + CONF_RETURNING_STATUS_VALUE, + CONF_DOCKED_STATUS_VALUE, + CONF_BATTERY_DP, + CONF_MODE_DP, + CONF_MODES, + CONF_FAN_SPEED_DP, + CONF_FAN_SPEEDS, + CONF_CLEAN_TIME_DP, + CONF_CLEAN_AREA_DP, + CONF_CLEAN_RECORD_DP, + CONF_LOCATE_DP, + CONF_FAULT_DP, + CONF_PAUSED_STATE, + CONF_RETURN_MODE, + CONF_STOP_STATUS, +) + +_LOGGER = logging.getLogger(__name__) + +CLEAN_TIME = "clean_time" +CLEAN_AREA = "clean_area" +CLEAN_RECORD = "clean_record" +MODES_LIST = "cleaning_mode_list" +MODE = "cleaning_mode" +FAULT = "fault" + +DEFAULT_IDLE_STATUS = "standby,sleep" +DEFAULT_RETURNING_STATUS = "docking" +DEFAULT_DOCKED_STATUS = "charging,chargecompleted" +DEFAULT_MODES = "smart,wall_follow,spiral,single" +DEFAULT_FAN_SPEEDS = "low,normal,high" +DEFAULT_PAUSED_STATE = "paused" +DEFAULT_RETURN_MODE = "chargego" +DEFAULT_STOP_STATUS = "standby" + + +def flow_schema(dps): + """Return schema used in config flow.""" + return { + vol.Required(CONF_IDLE_STATUS_VALUE, default=DEFAULT_IDLE_STATUS): str, + vol.Required(CONF_POWERGO_DP): vol.In(dps), + vol.Required(CONF_DOCKED_STATUS_VALUE, default=DEFAULT_DOCKED_STATUS): str, + vol.Optional( + CONF_RETURNING_STATUS_VALUE, default=DEFAULT_RETURNING_STATUS + ): str, + vol.Optional(CONF_BATTERY_DP): vol.In(dps), + vol.Optional(CONF_MODE_DP): vol.In(dps), + vol.Optional(CONF_MODES, default=DEFAULT_MODES): str, + vol.Optional(CONF_RETURN_MODE, default=DEFAULT_RETURN_MODE): str, + vol.Optional(CONF_FAN_SPEED_DP): vol.In(dps), + vol.Optional(CONF_FAN_SPEEDS, default=DEFAULT_FAN_SPEEDS): str, + vol.Optional(CONF_CLEAN_TIME_DP): vol.In(dps), + vol.Optional(CONF_CLEAN_AREA_DP): vol.In(dps), + vol.Optional(CONF_CLEAN_RECORD_DP): vol.In(dps), + vol.Optional(CONF_LOCATE_DP): vol.In(dps), + vol.Optional(CONF_FAULT_DP): vol.In(dps), + vol.Optional(CONF_PAUSED_STATE, default=DEFAULT_PAUSED_STATE): str, + vol.Optional(CONF_STOP_STATUS, default=DEFAULT_STOP_STATUS): str, + } + + +class LocaltuyaVacuum(LocalTuyaEntity, StateVacuumEntity): + """Tuya vacuum device.""" + + def __init__(self, device, config_entry, switchid, **kwargs): + """Initialize a new LocaltuyaVacuum.""" + super().__init__(device, config_entry, switchid, _LOGGER, **kwargs) + self._state = None + self._battery_level = None + self._attrs = {} + + self._idle_status_list = [] + if self.has_config(CONF_IDLE_STATUS_VALUE): + self._idle_status_list = self._config[CONF_IDLE_STATUS_VALUE].split(",") + + self._modes_list = [] + if self.has_config(CONF_MODES): + self._modes_list = self._config[CONF_MODES].split(",") + self._attrs[MODES_LIST] = self._modes_list + + self._docked_status_list = [] + if self.has_config(CONF_DOCKED_STATUS_VALUE): + self._docked_status_list = self._config[CONF_DOCKED_STATUS_VALUE].split(",") + + self._fan_speed_list = [] + if self.has_config(CONF_FAN_SPEEDS): + self._fan_speed_list = self._config[CONF_FAN_SPEEDS].split(",") + + self._fan_speed = "" + self._cleaning_mode = "" + + print("Initialized vacuum [{}]".format(self.name)) + + @property + def supported_features(self): + """Flag supported features.""" + supported_features = ( + SUPPORT_START + | SUPPORT_PAUSE + | SUPPORT_STOP + | SUPPORT_STATUS + | SUPPORT_STATE + ) + + if self.has_config(CONF_RETURN_MODE): + supported_features = supported_features | SUPPORT_RETURN_HOME + if self.has_config(CONF_FAN_SPEED_DP): + supported_features = supported_features | SUPPORT_FAN_SPEED + if self.has_config(CONF_BATTERY_DP): + supported_features = supported_features | SUPPORT_BATTERY + if self.has_config(CONF_LOCATE_DP): + supported_features = supported_features | SUPPORT_LOCATE + + return supported_features + + @property + def state(self): + """Return the vacuum state.""" + return self._state + + @property + def battery_level(self): + """Return the current battery level.""" + return self._battery_level + + @property + def extra_state_attributes(self): + """Return the specific state attributes of this vacuum cleaner.""" + return self._attrs + + @property + def fan_speed(self): + """Return the current fan speed.""" + return self._fan_speed + + @property + def fan_speed_list(self) -> list: + """Return the list of available fan speeds.""" + return self._fan_speed_list + + async def async_start(self, **kwargs): + """Turn the vacuum on and start cleaning.""" + await self._device.set_dp(True, self._config[CONF_POWERGO_DP]) + + async def async_pause(self, **kwargs): + """Stop the vacuum cleaner, do not return to base.""" + await self._device.set_dp(False, self._config[CONF_POWERGO_DP]) + + async def async_return_to_base(self, **kwargs): + """Set the vacuum cleaner to return to the dock.""" + if self.has_config(CONF_RETURN_MODE): + await self._device.set_dp( + self._config[CONF_RETURN_MODE], self._config[CONF_MODE_DP] + ) + else: + _LOGGER.error("Missing command for return home in commands set.") + + async def async_stop(self, **kwargs): + """Turn the vacuum off stopping the cleaning.""" + if self.has_config(CONF_STOP_STATUS): + await self._device.set_dp( + self._config[CONF_STOP_STATUS], self._config[CONF_MODE_DP] + ) + else: + _LOGGER.error("Missing command for stop in commands set.") + + async def async_clean_spot(self, **kwargs): + """Perform a spot clean-up.""" + return None + + async def async_locate(self, **kwargs): + """Locate the vacuum cleaner.""" + if self.has_config(CONF_LOCATE_DP): + await self._device.set_dp("", self._config[CONF_LOCATE_DP]) + + async def async_set_fan_speed(self, fan_speed, **kwargs): + """Set the fan speed.""" + await self._device.set_dp(fan_speed, self._config[CONF_FAN_SPEED_DP]) + + async def async_send_command(self, command, params=None, **kwargs): + """Send a command to a vacuum cleaner.""" + if command == "set_mode" and "mode" in params: + mode = params["mode"] + await self._device.set_dp(mode, self._config[CONF_MODE_DP]) + + def status_updated(self): + """Device status was updated.""" + state_value = str(self.dps(self._dp_id)) + + if state_value in self._idle_status_list: + self._state = STATE_IDLE + elif state_value in self._docked_status_list: + self._state = STATE_DOCKED + elif state_value == self._config[CONF_RETURNING_STATUS_VALUE]: + self._state = STATE_RETURNING + elif state_value == self._config[CONF_PAUSED_STATE]: + self._state = STATE_PAUSED + else: + self._state = STATE_CLEANING + + if self.has_config(CONF_BATTERY_DP): + self._battery_level = self.dps_conf(CONF_BATTERY_DP) + + self._cleaning_mode = "" + if self.has_config(CONF_MODES): + self._cleaning_mode = self.dps_conf(CONF_MODE_DP) + self._attrs[MODE] = self._cleaning_mode + + self._fan_speed = "" + if self.has_config(CONF_FAN_SPEEDS): + self._fan_speed = self.dps_conf(CONF_FAN_SPEED_DP) + + if self.has_config(CONF_CLEAN_TIME_DP): + self._attrs[CLEAN_TIME] = self.dps_conf(CONF_CLEAN_TIME_DP) + + if self.has_config(CONF_CLEAN_AREA_DP): + self._attrs[CLEAN_AREA] = self.dps_conf(CONF_CLEAN_AREA_DP) + + if self.has_config(CONF_CLEAN_RECORD_DP): + self._attrs[CLEAN_RECORD] = self.dps_conf(CONF_CLEAN_RECORD_DP) + + if self.has_config(CONF_FAULT_DP): + self._attrs[FAULT] = self.dps_conf(CONF_FAULT_DP) + if self._attrs[FAULT] != 0: + self._state = STATE_ERROR + + +async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaVacuum, flow_schema) diff --git a/img/10-integration_configure.png b/img/10-integration_configure.png new file mode 100644 index 0000000000000000000000000000000000000000..f023c22595dc0bc6661a457233dc74183e5415aa GIT binary patch literal 10229 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 literal 0 HcmV?d00001 diff --git a/img/11-config_menu.png b/img/11-config_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..22f29ff8e72ffca6a9e17d4a0e938879d8d4633b GIT binary patch literal 14953 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 G{Q6%Kvi_C; literal 0 HcmV?d00001 diff --git a/img/7-auth_keys.png b/img/7-auth_keys.png new file mode 100644 index 0000000000000000000000000000000000000000..b8cc331aafef085e4c73ce2a32f973ab2c4628af GIT binary patch literal 66636 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

  • |MKO(zl1!F+V=v3Smm>&3v}J}Da%#P z6y4FpreN2mc*$SVZ5yXaX_U%`CrsnUe_n9^ft{W^)+t)FDeIYqO5g4L%hR`;?k$S79*|uoe4oVNlE%HP>`UtGYky8@P zZfRq21&Mgr3yor0u1meh&oQ+NA*SDwL2huGaXp9D^^x}qmD0A*ww(J4ZyGJOS=`kx zBKi9_LR>l>$;(3imHPe_9BdrzVzM4;#{bBqg{gMv}-qF2ic2l{1qM^(dDHsDr~>;#P(aeF?NZM_lCknYqV)H#ZDsZYRbV z3GRHg>ts7D<YOqk3Am{Mh$G z33;hGs!s6Em(sM=#ysiGvMQEn*c>IHiF0GiGwXYhde1wt~2!KUWWY#f*o1}oJh1RqgQJGJL+rpn3K5O(#7kh z_tCz+>rmo;JhHLMIAU`d;)-V5z;=t#Qk-{{)ydvSlaQvEAXdcMii|*HZ>;F-L%w{i zrGX@euh-Wid*u0t&{oc;`E#i@T;I>#y}a%>@VF~GVq+COH#Z?NWKd~+Aok_-k$eD|e<9V{* z=d_okE}m%$#3T4}z1(p0VrrQ7SES^y0@gk`Ikv%oAYV2%ISH&L#T$C5L*ITO=??Y~ zp@;(Vai0Hu*hlm_9$umctchIN@04=d;y6@g4w0Rr%=XwlKNh!N+7cKw>KaPN4yMoF zkDwJ4oad=f5;<_<%d=A-UzZZ>e}FToTs-oOcPL$wHbc6UnD^wx1EZRGyhBybuHF*D z?6lRizNPr?FTdU_ofa>>Csatn4NLJxU+n$Tf<(J-M6Dfek)0zLQ9ZMIFYSu%_|Joi zX|w&714{`Hc`#We=13uPX+mzHhnE*i`$>5$U+1>_1^OG#eIH}x_Jc*g+MdJ;41=B4 zH+TW7$IA6vMzQ?Iu~waImZXD(;d?J`xAQGek`<^S&tL9MZV8fC4{ncq9s0Z($etAb zK5|YuB`qLn=9EYs%@9FTzMbIlx22BXs@uU=e5j>7w>gONBYd zg!crC#uq<~I!3+()W=9%yOtMBSCx^eD-yEoeR`E!>ApFm_Sf$M^YarM9QgUR*YC`K z&XDmZ3w{2Yk^+5_ra#QUnk0|?e=r;Wd=Q<$f3o{)HTlgIlabPHxe4x`&wbV6dQmAL z`wv~FY4^LH0MO>@o&S08C`ybj!Qf4FC_DHQ+gtEW64p$C zesFATtT4r^KP?V5N&AZ0`u_d1{EwJhbFX`*9k?p$I*aPgOkX`da{bA&U%NN>NAC|` zbG@ap(+}+p&VQL-9)UQ;b@9uN&H*RJRq5IP9niJkS@v28mhEnisbm+jhtB^cZ@YY` zOC}?IHRiYUdvgMtiCXuGQOlg%Hs zE-}(Oe_qo)z}quCQt7)A-HD-sy|VQA^PkR$j|^LPnmRM8#V21^dy@>+lQ#ZaX@OUE zU2CScNi0u2j3X&KQgi(^GVzX#{?gOua(Ih_jE-~w?eNdlzg8ANjmN#lZaGtqamKS6 zJ1_mj?axOqmod5hS1*AJ#k(o)RxIh3d;QdCzl8eFv%1-M28SaHyL?A4&wsn+b6JXa z%~&DUl}-yOl_JAjS*Xx4lj#0DnY6f>ko*2|)mdj$MEuH5dZ+sR2zgO;X(IND^+q$c zIH}lbnyxf2NoBU4xdemd+=ZW8lkpmfY?v0kE88l1_m-_)St&V79q1G8qa6L%{BLlW z20Zy&MV7RkK9>32OpR-ATxZLDk%l+X=mU!>StIILtXA7!-;!mVaUHG(wt98>`H$&O z|J&9BN5+F?ukaD=jo*WWHa?f_i$J+l3|y4GeNEbem0j$lq#+%Jg1{)TUadrqm~K?` zDu;#7J^HKdc`q0d4Pwu17X{guvy?P;j-!sB_4GX39a?|d%YGj#Y~3$FJp_3>C~vM0kxrU}>fkr|-tXl8X~oJCL`w zwr8GkMS6`+*^T++TmRM(U#U7|kJoGSqdkJgAAjzMVTNsajozFI! zS~fC%xRIV5#+@ltRj%u_^HkSnG^z{LPd4^^s3TQPKj&QM^Z=J7xnsX1^3U~03aq!| zX)PRiC%zj=EfVZVDHU3y4p7)J+V@o_&cF%7OWzZeb(~@KCnRjR6Nr1W-O3 z%@>bOjJH-xhcY$L05`Y&!6STc$AH@;Z^Aq%O|%E*L#WWT3}|0bJ1uub$YM*&^%xWl zHE61T*4cmUSx_Xf*jSA>O^M~?f19Z1oztd0mVfLswMV!j&aZ@?`00z~1oeD)m@%|U zB$&vT!1Bg^TK&(!8b)7>9npL7q2w@sykOvQIa%A!?uF}V&$cdvVk~=aY)9RtY?98m zO7&|keMugoP13*@f=BJ2$uL^%u!{^6L3i`s&8#p??(LdQPi2! zf1!BROMqGt|7Rht(SsrI%V@s;bSV|@3PbFCxi1=kN8Lq4CQNW!(I*)0#Ah>nD#1QUv_bKjNm^y~@;>?1; z$ik4{{G-|8C&F2ZMKNug^>3FDq}kC|*Gmd~ zSMC-&J6w9Y1{+PGZESzB%o2@{-#;rmvfz!kmKF(3ND`!EwMgziV_{sd4?JIq$}2@J z2-K1BOgtwe=_VAW*k0y;#K2eQ>Gk)dB5u-Dg_DbFlr+VGe5bnMag4gVKxMC;!mY2B zI<_)_ceXQ?ZGUd6+&_#I=6OFmTRXqqcBE^^k@}ONjXg-15VJ~$B3N8CwrnR`_OW-x ztuDojr?>trnAfa7P8=4Eb`sk?bJ6UXo_|t}bTdn6q*?bQ3l1=GbzT>rJy`a9FXxId z9#Kdjx}a{qYm>3oGq5tDI}vy003Z~vC+zIVd{fU1d)Y5f%%;q|LupRExwK8ARq1S+Xh^9mLbZFkSM9Yj5 zE7(kDyxU5|sm=RCV_oGO7eThue3bvdf+x~NorKkn;&1(jtkC5J^b-KI6)-k~Did0O zflc&uSpdj$|-f;`7)Y@TUMTKg%{C#OTIk_PNQ~tly z9vz(ga$1bP=3bizRE#fQZj8BS$6jdH3-GoK;y?2`t^ZWsQ?JUCVqz4UX3AlJMlawZ z*oR_4f@;Pqza?S0#uaJMC6J_7_haT=b>io>rq@nvmuDt+7lYTYosIMC>~~i22w8=^ zL%u<$DEnSzXcpf{qXIut<&xN%QmPDRQ61I?e&`vT56y0&arbroeqcZjSqeq`i zpqn|b#0}tDE-sagYbIu7AaY?@r=y~#2XqQhG=znj9^5l_0^R}lQ{ZQW`H65*mEQ@} z)n$W@M0XU7D=77)u>C+*iTDRrLV450JE&%8@#t>Rii`YbzXhsz=;Orf{p))1CQWB} zJG#20goTF-4c<9BR~%>SaL!m=eF3CeaOYzx3ZcjJ`-u8D){ zQC(f#=ycL;YgknvG6@|bM@}ZsJhPaAw47%c6c*N~U_2vt+o*hBH6=N@p}E;?v`}PN z?Y7x0FgO1O($e+oFeZ-olytdmRzpRC^Vf0l@RYZbvaIFhMY~}BS+Fe}x^o+71J&n8 zC;B1i&5mujwzu(&(*1c0L?Y@ghfCv>?D0aXdYa-??H`M;BJ&V==OecAd|Bqk#+`U( zc1{C<8{O*qAJpBB3KWfB0va-_?)Fxc!%W^?K(R{M+m{@#j2gLY6fuX7roT%)s+-x> z7OUQ%v3W2!L^JnFh1B}%;9SO6ub0GFBqi^^pKO!FCWM*9!@o+-VRThK9pX5y%H|VC zvt?s9hx`R)Q%p#M6+15I_bw1zwnzPeTg&(8&knBR!=;VKEk?~D8u3QIy;JXs6BppM zc%fDwfU0U&E;?o$7tk`|alC|q^|PMZw8}#&vf6f+b)<8PEOX9oI#&Tt!)E⪻W2N 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 literal 0 HcmV?d00001 diff --git a/img/9-cloud_setup.png b/img/9-cloud_setup.png new file mode 100644 index 0000000000000000000000000000000000000000..cc70a5183a8b8963953238ae493dbce5155afb10 GIT binary patch literal 21521 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 z Date: Mon, 30 May 2022 12:08:20 +0200 Subject: [PATCH 40/52] Fixed Adding new device manually --- custom_components/localtuya/config_flow.py | 30 ++++++++++++++-------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 1453321..a995fe1 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -406,10 +406,12 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): """Handle adding a new device.""" # Use cache if available or fallback to manual discovery self.editing_device = False + self.selected_device = None errors = {} if user_input is not None: if user_input[SELECTED_DEVICE] != CUSTOM_DEVICE: self.selected_device = user_input[SELECTED_DEVICE] + return await self.async_step_configure_device() self.discovered_devices = {} @@ -524,19 +526,25 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): defaults = self.config_entry.data[CONF_DEVICES][dev_id].copy() schema = schema_defaults(options_schema(self.entities), **defaults) placeholders = {"for_device": f" for device `{dev_id}`"} - elif dev_id is not None: - # Insert default values from discovery and cloud if present - device = self.discovered_devices[dev_id] - defaults[CONF_HOST] = device.get("ip") - defaults[CONF_DEVICE_ID] = device.get("gwId") - defaults[CONF_PROTOCOL_VERSION] = device.get("version") - cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD].device_list - if dev_id in cloud_devs: - defaults[CONF_LOCAL_KEY] = cloud_devs[dev_id].get(CONF_LOCAL_KEY) - defaults[CONF_FRIENDLY_NAME] = cloud_devs[dev_id].get(CONF_NAME) + else: + defaults[CONF_PROTOCOL_VERSION] = "3.3" + defaults[CONF_HOST] = "" + defaults[CONF_DEVICE_ID] = "" + defaults[CONF_LOCAL_KEY] = "" + defaults[CONF_FRIENDLY_NAME] = "" + if dev_id is not None: + # Insert default values from discovery and cloud if present + device = self.discovered_devices[dev_id] + defaults[CONF_HOST] = device.get("ip") + defaults[CONF_DEVICE_ID] = device.get("gwId") + defaults[CONF_PROTOCOL_VERSION] = device.get("version") + cloud_devs = self.hass.data[DOMAIN][DATA_CLOUD].device_list + if dev_id in cloud_devs: + defaults[CONF_LOCAL_KEY] = cloud_devs[dev_id].get(CONF_LOCAL_KEY) + defaults[CONF_FRIENDLY_NAME] = cloud_devs[dev_id].get(CONF_NAME) schema = schema_defaults(CONFIGURE_DEVICE_SCHEMA, **defaults) + placeholders = {"for_device": ""} - defaults.update(user_input or {}) return self.async_show_form( step_id="configure_device", From 110585b3f8e47bd87eabbf3ba1f7dc4666887526 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Tue, 31 May 2022 15:02:19 +0200 Subject: [PATCH 41/52] Changed deprecated async_get_registry() to async_get() --- custom_components/localtuya/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index b6db69d..1aaea32 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -303,7 +303,7 @@ async def async_remove_config_entry_device( """Remove a config entry from a device.""" dev_id = list(device_entry.identifiers)[0][1].split("_")[-1] - ent_reg = await er.async_get_registry(hass) + ent_reg = er.async_get(hass) entities = { ent.unique_id: ent.entity_id for ent in er.async_entries_for_config_entry(ent_reg, config_entry.entry_id) @@ -337,7 +337,7 @@ async def async_remove_config_entry_device( async def async_remove_orphan_entities(hass, entry): """Remove entities associated with config entry that has been removed.""" return - ent_reg = await er.async_get_registry(hass) + ent_reg = er.async_get(hass) entities = { ent.unique_id: ent.entity_id for ent in er.async_entries_for_config_entry(ent_reg, entry.entry_id) From 622c8e78a13a2f7753c595d205ebd7517b2bdf5b Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Tue, 31 May 2022 15:09:51 +0200 Subject: [PATCH 42/52] Improvements on README --- README.md | 8 ++++---- info.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6c94a6b..d7d58dd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A Home Assistant custom Integration for local handling of Tuya-based devices. -This custom integration updates device status via push updates instead of polling, so status updates are fast (even when manually operated). +This custom integration updates device status via pushing updates instead of polling, so status updates are fast (even when manually operated). The integration also supports the Tuya IoT Cloud APIs, for the retrieval of info and of the local_keys of the devices. @@ -42,7 +42,7 @@ For manual installation, copy the localtuya folder and all of its contents into # Adding the Integration -**NOTE: starting from v.4.0.0, configuration using YAML files is no longer supported. The integration can be configured only using the config flow.** +**NOTE: starting from v4.0.0, configuration using YAML files is no longer supported. The integration can only be configured using the config flow.** To start configuring the integration, just press the "+ADD INTEGRATION" button in the Settings - Integrations page, and select LocalTuya from the drop-down menu. @@ -126,9 +126,9 @@ You can obtain Energy monitoring (voltage, current) in two different ways: 1) Creating individual sensors, each one with the desired name. Note: Voltage and Consumption usually include the first decimal. You will need to scale the parament by 0.1 to get the correct values. -1) Access the voltage/current/current_consumption attributes of a switch, and define template sensors +2) Access the voltage/current/current_consumption attributes of a switch, and define template sensors Note: these values are already divided by 10 for Voltage and Consumption -1) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended. +3) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended. ``` sensor: diff --git a/info.md b/info.md index 31287ee..8185241 100644 --- a/info.md +++ b/info.md @@ -6,7 +6,7 @@ A Home Assistant custom Integration for local handling of Tuya-based devices. -This custom integration updates device status via push updates instead of polling, so status updates are fast (even when manually operated). +This custom integration updates device status via pushing updates instead of polling, so status updates are fast (even when manually operated). The integration also supports the Tuya IoT Cloud APIs, for the retrieval of info and of the local_keys of the devices. @@ -46,7 +46,7 @@ For manual installation, copy the localtuya folder and all of its contents into # Adding the Integration -**NOTE: starting from v.4.0.0, configuration using YAML files is no longer supported. The integration can be configured only using the config flow.** +**NOTE: starting from v4.0.0, configuration using YAML files is no longer supported. The integration can only be configured using the config flow.** To start configuring the integration, just press the "+ADD INTEGRATION" button in the Settings - Integrations page, and select LocalTuya from the drop-down menu. @@ -130,9 +130,9 @@ You can obtain Energy monitoring (voltage, current) in two different ways: 1) Creating individual sensors, each one with the desired name. Note: Voltage and Consumption usually include the first decimal. You will need to scale the parament by 0.1 to get the correct values. -1) Access the voltage/current/current_consumption attributes of a switch, and define template sensors +2) Access the voltage/current/current_consumption attributes of a switch, and define template sensors Note: these values are already divided by 10 for Voltage and Consumption -1) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended. +3) On some devices, you may find that the energy values are not updating frequently enough by default. If so, set the scan interval (see above) to an appropriate value. Settings below 10 seconds may cause stability issues, 30 seconds is recommended. ``` sensor: From 00c126acb8188b3f6110bfc0867ee68cb1e31274 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Wed, 1 Jun 2022 17:27:56 +0200 Subject: [PATCH 43/52] Fixed labels missing when editing Cloud configuration --- .../localtuya/translations/en.json | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index b80cf9e..8e05ae4 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -24,7 +24,8 @@ "client_id": "Client ID", "client_secret": "Secret", "user_id": "User ID", - "user_name": "Username" + "user_name": "Username", + "no_cloud": "Do not configure a Cloud API account" } } } @@ -54,7 +55,7 @@ "title": "LocalTuya Configuration", "description": "Please select the desired action.", "data": { - "add_device": "AAAAA a new device", + "add_device": "Add a new device", "edit_device": "Edit a device", "setup_cloud": "Reconfigure Cloud API account" } @@ -73,6 +74,18 @@ "selected_device": "Configured Devices" } }, + "cloud_setup": { + "title": "Cloud API account configuration", + "description": "Input the credentials for Tuya Cloud API.", + "data": { + "region": "API server region", + "client_id": "Client ID", + "client_secret": "Secret", + "user_id": "User ID", + "user_name": "Username", + "no_cloud": "Do not configure Cloud API account" + } + }, "configure_device": { "title": "Configure Tuya device", "description": "Fill in the device details{for_device}.", From da8ed8d82b6074652baca24633350e667d0e15d9 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Wed, 1 Jun 2022 17:31:58 +0200 Subject: [PATCH 44/52] Fix for 'KeyError: tuya_devices' at startup --- custom_components/localtuya/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 1aaea32..9b38548 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -65,6 +65,7 @@ SERVICE_SET_DP_SCHEMA = vol.Schema( async def async_setup(hass: HomeAssistant, config: dict): """Set up the LocalTuya integration component.""" hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN][TUYA_DEVICES] = {} device_cache = {} @@ -193,9 +194,9 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry): ) new_data = {} new_data[CONF_REGION] = "eu" - new_data[CONF_CLIENT_ID] = "xxx" - new_data[CONF_CLIENT_SECRET] = "xxx" - new_data[CONF_USER_ID] = "xxx" + new_data[CONF_CLIENT_ID] = "" + new_data[CONF_CLIENT_SECRET] = "" + new_data[CONF_USER_ID] = "" new_data[CONF_USERNAME] = DOMAIN new_data[CONF_DEVICES] = { config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy() @@ -229,7 +230,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up LocalTuya integration from a config entry.""" unsub_listener = entry.add_update_listener(update_listener) hass.data[DOMAIN][UNSUB_LISTENER] = unsub_listener - hass.data[DOMAIN][TUYA_DEVICES] = {} region = entry.data[CONF_REGION] client_id = entry.data[CONF_CLIENT_ID] From 8b11ffaf86e1e075b877382e60dd3e00a16e175e Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Wed, 1 Jun 2022 18:05:12 +0200 Subject: [PATCH 45/52] Turned all 'print' messages into _LOGGER.debug --- custom_components/localtuya/climate.py | 2 +- custom_components/localtuya/cloud_api.py | 18 ++++++++++-------- custom_components/localtuya/common.py | 4 ---- custom_components/localtuya/const.py | 1 + custom_components/localtuya/cover.py | 2 +- custom_components/localtuya/switch.py | 2 +- custom_components/localtuya/vacuum.py | 3 +-- 7 files changed, 15 insertions(+), 17 deletions(-) diff --git a/custom_components/localtuya/climate.py b/custom_components/localtuya/climate.py index 8474a21..1400ec7 100644 --- a/custom_components/localtuya/climate.py +++ b/custom_components/localtuya/climate.py @@ -176,7 +176,7 @@ class LocaltuyaClimate(LocalTuyaEntity, ClimateEntity): self._has_presets = self.has_config(CONF_ECO_DP) or self.has_config( CONF_PRESET_DP ) - print("Initialized climate [{}]".format(self.name)) + _LOGGER.debug("Initialized climate [%s]", self.name) @property def supported_features(self): diff --git a/custom_components/localtuya/cloud_api.py b/custom_components/localtuya/cloud_api.py index 707b2da..6802171 100644 --- a/custom_components/localtuya/cloud_api.py +++ b/custom_components/localtuya/cloud_api.py @@ -3,10 +3,13 @@ import functools import hashlib import hmac import json +import logging import time import requests +_LOGGER = logging.getLogger(__name__) + # Signature algorithm. def calc_sign(msg, key): @@ -55,7 +58,7 @@ class TuyaCloudApi: + "\n/" + url.split("//", 1)[-1].split("/", 1)[-1] # Url ) - # print("PAYLOAD: {}".format(payload)) + # _LOGGER.debug("PAYLOAD: %s", payload) return payload async def async_make_request(self, method, url, body=None, headers={}): @@ -70,7 +73,7 @@ class TuyaCloudApi: "sign_method": "HMAC-SHA256", } full_url = self._base_url + url - # print("\n" + method + ": [{}]".format(full_url)) + # _LOGGER.debug("\n" + method + ": [%s]", full_url) if method == "GET": func = functools.partial( @@ -83,7 +86,7 @@ class TuyaCloudApi: headers=dict(default_par, **headers), data=json.dumps(body), ) - # print("BODY: [{}]".format(body)) + # _LOGGER.debug("BODY: [%s]", body) elif method == "PUT": func = functools.partial( requests.put, @@ -121,14 +124,13 @@ class TuyaCloudApi: r_json = resp.json() if not r_json["success"]: - # print( - # "Request failed, reply is {}".format( - # json.dumps(r_json, indent=2, ensure_ascii=False) - # ) + # _LOGGER.debug( + # "Request failed, reply is %s", + # json.dumps(r_json, indent=2, ensure_ascii=False) # ) return f"Error {r_json['code']}: {r_json['msg']}" self.device_list = {dev["id"]: dev for dev in r_json["result"]} - # print("DEV__LIST: {}".format(self.device_list)) + # _LOGGER.debug("DEV_LIST: %s", self.device_list) return "ok" diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index e694869..1b63e8a 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -74,10 +74,6 @@ async def async_setup_entry( if len(entities_to_setup) > 0: - if dev_id not in hass.data[DOMAIN][TUYA_DEVICES]: - print("STRANO: {}".format(hass.data[DOMAIN][TUYA_DEVICES])) - return - tuyainterface = hass.data[DOMAIN][TUYA_DEVICES][dev_id] dps_config_fields = list(get_dps_for_platform(flow_schema)) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index c1d4248..c940304 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -39,6 +39,7 @@ CONF_ACTION = "action" CONF_ADD_DEVICE = "add_device" CONF_EDIT_DEVICE = "edit_device" CONF_SETUP_CLOUD = "setup_cloud" +CONF_NO_CLOUD = "no_cloud" # light CONF_BRIGHTNESS_LOWER = "brightness_lower" diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 43f59a6..2a3eb8b 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -75,7 +75,7 @@ class LocaltuyaCover(LocalTuyaEntity, CoverEntity): self._state = self._stop_cmd self._previous_state = self._state self._current_cover_position = 0 - print("Initialized cover [{}]".format(self.name)) + _LOGGER.debug("Initialized cover [%s]", self.name) @property def supported_features(self): diff --git a/custom_components/localtuya/switch.py b/custom_components/localtuya/switch.py index f43d910..e884095 100644 --- a/custom_components/localtuya/switch.py +++ b/custom_components/localtuya/switch.py @@ -40,7 +40,7 @@ class LocaltuyaSwitch(LocalTuyaEntity, SwitchEntity): """Initialize the Tuya switch.""" super().__init__(device, config_entry, switchid, _LOGGER, **kwargs) self._state = None - print("Initialized switch [{}]".format(self.name)) + _LOGGER.debug("Initialized switch [%s]", self.name) @property def is_on(self): diff --git a/custom_components/localtuya/vacuum.py b/custom_components/localtuya/vacuum.py index dc75044..7bf4ed6 100644 --- a/custom_components/localtuya/vacuum.py +++ b/custom_components/localtuya/vacuum.py @@ -117,8 +117,7 @@ class LocaltuyaVacuum(LocalTuyaEntity, StateVacuumEntity): self._fan_speed = "" self._cleaning_mode = "" - - print("Initialized vacuum [{}]".format(self.name)) + _LOGGER.debug("Initialized vacuum [%s]", self.name) @property def supported_features(self): From 9a0f5b8edb29c5fee68e99a32aaff60f8e7b03b9 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Fri, 3 Jun 2022 12:34:32 +0200 Subject: [PATCH 46/52] Improved code readability (racu10's suggestion) --- custom_components/localtuya/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/localtuya/common.py b/custom_components/localtuya/common.py index 1b63e8a..79eadc9 100644 --- a/custom_components/localtuya/common.py +++ b/custom_components/localtuya/common.py @@ -72,7 +72,7 @@ async def async_setup_entry( if entity[CONF_PLATFORM] == domain ] - if len(entities_to_setup) > 0: + if entities_to_setup: tuyainterface = hass.data[DOMAIN][TUYA_DEVICES][dev_id] From 77d3d97845e2f1688112c4bce8073a15b1e0f04f Mon Sep 17 00:00:00 2001 From: rospogrigio <49229287+rospogrigio@users.noreply.github.com> Date: Mon, 6 Jun 2022 10:54:23 +0200 Subject: [PATCH 47/52] Add files via upload Updated cloud setup screenshot --- img/9-cloud_setup.png | Bin 21521 -> 23243 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/img/9-cloud_setup.png b/img/9-cloud_setup.png index cc70a5183a8b8963953238ae493dbce5155afb10..25380a31084b7aca89d8279c44d8370194e382fe 100644 GIT binary patch literal 23243 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 SMgo?`+k8BIJsRA<&-fdCpY*B# literal 21521 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 z Date: Mon, 6 Jun 2022 13:01:10 +0200 Subject: [PATCH 48/52] Updated README for the 'Do not configure cloud API' button --- README.md | 2 +- info.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d7d58dd..d5d3333 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ The place to find the Client ID and Secret is described in this link (in the ["G After pressing the Submit button, the first setup is complete and the Integration will be added. -> **Note: it is not mandatory to configure the Cloud API account: you can choose to leave the default "xxx" values, and the Integration will be added anyway.** +> **Note: it is not mandatory to input the Cloud API credentials: you can choose to tick the "Do not configure a Cloud API account" button, and the Integration will be added anyway.** After the Integration has been set up, devices can be added and configured pressing the Configure button in the Integrations page: diff --git a/info.md b/info.md index 8185241..900b0e0 100644 --- a/info.md +++ b/info.md @@ -66,7 +66,7 @@ The place to find the Client ID and Secret is described in this link (in the ["G After pressing the Submit button, the first setup is complete and the Integration will be added. -> **Note: it is not mandatory to configure the Cloud API account: you can choose to leave the default "xxx" values, and the Integration will be added anyway.** +> **Note: it is not mandatory to input the Cloud API credentials: you can choose to tick the "Do not configure a Cloud API account" button, and the Integration will be added anyway.** After the Integration has been set up, devices can be added and configured pressing the Configure button in the Integrations page: From 4c0072f404fdfd5b425f38668b8f69fe83e4bd71 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Mon, 6 Jun 2022 15:59:50 +0200 Subject: [PATCH 49/52] Introduced checkbox to choose whether to setup Cloud API account --- custom_components/localtuya/__init__.py | 44 ++++++++++++++++------ custom_components/localtuya/config_flow.py | 42 ++++++++++++++++----- 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 9b38548..e431dd3 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -29,9 +29,10 @@ from homeassistant.helpers.event import async_track_time_interval from .cloud_api import TuyaCloudApi from .common import TuyaDevice, async_config_entry_by_device_id -from .config_flow import config_schema +from .config_flow import config_schema, ENTRIES_VERSION from .const import ( ATTR_UPDATED_AT, + CONF_NO_CLOUD, CONF_PRODUCT_KEY, CONF_USER_ID, DATA_CLOUD, @@ -183,10 +184,10 @@ async def async_setup(hass: HomeAssistant, config: dict): async def async_migrate_entry(hass, config_entry: ConfigEntry): """Migrate old entries merging all of them in one.""" - new_version = 2 + new_version = ENTRIES_VERSION + stored_entries = hass.config_entries.async_entries(DOMAIN) if config_entry.version == 1: _LOGGER.debug("Migrating config entry from version %s", config_entry.version) - stored_entries = hass.config_entries.async_entries(DOMAIN) if config_entry.entry_id == stored_entries[0].entry_id: _LOGGER.debug( @@ -198,6 +199,7 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry): new_data[CONF_CLIENT_SECRET] = "" new_data[CONF_USER_ID] = "" new_data[CONF_USERNAME] = DOMAIN + new_data[CONF_NO_CLOUD] = True new_data[CONF_DEVICES] = { config_entry.data[CONF_DEVICE_ID]: config_entry.data.copy() } @@ -223,24 +225,41 @@ async def async_migrate_entry(hass, config_entry: ConfigEntry): config_entry.entry_id, new_version, ) + return True async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up LocalTuya integration from a config entry.""" - unsub_listener = entry.add_update_listener(update_listener) - hass.data[DOMAIN][UNSUB_LISTENER] = unsub_listener + if entry.version < ENTRIES_VERSION: + _LOGGER.debug( + "Skipping setup for entry %s since its version (%s) is old", + entry.entry_id, + entry.version, + ) + return + + _LOGGER.debug("ASE: %s %s", entry.entry_id, entry.data) region = entry.data[CONF_REGION] client_id = entry.data[CONF_CLIENT_ID] secret = entry.data[CONF_CLIENT_SECRET] user_id = entry.data[CONF_USER_ID] tuya_api = TuyaCloudApi(hass, region, client_id, secret, user_id) - res = await tuya_api.async_get_access_token() - if res != "ok": - _LOGGER.error("Cloud API connection failed: %s", res) - _LOGGER.info("Cloud API connection succeeded.") - res = await tuya_api.async_get_devices_list() + no_cloud = True + if CONF_NO_CLOUD in entry.data: + no_cloud = entry.data.get(CONF_NO_CLOUD) + _LOGGER.debug("ASE DC: %s", no_cloud) + if no_cloud: + _LOGGER.info("Cloud API account not configured.") + # wait 1 second to make sure possible migration has finished + await asyncio.sleep(1) + else: + res = await tuya_api.async_get_access_token() + if res != "ok": + _LOGGER.error("Cloud API connection failed: %s", res) + _LOGGER.info("Cloud API connection succeeded.") + res = await tuya_api.async_get_devices_list() hass.data[DOMAIN][DATA_CLOUD] = tuya_api async def setup_entities(dev_id): @@ -262,6 +281,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): for dev_id in entry.data[CONF_DEVICES]: hass.async_create_task(setup_entities(dev_id)) + unsub_listener = entry.add_update_listener(update_listener) + hass.data[DOMAIN][entry.entry_id] = {UNSUB_LISTENER: unsub_listener} + return True @@ -269,7 +291,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload a config entry.""" platforms = {} - hass.data[DOMAIN][UNSUB_LISTENER]() + hass.data[DOMAIN][entry.entry_id][UNSUB_LISTENER]() for dev_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): if device.connected: await device.close() diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index a995fe1..cb74ab1 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -38,6 +38,7 @@ from .const import ( CONF_PRODUCT_NAME, CONF_PROTOCOL_VERSION, CONF_SETUP_CLOUD, + CONF_NO_CLOUD, CONF_USER_ID, DATA_CLOUD, DATA_DISCOVERY, @@ -48,6 +49,8 @@ from .discovery import discover _LOGGER = logging.getLogger(__name__) +ENTRIES_VERSION = 2 + PLATFORM_TO_ADD = "platform_to_add" NO_ADDITIONAL_ENTITIES = "no_additional_entities" SELECTED_DEVICE = "selected_device" @@ -69,10 +72,11 @@ CONFIGURE_SCHEMA = vol.Schema( CLOUD_SETUP_SCHEMA = vol.Schema( { vol.Required(CONF_REGION, default="eu"): vol.In(["eu", "us", "cn", "in"]), - vol.Required(CONF_CLIENT_ID): cv.string, - vol.Required(CONF_CLIENT_SECRET): cv.string, - vol.Required(CONF_USER_ID): cv.string, + vol.Optional(CONF_CLIENT_ID): cv.string, + vol.Optional(CONF_CLIENT_SECRET): cv.string, + vol.Optional(CONF_USER_ID): cv.string, vol.Optional(CONF_USERNAME, default=DOMAIN): cv.string, + vol.Required(CONF_NO_CLOUD, default=False): bool, } ) @@ -279,7 +283,7 @@ async def attempt_cloud_connection(hass, user_input): class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for LocalTuya integration.""" - VERSION = 2 + VERSION = ENTRIES_VERSION CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL @staticmethod @@ -296,9 +300,14 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors = {} placeholders = {} if user_input is not None: + if user_input.get(CONF_NO_CLOUD): + for i in [CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_USER_ID]: + user_input[i] = "" + return await self._create_entry(user_input) + cloud_api, res = await attempt_cloud_connection(self.hass, user_input) - if len(res) == 0: + if not res: return await self._create_entry(user_input) errors["base"] = res["reason"] placeholders = {"msg": res["msg"]} @@ -315,8 +324,8 @@ class LocaltuyaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): async def _create_entry(self, user_input): """Register new entry.""" - if self._async_current_entries(): - return self.async_abort(reason="already_configured") + # if self._async_current_entries(): + # return self.async_abort(reason="already_configured") await self.async_set_unique_id(user_input.get(CONF_USER_ID)) user_input[CONF_DEVICES] = {} @@ -370,9 +379,22 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): errors = {} placeholders = {} if user_input is not None: + if user_input.get(CONF_NO_CLOUD): + new_data = self.config_entry.data.copy() + new_data.update(user_input) + for i in [CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_USER_ID]: + new_data[i] = "" + self.hass.config_entries.async_update_entry( + self.config_entry, + data=new_data, + ) + return self.async_create_entry( + title=new_data.get(CONF_USERNAME), data={} + ) + cloud_api, res = await attempt_cloud_connection(self.hass, user_input) - if len(res) == 0: + if not res: new_data = self.config_entry.data.copy() new_data.update(user_input) cloud_devs = cloud_api.device_list @@ -394,6 +416,7 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): defaults = self.config_entry.data.copy() defaults.update(user_input or {}) + defaults[CONF_NO_CLOUD] = False return self.async_show_form( step_id="cloud_setup", @@ -490,12 +513,11 @@ class LocalTuyaOptionsFlowHandler(config_entries.OptionsFlow): self.device_data.update( { CONF_DEVICE_ID: dev_id, - CONF_MODEL: self.device_data[CONF_MODEL], CONF_DPS_STRINGS: self.dps_strings, CONF_ENTITIES: [], } ) - if len(user_input[CONF_ENTITIES]) > 0: + if user_input[CONF_ENTITIES]: entity_ids = [ int(entity.split(":")[0]) for entity in user_input[CONF_ENTITIES] From d8b1946ed5244261d9f3da3df787c4d955643e93 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Tue, 7 Jun 2022 10:54:26 +0200 Subject: [PATCH 50/52] Cleaned debug messages --- custom_components/localtuya/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index b86eb3c..3b4ce31 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -239,8 +239,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): ) return - _LOGGER.debug("ASE: %s %s", entry.entry_id, entry.data) - region = entry.data[CONF_REGION] client_id = entry.data[CONF_CLIENT_ID] secret = entry.data[CONF_CLIENT_SECRET] @@ -249,7 +247,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): no_cloud = True if CONF_NO_CLOUD in entry.data: no_cloud = entry.data.get(CONF_NO_CLOUD) - _LOGGER.debug("ASE DC: %s", no_cloud) if no_cloud: _LOGGER.info("Cloud API account not configured.") # wait 1 second to make sure possible migration has finished From 47d399e3a97c93b5b0d6b2d59a071eff4cc92a7a Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Wed, 8 Jun 2022 09:59:19 +0200 Subject: [PATCH 51/52] Improved description of reload service; removed some print messages --- custom_components/localtuya/services.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/localtuya/services.yaml b/custom_components/localtuya/services.yaml index b276d2f..f10af4a 100644 --- a/custom_components/localtuya/services.yaml +++ b/custom_components/localtuya/services.yaml @@ -1,5 +1,5 @@ reload: - description: Reload localtuya and re-process yaml configuration. + description: Reload localtuya and reconnect to all devices. set_dp: description: Change the value of a datapoint (DP) From 1efe8db021a89fb6e1e377466715ffdd75268118 Mon Sep 17 00:00:00 2001 From: rospogrigio Date: Wed, 8 Jun 2022 14:08:48 +0200 Subject: [PATCH 52/52] Fixed restoring entity state when reloading --- custom_components/localtuya/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 3b4ce31..f1af7f0 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -288,11 +288,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): """Unload a config entry.""" platforms = {} - hass.data[DOMAIN][entry.entry_id][UNSUB_LISTENER]() - for dev_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): - if device.connected: - await device.close() - for dev_id, dev_entry in entry.data[CONF_DEVICES].items(): for entity in dev_entry[CONF_ENTITIES]: platforms[entity[CONF_PLATFORM]] = True @@ -305,6 +300,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): ] ) ) + + hass.data[DOMAIN][entry.entry_id][UNSUB_LISTENER]() + for dev_id, device in hass.data[DOMAIN][TUYA_DEVICES].items(): + if device.connected: + await device.close() + if unload_ok: hass.data[DOMAIN][TUYA_DEVICES] = {}