Quality Checklist¶
The Implementation Checklist gets your integration functional — it launches, pairs, controls, and reports. This checklist gets it reliable and maintainable: it's the bar an integration must clear before it's released.
Items are ordered from vital (never ship without them) to nice-to-have. Like the implementation checklist, you can mirror this in your repo's README to track progress.
Reliability — vital¶
An integration must never take the Hub down, and never spam the logs. The Hub isolates a crashing integration where it can, but a quality integration doesn't rely on that.
- [ ] Recovers automatically. Connection loss, an offline device, or a restarted backend/radio must recover on their own — no manual restart. Reconnection is retried with backoff, not a tight loop.
- [ ] No exception escapes the controller. Every background task, subscription loop, and callback catches its own errors. An unhandled exception in a background task dies silently; one raised inside a
self.dependencies.output.*call propagates back into the Hub. Neither is acceptable. - [ ] Failures are surfaced, not raised. An expected/transient failure (device offline, bad reading, timeout) is logged once (no repeated spam) and reflected on the device —
device.available = Falseand a human-readablelast_error— then cleared on recovery. It does not bubble up as an exception. - [ ] Re-authenticates automatically when credentials expire or are rejected (if the protocol uses credentials).
- [ ] Fully asynchronous. No blocking I/O on the event loop; blocking or CPU-heavy work runs off-loop (a thread/executor) so it can't stall the Hub.
- [ ] Availability is live. Offline/online is detected while the Hub is running, not only on reboot (this also appears on the implementation checklist — it's a reliability requirement, so it's repeated here).
- [ ] Stable identity. Device and parameter UUIDs are derived through the SDK helpers, so they stay identical across restarts and re-pairs — state and automations survive.
Tested — vital¶
- [ ] End-to-end automated tests drive the controller through pair → command → fetch → events →
unpairagainst a virtual/simulated device (the SDK'smajordom_integration_sdk.testingdoubles), with no physical hardware required. - [ ] Failure paths are tested — offline device, transport error, rejected credentials — asserting the integration degrades gracefully (sets
available/last_error, does not raise). - [ ] Broad device coverage where the protocol has many device/parameter types — exercise the full mapping surface, not one happy-path device. (A protocol-provided virtual-device catalogue, run in CI, is the ideal.)
Maintainable — important¶
- [ ] Fully typed — passes
tywith no package-wide ignore blocks; any exceptions are file/rule-scoped and commented with why. - [ ] Clean — passes the full
poe check(ruff lint + format,ty, tests, build) with no warnings. - [ ] Readable & structured — clear names, comments where the intent isn't obvious; conversion logic lives in a mapper and models are separated, per the template layout.
- [ ] Efficient — prefer event subscriptions over polling; batch or chunk reads; avoid redundant network and CPU work.
- [ ] Diagnosable — logging at the right levels, enough to debug a device problem from the logs alone. A device-state diagnostic dump is a bonus.
- [ ] Owned — a listed maintainer who keeps it working as the protocol and the upstream library evolve.
User experience — important¶
- [ ] Rich parameter metadata. Every parameter has the correct visibility (
user/setting/system) and the device's main parameter is set — so the app can present a clean control-center action, a sensible on-tap parameter list, a settings page, and hidden system internals. Getting this right is what makes a device feel native rather than a raw dump of protocol attributes; a poorly-mapped device is a poor device, however reliable it is.
Extras — nice-to-have¶
- [ ] Logical, localizable naming — device and parameter names read well to a non-technical user and can be translated.
- [ ] Firmware/software updates — device firmware can be updated through the Hub, where supported. (The update API is still WIP.)
- [ ] User documentation — a supported-device list, setup notes, and troubleshooting aimed at non-technical users.
Reporting errors¶
The reliability rules above hinge on one habit: catch errors at the edges and turn them into state, not exceptions.
- Inside a background task or subscription loop, wrap the body in a
try/except. On a per-device failure, set that device'savailable/last_errorand keep the loop alive. - Never let an exception cross into a
self.dependencies.output.*call — the Hub treats those callbacks as trusted and an exception there can disrupt Hub-side handling. - Log an expected failure once at the appropriate level; don't re-log the same condition every poll.
For controller-level problems that aren't tied to a single device (e.g. the backend/radio went away entirely), call controller_did_encounter_error — it surfaces a plain-language message to the user, and still_running=False marks the whole integration as failed. See Reporting errors to the user.