feat: expose coordinator polling intervals as user-configurable options - #234
Conversation
Adds a new "Advanced polling" section to the integration's options flow with three knobs that previously lived as hard-coded constants in const.py: - scan_interval_slow (sec) - refresh interval used while the IoT MQTT listener is connected. Default 190 s, matches previous behaviour. - scan_interval_default (sec) - fallback interval used when the IoT MQTT listener is disconnected. Default 60 s. - smart_polling_boost_duration (sec) - how long the temporary fast poll boost lasts after an event-triggered binary sensor flips on. Default 15 s, equivalent to the previous 3 ticks * 5 s. Rationale: with the current /user/get-only MQTT subscription, the coordinator essentially polls the PetKit REST API for state updates. The hard-coded 190 s slow interval is conservative for cloud usage but inconvenient for users who want near-real-time updates from event sensors (e.g. binary_sensor.toilet_occupied) and are happy to accept the extra REST traffic. Implementation notes: - Coordinator reads the new section in __init__ and exposes the values as instance attributes (scan_interval_slow, scan_interval_default, smart_polling_boost_duration). - enable_smart_polling() now derives the tick count from the boost duration option; an explicit boost_duration argument is still accepted for backwards compatibility. - Existing constants stay as defaults so missing options fall back to identical historical behaviour. A migration step (entry version 8 -> 9) seeds the new section on upgrade. - Updated existing callers in binary_sensor.py and button.py to use the option-driven default. - English and Russian translations added (existing options sections also gained the previously-untranslated notifications_options Russian strings).
|
|
Hello @fullstackPrincess However, your PR raises a very interesting point: the Petkit integration connects to MQTT but never receives any notifications from the servers? |
|
Hi @Jezza34000 — thanks for the thoughtful response, and especially for highlighting that "do they actually get pushed at all?" is the more interesting question. I went and tried to answer it empirically, and the result was surprising enough that I think it changes the picture for this PR. TL;DRThe Aliyun IoT broker does push device events — the integration just isn't receiving them. A standalone paho-mqtt client connected with the exact same credentials, client id,
So time-based polling isn't compensating for cloud silence — there's an integration-side blind spot or threading bug that drops these messages on the floor. What I'm trying to react toThis PR's motivating use case for me is firing a Home Assistant automation the moment a cat enters the litter box — the These Methodology
PetKit mobile app was logged out of this account everywhere during both phases, so there was no second client competing for the Aliyun IoT identity. Result detail
So where could the integration be losing them?I haven't tracked down the root cause yet — but a few things to look at, since the on-the-wire delivery is provably fine:
Suggested next stepGiven the above, I'd argue this PR is still useful — it gives users a knob to keep their setup usable today without forking the integration — but the more important fix is the underlying
Whichever you prefer. LogsSanitized full logs (user id, device ids, MQTT tenant subdomain, secrets all masked):
|
|
One more thought, in case the "expose three more knobs in the options form" part is what bothers you most about the PR (rather than the values themselves being configurable at all): If the concern is keeping the options UI small and unintimidating, the same configurability could be done without surfacing anything in the form — the options would still survive HACS updates because they live in the config entry, not in the integration code:
Any of those would let me delete the OptionsFlow part of this PR and keep just the coordinator changes plus a thin override-reading layer. Happy to rework the diff in whichever direction you prefer — including "merge nothing, fix the listener bug, no knobs needed." Just want to flag that the persistence-across-updates concern can be solved without growing the UI if that's the sticking point. |
|
Thank you for this incredibly thorough and well-documented investigation On my side, I ran my own tests and I'm unable to reproduce the bug: the listener does receive messages as expected in my environment. So there must be something else going on that we haven't identified yet unfortunately If anything new comes to mind or if you manage to narrow it down further, I'm very interested to hear it. For me, the main concern remains finding why this bug occurs in the first place. This PR only has value if the bug is actually present if the integration works correctly out of the box, there is simply no need to expose polling interval configuration. In my case, even the most ephemeral entities update correctly: for example, Regarding the configuration options you proposed: the option is actually already there, just hidden a user can already edit My last concern is about exposing polling intervals to end users: the risk is that users will inevitably set very low values to get more frequent updates, which leads to excessive load on Petkit's servers. There is actually a precedent of a Home Assistant integration that got blocked by a manufacturer because of too aggressive polling (I can't remember the name right now), but it happened. |
|
This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days. |



📝 Proposed Change / Description
Adds a new Advanced polling section to the integration's options flow that surfaces three coordinator timings that previously lived as hard-coded constants in
const.py:scan_interval_slowSCAN_INTERVAL_SLOWscan_interval_defaultDEFAULT_SCAN_INTERVALsmart_polling_boost_durationenable_smart_polling(3)(3 ticks × 5 s)Why
With the current
/<product_key>/<deviceName>/user/get-only MQTT subscription, the IoT MQTT listener typically does not receive any device-state push messages from the PetKit cloud (messages_receivedstays at0for many setups, including mine on theapi-ru.petkit.cnregional cloud). State updates therefore come exclusively from the REST polling path insidePetkitDataUpdateCoordinator.The hard-coded 190 s slow interval is fine for low cloud load, but inconvenient when an event-triggered binary sensor (e.g.
binary_sensor.toilet_occupied) needs to fire an automation in something closer to real time. With this PR, users who are happy to accept the extra REST traffic can drop the slow interval to e.g. 5–30 s without forking the integration; users who want to be gentle on the cloud can raise it.The defaults are unchanged so behaviour is preserved out of the box.
Fixes: N/A
🔖 Type of change
🐾 Affected devices
The change touches the shared coordinator + options flow, so it benefits every device that relies on cloud polling — most relevant for litter boxes (per-visit events), feeders (manual feed feedback), and air purifiers, but applies across the board.
🧪 How has this been tested?
ghcr.io/home-assistant/home-assistant:stable)Device(s) tested on: PetKit PuraMax 2 + Air Smart Spray (K3) on
api-ru.petkit.cn.What I verified:
async_migrate_entrybumps the entry to version 9 and adds the new section with defaults; existing entries keep their previous behaviour.async_reload_entryupdate listener.scan_interval_slow=5and confirmed in debug logs (pypetkitapi.client) that REST refreshes happen every ~5 s with no429/ auth errors over an extended observation window. Reverted to defaults — refreshes return to ~190 s as expected.binary_sensor.*_toilet_occupiedflippingonstill triggers the temporary 5 s ticking; withsmart_polling_boost_duration=15it lasts 3 fast ticks as before. Setting it to30correctly extends the burst to 6 ticks.✅ Checklist
pre-commit runover the touched files passes (black / ruff / codespell / prettier / detect-secrets / prettier-json all green)mainℹ️ Additional context
enable_smart_polling()signature. The argument was made optional (boost_duration: int | None = None). Existing callers were updated to call it without arguments so the coordinator uses the user-configured duration; explicit overrides still work for any future caller that needs a one-off duration.name,data,data_description,description) are added for the new section. A Russian translation for the new section is included; while editingru.jsonI also added the previously-missingnotifications_optionsblock (it already exists inen.jsonbut had no Russian counterpart) — happy to split this into a separate PR if preferred.📸 Logs
Polling interval taking effect after setting
scan_interval_slow=5via UI (debug logs frompypetkitapi.client):The new section is additive. Default values reproduce the exact prior behaviour and the migration step seeds them automatically for upgraded entries, so no user action is required after this PR is merged.
Thanks for the helping paw! 🐾