Refactor discovery and connection routing#1725
Draft
ZeliardM wants to merge 1 commit into
Draft
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1725 +/- ##
==========================================
- Coverage 93.29% 92.24% -1.05%
==========================================
Files 157 157
Lines 9932 10657 +725
Branches 1022 1182 +160
==========================================
+ Hits 9266 9831 +565
- Misses 471 569 +98
- Partials 195 257 +62 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR consolidates discovery, device creation, and connection routing into a single flow.
Discovery responses are decoded and normalized by their respective endpoints, then passed through shared logic that:
Previously, parts of this logic were duplicated across discovery, direct connections, the CLI, and devtools. Centralizing it should make it easier to add new discovery endpoints, device families, and connection types without updating several separate selection paths.
This PR also supersedes:
Discovery
Discovery supports three endpoints:
The two TDP ports are handled independently because they may serve different device populations.
UDP responses are held until discovery finishes. TDP responses are processed immediately and take precedence over UDP responses from the same IP. Once a host responds through TDP, any stored or later UDP response for that host is ignored.
TDP remains authoritative even when processing its response results in an authentication error, unsupported-device result, or invalid-response error. This prevents discovery from falling back to a less useful UDP response for the same device.
Each endpoint handles its own decoding and then produces a common discovery candidate. The shared processing path also:
Raw-response callbacks now include the discovery source and port, allowing callers to distinguish UDP responses from either TDP endpoint.
Device and connection routing
Device-family, protocol, transport, and concrete-class selection now live in
device_factory.Advertised device families are kept separate from connection routes. After discovery or direct connection input has been normalized, the same route definitions are used by:
Device.connect();Discover.try_connect_all().Existing special cases for cameras, doorbells, hubs, robot vacuums, HTTPS, fixed-encryption devices, and the IOT, SMART, and SMARTCAM protocol families are preserved.
Direct-connection attempts are also generated from these route definitions instead of using a separate matrix of possible family and encryption combinations.
IOT family names such as
IOT.SMARTPLUGSWITCHandIOT.SMARTBULBdescribe broad advertised families rather than concrete Python classes. The factory determines the actual class fromget_sysinfo, regardless of whether that information came from UDP discovery or was queried using a protocol selected from a TDP response.This keeps class selection correct for plugs, power strips, wall switches, dimmers, bulbs, and light strips across both discovery methods.
Unknown IOT device types now raise
UnsupportedDeviceErrorinstead of exposing a lookup error or falling back to a plug.Connection parameters and versioned KLAP
DeviceConnectionParametersnow contains the full connection identity used for discovery, direct connections, serialization, and route selection.The TDP
new_klapvalue is exposed as the optional integer fieldklap_version.For IOT KLAP devices:
klap_versionuses the originalKlapTransport;klap_versionusesKlapTransportV2; andlogin_versionremains separate and does not select the IOT KLAP handshake.SMART KLAP devices continue to use their existing KLAP V2 route.
The CLI and fixture devtool expose this setting as
-kv/--klap-version, alongside device family, encryption type, login version, and HTTPS options.Discovery outcomes and errors
Discovery distinguishes between:
DiscoveryAuthenticationErroradds the host and discovery response to authentication failures raised during discovery.UnsupportedAuthenticationErrorinherits from bothAuthenticationErrorandUnsupportedDeviceError. A device is only classified this way after an authentication attempt actually fails. Discovery metadata by itself is not enough to classify missing or incorrect credentials as unsupported onboarding.The discovery API has separate callbacks for unsupported devices and authentication failures. These callbacks use the same task and exception handling as successful-device callbacks.
Protocols owned by discovery are also closed when initialization or callback processing fails, or when discovery is interrupted.
The new exceptions are exported from the package and included in the API reference.
CLI
The discovery CLI now consumes the same normalized discovery and connection data as the library.
The default discovery command updates supported devices before displaying them and reports a unique total across successful, unsupported, and authentication-blocked devices.
discover listuses one row format for all outcomes. Each row may include:The format is shared across successful updates, timeouts, authentication failures, unsupported devices, unsupported authentication, and other update errors.
Targeted hostname discovery also uses the resolved response identity, avoiding duplicate rows for the hostname and its IP address.
discover rawincludes the source port and applies the appropriate UDP or TDP redactor.discover configfirst attempts targeted discovery and reports the route from the authoritative response. It only falls back to direct probing when discovery does not return usable connection information. Direct-probing output includes the complete route being attempted.The remaining connection options have been made more predictable:
--type;--device-familyand--encrypt-type;--hostand cannot be passed todiscover; andCredential hashes are preserved through discovery, direct probing, configuration output, and devtools.
Devtools
dump_devinfonow uses the same response-selection and direct-connection logic as the library and CLI.It supports the same device-family, encryption, login-version, KLAP-version, HTTPS, credential-hash, timeout, and port options.
It also reports authentication and unsupported-device outcomes and preserves the authoritative raw discovery response when generating fixtures.
Redaction and discovery-formatting helpers used across modules have been renamed from private helpers to shared public helpers.
Documentation and fixtures
The user and developer documentation has been updated for the revised discovery and connection flow.
It covers:
The exception reference includes the new discovery authentication errors.
An authenticated IOT KLAP HS300 fixture and a serialized versioned-KLAP
DeviceConfigfixture cover the new route and update the generated supported-device documentation.API notes
Discover.try_connect_all()remains the public entry point for direct probing, although its implementation has moved into the device factory.ConnectAttemptnow includes the completeconnection_type, so callbacks can determine the exact route that was attempted. Code that tuple-unpacked the previous four fields will need to use the updated structure.Because
klap_versionis optional, existing serializedDeviceConfigdata remains compatible.Validation
The relevant discovery, device-factory, device-config, CLI, and devtool tests pass.
The changed Python files also pass Ruff and mypy, and the documentation builds successfully with warnings treated as errors.