Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Do not do update() in discover_single #542

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 21, 2023

Conversation

sdb9696
Copy link
Collaborator

@sdb9696 sdb9696 commented Nov 7, 2023

This change removes the device.update() call in discover_single so that the logic is consistent with discover() which only uses UDP and relies solely on device.update_from_discover_info(). Reasons are:

  1. CLI does device.update() anyway so it's a duplicate call there.
  2. Consistent with approach in Add klap protocol PR which removed unnecessary device.update() calls.
  3. Separation of discovery UDP protocol from device device.update() should make downstream issues clearer.

EDIT:
This change inadvertently reduced test coverage because by a fluke test_discover_single() was hitting the following path in smartdevice.py L348 when the fixture specified it didn't support emeter:

            if not module.is_supported:
                _LOGGER.debug("Module %s not supported, skipping" % module)
                continue

So I have added a test to restore the coverage and made sure that any fixtures that have "module not support" for a module will correctly pass that back from the test framework protocol.

Copy link

codecov bot commented Nov 7, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (30f217b) 81.63% compared to head (19bca91) 81.74%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #542      +/-   ##
==========================================
+ Coverage   81.63%   81.74%   +0.11%     
==========================================
  Files          28       28              
  Lines        2417     2421       +4     
  Branches      683      685       +2     
==========================================
+ Hits         1973     1979       +6     
+ Misses        383      382       -1     
+ Partials       61       60       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rytilahti
Copy link
Member

@bdraco friendly ping to keep you in the loop.

@bdraco
Copy link
Member

bdraco commented Nov 8, 2023

I'm not sure we can take the device update out without breaking the power strips, unless we pass the device type in. As soon as I get home, I'll review this and tested, as you may already be doing that and I just can't see it on my mobile.

@bdraco
Copy link
Member

bdraco commented Nov 8, 2023

All the child plugs show as unavailable without the update

@bdraco
Copy link
Member

bdraco commented Nov 8, 2023

The config entry sets up ok, but everything is unavailable
Screenshot 2023-11-08 at 1 06 40 PM
Screenshot 2023-11-08 at 1 06 38 PM

@bdraco
Copy link
Member

bdraco commented Nov 8, 2023

diff --git a/kasa/discover.py b/kasa/discover.py
index 9883ad2..2ce408d 100755
--- a/kasa/discover.py
+++ b/kasa/discover.py
@@ -326,6 +326,11 @@ class Discover:
         if ip in protocol.discovered_devices:
             dev = protocol.discovered_devices[ip]
             dev.host = host
+            # If the device has children we have
+            # to update the device since we will
+            # otherwise have missing children
+            if dev.sys_info.get("child_num"):
+                await dev.update()
             return dev
         elif ip in protocol.unsupported_devices:
             raise UnsupportedDeviceException(

This fixes the powerstrips. It would be better to abstract this as a property in the smart device object

@bdraco
Copy link
Member

bdraco commented Nov 8, 2023

The power strips are really poorly responsive since we switch to the 5s interval because it updates all the children every time one is switched

@bdraco
Copy link
Member

bdraco commented Nov 8, 2023

Also it looks like setting up devices across subnets no longer works with since the switch to UDP

Screenshot 2023-11-08 at 1 37 45 PM

@bdraco
Copy link
Member

bdraco commented Nov 8, 2023

Actually that one is on the same subnet 😢

@bdraco
Copy link
Member

bdraco commented Nov 8, 2023

The device sets up fine if I change the timeout to 10

--- a/kasa/discover.py
+++ b/kasa/discover.py
@@ -258,7 +258,7 @@ class Discover:
         host: str,
         *,
         port: Optional[int] = None,
-        timeout=5,
+        timeout=10,
         credentials: Optional[Credentials] = None,
     ) -> SmartDevice:
         """Discover a single device by the given IP address.

@bdraco
Copy link
Member

bdraco commented Nov 8, 2023

I fixed the power strips being overloaded with too many requests in home-assistant/core#103668

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 9, 2023

diff --git a/kasa/discover.py b/kasa/discover.py
index 9883ad2..2ce408d 100755
--- a/kasa/discover.py
+++ b/kasa/discover.py
@@ -326,6 +326,11 @@ class Discover:
         if ip in protocol.discovered_devices:
             dev = protocol.discovered_devices[ip]
             dev.host = host
+            # If the device has children we have
+            # to update the device since we will
+            # otherwise have missing children
+            if dev.sys_info.get("child_num"):
+                await dev.update()
             return dev
         elif ip in protocol.unsupported_devices:
             raise UnsupportedDeviceException(

This fixes the powerstrips. It would be better to abstract this as a property in the smart device object

Rather than doing it that way I've included a check to see if the type is SmartStrip. This is to future proof for klap/tuya devices that will update_from_discover_info() with a limited set of properties. It also seems simpler than adding a new property to the SmartDevice. kwarg: update_parent_devices: bool = True,

  dev.host = host
            # Call device update on devices that have children
            if update_parent_devices and isinstance(dev, SmartStrip):
                await dev.update()
            return dev

kwarg: update_parent_devices defaults to True

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 9, 2023

Actually that one is on the same subnet 😢

I don't anticipate subnet related issues with UDP discover_single() because it sets the target ip to the host IP (no 255s). This should be able to go across subnets because it's not a broadcast message.

@bdraco
Copy link
Member

bdraco commented Nov 19, 2023

Rather than doing it that way I've included a check to see if the type is SmartStrip. This is to future proof for klap/tuya devices that will update_from_discover_info() with a limited set of properties.

I think this will still break with any devices that have children that are not smart strips since its the children that need to be updated. I don't think there are any other devices that I have that have children though

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 19, 2023

I couldn’t see children properties on any other device types. Do you think there are others? Maybe the smartlightstrip?

@bdraco
Copy link
Member

bdraco commented Nov 19, 2023

I think only tplink can answer that, but they all have child_num set so I think its safer to check that as it better shows the intent.

suggested change bdraco@638a9b5

@bdraco
Copy link
Member

bdraco commented Nov 20, 2023

@sdb9696 Can you fix the conflicts here?

@sdb9696 sdb9696 force-pushed the no_update_after_disco branch from 71ada4f to bdf088b Compare November 20, 2023 17:02
@sdb9696 sdb9696 force-pushed the no_update_after_disco branch from bdf088b to f7627fe Compare November 20, 2023 17:07
@bdraco
Copy link
Member

bdraco commented Nov 20, 2023

I will revert HA to use this and than test after dinner

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 20, 2023

@sdb9696 Can you fix the conflicts here?

Ok that's done and includes your suggested changes for adding the haschildren property. It will also call device.update() for the new klap protocol because that update is required to access the child_num setting.

@bdraco
Copy link
Member

bdraco commented Nov 20, 2023

It will also call device.update() for the new klap protocol because that update is required to access the child_num setting.

Are there any klap devices that have children that we know of at this point?

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 20, 2023

Are there any klap devices that have children that we know of at this point?

Not that we know of.

@bdraco
Copy link
Member

bdraco commented Nov 20, 2023

I think its ok to avoid the update for the klap devices unless its otherwise needed. We can always add support for klap devices with children in a future update

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 20, 2023

But you can't access the child_num setting until an update has occurred. It will give an error on all klap devices regardless of whether they have children or not.

@bdraco
Copy link
Member

bdraco commented Nov 20, 2023

It looks like we call update_from_discover_info for klap devices as well. Does self._last_update = info not get set for these devices ?

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 20, 2023

It looks like we call update_from_discover_info for klap devices as well. Does self._last_update = info not get set for these devices ?

It's a limited set of properties, not the full sys_info list. If you try to access properties not in the limited list it will error.

@bdraco
Copy link
Member

bdraco commented Nov 20, 2023

It looks like we call update_from_discover_info for klap devices as well. Does self._last_update = info not get set for these devices ?

It's a limited set of properties, not the full sys_info list. If you try to access properties not in the limited list it will error.

Thats annoying. Since we only support children on the power strips, maybe go back to what you had before but move it behind the has_children property and take off @requires_update?

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 20, 2023

It looks like we call update_from_discover_info for klap devices as well. Does self._last_update = info not get set for these devices ?

It's a limited set of properties, not the full sys_info list. If you try to access properties not in the limited list it will error.

Thats annoying. Since we only support children on the power strips, maybe go back to what you had before but move it behind the has_children property and take off @requires_update?

So the has_children property in smartdevice checks if it’s a lightstrip instance instead of looking at child_num?

@bdraco
Copy link
Member

bdraco commented Nov 20, 2023

So the has_children property in smartdevice checks if it’s a lightstrip instance instead of looking at child_num?

Its the only the power strips (DeviceType.Strip) that have children (yes the names are confusing) not the light strips (DeviceType.LightStrip)

Something like this

diff --git a/kasa/smartdevice.py b/kasa/smartdevice.py
index 6461f33..d019912 100755
--- a/kasa/smartdevice.py
+++ b/kasa/smartdevice.py
@@ -411,11 +411,14 @@ class SmartDevice:
         sys_info = self._sys_info
         return str(sys_info["model"])
 
-    @property  # type: ignore
-    @requires_update
+    @property
     def has_children(self) -> bool:
         """Check if the device has children devices."""
-        return bool(self._sys_info.get("child_num"))
+        # Ideally we would check for the 'child_num' key in sys_info,
+        # but devices that speak klap do not populate this key via
+        # update_from_discover_info so we check for the devices
+        # we know have children instead.
+        return self.is_strip
 
     @property  # type: ignore
     @requires_update

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 20, 2023

So the has_children property in smartdevice checks if it’s a lightstrip instance instead of looking at child_num?

Its the only the power strips (DeviceType.Strip) that have children (yes the names are confusing) not the light strips (DeviceType.LightStrip)

Something like this

diff --git a/kasa/smartdevice.py b/kasa/smartdevice.py
index 6461f33..d019912 100755
--- a/kasa/smartdevice.py
+++ b/kasa/smartdevice.py
@@ -411,11 +411,14 @@ class SmartDevice:
         sys_info = self._sys_info
         return str(sys_info["model"])
 
-    @property  # type: ignore
-    @requires_update
+    @property
     def has_children(self) -> bool:
         """Check if the device has children devices."""
-        return bool(self._sys_info.get("child_num"))
+        # Ideally we would check for the 'child_num' key in sys_info,
+        # but devices that speak klap do not populate this key via
+        # update_from_discover_info so we check for the devices
+        # we know have children instead.
+        return self.is_strip
 
     @property  # type: ignore
     @requires_update

Yes sorry, meant strip. Makes sense, will update tomorrow am.

@sdb9696
Copy link
Collaborator Author

sdb9696 commented Nov 21, 2023

Ok that's done

Copy link
Member

@bdraco bdraco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thanks @sdb9696

Will push to my production and test shortly

@bdraco
Copy link
Member

bdraco commented Nov 21, 2023

Took a few tries, but the power strip did eventually set up so this is good to go.

@bdraco bdraco merged commit 27c4799 into python-kasa:master Nov 21, 2023
@sdb9696 sdb9696 deleted the no_update_after_disco branch November 21, 2023 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants