From 9920d620f72580890a38e72f748bcef032054ca0 Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Tue, 8 Sep 2020 21:00:44 -0700 Subject: [PATCH 01/15] Testing covers --- custom_components/localtuya/strings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/custom_components/localtuya/strings.json b/custom_components/localtuya/strings.json index 898b99d48..e972feaff 100644 --- a/custom_components/localtuya/strings.json +++ b/custom_components/localtuya/strings.json @@ -8,7 +8,8 @@ "cannot_connect": "Cannot connect to device. Verify that address is correct.", "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.", - "switch_already_configured": "Switch with this ID has already been configured." + "switch_already_configured": "Switch with this ID has already been configured.", + "cover_already_configured": "Cover with this ID has already been configured." }, "step": { "user": { From 5b2c79899c692f4204b5dcf7115b3419ccd561ad Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Thu, 10 Sep 2020 11:33:03 -0700 Subject: [PATCH 02/15] git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9b5e2e844..a7361f743 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *~ __pycache__ +.DS_Store From a95a6c50cafceb0bdb239cea2cab59cf785aed3d Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Wed, 16 Sep 2020 01:46:17 -0700 Subject: [PATCH 03/15] update to last merge --- custom_components/localtuya/strings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/custom_components/localtuya/strings.json b/custom_components/localtuya/strings.json index e972feaff..898b99d48 100644 --- a/custom_components/localtuya/strings.json +++ b/custom_components/localtuya/strings.json @@ -8,8 +8,7 @@ "cannot_connect": "Cannot connect to device. Verify that address is correct.", "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.", - "switch_already_configured": "Switch with this ID has already been configured.", - "cover_already_configured": "Cover with this ID has already been configured." + "switch_already_configured": "Switch with this ID has already been configured." }, "step": { "user": { From d2a2f725df8ea8e578d5d8e90b4c4ae0197079ae Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Wed, 16 Sep 2020 03:30:18 -0700 Subject: [PATCH 04/15] rebased and reloaded --- custom_components/localtuya/config_flow.py | 13 +- custom_components/localtuya/const.py | 15 +- custom_components/localtuya/cover.py | 336 ++++++++++++++---- .../localtuya/translations/en.json | 6 +- 4 files changed, 289 insertions(+), 81 deletions(-) diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 12dcac7a2..4964bf5a4 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -16,6 +16,7 @@ CONF_FRIENDLY_NAME, CONF_PLATFORM, CONF_SWITCHES, + CONF_COVERS, ) from . import pytuya @@ -204,10 +205,7 @@ 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] == user_input[CONF_ID] for switch in self.entities - ) - if not already_configured: + already_configured = any(switch[CONF_ID] == user_input[CONF_ID] for switch in self.entities) or any(cover[CONF_ID] == user_input[CONF_ID] for cover 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() @@ -238,8 +236,11 @@ def _convert_entity(conf): self.platform = user_input[CONF_PLATFORM] if len(user_input.get(CONF_SWITCHES, [])) > 0: - for switch_conf in user_input[CONF_SWITCHES].values(): - self.entities.append(_convert_entity(switch_conf)) + for switch_conf in user_input[CONF_SWITCHES].values(): + self.entities.append(_convert_entity(switch_conf)) + elif len(user_input.get(CONF_COVERS, [])) > 0: + for cover_conf in user_input[CONF_COVERS].values(): + self.entities.append(_convert_entity(cover_conf)) else: self.entities.append(_convert_entity(user_input)) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index 34f2479ec..ad34f6128 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -15,9 +15,18 @@ CONF_VOLTAGE = "voltage" # cover -CONF_OPEN_CMD = 'open_cmd' -CONF_CLOSE_CMD = 'close_cmd' -CONF_STOP_CMD = 'stop_cmd' +ATTR_OPEN_CMD = "open_cmd" +ATTR_CLOSE_CMD = "close_cmd" +ATTR_STOP_CMD = "stop_cmd" +ATTR_GET_POSITION_KEY = "get_position_key" +ATTR_SET_POSITION_KEY = "set_position_key" +CONF_OPEN_CMD = "open_cmd" +CONF_CLOSE_CMD = "close_cmd" +CONF_STOP_CMD = "stop_cmd" +CONF_GET_POSITION_KEY = "get_position_key" +CONF_SET_POSITION_KEY = "set_position_key" +CONF_COVER_COMMAND_KEY = "cover_command_key" +CONF_LAST_MOVEMENT_KEY = "last_movement_key" DOMAIN = "localtuya" diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 2e13040d3..7f1753510 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -4,18 +4,23 @@ Sample config yaml: cover: - - platform: localtuya #REQUIRED - host: 192.168.0.123 #REQUIRED - local_key: 1234567891234567 #REQUIRED - device_id: 123456789123456789abcd #REQUIRED - name: cover_guests #REQUIRED - friendly_name: Cover guests #REQUIRED - protocol_version: 3.3 #REQUIRED - id: 1 #OPTIONAL - icon: mdi:blinds #OPTIONAL - open_cmd: open #OPTIONAL, default is 'on' - close_cmd: close #OPTIONAL, default is 'off' - stop_cmd: stop #OPTIONAL, default is 'stop' +- platform: localtuya #REQUIRED + host: 192.168.0.123 #REQUIRED + local_key: 1234567891234567 #REQUIRED + device_id: 123456789123456789abcd #REQUIRED + name: cover_guests #REQUIRED + friendly_name: Cover guests #REQUIRED + protocol_version: 3.3 #REQUIRED + get_position_key: 3 #REQUIRED, default is 0 + set_position_key: 2 #REQUIRED, default is 0 + last_movement_key: 7 #REQUIRED, default is 0 + cover_command_key: 1 #REQUIRED, default is 0 + id: 1 #OPTIONAL + icon: mdi:blinds #OPTIONAL + open_cmd: open #OPTIONAL, default is 'on' + close_cmd: close #OPTIONAL, default is 'off' + stop_cmd: stop #OPTIONAL, default is 'stop' + get_position_key: 1 #OPTIONAL, default is 0 """ import logging @@ -32,10 +37,13 @@ SUPPORT_OPEN, SUPPORT_STOP, SUPPORT_SET_POSITION, + ATTR_POSITION ) from homeassistant.const import ( CONF_ID, + CONF_COVERS, CONF_FRIENDLY_NAME, + CONF_NAME, ) import homeassistant.helpers.config_validation as cv @@ -46,11 +54,15 @@ import_from_yaml, ) from .const import ( + CONF_COVER_COMMAND_KEY, CONF_OPEN_CMD, CONF_CLOSE_CMD, CONF_STOP_CMD, + CONF_GET_POSITION_KEY, + CONF_SET_POSITION_KEY, + CONF_LAST_MOVEMENT_KEY ) -from .const import CONF_OPEN_CMD, CONF_CLOSE_CMD, CONF_STOP_CMD + from .pytuya import TuyaDevice _LOGGER = logging.getLogger(__name__) @@ -58,13 +70,25 @@ DEFAULT_OPEN_CMD = "on" DEFAULT_CLOSE_CMD = "off" DEFAULT_STOP_CMD = "stop" +DEFAULT_SET_POSITION_KEY = 0 +DEFAULT_GET_POSITION_KEY = 0 +DEFAULT_LAST_MOVEMENT_KEY = 0 +DEFAULT_COVER_COMMAND_KEY = 0 + +MIN_POSITION = 0 +MAX_POSITION = 100 +UPDATE_RETRY_LIMIT = 3 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BASE_PLATFORM_SCHEMA).extend( { + vol.Required(CONF_COVER_COMMAND_KEY, default=DEFAULT_COVER_COMMAND_KEY): cv.positive_int, # TODO only allow user to select from returned dps list vol.Optional(CONF_OPEN_CMD, default=DEFAULT_OPEN_CMD): cv.string, vol.Optional(CONF_CLOSE_CMD, default=DEFAULT_CLOSE_CMD): cv.string, vol.Optional(CONF_STOP_CMD, default=DEFAULT_STOP_CMD): cv.string, + vol.Optional(CONF_SET_POSITION_KEY, default=DEFAULT_SET_POSITION_KEY): cv.positive_int, # TODO only allow user to select from returned dps list + vol.Optional(CONF_GET_POSITION_KEY, default=DEFAULT_GET_POSITION_KEY): cv.positive_int, # TODO only allow user to select from returned dps list + vol.Optional(CONF_LAST_MOVEMENT_KEY, default=DEFAULT_LAST_MOVEMENT_KEY): cv.positive_int, # TODO only allow user to select from returned dps list } ) @@ -72,10 +96,14 @@ def flow_schema(dps): """Return schema used in config flow.""" return { - vol.Optional(CONF_OPEN_CMD, default=DEFAULT_OPEN_CMD): str, - vol.Optional(CONF_CLOSE_CMD, default=DEFAULT_CLOSE_CMD): str, - vol.Optional(CONF_STOP_CMD, default=DEFAULT_STOP_CMD): str, - } + vol.Required(CONF_COVER_COMMAND_KEY, default=DEFAULT_COVER_COMMAND_KEY): cv.positive_int, + vol.Optional(CONF_OPEN_CMD, default=DEFAULT_OPEN_CMD): str, + vol.Optional(CONF_CLOSE_CMD, default=DEFAULT_CLOSE_CMD): str, + vol.Optional(CONF_STOP_CMD, default=DEFAULT_STOP_CMD): str, + vol.Optional(CONF_SET_POSITION_KEY, default=DEFAULT_SET_POSITION_KEY): cv.positive_int, + vol.Optional(CONF_GET_POSITION_KEY, default=DEFAULT_GET_POSITION_KEY): cv.positive_int, + vol.Optional(CONF_LAST_MOVEMENT_KEY, default=DEFAULT_LAST_MOVEMENT_KEY): cv.positive_int, + } async def async_setup_entry(hass, config_entry, async_add_entities): @@ -97,6 +125,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities): TuyaCache(device, config_entry.data[CONF_FRIENDLY_NAME]), config_entry, device_config[CONF_ID], + # TuyaCache(device), + # device_config[CONF_FRIENDLY_NAME], + # device_config[CONF_ID], + # device_config.get(CONF_COVER_COMMAND_KEY), + # device_config.get(CONF_OPEN_CMD), + # device_config.get(CONF_CLOSE_CMD), + # device_config.get(CONF_STOP_CMD), + # device_config.get(CONF_SET_POSITION_KEY), + # device_config.get(CONF_GET_POSITION_KEY), + # device_config.get(CONF_LAST_MOVEMENT_KEY), ) ) @@ -114,9 +152,9 @@ class TuyaCache: def __init__(self, device, friendly_name): """Initialize the cache.""" self._cached_status = "" + self._friendly_name = friendly_name self._cached_status_time = 0 self._device = device - self._friendly_name = friendly_name self._lock = Lock() @property @@ -146,7 +184,7 @@ def __get_status(self): def set_dps(self, state, dps_index): #_LOGGER.info("running def set_dps from cover") - """Change the Tuya switch status and clear the cache.""" + """Change the Tuya cover status and clear the cache.""" self._cached_status = "" self._cached_status_time = 0 for i in range(5): @@ -166,7 +204,7 @@ def set_dps(self, state, dps_index): # raise ConnectionError("Failed to set status.") def status(self): - """Get state of Tuya switch and cache the results.""" + """Get state of Tuya cover and cache the results.""" # _LOGGER.info("running def status(self) from cover") self._lock.acquire() try: @@ -196,9 +234,184 @@ def __init__( print( "Initialized tuya cover [{}] with switch status [{}] and state [{}]".format( self.name, self._status, self._state + + # def __init__(self, device, friendly_name, coverid, cover_command_key, open_cmd, close_cmd, stop_cmd, set_position_key, get_position_key, last_movement_key): + # self._device = device + # self._available = False + # self._name = friendly_name + # self._cover_id = coverid + # self._status = None + # self._state = None + # self._last_movement = None + # self._last_position_set = None + # self._last_command = None + # self._current_cover_position = 50 + # self._cover_command_key = cover_command_key + # self._open_cmd = open_cmd + # self._close_cmd = close_cmd + # self._stop_cmd = stop_cmd + # self._get_position_key = get_position_key + # self._set_position_key = set_position_key + # self._last_movement_key = last_movement_key + + # print( + # "Initialized tuya cover [{}] with cover status [{}] and state [{}]".format( + # self._name, self._status, self._state ) ) + @property + def device_info(self): + return { + "identifiers": { + # Serial numbers are unique identifiers within a specific domain + ("LocalTuya", f"local_{self._device.unique_id}") + }, + "name": self._device._friendly_name, + "manufacturer": "Tuya generic", + "model": "SmartCover", + "sw_version": "3.3", + } + + @property + def unique_id(self): + """Return unique device identifier.""" + return f"local_{self._device.unique_id}_{self._cover_id}" + + @property + def name(self): + """Get name of Tuya cover.""" + return self._name + + @property + def cover_command_key(self): + """Get name of Tuya cover.""" + #_LOGGER.info("def cover_command_key called=%s", self._cover_command_key) + return self._cover_command_key + + @property + def open_cmd(self): + """Get name of open command.""" + #_LOGGER.info("def open_cmd called=%s", self._open_cmd) + return self._open_cmd + + @property + def close_cmd(self): + """Get name of close command.""" + #_LOGGER.info("def close_cmd called=%s", self._close_cmd) + return self._close_cmd + + @property + def stop_cmd(self): + """Get name of stop command.""" + #_LOGGER.info("def close_cmd called=%s", self._stop_cmd) + return self._stop_cmd + + @property + def last_movement_key(self): + """Get last movement key""" + #_LOGGER.info("def last_movement_key called=%s", self._last_movement_key) + return self._last_movement_key + + + @property + def last_movement(self): + #_LOGGER.info("def last_movement called=%s", self._last_movement) + """Return the last movement of the device (should be "Opening" or "Closing")""" + return self._last_movement + + def last_position_set(self): + #_LOGGER.info("def last_position_set called=%s", self._last_position_set) + """Return the last position set of the device""" + return self._last_position_set + + @property + def last_command(self): + #_LOGGER.info("def last_command called=%s", self._last_command) + """Return the last command of the device""" + return self._last_command + + @property + def is_open(self): + """Check if the cover is partially or fully open.""" + #_LOGGER.info("def is_open called, self._current_cover_position = %s", self._current_cover_position) + if self._current_cover_position != 100: + #_LOGGER.info("is_open returning true)") + return True + #_LOGGER.info("is_open returning false") + return False + + @property + def is_closed(self): + """Check if the cover is fully closed.""" + #_LOGGER.info("def is_closed called") + if self._current_cover_position == 100: + #_LOGGER.info("is_closed returning true)") + return True + #_LOGGER.info("is_closed returning false") + return False + + def update(self): + """Update cover attributes and store in cache.""" + #_LOGGER.info("update(self) called") + try: + #_LOGGER.info("about to call status=self._device.status() from def update(self)") + status = self._device.status() + #_LOGGER.info("def update set status =%s", status) + self._cached_status = status + #_LOGGER.info("self._cached_status set =%s", self._cached_status) + + self._update_last_command() + self._update_last_movement() + self._update_last_position_set() + self._update_current_cover_position() + + except: + #_LOGGER.info("def update returned except, setting available to false") + self._available = False + else: + #_LOGGER.info("def update reached else, setting available to true") + self._available = True + + def _update_last_movement(self): + #_LOGGER.info("_update_last_movement called, currently = %s and self._cached_status=%s ", self._last_movement, self._cached_status) + + self._last_movement = self._cached_status['dps'][str(self._last_movement_key)] + #_LOGGER.info("_update_last_movement set, now = %s", self._last_movement) + + def _update_last_command(self): + #_LOGGER.info("_update_last_command called, currently = %s and self._cached_status=%s", self._last_command, self._cached_status) + self._last_command = self._cached_status['dps'][str(self._cover_command_key)] + #_LOGGER.info("_update_last_command set, now = %s", self._last_command) + + + def _update_last_position_set(self): + #_LOGGER.info("_update_last_position_set called, currently = %s and self._cached_status=%s", self._last_position_set, self._cached_status) + self._last_position_set = self._cached_status['dps'][str(self._set_position_key)] + #_LOGGER.info("_update_last_position_set set, now = %s", self._last_position_set) + + + def _update_current_cover_position(self): + #_LOGGER.info("_update_current_cover_position called, currently = %s and self._cached_status=%s", self._current_cover_position, self._cached_status) + self._current_cover_position = self._cached_status['dps'][str(self._get_position_key)] + #_LOGGER.info("_update_current_cover_position set, now = %s", self._current_cover_position) + + @property + def current_cover_position(self): + #_LOGGER.info("current_cover_position called") + """Return position of Tuya cover.""" + return self._current_cover_position + + @property + def min_position(self): + """Return minimum position of Tuya cover.""" + return MIN_POSITION + + @property + def max_position(self): + """Return maximum position of Tuya cover.""" + return MAX_POSITION + @property def available(self): """Return if device is available or not.""" @@ -207,70 +420,35 @@ def available(self): @property def supported_features(self): """Flag supported features.""" - supported_features = ( - SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP | SUPPORT_SET_POSITION - ) + supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP | SUPPORT_SET_POSITION return supported_features - @property - def current_cover_position(self): - # self.update() - # state = self._state - # _LOGGER.info("curr_pos() : %i", self._position) - # print('curr_pos() : state [{}]'.format(state)) - return self._position - @property def is_opening(self): - # self.update() - state = self._state - # print('is_opening() : state [{}]'.format(state)) - if state == "on": + #_LOGGER.info("is_opening called") + last_movement = self._last_movement; + if last_movement == 'Opening': return True return False @property def is_closing(self): - # self.update() - state = self._state - # print('is_closing() : state [{}]'.format(state)) - if state == "off": + #_LOGGER.info("is_closing called") + last_movement = self._last_movement; + if last_movement == 'Closing': return True return False - @property - def is_closed(self): - """Return if the cover is closed or not.""" - # _LOGGER.info("running is_closed from cover") - # self.update() - state = self._state - # print('is_closed() : state [{}]'.format(state)) - if state == "off": - return False - if state == "on": - return True - return None - def set_cover_position(self, **kwargs): - # _LOGGER.info("running set_cover_position from cover") - """Move the cover to a specific position.""" - - newpos = float(kwargs["position"]) - # _LOGGER.info("Set new pos: %f", newpos) - - currpos = self.current_cover_position - posdiff = abs(newpos - currpos) - # 25 sec corrisponde alla chiusura/apertura completa - mydelay = posdiff / 2.0 - if newpos > currpos: - # _LOGGER.info("Opening to %f: delay %f", newpos, mydelay ) - self.open_cover() - else: - # _LOGGER.info("Closing to %f: delay %f", newpos, mydelay ) - self.close_cover() - sleep(mydelay) - self.stop_cover() - self._position = 50 # newpos + """Set the cover to a specific position from 0-100""" + #_LOGGER.debug("set_cover_position called") + if ATTR_POSITION in kwargs: + converted_position = int(kwargs[ATTR_POSITION]) + if converted_position in range(0,101): + _LOGGER.info("set_cover_position about to set position to =%s", converted_position) + self._device.set_dps(converted_position, self._config[SET_POSITION_KEY]) + else: + _LOGGER.warning("set_position given number outside range") def open_cover(self, **kwargs): @@ -291,3 +469,19 @@ def stop_cover(self, **kwargs): def status_updated(self): """Device status was updated. """ self._state = self.dps(self._dps_id) + + + # def open_cover(self, **kwargs): + # """Open the cover.""" + # #_LOGGER.info("open_cover called where self._cover_command_key=%s and self._open_cmd=%s", self._cover_command_key, self._open_cmd) + # self._device.set_dps(str(self._cover_command_key), str(self._open_cmd)) + + # def close_cover(self, **kwargs): + # """Close the cover.""" + # #_LOGGER.info("close_cover called where self._cover_command_key=%s and self._close_cmd=%s", self._cover_command_key, self._close_cmd) + # self._device.set_dps(str(self._cover_command_key), str(self._close_cmd)) + + # def stop_cover(self, **kwargs): + # """Stop the cover.""" + # #_LOGGER.info("stop_cover called where self._cover_command_key=%s and self._stop_cmd=%s", self._cover_command_key, self._stop_cmd) + # self._device.set_dps(str(self._cover_command_key), str(self.stop_cmd)) \ No newline at end of file diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 8bfbe5812..6f0c5266b 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -40,7 +40,11 @@ "voltage": "Voltage", "open_cmd": "Open Command", "close_cmd": "Close Command", - "stop_cmd": "Stop Command" + "stop_cmd": "Stop Command", + "get_position_key": "Get Position Key", + "set_position_key": "Set Position Key", + "cover_command_key": "Open/Close/Stop Key", + "last_movement_key": "Last Movement Key" } } } From d1c3c3586d12b8662518f52e3bd0fc0311145bec Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Wed, 16 Sep 2020 18:42:47 -0700 Subject: [PATCH 05/15] Friendly name fix and set_dps variable reversal fix --- custom_components/localtuya/config_flow.py | 3 ++- custom_components/localtuya/cover.py | 27 ++++++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 4964bf5a4..393fd547c 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -205,7 +205,8 @@ 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] == user_input[CONF_ID] for switch in self.entities) or any(cover[CONF_ID] == user_input[CONF_ID] for cover in self.entities) if not already_configured: + already_configured = any(switch[CONF_ID] == user_input[CONF_ID] for switch in self.entities) or any(cover[CONF_ID] == user_input[CONF_ID] for cover 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() diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 7f1753510..145a2f35b 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -149,10 +149,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class TuyaCache: """Cache wrapper for pytuya.TuyaDevice""" - def __init__(self, device, friendly_name): + def __init__(self, device): """Initialize the cache.""" self._cached_status = "" - self._friendly_name = friendly_name self._cached_status_time = 0 self._device = device self._lock = Lock() @@ -182,15 +181,15 @@ def __get_status(self): # return None raise ConnectionError("Failed to update status .") - def set_dps(self, state, dps_index): + def set_dps(self, value, dps_index): #_LOGGER.info("running def set_dps from cover") """Change the Tuya cover status and clear the cache.""" self._cached_status = "" self._cached_status_time = 0 for i in range(5): try: - #_LOGGER.info("Running a try from def set_dps from cover where state=%s and dps_index=%s", state, dps_index) - return self._device.set_dps(state, dps_index) + #_LOGGER.info("Running a try from def set_dps from cover where value=%s and dps_index=%s", state, dps_index) + return self._device.set_dps(value, dps_index) except Exception: print( "Failed to set status of device [{}]".format(self._device.address) @@ -267,7 +266,7 @@ def device_info(self): # Serial numbers are unique identifiers within a specific domain ("LocalTuya", f"local_{self._device.unique_id}") }, - "name": self._device._friendly_name, + "name": self._name, "manufacturer": "Tuya generic", "model": "SmartCover", "sw_version": "3.3", @@ -484,4 +483,18 @@ def status_updated(self): # def stop_cover(self, **kwargs): # """Stop the cover.""" # #_LOGGER.info("stop_cover called where self._cover_command_key=%s and self._stop_cmd=%s", self._cover_command_key, self._stop_cmd) - # self._device.set_dps(str(self._cover_command_key), str(self.stop_cmd)) \ No newline at end of file + # self._device.set_dps(str(self._cover_command_key), str(self.stop_cmd)) + #_LOGGER.info("open_cover called where self._cover_command_key=%s and self._open_cmd=%s", self._cover_command_key, self._open_cmd) + # self._device.set_dps(str(self._open_cmd), str(self._cover_command_key)) + + # def close_cover(self, **kwargs): + # """Close the cover.""" + # #_LOGGER.info("close_cover called where self._cover_command_key=%s and self._close_cmd=%s", self._cover_command_key, self._close_cmd) + # self._device.set_dps(str(self._close_cmd), str(self._cover_command_key)) + + # def stop_cover(self, **kwargs): + # """Stop the cover.""" + # #_LOGGER.info("stop_cover called where self._cover_command_key=%s and self._stop_cmd=%s", self._cover_command_key, self._stop_cmd) + # self._device.set_dps(str(self._stop_cmd), str(self._cover_command_key)) + + \ No newline at end of file From 93feb9e047ca58fe378925109167cfa14e69ae47 Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Thu, 17 Sep 2020 09:36:30 -0700 Subject: [PATCH 06/15] Revert #20, fix friendly_name key error --- custom_components/localtuya/cover.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 145a2f35b..63029574e 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -149,11 +149,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class TuyaCache: """Cache wrapper for pytuya.TuyaDevice""" - def __init__(self, device): + def __init__(self, device, friendly_name): """Initialize the cache.""" self._cached_status = "" self._cached_status_time = 0 self._device = device + self._friendly_name = friendly_name self._lock = Lock() @property @@ -266,7 +267,7 @@ def device_info(self): # Serial numbers are unique identifiers within a specific domain ("LocalTuya", f"local_{self._device.unique_id}") }, - "name": self._name, + "name": self._device._friendly_name, "manufacturer": "Tuya generic", "model": "SmartCover", "sw_version": "3.3", From cf687737f24b562620c1b260b4a825330c34e663 Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Fri, 18 Sep 2020 22:05:30 -0700 Subject: [PATCH 07/15] Updates --- custom_components/localtuya/const.py | 12 +-- custom_components/localtuya/cover.py | 101 ++++++++---------- .../localtuya/translations/en.json | 7 +- 3 files changed, 49 insertions(+), 71 deletions(-) diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index ad34f6128..f77984d55 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -15,18 +15,12 @@ CONF_VOLTAGE = "voltage" # cover -ATTR_OPEN_CMD = "open_cmd" -ATTR_CLOSE_CMD = "close_cmd" -ATTR_STOP_CMD = "stop_cmd" -ATTR_GET_POSITION_KEY = "get_position_key" -ATTR_SET_POSITION_KEY = "set_position_key" CONF_OPEN_CMD = "open_cmd" CONF_CLOSE_CMD = "close_cmd" CONF_STOP_CMD = "stop_cmd" -CONF_GET_POSITION_KEY = "get_position_key" -CONF_SET_POSITION_KEY = "set_position_key" -CONF_COVER_COMMAND_KEY = "cover_command_key" -CONF_LAST_MOVEMENT_KEY = "last_movement_key" +CONF_GET_POSITION_DPS = "get_position_dps" +CONF_SET_POSITION_DPS = "set_position_dps" +CONF_LAST_MOVEMENT_DPS = "last_movement_dps" DOMAIN = "localtuya" diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 63029574e..cce68b68c 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -8,20 +8,17 @@ host: 192.168.0.123 #REQUIRED local_key: 1234567891234567 #REQUIRED device_id: 123456789123456789abcd #REQUIRED - name: cover_guests #REQUIRED + name: cover_guests #OPTIONAL friendly_name: Cover guests #REQUIRED protocol_version: 3.3 #REQUIRED - get_position_key: 3 #REQUIRED, default is 0 - set_position_key: 2 #REQUIRED, default is 0 - last_movement_key: 7 #REQUIRED, default is 0 - cover_command_key: 1 #REQUIRED, default is 0 + get_position_dps: 3 #OPTIONAL, default is 0 + set_position_dps: 2 #OPTIONAL, default is 0 + last_movement_dps: 7 #OPTIONAL, default is 0 id: 1 #OPTIONAL icon: mdi:blinds #OPTIONAL open_cmd: open #OPTIONAL, default is 'on' close_cmd: close #OPTIONAL, default is 'off' stop_cmd: stop #OPTIONAL, default is 'stop' - get_position_key: 1 #OPTIONAL, default is 0 - """ import logging from time import time, sleep @@ -54,13 +51,12 @@ import_from_yaml, ) from .const import ( - CONF_COVER_COMMAND_KEY, CONF_OPEN_CMD, CONF_CLOSE_CMD, CONF_STOP_CMD, - CONF_GET_POSITION_KEY, - CONF_SET_POSITION_KEY, - CONF_LAST_MOVEMENT_KEY + CONF_GET_POSITION_DPS, + CONF_SET_POSITION_DPS, + CONF_LAST_MOVEMENT_DPS ) from .pytuya import TuyaDevice @@ -70,10 +66,9 @@ DEFAULT_OPEN_CMD = "on" DEFAULT_CLOSE_CMD = "off" DEFAULT_STOP_CMD = "stop" -DEFAULT_SET_POSITION_KEY = 0 -DEFAULT_GET_POSITION_KEY = 0 -DEFAULT_LAST_MOVEMENT_KEY = 0 -DEFAULT_COVER_COMMAND_KEY = 0 +DEFAULT_SET_POSITION_DPS = 0 +DEFAULT_GET_POSITION_DPS = 0 +DEFAULT_LAST_MOVEMENT_DPS = 0 MIN_POSITION = 0 MAX_POSITION = 100 @@ -82,13 +77,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BASE_PLATFORM_SCHEMA).extend( { - vol.Required(CONF_COVER_COMMAND_KEY, default=DEFAULT_COVER_COMMAND_KEY): cv.positive_int, # TODO only allow user to select from returned dps list vol.Optional(CONF_OPEN_CMD, default=DEFAULT_OPEN_CMD): cv.string, vol.Optional(CONF_CLOSE_CMD, default=DEFAULT_CLOSE_CMD): cv.string, vol.Optional(CONF_STOP_CMD, default=DEFAULT_STOP_CMD): cv.string, - vol.Optional(CONF_SET_POSITION_KEY, default=DEFAULT_SET_POSITION_KEY): cv.positive_int, # TODO only allow user to select from returned dps list - vol.Optional(CONF_GET_POSITION_KEY, default=DEFAULT_GET_POSITION_KEY): cv.positive_int, # TODO only allow user to select from returned dps list - vol.Optional(CONF_LAST_MOVEMENT_KEY, default=DEFAULT_LAST_MOVEMENT_KEY): cv.positive_int, # TODO only allow user to select from returned dps list + vol.Optional(CONF_SET_POSITION_DPS, default=DEFAULT_SET_POSITION_DPS): cv.positive_int, # TODO only allow user to select from returned dps list + vol.Optional(CONF_GET_POSITION_DPS, default=DEFAULT_GET_POSITION_DPS): cv.positive_int, # TODO only allow user to select from returned dps list + vol.Optional(CONF_LAST_MOVEMENT_DPS, default=DEFAULT_LAST_MOVEMENT_DPS): cv.positive_int, # TODO only allow user to select from returned dps list } ) @@ -96,13 +90,12 @@ def flow_schema(dps): """Return schema used in config flow.""" return { - vol.Required(CONF_COVER_COMMAND_KEY, default=DEFAULT_COVER_COMMAND_KEY): cv.positive_int, vol.Optional(CONF_OPEN_CMD, default=DEFAULT_OPEN_CMD): str, vol.Optional(CONF_CLOSE_CMD, default=DEFAULT_CLOSE_CMD): str, vol.Optional(CONF_STOP_CMD, default=DEFAULT_STOP_CMD): str, - vol.Optional(CONF_SET_POSITION_KEY, default=DEFAULT_SET_POSITION_KEY): cv.positive_int, - vol.Optional(CONF_GET_POSITION_KEY, default=DEFAULT_GET_POSITION_KEY): cv.positive_int, - vol.Optional(CONF_LAST_MOVEMENT_KEY, default=DEFAULT_LAST_MOVEMENT_KEY): cv.positive_int, + vol.Optional(CONF_SET_POSITION_DPS, default=DEFAULT_SET_POSITION_DPS): cv.positive_int, + vol.Optional(CONF_GET_POSITION_DPS, default=DEFAULT_GET_POSITION_DPS): cv.positive_int, + vol.Optional(CONF_LAST_MOVEMENT_DPS, default=DEFAULT_LAST_MOVEMENT_DPS): cv.positive_int, } @@ -128,13 +121,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities): # TuyaCache(device), # device_config[CONF_FRIENDLY_NAME], # device_config[CONF_ID], - # device_config.get(CONF_COVER_COMMAND_KEY), # device_config.get(CONF_OPEN_CMD), # device_config.get(CONF_CLOSE_CMD), # device_config.get(CONF_STOP_CMD), - # device_config.get(CONF_SET_POSITION_KEY), - # device_config.get(CONF_GET_POSITION_KEY), - # device_config.get(CONF_LAST_MOVEMENT_KEY), + # device_config.get(CONF_SET_POSITION_DPS), + # device_config.get(CONF_GET_POSITION_DPS), + # device_config.get(CONF_LAST_MOVEMENT_DPS), ) ) @@ -235,7 +227,7 @@ def __init__( "Initialized tuya cover [{}] with switch status [{}] and state [{}]".format( self.name, self._status, self._state - # def __init__(self, device, friendly_name, coverid, cover_command_key, open_cmd, close_cmd, stop_cmd, set_position_key, get_position_key, last_movement_key): + # def __init__(self, device, friendly_name, coverid, open_cmd, close_cmd, stop_cmd, set_position_dps, get_position_dps, last_movement_dps): # self._device = device # self._available = False # self._name = friendly_name @@ -246,13 +238,12 @@ def __init__( # self._last_position_set = None # self._last_command = None # self._current_cover_position = 50 - # self._cover_command_key = cover_command_key # self._open_cmd = open_cmd # self._close_cmd = close_cmd # self._stop_cmd = stop_cmd - # self._get_position_key = get_position_key - # self._set_position_key = set_position_key - # self._last_movement_key = last_movement_key + # self._get_position_dps = get_position_dps + # self._set_position_dps = set_position_dps + # self._last_movement_dps = last_movement_dps # print( # "Initialized tuya cover [{}] with cover status [{}] and state [{}]".format( @@ -282,12 +273,6 @@ def unique_id(self): def name(self): """Get name of Tuya cover.""" return self._name - - @property - def cover_command_key(self): - """Get name of Tuya cover.""" - #_LOGGER.info("def cover_command_key called=%s", self._cover_command_key) - return self._cover_command_key @property def open_cmd(self): @@ -308,10 +293,10 @@ def stop_cmd(self): return self._stop_cmd @property - def last_movement_key(self): + def last_movement_dps(self): """Get last movement key""" - #_LOGGER.info("def last_movement_key called=%s", self._last_movement_key) - return self._last_movement_key + #_LOGGER.info("def last_movement_dps called=%s", self._last_movement_dps) + return self._last_movement_dps @property @@ -376,24 +361,24 @@ def update(self): def _update_last_movement(self): #_LOGGER.info("_update_last_movement called, currently = %s and self._cached_status=%s ", self._last_movement, self._cached_status) - self._last_movement = self._cached_status['dps'][str(self._last_movement_key)] + self._last_movement = self._cached_status['dps'][str(self._last_movement_dps)] #_LOGGER.info("_update_last_movement set, now = %s", self._last_movement) def _update_last_command(self): #_LOGGER.info("_update_last_command called, currently = %s and self._cached_status=%s", self._last_command, self._cached_status) - self._last_command = self._cached_status['dps'][str(self._cover_command_key)] + self._last_command = self._cached_status['dps'][str(self._dps_id)] #_LOGGER.info("_update_last_command set, now = %s", self._last_command) def _update_last_position_set(self): #_LOGGER.info("_update_last_position_set called, currently = %s and self._cached_status=%s", self._last_position_set, self._cached_status) - self._last_position_set = self._cached_status['dps'][str(self._set_position_key)] + self._last_position_set = self._cached_status['dps'][str(self._set_position_dps)] #_LOGGER.info("_update_last_position_set set, now = %s", self._last_position_set) def _update_current_cover_position(self): #_LOGGER.info("_update_current_cover_position called, currently = %s and self._cached_status=%s", self._current_cover_position, self._cached_status) - self._current_cover_position = self._cached_status['dps'][str(self._get_position_key)] + self._current_cover_position = self._cached_status['dps'][str(self._get_position_dps)] #_LOGGER.info("_update_current_cover_position set, now = %s", self._current_cover_position) @property @@ -446,7 +431,7 @@ def set_cover_position(self, **kwargs): converted_position = int(kwargs[ATTR_POSITION]) if converted_position in range(0,101): _LOGGER.info("set_cover_position about to set position to =%s", converted_position) - self._device.set_dps(converted_position, self._config[SET_POSITION_KEY]) + self._device.set_dps(converted_position, self._config[SET_POSITION_DPS]) else: _LOGGER.warning("set_position given number outside range") @@ -473,29 +458,29 @@ def status_updated(self): # def open_cover(self, **kwargs): # """Open the cover.""" - # #_LOGGER.info("open_cover called where self._cover_command_key=%s and self._open_cmd=%s", self._cover_command_key, self._open_cmd) - # self._device.set_dps(str(self._cover_command_key), str(self._open_cmd)) + # #_LOGGER.info("open_cover called where self._dps_id=%s and self._open_cmd=%s", self._dps_id, self._open_cmd) + # self._device.set_dps(str(self._dps_id), str(self._open_cmd)) # def close_cover(self, **kwargs): # """Close the cover.""" - # #_LOGGER.info("close_cover called where self._cover_command_key=%s and self._close_cmd=%s", self._cover_command_key, self._close_cmd) - # self._device.set_dps(str(self._cover_command_key), str(self._close_cmd)) + # #_LOGGER.info("close_cover called where self._dps_id=%s and self._close_cmd=%s", self._dps_id, self._close_cmd) + # self._device.set_dps(str(self._dps_id), str(self._close_cmd)) # def stop_cover(self, **kwargs): # """Stop the cover.""" - # #_LOGGER.info("stop_cover called where self._cover_command_key=%s and self._stop_cmd=%s", self._cover_command_key, self._stop_cmd) - # self._device.set_dps(str(self._cover_command_key), str(self.stop_cmd)) - #_LOGGER.info("open_cover called where self._cover_command_key=%s and self._open_cmd=%s", self._cover_command_key, self._open_cmd) - # self._device.set_dps(str(self._open_cmd), str(self._cover_command_key)) + # #_LOGGER.info("stop_cover called where self._dps_id=%s and self._stop_cmd=%s", self._dps_id, self._stop_cmd) + # self._device.set_dps(str(self._dps_id), str(self.stop_cmd)) + #_LOGGER.info("open_cover called where self._dps_id=%s and self._open_cmd=%s", self._dps_id, self._open_cmd) + # self._device.set_dps(str(self._open_cmd), str(self._dps_id)) # def close_cover(self, **kwargs): # """Close the cover.""" - # #_LOGGER.info("close_cover called where self._cover_command_key=%s and self._close_cmd=%s", self._cover_command_key, self._close_cmd) - # self._device.set_dps(str(self._close_cmd), str(self._cover_command_key)) + # #_LOGGER.info("close_cover called where self._dps_id=%s and self._close_cmd=%s", self._dps_id, self._close_cmd) + # self._device.set_dps(str(self._close_cmd), str(self._dps_id)) # def stop_cover(self, **kwargs): # """Stop the cover.""" - # #_LOGGER.info("stop_cover called where self._cover_command_key=%s and self._stop_cmd=%s", self._cover_command_key, self._stop_cmd) - # self._device.set_dps(str(self._stop_cmd), str(self._cover_command_key)) + # #_LOGGER.info("stop_cover called where self._dps_id=%s and self._stop_cmd=%s", self._dps_id, self._stop_cmd) + # self._device.set_dps(str(self._stop_cmd), str(self._dps_id)) \ No newline at end of file diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 6f0c5266b..5d48e4c21 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -41,10 +41,9 @@ "open_cmd": "Open Command", "close_cmd": "Close Command", "stop_cmd": "Stop Command", - "get_position_key": "Get Position Key", - "set_position_key": "Set Position Key", - "cover_command_key": "Open/Close/Stop Key", - "last_movement_key": "Last Movement Key" + "get_position_dps": "Get Position Key", + "set_position_dps": "Set Position Key", + "last_movement_dps": "Last Movement Key" } } } From 8f84b61693b50ab27da955bccb293625ab4ec2e6 Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Sat, 19 Sep 2020 01:45:00 -0700 Subject: [PATCH 08/15] Rebased to new master. Works via YAML or config. Small legibility tweaks still outstanding. --- custom_components/localtuya/cover.py | 118 ++++++++++-------- .../localtuya/pytuya/__init__.py | 2 + .../localtuya/translations/en.json | 6 +- 3 files changed, 71 insertions(+), 55 deletions(-) diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index cce68b68c..4f9814b66 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -8,17 +8,17 @@ host: 192.168.0.123 #REQUIRED local_key: 1234567891234567 #REQUIRED device_id: 123456789123456789abcd #REQUIRED - name: cover_guests #OPTIONAL friendly_name: Cover guests #REQUIRED protocol_version: 3.3 #REQUIRED + name: cover_guests #OPTIONAL + open_cmd: open #OPTIONAL, default is 'on' + close_cmd: close #OPTIONAL, default is 'off' + stop_cmd: stop #OPTIONAL, default is 'stop' get_position_dps: 3 #OPTIONAL, default is 0 set_position_dps: 2 #OPTIONAL, default is 0 last_movement_dps: 7 #OPTIONAL, default is 0 id: 1 #OPTIONAL icon: mdi:blinds #OPTIONAL - open_cmd: open #OPTIONAL, default is 'on' - close_cmd: close #OPTIONAL, default is 'off' - stop_cmd: stop #OPTIONAL, default is 'stop' """ import logging from time import time, sleep @@ -63,8 +63,8 @@ _LOGGER = logging.getLogger(__name__) -DEFAULT_OPEN_CMD = "on" -DEFAULT_CLOSE_CMD = "off" +DEFAULT_OPEN_CMD = "open" +DEFAULT_CLOSE_CMD = "close" DEFAULT_STOP_CMD = "stop" DEFAULT_SET_POSITION_DPS = 0 DEFAULT_GET_POSITION_DPS = 0 @@ -135,6 +135,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): def setup_platform(hass, config, add_devices, discovery_info=None): """Set up of the Tuya cover.""" + _LOGGER.info("running setup_platform") return import_from_yaml(hass, config, DOMAIN) @@ -143,6 +144,7 @@ class TuyaCache: def __init__(self, device, friendly_name): """Initialize the cache.""" + _LOGGER.info("initializing cache where device=%s friendly_name=%s", device, friendly_name) self._cached_status = "" self._cached_status_time = 0 self._device = device @@ -152,10 +154,11 @@ def __init__(self, device, friendly_name): @property def unique_id(self): """Return unique device identifier.""" + _LOGGER.info("unique_id returning self._device.id=%s", self._device.id) return self._device.id def __get_status(self): - # _LOGGER.info("running def __get_status from cover") + _LOGGER.info("running def __get_status from cover") for i in range(5): try: status = self._device.status() @@ -175,7 +178,7 @@ def __get_status(self): raise ConnectionError("Failed to update status .") def set_dps(self, value, dps_index): - #_LOGGER.info("running def set_dps from cover") + _LOGGER.info("set_dps where value=%s dps_index=%s", value, dps_index) """Change the Tuya cover status and clear the cache.""" self._cached_status = "" self._cached_status_time = 0 @@ -194,10 +197,11 @@ def set_dps(self, value, dps_index): return # raise ConnectionError("Failed to set status.") + self.update() def status(self): """Get state of Tuya cover and cache the results.""" - # _LOGGER.info("running def status(self) from cover") + _LOGGER.info("running def status(self) from cover") self._lock.acquire() try: now = time() @@ -216,13 +220,19 @@ def __init__( self, device, config_entry, - switchid, + coverid, **kwargs, ): - #_LOGGER.info("running def __init__ of LocaltuyaCover(CoverEntity) with self=%s device=%s name=%s friendly_name=%s icon=%s switchid=%s open_cmd=%s close_cmd=%s stop_cmd=%s", self, device, name, friendly_name, icon, switchid, open_cmd, close_cmd, stop_cmd) - super().__init__(device, config_entry, switchid, **kwargs) + #_LOGGER.info("running def __init__ of LocaltuyaCover(CoverEntity) with self=%s device=%s name=%s friendly_name=%s icon=%s coverid=%s open_cmd=%s close_cmd=%s stop_cmd=%s", self, device, name, friendly_name, icon, coverid, open_cmd, close_cmd, stop_cmd) + super().__init__(device, config_entry, coverid, **kwargs) self._state = None - self._position = 50 + self._position = None + self._current_cover_position = None + self._last_movement = None + self._last_position_set = None + self._last_command = None + #self._position = 50 + _LOGGER.info("Initialized device=%s config_entry=%s coverid=%s", device, config_entry, coverid) print( "Initialized tuya cover [{}] with switch status [{}] and state [{}]".format( self.name, self._status, self._state @@ -259,37 +269,27 @@ def device_info(self): ("LocalTuya", f"local_{self._device.unique_id}") }, "name": self._device._friendly_name, - "manufacturer": "Tuya generic", + "manufacturer": "Tuya Generic", "model": "SmartCover", "sw_version": "3.3", } - @property - def unique_id(self): - """Return unique device identifier.""" - return f"local_{self._device.unique_id}_{self._cover_id}" - - @property - def name(self): - """Get name of Tuya cover.""" - return self._name - @property def open_cmd(self): """Get name of open command.""" - #_LOGGER.info("def open_cmd called=%s", self._open_cmd) + _LOGGER.info("def open_cmd called=%s", self._open_cmd) return self._open_cmd @property def close_cmd(self): """Get name of close command.""" - #_LOGGER.info("def close_cmd called=%s", self._close_cmd) + _LOGGER.info("def close_cmd called=%s", self._close_cmd) return self._close_cmd @property def stop_cmd(self): """Get name of stop command.""" - #_LOGGER.info("def close_cmd called=%s", self._stop_cmd) + _LOGGER.info("def close_cmd called=%s", self._stop_cmd) return self._stop_cmd @property @@ -338,52 +338,63 @@ def is_closed(self): def update(self): """Update cover attributes and store in cache.""" - #_LOGGER.info("update(self) called") + _LOGGER.info("update(self) called") try: - #_LOGGER.info("about to call status=self._device.status() from def update(self)") + _LOGGER.info("about to call status=self._device.status() from def update(self)") status = self._device.status() - #_LOGGER.info("def update set status =%s", status) + _LOGGER.info("def update set status =%s", status) self._cached_status = status - #_LOGGER.info("self._cached_status set =%s", self._cached_status) + _LOGGER.info("self._cached_status set =%s", self._cached_status) + _LOGGER.info("about to call self._update_last_command()") self._update_last_command() + _LOGGER.info("about to call self._update_last_movement()") self._update_last_movement() + _LOGGER.info("about to call self._update_last_position_set()") self._update_last_position_set() + _LOGGER.info("about to call self._update_current_cover_position)") self._update_current_cover_position() except: - #_LOGGER.info("def update returned except, setting available to false") + _LOGGER.info("def update returned except, setting available to false") self._available = False else: - #_LOGGER.info("def update reached else, setting available to true") + _LOGGER.info("def update reached else, setting available to true") self._available = True def _update_last_movement(self): - #_LOGGER.info("_update_last_movement called, currently = %s and self._cached_status=%s ", self._last_movement, self._cached_status) - - self._last_movement = self._cached_status['dps'][str(self._last_movement_dps)] - #_LOGGER.info("_update_last_movement set, now = %s", self._last_movement) + _LOGGER.info("_update_last_movement called") + _LOGGER.info("self._config=%s", self._config) + _LOGGER.info("CONF_LAST_MOVEMENT_DPS=%s", CONF_LAST_MOVEMENT_DPS) + _LOGGER.info("self._config[CONF_LAST_MOVEMENT_DPS]=%s", self._config[CONF_LAST_MOVEMENT_DPS]) + _LOGGER.info("_update_last_movement called, currently = %s and self._cached_status=%s ", self._last_movement, self._cached_status) + self._last_movement = self._cached_status['dps'][str(self._config[CONF_LAST_MOVEMENT_DPS])] + _LOGGER.info("_update_last_movement set, now = %s", self._last_movement) def _update_last_command(self): - #_LOGGER.info("_update_last_command called, currently = %s and self._cached_status=%s", self._last_command, self._cached_status) + _LOGGER.info("_update_last_command called") + _LOGGER.info("self._config=%s", self._config) + _LOGGER.info("_update_last_command called, currently = %s and self._cached_status=%s", self._last_command, self._cached_status) self._last_command = self._cached_status['dps'][str(self._dps_id)] - #_LOGGER.info("_update_last_command set, now = %s", self._last_command) + _LOGGER.info("_update_last_command set, now = %s", self._last_command) def _update_last_position_set(self): - #_LOGGER.info("_update_last_position_set called, currently = %s and self._cached_status=%s", self._last_position_set, self._cached_status) - self._last_position_set = self._cached_status['dps'][str(self._set_position_dps)] - #_LOGGER.info("_update_last_position_set set, now = %s", self._last_position_set) + _LOGGER.info("_update_last_position called") + _LOGGER.info("_update_last_position_set called, currently = %s and self._cached_status=%s", self._last_position_set, self._cached_status) + self._last_position_set = self._cached_status['dps'][str(self._config[CONF_SET_POSITION_DPS])] + _LOGGER.info("_update_last_position_set set, now = %s", self._last_position_set) def _update_current_cover_position(self): - #_LOGGER.info("_update_current_cover_position called, currently = %s and self._cached_status=%s", self._current_cover_position, self._cached_status) - self._current_cover_position = self._cached_status['dps'][str(self._get_position_dps)] - #_LOGGER.info("_update_current_cover_position set, now = %s", self._current_cover_position) + _LOGGER.info("_update_current_cover_position called") + _LOGGER.info("_update_current_cover_position called, currently = %s and self._cached_status=%s", self._current_cover_position, self._cached_status) + self._current_cover_position = self._cached_status['dps'][str(self._config[CONF_GET_POSITION_DPS])] + _LOGGER.info("_update_current_cover_position set, now = %s", self._current_cover_position) @property def current_cover_position(self): - #_LOGGER.info("current_cover_position called") + _LOGGER.info("current_cover_position called") """Return position of Tuya cover.""" return self._current_cover_position @@ -410,7 +421,7 @@ def supported_features(self): @property def is_opening(self): - #_LOGGER.info("is_opening called") + _LOGGER.info("is_opening called") last_movement = self._last_movement; if last_movement == 'Opening': return True @@ -418,7 +429,7 @@ def is_opening(self): @property def is_closing(self): - #_LOGGER.info("is_closing called") + _LOGGER.info("is_closing called") last_movement = self._last_movement; if last_movement == 'Closing': return True @@ -426,29 +437,32 @@ def is_closing(self): def set_cover_position(self, **kwargs): """Set the cover to a specific position from 0-100""" - #_LOGGER.debug("set_cover_position called") + _LOGGER.debug("set_cover_position called") if ATTR_POSITION in kwargs: converted_position = int(kwargs[ATTR_POSITION]) if converted_position in range(0,101): _LOGGER.info("set_cover_position about to set position to =%s", converted_position) - self._device.set_dps(converted_position, self._config[SET_POSITION_DPS]) + self._device.set_dps(converted_position, self._config[CONF_SET_POSITION_DPS]) else: _LOGGER.warning("set_position given number outside range") def open_cover(self, **kwargs): """Open the cover.""" + _LOGGER.debug("open_cover called") _LOGGER.debug("Launching command %s to cover ", self._config[CONF_OPEN_CMD]) self._device.set_dps(self._config[CONF_OPEN_CMD], self._dps_id) def close_cover(self, **kwargs): """Close cover.""" + _LOGGER.debug("close_cover called") _LOGGER.debug("Launching command %s to cover ", self._config[CONF_CLOSE_CMD]) self._device.set_dps(self._config[CONF_CLOSE_CMD], self._dps_id) def stop_cover(self, **kwargs): """Stop the cover.""" - _LOGGER.debug("Laudebugching command %s to cover ", self._config[CONF_STOP_CMD]) + _LOGGER.debug("stop_cover called called") + _LOGGER.debug("Launching command %s to cover ", self._config[CONF_STOP_CMD]) self._device.set_dps(self._config[CONF_STOP_CMD], self._dps_id) def status_updated(self): @@ -470,7 +484,7 @@ def status_updated(self): # """Stop the cover.""" # #_LOGGER.info("stop_cover called where self._dps_id=%s and self._stop_cmd=%s", self._dps_id, self._stop_cmd) # self._device.set_dps(str(self._dps_id), str(self.stop_cmd)) - #_LOGGER.info("open_cover called where self._dps_id=%s and self._open_cmd=%s", self._dps_id, self._open_cmd) + #_LOGGER.info("open_cover called where self._dps_id=%s and self._open_cmd=%s", self._dps_id, self._open_cmd) # self._device.set_dps(str(self._open_cmd), str(self._dps_id)) # def close_cover(self, **kwargs): diff --git a/custom_components/localtuya/pytuya/__init__.py b/custom_components/localtuya/pytuya/__init__.py index a701597ff..54540fc0d 100644 --- a/custom_components/localtuya/pytuya/__init__.py +++ b/custom_components/localtuya/pytuya/__init__.py @@ -196,6 +196,8 @@ def __init__(self, dev_id, address, local_key, connection_timeout=10): self.dev_type = 'type_0a' self.port = 6668 # default - do not expect caller to pass in + + log.debug("Initiated TuyaDevice where self.id=%s self.address=%s self.local_key=%s self.connection_timeout=%s self.version=%s self.dev_type=%s self.port=%s", self.id, self.address, self.local_key, self.connection_timeout, self.version, self.dev_type, self.port) def __repr__(self): return '%r' % ((self.id, self.address),) # FIXME can do better than this diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 5d48e4c21..35f7814e7 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -41,9 +41,9 @@ "open_cmd": "Open Command", "close_cmd": "Close Command", "stop_cmd": "Stop Command", - "get_position_dps": "Get Position Key", - "set_position_dps": "Set Position Key", - "last_movement_dps": "Last Movement Key" + "get_position_dps": "Get Position DPS", + "set_position_dps": "Set Position DPS", + "last_movement_dps": "Last Movement DPS" } } } From 10940479f3e4b69307adb8db8ba920e3b2183169 Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Sat, 19 Sep 2020 14:46:13 -0700 Subject: [PATCH 09/15] postlund feedback updates --- custom_components/localtuya/__init__.py | 2 + custom_components/localtuya/config_flow.py | 7 +- custom_components/localtuya/const.py | 6 +- custom_components/localtuya/cover.py | 288 +++--------------- .../localtuya/translations/en.json | 6 +- 5 files changed, 48 insertions(+), 261 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index ee0da21a8..1843b0b02 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -157,8 +157,10 @@ def update(self): self._status = self._device.status() self.status_updated() except Exception: + _LOGGER.debug("update hit exception, setting self._available=false") self._available = False else: + _LOGGER.debug("update succeeded, setting self._available=true") self._available = True def status_updated(self): diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index 393fd547c..caf6afa66 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -16,7 +16,6 @@ CONF_FRIENDLY_NAME, CONF_PLATFORM, CONF_SWITCHES, - CONF_COVERS, ) from . import pytuya @@ -205,7 +204,7 @@ 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] == user_input[CONF_ID] for switch in self.entities) or any(cover[CONF_ID] == user_input[CONF_ID] for cover in self.entities) + already_configured = any(entity[CONF_ID] == user_input[CONF_ID] for entity in self.entities) if not already_configured: user_input[CONF_PLATFORM] = self.platform self.entities.append(strip_dps_values(user_input, self.dps_strings)) @@ -239,13 +238,9 @@ def _convert_entity(conf): if len(user_input.get(CONF_SWITCHES, [])) > 0: for switch_conf in user_input[CONF_SWITCHES].values(): self.entities.append(_convert_entity(switch_conf)) - elif len(user_input.get(CONF_COVERS, [])) > 0: - for cover_conf in user_input[CONF_COVERS].values(): - self.entities.append(_convert_entity(cover_conf)) else: self.entities.append(_convert_entity(user_input)) - #print('ENTITIES: [{}] '.format(self.entities)) config = { CONF_FRIENDLY_NAME: f"{user_input[CONF_FRIENDLY_NAME]}", CONF_HOST: user_input[CONF_HOST], diff --git a/custom_components/localtuya/const.py b/custom_components/localtuya/const.py index f77984d55..a1df8a458 100644 --- a/custom_components/localtuya/const.py +++ b/custom_components/localtuya/const.py @@ -18,9 +18,9 @@ CONF_OPEN_CMD = "open_cmd" CONF_CLOSE_CMD = "close_cmd" CONF_STOP_CMD = "stop_cmd" -CONF_GET_POSITION_DPS = "get_position_dps" -CONF_SET_POSITION_DPS = "set_position_dps" -CONF_LAST_MOVEMENT_DPS = "last_movement_dps" +CONF_GET_POSITION = "get_position" +CONF_SET_POSITION = "set_position" +CONF_LAST_MOVEMENT = "last_movement" DOMAIN = "localtuya" diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 4f9814b66..0f95ef4ee 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -1,5 +1,10 @@ """ -Simple platform to locally control Tuya-based cover devices. +Platform to locally control Tuya-based cover devices. + +It is recommend to setup LocalTUya using the graphical configuration flow: +Configuration-->Integrations-->+-->LocalTuya + +YAML may be used as an alternative setup method. Sample config yaml: @@ -14,9 +19,9 @@ open_cmd: open #OPTIONAL, default is 'on' close_cmd: close #OPTIONAL, default is 'off' stop_cmd: stop #OPTIONAL, default is 'stop' - get_position_dps: 3 #OPTIONAL, default is 0 - set_position_dps: 2 #OPTIONAL, default is 0 - last_movement_dps: 7 #OPTIONAL, default is 0 + get_position: 3 #OPTIONAL, default is 0 + set_position: 2 #OPTIONAL, default is 0 + last_movement: 7 #OPTIONAL, default is 0 id: 1 #OPTIONAL icon: mdi:blinds #OPTIONAL """ @@ -38,7 +43,6 @@ ) from homeassistant.const import ( CONF_ID, - CONF_COVERS, CONF_FRIENDLY_NAME, CONF_NAME, ) @@ -54,9 +58,9 @@ CONF_OPEN_CMD, CONF_CLOSE_CMD, CONF_STOP_CMD, - CONF_GET_POSITION_DPS, - CONF_SET_POSITION_DPS, - CONF_LAST_MOVEMENT_DPS + CONF_GET_POSITION, + CONF_SET_POSITION, + CONF_LAST_MOVEMENT ) from .pytuya import TuyaDevice @@ -66,9 +70,9 @@ DEFAULT_OPEN_CMD = "open" DEFAULT_CLOSE_CMD = "close" DEFAULT_STOP_CMD = "stop" -DEFAULT_SET_POSITION_DPS = 0 -DEFAULT_GET_POSITION_DPS = 0 -DEFAULT_LAST_MOVEMENT_DPS = 0 +DEFAULT_SET_POSITION = 0 +DEFAULT_GET_POSITION = 0 +DEFAULT_LAST_MOVEMENT = 0 MIN_POSITION = 0 MAX_POSITION = 100 @@ -80,9 +84,9 @@ vol.Optional(CONF_OPEN_CMD, default=DEFAULT_OPEN_CMD): cv.string, vol.Optional(CONF_CLOSE_CMD, default=DEFAULT_CLOSE_CMD): cv.string, vol.Optional(CONF_STOP_CMD, default=DEFAULT_STOP_CMD): cv.string, - vol.Optional(CONF_SET_POSITION_DPS, default=DEFAULT_SET_POSITION_DPS): cv.positive_int, # TODO only allow user to select from returned dps list - vol.Optional(CONF_GET_POSITION_DPS, default=DEFAULT_GET_POSITION_DPS): cv.positive_int, # TODO only allow user to select from returned dps list - vol.Optional(CONF_LAST_MOVEMENT_DPS, default=DEFAULT_LAST_MOVEMENT_DPS): cv.positive_int, # TODO only allow user to select from returned dps list + vol.Optional(CONF_SET_POSITION, default=DEFAULT_SET_POSITION): cv.positive_int, + vol.Optional(CONF_GET_POSITION, default=DEFAULT_GET_POSITION): cv.positive_int, + vol.Optional(CONF_LAST_MOVEMENT, default=DEFAULT_LAST_MOVEMENT): cv.positive_int, } ) @@ -93,9 +97,9 @@ def flow_schema(dps): vol.Optional(CONF_OPEN_CMD, default=DEFAULT_OPEN_CMD): str, vol.Optional(CONF_CLOSE_CMD, default=DEFAULT_CLOSE_CMD): str, vol.Optional(CONF_STOP_CMD, default=DEFAULT_STOP_CMD): str, - vol.Optional(CONF_SET_POSITION_DPS, default=DEFAULT_SET_POSITION_DPS): cv.positive_int, - vol.Optional(CONF_GET_POSITION_DPS, default=DEFAULT_GET_POSITION_DPS): cv.positive_int, - vol.Optional(CONF_LAST_MOVEMENT_DPS, default=DEFAULT_LAST_MOVEMENT_DPS): cv.positive_int, + vol.Optional(CONF_SET_POSITION, default=DEFAULT_SET_POSITION): cv.positive_int, + vol.Optional(CONF_GET_POSITION, default=DEFAULT_GET_POSITION): cv.positive_int, + vol.Optional(CONF_LAST_MOVEMENT, default=DEFAULT_LAST_MOVEMENT): cv.positive_int, } @@ -118,15 +122,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): TuyaCache(device, config_entry.data[CONF_FRIENDLY_NAME]), config_entry, device_config[CONF_ID], - # TuyaCache(device), - # device_config[CONF_FRIENDLY_NAME], - # device_config[CONF_ID], - # device_config.get(CONF_OPEN_CMD), - # device_config.get(CONF_CLOSE_CMD), - # device_config.get(CONF_STOP_CMD), - # device_config.get(CONF_SET_POSITION_DPS), - # device_config.get(CONF_GET_POSITION_DPS), - # device_config.get(CONF_LAST_MOVEMENT_DPS), ) ) @@ -135,7 +130,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): def setup_platform(hass, config, add_devices, discovery_info=None): """Set up of the Tuya cover.""" - _LOGGER.info("running setup_platform") return import_from_yaml(hass, config, DOMAIN) @@ -144,7 +138,6 @@ class TuyaCache: def __init__(self, device, friendly_name): """Initialize the cache.""" - _LOGGER.info("initializing cache where device=%s friendly_name=%s", device, friendly_name) self._cached_status = "" self._cached_status_time = 0 self._device = device @@ -154,11 +147,9 @@ def __init__(self, device, friendly_name): @property def unique_id(self): """Return unique device identifier.""" - _LOGGER.info("unique_id returning self._device.id=%s", self._device.id) return self._device.id def __get_status(self): - _LOGGER.info("running def __get_status from cover") for i in range(5): try: status = self._device.status() @@ -174,17 +165,15 @@ def __get_status(self): _LOGGER.error( "Failed to update status of device %s", self._device.address ) - # return None raise ConnectionError("Failed to update status .") def set_dps(self, value, dps_index): - _LOGGER.info("set_dps where value=%s dps_index=%s", value, dps_index) + _LOGGER.debug("set_dps where value=%s dps_index=%s", value, dps_index) """Change the Tuya cover status and clear the cache.""" self._cached_status = "" self._cached_status_time = 0 for i in range(5): try: - #_LOGGER.info("Running a try from def set_dps from cover where value=%s and dps_index=%s", state, dps_index) return self._device.set_dps(value, dps_index) except Exception: print( @@ -195,13 +184,10 @@ def set_dps(self, value, dps_index): "Failed to set status of device %s", self._device.address ) return - - # raise ConnectionError("Failed to set status.") self.update() def status(self): """Get state of Tuya cover and cache the results.""" - _LOGGER.info("running def status(self) from cover") self._lock.acquire() try: now = time() @@ -223,178 +209,40 @@ def __init__( coverid, **kwargs, ): - #_LOGGER.info("running def __init__ of LocaltuyaCover(CoverEntity) with self=%s device=%s name=%s friendly_name=%s icon=%s coverid=%s open_cmd=%s close_cmd=%s stop_cmd=%s", self, device, name, friendly_name, icon, coverid, open_cmd, close_cmd, stop_cmd) super().__init__(device, config_entry, coverid, **kwargs) - self._state = None self._position = None self._current_cover_position = None self._last_movement = None self._last_position_set = None self._last_command = None - #self._position = 50 - _LOGGER.info("Initialized device=%s config_entry=%s coverid=%s", device, config_entry, coverid) print( - "Initialized tuya cover [{}] with switch status [{}] and state [{}]".format( - self.name, self._status, self._state - - # def __init__(self, device, friendly_name, coverid, open_cmd, close_cmd, stop_cmd, set_position_dps, get_position_dps, last_movement_dps): - # self._device = device - # self._available = False - # self._name = friendly_name - # self._cover_id = coverid - # self._status = None - # self._state = None - # self._last_movement = None - # self._last_position_set = None - # self._last_command = None - # self._current_cover_position = 50 - # self._open_cmd = open_cmd - # self._close_cmd = close_cmd - # self._stop_cmd = stop_cmd - # self._get_position_dps = get_position_dps - # self._set_position_dps = set_position_dps - # self._last_movement_dps = last_movement_dps - - # print( - # "Initialized tuya cover [{}] with cover status [{}] and state [{}]".format( - # self._name, self._status, self._state + "Initialized tuya cover [{}] with switch status [{}]".format( + self.name, self._status ) ) - @property - def device_info(self): - return { - "identifiers": { - # Serial numbers are unique identifiers within a specific domain - ("LocalTuya", f"local_{self._device.unique_id}") - }, - "name": self._device._friendly_name, - "manufacturer": "Tuya Generic", - "model": "SmartCover", - "sw_version": "3.3", - } - - @property - def open_cmd(self): - """Get name of open command.""" - _LOGGER.info("def open_cmd called=%s", self._open_cmd) - return self._open_cmd - - @property - def close_cmd(self): - """Get name of close command.""" - _LOGGER.info("def close_cmd called=%s", self._close_cmd) - return self._close_cmd - - @property - def stop_cmd(self): - """Get name of stop command.""" - _LOGGER.info("def close_cmd called=%s", self._stop_cmd) - return self._stop_cmd - - @property - def last_movement_dps(self): - """Get last movement key""" - #_LOGGER.info("def last_movement_dps called=%s", self._last_movement_dps) - return self._last_movement_dps - - - @property - def last_movement(self): - #_LOGGER.info("def last_movement called=%s", self._last_movement) - """Return the last movement of the device (should be "Opening" or "Closing")""" - return self._last_movement - - def last_position_set(self): - #_LOGGER.info("def last_position_set called=%s", self._last_position_set) - """Return the last position set of the device""" - return self._last_position_set - - @property - def last_command(self): - #_LOGGER.info("def last_command called=%s", self._last_command) - """Return the last command of the device""" - return self._last_command - - @property - def is_open(self): - """Check if the cover is partially or fully open.""" - #_LOGGER.info("def is_open called, self._current_cover_position = %s", self._current_cover_position) - if self._current_cover_position != 100: - #_LOGGER.info("is_open returning true)") - return True - #_LOGGER.info("is_open returning false") - return False - @property def is_closed(self): """Check if the cover is fully closed.""" - #_LOGGER.info("def is_closed called") if self._current_cover_position == 100: - #_LOGGER.info("is_closed returning true)") return True - #_LOGGER.info("is_closed returning false") return False - def update(self): - """Update cover attributes and store in cache.""" - _LOGGER.info("update(self) called") + def status_updated(self): + """Device status was updated.""" + _LOGGER.debug("status_updated called") + self._cached_status = self._status + _LOGGER.debug("status_updated called, self._status=%s, self._cached_status=%s", self._status, self._cached_status) try: - _LOGGER.info("about to call status=self._device.status() from def update(self)") - status = self._device.status() - _LOGGER.info("def update set status =%s", status) - self._cached_status = status - _LOGGER.info("self._cached_status set =%s", self._cached_status) - - _LOGGER.info("about to call self._update_last_command()") - self._update_last_command() - _LOGGER.info("about to call self._update_last_movement()") - self._update_last_movement() - _LOGGER.info("about to call self._update_last_position_set()") - self._update_last_position_set() - _LOGGER.info("about to call self._update_current_cover_position)") - self._update_current_cover_position() - + self._last_movement = self._cached_status['dps'][str(self._config[CONF_LAST_MOVEMENT])] + self._last_position_set = self._cached_status['dps'][str(self._config[CONF_SET_POSITION])] + self._current_cover_position = self._cached_status['dps'][str(self._config[CONF_GET_POSITION])] + self._last_command = self._cached_status['dps'][str(self._dps_id)] except: - _LOGGER.info("def update returned except, setting available to false") - self._available = False - else: - _LOGGER.info("def update reached else, setting available to true") - self._available = True - - def _update_last_movement(self): - _LOGGER.info("_update_last_movement called") - _LOGGER.info("self._config=%s", self._config) - _LOGGER.info("CONF_LAST_MOVEMENT_DPS=%s", CONF_LAST_MOVEMENT_DPS) - _LOGGER.info("self._config[CONF_LAST_MOVEMENT_DPS]=%s", self._config[CONF_LAST_MOVEMENT_DPS]) - _LOGGER.info("_update_last_movement called, currently = %s and self._cached_status=%s ", self._last_movement, self._cached_status) - self._last_movement = self._cached_status['dps'][str(self._config[CONF_LAST_MOVEMENT_DPS])] - _LOGGER.info("_update_last_movement set, now = %s", self._last_movement) - - def _update_last_command(self): - _LOGGER.info("_update_last_command called") - _LOGGER.info("self._config=%s", self._config) - _LOGGER.info("_update_last_command called, currently = %s and self._cached_status=%s", self._last_command, self._cached_status) - self._last_command = self._cached_status['dps'][str(self._dps_id)] - _LOGGER.info("_update_last_command set, now = %s", self._last_command) - - - def _update_last_position_set(self): - _LOGGER.info("_update_last_position called") - _LOGGER.info("_update_last_position_set called, currently = %s and self._cached_status=%s", self._last_position_set, self._cached_status) - self._last_position_set = self._cached_status['dps'][str(self._config[CONF_SET_POSITION_DPS])] - _LOGGER.info("_update_last_position_set set, now = %s", self._last_position_set) - - - def _update_current_cover_position(self): - _LOGGER.info("_update_current_cover_position called") - _LOGGER.info("_update_current_cover_position called, currently = %s and self._cached_status=%s", self._current_cover_position, self._cached_status) - self._current_cover_position = self._cached_status['dps'][str(self._config[CONF_GET_POSITION_DPS])] - _LOGGER.info("_update_current_cover_position set, now = %s", self._current_cover_position) + _LOGGER.warning("status_updated failed to update certain variables") @property def current_cover_position(self): - _LOGGER.info("current_cover_position called") """Return position of Tuya cover.""" return self._current_cover_position @@ -416,85 +264,27 @@ def available(self): @property def supported_features(self): """Flag supported features.""" - supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP | SUPPORT_SET_POSITION - return supported_features - - @property - def is_opening(self): - _LOGGER.info("is_opening called") - last_movement = self._last_movement; - if last_movement == 'Opening': - return True - return False - - @property - def is_closing(self): - _LOGGER.info("is_closing called") - last_movement = self._last_movement; - if last_movement == 'Closing': - return True - return False + return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP | SUPPORT_SET_POSITION + #TODO set supported features dynamically based on config or yaml input def set_cover_position(self, **kwargs): """Set the cover to a specific position from 0-100""" - _LOGGER.debug("set_cover_position called") if ATTR_POSITION in kwargs: converted_position = int(kwargs[ATTR_POSITION]) if converted_position in range(0,101): - _LOGGER.info("set_cover_position about to set position to =%s", converted_position) - self._device.set_dps(converted_position, self._config[CONF_SET_POSITION_DPS]) + _LOGGER.debug("set_cover_position about to set position to =%s", converted_position) + self._device.set_dps(converted_position, self._config[CONF_SET_POSITION]) else: _LOGGER.warning("set_position given number outside range") - def open_cover(self, **kwargs): """Open the cover.""" - _LOGGER.debug("open_cover called") - _LOGGER.debug("Launching command %s to cover ", self._config[CONF_OPEN_CMD]) self._device.set_dps(self._config[CONF_OPEN_CMD], self._dps_id) def close_cover(self, **kwargs): """Close cover.""" - _LOGGER.debug("close_cover called") - _LOGGER.debug("Launching command %s to cover ", self._config[CONF_CLOSE_CMD]) self._device.set_dps(self._config[CONF_CLOSE_CMD], self._dps_id) def stop_cover(self, **kwargs): """Stop the cover.""" - _LOGGER.debug("stop_cover called called") - _LOGGER.debug("Launching command %s to cover ", self._config[CONF_STOP_CMD]) - self._device.set_dps(self._config[CONF_STOP_CMD], self._dps_id) - - def status_updated(self): - """Device status was updated. """ - self._state = self.dps(self._dps_id) - - - # def open_cover(self, **kwargs): - # """Open the cover.""" - # #_LOGGER.info("open_cover called where self._dps_id=%s and self._open_cmd=%s", self._dps_id, self._open_cmd) - # self._device.set_dps(str(self._dps_id), str(self._open_cmd)) - - # def close_cover(self, **kwargs): - # """Close the cover.""" - # #_LOGGER.info("close_cover called where self._dps_id=%s and self._close_cmd=%s", self._dps_id, self._close_cmd) - # self._device.set_dps(str(self._dps_id), str(self._close_cmd)) - - # def stop_cover(self, **kwargs): - # """Stop the cover.""" - # #_LOGGER.info("stop_cover called where self._dps_id=%s and self._stop_cmd=%s", self._dps_id, self._stop_cmd) - # self._device.set_dps(str(self._dps_id), str(self.stop_cmd)) - #_LOGGER.info("open_cover called where self._dps_id=%s and self._open_cmd=%s", self._dps_id, self._open_cmd) - # self._device.set_dps(str(self._open_cmd), str(self._dps_id)) - - # def close_cover(self, **kwargs): - # """Close the cover.""" - # #_LOGGER.info("close_cover called where self._dps_id=%s and self._close_cmd=%s", self._dps_id, self._close_cmd) - # self._device.set_dps(str(self._close_cmd), str(self._dps_id)) - - # def stop_cover(self, **kwargs): - # """Stop the cover.""" - # #_LOGGER.info("stop_cover called where self._dps_id=%s and self._stop_cmd=%s", self._dps_id, self._stop_cmd) - # self._device.set_dps(str(self._stop_cmd), str(self._dps_id)) - - \ No newline at end of file + self._device.set_dps(self._config[CONF_STOP_CMD], self._dps_id) \ No newline at end of file diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 35f7814e7..2fe6c119b 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -41,9 +41,9 @@ "open_cmd": "Open Command", "close_cmd": "Close Command", "stop_cmd": "Stop Command", - "get_position_dps": "Get Position DPS", - "set_position_dps": "Set Position DPS", - "last_movement_dps": "Last Movement DPS" + "get_position": "Get Position DPS", + "set_position": "Set Position DPS", + "last_movement": "Last Movement DPS" } } } From 86a00dd8a7a247cb437d0216fa16194cc9040d5d Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Mon, 21 Sep 2020 10:27:36 -0700 Subject: [PATCH 10/15] postlund comments --- custom_components/localtuya/__init__.py | 2 - custom_components/localtuya/config_flow.py | 8 ++-- custom_components/localtuya/cover.py | 38 +++---------------- .../localtuya/translations/en.json | 2 +- 4 files changed, 11 insertions(+), 39 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 1843b0b02..ee0da21a8 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -157,10 +157,8 @@ def update(self): self._status = self._device.status() self.status_updated() except Exception: - _LOGGER.debug("update hit exception, setting self._available=false") self._available = False else: - _LOGGER.debug("update succeeded, setting self._available=true") self._available = True def status_updated(self): diff --git a/custom_components/localtuya/config_flow.py b/custom_components/localtuya/config_flow.py index caf6afa66..cf9c4b866 100644 --- a/custom_components/localtuya/config_flow.py +++ b/custom_components/localtuya/config_flow.py @@ -204,7 +204,9 @@ 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(entity[CONF_ID] == user_input[CONF_ID] for entity in self.entities) + already_configured = any( + switch[CONF_ID] == user_input[CONF_ID] 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)) @@ -236,8 +238,8 @@ def _convert_entity(conf): self.platform = user_input[CONF_PLATFORM] if len(user_input.get(CONF_SWITCHES, [])) > 0: - for switch_conf in user_input[CONF_SWITCHES].values(): - self.entities.append(_convert_entity(switch_conf)) + for switch_conf in user_input[CONF_SWITCHES].values(): + self.entities.append(_convert_entity(switch_conf)) else: self.entities.append(_convert_entity(user_input)) diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 0f95ef4ee..177caec96 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -44,7 +44,6 @@ from homeassistant.const import ( CONF_ID, CONF_FRIENDLY_NAME, - CONF_NAME, ) import homeassistant.helpers.config_validation as cv @@ -74,11 +73,6 @@ DEFAULT_GET_POSITION = 0 DEFAULT_LAST_MOVEMENT = 0 -MIN_POSITION = 0 -MAX_POSITION = 100 -UPDATE_RETRY_LIMIT = 3 - - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(BASE_PLATFORM_SCHEMA).extend( { vol.Optional(CONF_OPEN_CMD, default=DEFAULT_OPEN_CMD): cv.string, @@ -224,43 +218,21 @@ def __init__( @property def is_closed(self): """Check if the cover is fully closed.""" - if self._current_cover_position == 100: - return True - return False + return self._current_cover_position == 100: def status_updated(self): """Device status was updated.""" - _LOGGER.debug("status_updated called") self._cached_status = self._status - _LOGGER.debug("status_updated called, self._status=%s, self._cached_status=%s", self._status, self._cached_status) - try: - self._last_movement = self._cached_status['dps'][str(self._config[CONF_LAST_MOVEMENT])] - self._last_position_set = self._cached_status['dps'][str(self._config[CONF_SET_POSITION])] - self._current_cover_position = self._cached_status['dps'][str(self._config[CONF_GET_POSITION])] - self._last_command = self._cached_status['dps'][str(self._dps_id)] - except: - _LOGGER.warning("status_updated failed to update certain variables") + self._last_movement = self._cached_status['dps'][str(self._config[CONF_LAST_MOVEMENT])] + self._last_position_set = self._cached_status['dps'][str(self._config[CONF_SET_POSITION])] + self._current_cover_position = self._cached_status['dps'][str(self._config[CONF_GET_POSITION])] + self._last_command = self._cached_status['dps'][str(self._dps_id)] @property def current_cover_position(self): """Return position of Tuya cover.""" return self._current_cover_position - @property - def min_position(self): - """Return minimum position of Tuya cover.""" - return MIN_POSITION - - @property - def max_position(self): - """Return maximum position of Tuya cover.""" - return MAX_POSITION - - @property - def available(self): - """Return if device is available or not.""" - return self._available - @property def supported_features(self): """Flag supported features.""" diff --git a/custom_components/localtuya/translations/en.json b/custom_components/localtuya/translations/en.json index 2fe6c119b..2d8f46580 100644 --- a/custom_components/localtuya/translations/en.json +++ b/custom_components/localtuya/translations/en.json @@ -43,7 +43,7 @@ "stop_cmd": "Stop Command", "get_position": "Get Position DPS", "set_position": "Set Position DPS", - "last_movement": "Last Movement DPS" + "last_movement": "Last Movement DPS", } } } From 151773ac52a0c47a678cff0f65c104421c71b74a Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Mon, 21 Sep 2020 10:31:48 -0700 Subject: [PATCH 11/15] tweak --- custom_components/localtuya/pytuya/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/custom_components/localtuya/pytuya/__init__.py b/custom_components/localtuya/pytuya/__init__.py index dcd14efb7..cad5abe12 100644 --- a/custom_components/localtuya/pytuya/__init__.py +++ b/custom_components/localtuya/pytuya/__init__.py @@ -198,8 +198,6 @@ def __init__(self, dev_id, address, local_key, connection_timeout=10): self.port = 6668 # default - do not expect caller to pass in - log.debug("Initiated TuyaDevice where self.id=%s self.address=%s self.local_key=%s self.connection_timeout=%s self.version=%s self.dev_type=%s self.port=%s", self.id, self.address, self.local_key, self.connection_timeout, self.version, self.dev_type, self.port) - def __repr__(self): return '%r' % ((self.id, self.address),) # FIXME can do better than this From a8e23b4e6d680a39e86d3ad7f29b37c6fbfa9424 Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Mon, 21 Sep 2020 14:43:43 -0700 Subject: [PATCH 12/15] postlund tweaks --- custom_components/localtuya/cover.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 0d257044c..fc07cb727 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -217,11 +217,10 @@ def is_closed(self): def status_updated(self): """Device status was updated.""" - self._cached_status = self._status - self._last_movement = self._cached_status['dps'][str(self._config[CONF_LAST_MOVEMENT])] - self._last_position_set = self._cached_status['dps'][str(self._config[CONF_SET_POSITION])] - self._current_cover_position = self._cached_status['dps'][str(self._config[CONF_GET_POSITION])] - self._last_command = self._cached_status['dps'][str(self._dps_id)] + self._last_movement = self.dps(str._config.get(CONF_LAST_MOVEMENT)) + self._last_position_set = self.dps(str._config.get(CONF_SET_POSITION)) + self._current_cover_position = self.dps(str._config.get(CONF_GET_POSITION)) + self._last_command = self.dps(str._dps_id) @property def current_cover_position(self): From 041c888f7520a7d7cf583c7109893fb68aada88a Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Mon, 21 Sep 2020 22:26:24 -0700 Subject: [PATCH 13/15] testing not ready to merge --- custom_components/localtuya/__init__.py | 2 ++ custom_components/localtuya/cover.py | 21 ++++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index 64d7e5744..f5d7f4a04 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -189,9 +189,11 @@ def update(self): self._status = self._device.status() self.status_updated() except Exception: + _LOGGER.info("update set available to False") self._available = False else: self._available = True + _LOGGER.info("update set available to True") def status_updated(self): """Device status was updated. diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index fc07cb727..56d6b8551 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -126,6 +126,7 @@ class TuyaCache: """Cache wrapper for pytuya.TuyaDevice""" def __init__(self, device, friendly_name): + _LOGGER.info("initiating TuyaCache") """Initialize the cache.""" self._cached_status = "" self._cached_status_time = 0 @@ -213,14 +214,24 @@ def __init__( @property def is_closed(self): """Check if the cover is fully closed.""" - return self._current_cover_position == 100: + return self._current_cover_position == 100 def status_updated(self): """Device status was updated.""" - self._last_movement = self.dps(str._config.get(CONF_LAST_MOVEMENT)) - self._last_position_set = self.dps(str._config.get(CONF_SET_POSITION)) - self._current_cover_position = self.dps(str._config.get(CONF_GET_POSITION)) - self._last_command = self.dps(str._dps_id) + _LOGGER.info("status_updated called ") + _LOGGER.info("self._config=%s", self._config) + _LOGGER.info("self._config_entry=%s", self._config_entry) + _LOGGER.info("CONF_LAST_MOV=%s CONF_SET_POS=%s CONF_GET_POS=%s self._dps_id=%s", CONF_LAST_MOVEMENT, CONF_SET_POSITION, CONF_GET_POSITION, self._dps_id) + self.dps("7") + _LOGGER.info("self.dps(7)=%s", self.dps("7")) + self._last_movement = self.dps(str(self._config.get(CONF_LAST_MOVEMENT))) + _LOGGER.info("last_movement set to =%s", self._last_movement) + self._last_position = self.dps(str(self._config.get(CONF_SET_POSITION))) + _LOGGER.info("last_position_set set to =%s", self._last_position_set) + self._current_cover_position = self.dps(str(self._config.get(CONF_GET_POSITION))) + _LOGGER.info("current_cover_position set to =%s", self._current_cover_position) + self._last_command = self.dps(str(self._dps_id)) + _LOGGER.info("last_command set to =%s", self._last_command) @property def current_cover_position(self): From 66b385ef65795c150dd50f9189a6a07f3aaab822 Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Mon, 21 Sep 2020 22:37:52 -0700 Subject: [PATCH 14/15] remove internal logging --- custom_components/localtuya/__init__.py | 2 -- custom_components/localtuya/cover.py | 14 -------------- 2 files changed, 16 deletions(-) diff --git a/custom_components/localtuya/__init__.py b/custom_components/localtuya/__init__.py index f5d7f4a04..64d7e5744 100644 --- a/custom_components/localtuya/__init__.py +++ b/custom_components/localtuya/__init__.py @@ -189,11 +189,9 @@ def update(self): self._status = self._device.status() self.status_updated() except Exception: - _LOGGER.info("update set available to False") self._available = False else: self._available = True - _LOGGER.info("update set available to True") def status_updated(self): """Device status was updated. diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 56d6b8551..1cb4fcde5 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -126,7 +126,6 @@ class TuyaCache: """Cache wrapper for pytuya.TuyaDevice""" def __init__(self, device, friendly_name): - _LOGGER.info("initiating TuyaCache") """Initialize the cache.""" self._cached_status = "" self._cached_status_time = 0 @@ -158,7 +157,6 @@ def __get_status(self): raise ConnectionError("Failed to update status .") def set_dps(self, value, dps_index): - _LOGGER.debug("set_dps where value=%s dps_index=%s", value, dps_index) """Change the Tuya cover status and clear the cache.""" self._cached_status = "" self._cached_status_time = 0 @@ -218,20 +216,10 @@ def is_closed(self): def status_updated(self): """Device status was updated.""" - _LOGGER.info("status_updated called ") - _LOGGER.info("self._config=%s", self._config) - _LOGGER.info("self._config_entry=%s", self._config_entry) - _LOGGER.info("CONF_LAST_MOV=%s CONF_SET_POS=%s CONF_GET_POS=%s self._dps_id=%s", CONF_LAST_MOVEMENT, CONF_SET_POSITION, CONF_GET_POSITION, self._dps_id) - self.dps("7") - _LOGGER.info("self.dps(7)=%s", self.dps("7")) self._last_movement = self.dps(str(self._config.get(CONF_LAST_MOVEMENT))) - _LOGGER.info("last_movement set to =%s", self._last_movement) self._last_position = self.dps(str(self._config.get(CONF_SET_POSITION))) - _LOGGER.info("last_position_set set to =%s", self._last_position_set) self._current_cover_position = self.dps(str(self._config.get(CONF_GET_POSITION))) - _LOGGER.info("current_cover_position set to =%s", self._current_cover_position) self._last_command = self.dps(str(self._dps_id)) - _LOGGER.info("last_command set to =%s", self._last_command) @property def current_cover_position(self): @@ -249,10 +237,8 @@ def set_cover_position(self, **kwargs): if ATTR_POSITION in kwargs: converted_position = int(kwargs[ATTR_POSITION]) if converted_position in range(0,101): - _LOGGER.debug("set_cover_position about to set position to =%s", converted_position) self._device.set_dps(converted_position, self._config[CONF_SET_POSITION]) else: - _LOGGER.warning("set_position given number outside range") def open_cover(self, **kwargs): """Open the cover.""" From 13d2ba9372e0f65e4c16056631f6b84e783a79d5 Mon Sep 17 00:00:00 2001 From: Andrew Meepos Date: Mon, 21 Sep 2020 22:43:08 -0700 Subject: [PATCH 15/15] rebase to master --- custom_components/localtuya/cover.py | 67 ------------------- .../localtuya/pytuya/__init__.py | 2 +- 2 files changed, 1 insertion(+), 68 deletions(-) diff --git a/custom_components/localtuya/cover.py b/custom_components/localtuya/cover.py index 73163700d..14dec7847 100644 --- a/custom_components/localtuya/cover.py +++ b/custom_components/localtuya/cover.py @@ -121,73 +121,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up of the Tuya cover.""" return import_from_yaml(hass, config, DOMAIN) - -class TuyaCache: - """Cache wrapper for pytuya.TuyaDevice""" - - def __init__(self, device, friendly_name): - """Initialize the cache.""" - self._cached_status = "" - self._cached_status_time = 0 - self._device = device - self._friendly_name = friendly_name - self._lock = Lock() - - @property - def unique_id(self): - """Return unique device identifier.""" - return self._device.id - - def __get_status(self): - for i in range(5): - try: - status = self._device.status() - return status - except Exception: - print( - "Failed to update status of device [{}]".format( - self._device.address - ) - ) - sleep(1.0) - if i + 1 == 3: - _LOGGER.error( - "Failed to update status of device %s", self._device.address - ) - raise ConnectionError("Failed to update status .") - - def set_dps(self, value, dps_index): - """Change the Tuya cover status and clear the cache.""" - self._cached_status = "" - self._cached_status_time = 0 - for i in range(5): - try: - return self._device.set_dps(value, dps_index) - except Exception: - print( - "Failed to set status of device [{}]".format(self._device.address) - ) - if i + 1 == 3: - _LOGGER.error( - "Failed to set status of device %s", self._device.address - ) - return - self.update() - - def status(self): - """Get state of Tuya cover and cache the results.""" - self._lock.acquire() - try: - now = time() - if not self._cached_status or now - self._cached_status_time > 15: - sleep(0.5) - self._cached_status = self.__get_status() - self._cached_status_time = time() - return self._cached_status - finally: - self._lock.release() - - class LocaltuyaCover(LocalTuyaEntity, CoverEntity): """Tuya cover devices.""" diff --git a/custom_components/localtuya/pytuya/__init__.py b/custom_components/localtuya/pytuya/__init__.py index 463fd8a2a..074633708 100644 --- a/custom_components/localtuya/pytuya/__init__.py +++ b/custom_components/localtuya/pytuya/__init__.py @@ -197,7 +197,7 @@ def __init__(self, dev_id, address, local_key, protocol_version, connection_time self.dps_to_request = {} self.port = 6668 # default - do not expect caller to pass in - + def __repr__(self): return '%r' % ((self.id, self.address),) # FIXME can do better than this