Conversation
Remote monitoring has no endpoint that returns the current cook state -- the appliance pushes it to the mobile app as a Firebase Cloud Messaging data message. miaucl#251 added the REST surface and the payload decoder but left receiving the messages to the caller, which meant every consumer had to reimplement FCM registration and re-derive the app's Firebase identifiers. `CookidooRemoteMonitoring` closes that gap: it obtains an FCM registration token without an Android device, registers it with `register_push_token`, and decodes each incoming message with `cooking_activity_from_push` before handing it to a callback. FCM credentials can be persisted and restored to keep the same push token across restarts, and a rotated token is re-registered automatically. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9tJdQdLB7bcvqpRp16LXC
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #260 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 8 9 +1
Lines 1254 1359 +105
Branches 113 125 +12
==========================================
+ Hits 1254 1359 +105 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
It would be great to have some sort of setup to test firebase integration systematically. The smoke tests won't do it, as it is push and not pull, so no api to query. Do you have an idea? |
miaucl
left a comment
There was a problem hiding this comment.
Looks good in general, small bug found:
Bug found — stop() crashes if start() fails partway:
start() assigns self._client before awaiting checkin_or_register() . If that (or the subsequent .start() ) raises — e.g. registration with Google's backend fails — self._client is already set, but the underlying FcmPushClient was never actually started, so its internal stopping_lock stays None . A caller doing the natural cleanup:
try:
await monitoring.start()
except Exception:
await monitoring.stop()
gets a second exception ( TypeError: 'NoneType' object does not support the asynchronous context manager protocol ) masking the original failure. Reproduced deterministically with mocks:
start() failed as expected: registration failed
stop() ALSO FAILED: TypeError 'NoneType' object does not support the asynchronous context manager protocol
Fix: only assign self._client (or guard stop() ) after checkin_or_register() / .start() succeed, e.g. track a local variable and assign self._client last, or wrap the self._client.stop() call in stop() with a try/except like the unregister call already does.
Summary
#251 added the remote-monitoring REST surface and
cooking_activity_from_push(), but deliberately left receiving the messages to the caller. In practice that means every consumer has to reimplement FCM registration and re-derive the Cookidoo app's Firebase identifiers from the APK before it can see a single cook state — there is no endpoint to poll, so without this there is no way to use the feature at all.This adds
CookidooRemoteMonitoring, which closes the loop:firebase-messaging)register_push_token()cooking_activity_from_push()and hands a typedCookidooCookingActivityto a callbackstop()unregisters the token and shuts the client downDetails worth a look
stop()cancels.PushNotificationServicealso reads them fromcookingActivity/remoteMonitoringInfo, as an object or a JSON string. All three are accepted.FCM_PROJECT_ID,FCM_APP_ID,FCM_API_KEY,FCM_SENDER_ID) are the app's public client identifiers from its google-services resources — the same class of value as the OAuth2 client id, not credentials.Why in the library rather than in the consumer
This started as integration-side code for the Home Assistant PR, per the note in #251. Moving it here follows the
ring-doorbellprecedent — it ownsfirebase-messagingand its Firebase ids, and theringintegration just consumes events — and matches Home Assistant's rule that protocol logic belongs in the library, not the integration.firebase-messaging~=0.4is a new runtime dependency (same package and major thatring-doorbelluses).Verification
pytest: 380 passed, coverage 100% maintained (remote_monitoring.py99 stmts / 24 branches, fully covered)ruff check/ruff format --check: clean on the added filesmypy: clean (16 source files)Two files on
master(cookidoo_api/helpers.py,tests/responses.py) are reformatted by ruff 0.16.6 — pre-existing drift, deliberately left untouched here.Follow-up
Unblocks the Home Assistant PR that surfaces paired appliances as devices with live cook-state entities.