Sfoglia il codice sorgente

Avoid switching protocols once communication is working (#37)

* Be less eager about changing protocol.

Once the device is communicating, only switch protocols after the connection retry limit is reached.
This prevents compounding the problem, in particular when v3.3 protocol devices send back responses that pytuya does not handle, as switching to v3.1 seems to cause these devices to go offline for a few minutes sometimes.

Co-authored-by: Jason Rumney <jasonrumney@clarion.com.my>
Jason Rumney 5 anni fa
parent
commit
d539e5887d
2 ha cambiato i file con 23 aggiunte e 1 eliminazioni
  1. 4 1
      custom_components/goldair_climate/device.py
  2. 19 0
      tests/test_device.py

+ 4 - 1
custom_components/goldair_climate/device.py

@@ -37,6 +37,7 @@ class GoldairTuyaDevice(object):
 
         self._name = name
         self._api_protocol_version_index = None
+        self._api_protocol_working = False
         self._api = pytuya.Device(dev_id, address, local_key, "device")
         self._refresh_task = None
         self._rotate_api_protocol_version()
@@ -198,13 +199,15 @@ class GoldairTuyaDevice(object):
         for i in range(self._CONNECTION_ATTEMPTS):
             try:
                 func()
+                self._api_protocol_working = True
                 break
             except Exception as e:
                 _LOGGER.debug(f"Retrying after exception {e}")
                 if i + 1 == self._CONNECTION_ATTEMPTS:
                     self._reset_cached_state()
+                    self._api_protocol_working = False
                     _LOGGER.error(error_message)
-                else:
+                elif self._api_protocol_working is False:
                     self._rotate_api_protocol_version()
 
     def _get_cached_state(self):

+ 19 - 0
tests/test_device.py

@@ -202,6 +202,25 @@ class TestDevice(IsolatedAsyncioTestCase):
             [call(3.1), call(3.3), call(3.1)]
         )
 
+    def test_api_protocol_version_is_stable_once_successful(self):
+        self.subject._api.set_version.assert_called_once_with(3.3)
+        self.subject._api.set_version.reset_mock()
+
+        self.subject._api.status.side_effect = [
+            {"dps": {"1": False}},
+            Exception("Error"),
+            Exception("Error"),
+            Exception("Error"),
+            Exception("Error"),
+            Exception("Error"),
+            Exception("Error"),
+        ]
+        self.subject.refresh()
+        self.subject.refresh()
+        self.subject.refresh()
+
+        self.subject._api.set_version.assert_has_calls([call(3.1), call(3.3)])
+
     def test_reset_cached_state_clears_cached_state_and_pending_updates(self):
         self.subject._cached_state = {"1": True, "updated_at": time()}
         self.subject._pending_updates = {"1": False}