Fire raw_device_initialized after registering endpoints - #180
Fire raw_device_initialized after registering endpoints#180zigpy-review-bot wants to merge 9 commits into
raw_device_initialized after registering endpoints#180Conversation
zigpy 2.0.0 moved the quirks API out of zigpy into zha-device-handlers, so importing CustomDevice/CustomCluster from zigpy.quirks now fails with ModuleNotFoundError (zhaquirks is not, and must not be, a dependency). - Reimplement XBeeGroup/XBeeGroupResponse as plain Groups subclasses kept out of the global cluster registry via _skip_registry. - Reimplement XBeeCoordinator as a zigpy.device.Device that builds its coordinator endpoint and clusters directly instead of via a quirks CustomDevice 'replacement' dict. - Rewrite ControllerApplication.add_endpoint to register endpoints and clusters directly rather than mutating the quirks replacement dict. - Replace the deprecated Device.update_last_seen() call with a direct last_seen assignment.
zigpy 2.0.0 switched its serial layer from pyserial-asyncio-fast to serialx, whose Serial class does not expose a BAUDRATES attribute. The baudrate setter (used when entering AT command mode) relied on self._transport.serial.BAUDRATES and would raise AttributeError. - Define the standard baudrate list on Gateway and validate against it. - Drop the stale 'import serial_asyncio_fast' from the uart tests and patch zigpy.serial.create_serial_connection (what uart.connect actually calls) instead.
- Require zigpy>=2.0.0 (where the quirks API was moved out) instead of the long-outdated 0.70.0 floor. - Require Python >=3.11 to match zigpy 2.0.0, and update the tox env list (py311-py313) and pyupgrade target (--py311-plus) accordingly. - Drop the unused, unmaintained asynctest test dependency; the tests use unittest.mock.
The --py311-plus pyupgrade target rewrites the deprecated asyncio.TimeoutError alias to the builtin TimeoutError and replaces typing.Dict/Optional with builtin generics and PEP 604 unions; autoflake drops the now-unused imports.
CI runs via the shared zigpy/workflows workflow, not tox; the file is no longer referenced.
Matches the spelling of zigpy's own (deprecated) `update_last_seen()` and drops the `timezone` import; `datetime.UTC` is available on Python 3.11+.
`XBeeCoordinator` builds its own `0xE6` endpoint and only read `replaces` for `.status`, which the call site assigned two lines earlier, so the plain `Device` it was handed — and the `add_endpoint()` call on it — had no effect.
Coverage was already 100%, but nothing asserted the shape of what `start_network()` builds, which is what let the old quirks-based `add_endpoint()` silently degenerate to bare endpoints.
`zigpy.appdb.PersistingListener.raw_device_initialized()` clones the device synchronously, and `_raw_device_initialized_internal()` is the only code path that ever writes `endpoints`/`clusters` rows (`device_initialized()` is a `pass`). Firing the event before `register_endpoints()` therefore persisted only the XBee endpoint `0xE6`; endpoints 1 and 2 never reached the database. ZHA's handler has the same problem: it puts `device.get_signature()` into the event it emits, so the signature was missing those endpoints too. Moving the event below `register_endpoints()` fixes both. The device has to be in `self.devices` before that call, since `add_endpoint()` resolves `self._device` out of it, so only the event moves.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #180 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 7 7
Lines 718 733 +15
=========================================
+ Hits 718 733 +15 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@zigpy-review-bot Is this even an issue it's not persisted to the DB (or shown in ZHA diagnostics)? Isn't it just needed to register, which is anyway done on every restart? Also do review rounds on this and push more commits if it makes sense to adjust anything. |
|
Checked this properly — you're right, and it's worse than "not much of an issue": the premise I gave in #179 and in this PR's description doesn't hold, on either half. The DB gets the complete coordinator with or without this PR. Probe: both orderings against a real sqlite DB, driving
|
Follow-up to #179, from #179 (comment).
start_network()firesraw_device_initializedbeforeregister_endpoints(), so every listener sees a coordinator that still only has the XBee endpoint0xE6.zigpy.appdb.PersistingListener.raw_device_initialized()clones the device synchronously inside the event, and_raw_device_initialized_internal()is the only code path that ever writesendpoints/clustersrows (PersistingListener.device_initialized()is apass), so endpoints 1 and 2 never reach the database. ZHA's handler (zha/application/gateway.py:416) putsdevice.get_signature()into the event it emits, so that signature is missing them too.Moving the event below
register_endpoints()fixes both. Theself.devices[...]insertion has to stay aboveregister_endpoints(), sinceadd_endpoint()resolvesself._deviceout ofself.devices— so only thelistener_event()call moves.Not a regression from #179: the event has been in this position all along. #179 is what makes it matter, because it gives endpoints 1 and 2 real profiles, device types and clusters for the first time. Impact is low in practice —
start_network()rebuilds the coordinator device on every startup, so the incomplete rows self-heal and nothing user-visible reads them today.This is stacked on #179 — it branches off that PR's head, so the diff shown here includes #179's commits until it merges. Only
1711d8fbelongs to this PR. Happy to rebase ontodevonce #179 is in.Testing
pytest: 108 passed, 100% coverage,DeprecationWarningas errors. The new test (test_start_network_raw_device_initialized_after_endpoints) asserts the listener sees all endpoints at event time, and fails on the unfixed ordering.pre-commit: all hooks pass.Probed against a real sqlite DB driving the actual
start_network()sequence, at Support zigpy 2.0.0 #179's head: