Bladeren bron

Get HA to call async_refresh once for each device during startup

Since the persistent connection changes, devices are appearing
unavailable until startup completes, since we have to wait for startup
to complete before opening any persistent connection in order to avoid
being detected as delaying startup ourselves.

If we can make one poll to get initial data, then the devices can
activate, even if something is delaying startup.  The risk though is
that unresponsive devices can then be responsible for delaying startup
as the connection will be active until they timeout.  But since we
only do this once per device, it should be less of a problem than it
was when we were polling.
Jason Rumney 3 jaren geleden
bovenliggende
commit
7c971be759

+ 9 - 1
custom_components/tuya_local/device.py

@@ -141,10 +141,18 @@ class TuyaLocalDevice(object):
         _LOGGER.debug(f"Monitor loop for {self.name} stopped")
         _LOGGER.debug(f"Monitor loop for {self.name} stopped")
         self._refresh_task = None
         self._refresh_task = None
 
 
-    def register_entity(self, entity):
+    async def async_register_entity(self, entity):
+        # If this is the first child entity to register, refresh the device
+        # state
+        should_poll = len(self._children) == 0
+
         self._children.append(entity)
         self._children.append(entity)
         if not self._running and not self._startup_listener:
         if not self._running and not self._startup_listener:
             self.start()
             self.start()
+        if self.has_returned_state:
+            await entity.async_schedule_update_ha_state()
+        elif should_poll:
+            await entity.async_schedule_update_ha_state(True)
 
 
     async def async_unregister_entity(self, entity):
     async def async_unregister_entity(self, entity):
         self._children.remove(entity)
         self._children.remove(entity)

+ 1 - 1
custom_components/tuya_local/helpers/mixin.py

@@ -85,7 +85,7 @@ class TuyaLocalEntity:
         await self._device.async_refresh()
         await self._device.async_refresh()
 
 
     async def async_added_to_hass(self):
     async def async_added_to_hass(self):
-        self._device.register_entity(self)
+        await self._device.async_register_entity(self)
 
 
     async def async_will_remove_from_hass(self):
     async def async_will_remove_from_hass(self):
         await self._device.async_unregister_entity(self)
         await self._device.async_unregister_entity(self)

+ 1 - 1
tests/test_config_flow.py

@@ -29,7 +29,7 @@ def auto_enable_custom_integrations(enable_custom_integrations):
 @pytest.fixture(autouse=True)
 @pytest.fixture(autouse=True)
 def prevent_task_creation():
 def prevent_task_creation():
     with patch(
     with patch(
-        "custom_components.tuya_local.device.TuyaLocalDevice.register_entity",
+        "custom_components.tuya_local.device.TuyaLocalDevice.async_register_entity",
     ):
     ):
         yield
         yield
 
 

+ 16 - 11
tests/test_device.py

@@ -463,50 +463,55 @@ class TestDevice(IsolatedAsyncioTestCase):
         # Was the refresh task left empty?
         # Was the refresh task left empty?
         self.assertIsNone(self.subject._refresh_task)
         self.assertIsNone(self.subject._refresh_task)
 
 
-    def test_register_first_entity_ha_running(self):
+    async def test_register_first_entity_ha_running(self):
         # Set up preconditions
         # Set up preconditions
         self.subject._children = []
         self.subject._children = []
         self.subject._running = False
         self.subject._running = False
         self.subject._startup_listener = None
         self.subject._startup_listener = None
         self.subject.start = Mock()
         self.subject.start = Mock()
+        entity = AsyncMock()
 
 
         # Call the function under test
         # Call the function under test
-        self.subject.register_entity("Entity")
+        await self.subject.async_register_entity(entity)
 
 
         # Was the entity added to the list?
         # Was the entity added to the list?
-        self.assertEqual(self.subject._children, ["Entity"])
+        self.assertEqual(self.subject._children, [entity])
 
 
         # Did we start the loop?
         # Did we start the loop?
         self.subject.start.assert_called_once()
         self.subject.start.assert_called_once()
 
 
-    def test_register_subsequent_entity_ha_running(self):
+    async def test_register_subsequent_entity_ha_running(self):
         # Set up preconditions
         # Set up preconditions
-        self.subject._children = ["First"]
+        first = AsyncMock()
+        second = AsyncMock()
+        self.subject._children = [first]
         self.subject._running = True
         self.subject._running = True
         self.subject._startup_listener = None
         self.subject._startup_listener = None
         self.subject.start = Mock()
         self.subject.start = Mock()
 
 
         # Call the function under test
         # Call the function under test
-        self.subject.register_entity("Entity")
+        await self.subject.async_register_entity(second)
 
 
         # Was the entity added to the list?
         # Was the entity added to the list?
-        self.assertCountEqual(self.subject._children, ["First", "Entity"])
+        self.assertCountEqual(self.subject._children, [first, second])
 
 
         # Did we avoid restarting the loop?
         # Did we avoid restarting the loop?
         self.subject.start.assert_not_called()
         self.subject.start.assert_not_called()
 
 
-    def test_register_subsequent_entity_ha_starting(self):
+    async def test_register_subsequent_entity_ha_starting(self):
         # Set up preconditions
         # Set up preconditions
-        self.subject._children = ["First"]
+        first = AsyncMock()
+        second = AsyncMock()
+        self.subject._children = [first]
         self.subject._running = False
         self.subject._running = False
         self.subject._startup_listener = Mock()
         self.subject._startup_listener = Mock()
         self.subject.start = Mock()
         self.subject.start = Mock()
 
 
         # Call the function under test
         # Call the function under test
-        self.subject.register_entity("Entity")
+        await self.subject.async_register_entity(second)
 
 
         # Was the entity added to the list?
         # Was the entity added to the list?
-        self.assertCountEqual(self.subject._children, ["First", "Entity"])
+        self.assertCountEqual(self.subject._children, [first, second])
         # Did we avoid restarting the loop?
         # Did we avoid restarting the loop?
         self.subject.start.assert_not_called()
         self.subject.start.assert_not_called()