fix(ctw3): normalize detect_status so drink-counter increments (Closes #65)#69
Merged
fix(ctw3): normalize detect_status so drink-counter increments (Closes #65)#69
Conversation
) CTW3 firmware 111 emits `0x02` on byte 19 of the CMD 210 state payload when the proximity sensor detects a pet, not `0x01` as previously assumed. The parser stored the raw byte unchanged, so: - `binary_sensor.pet_drinks` (uses `bool(detect_status)`) toggled correctly — value `2` is truthy. - `sensor.drink_events_today` requires a strict `0 -> 1` edge in the coordinator and therefore never incremented. Captured ground-truth (Logs/home-assistant_petkit_ble_2026-05-01T12-01-29.952Z.log): 13:59:13 byte[19]=0x00->0x02 <- cat starts drinking 14:00:23 byte[19]=0x02->0x00 <- cat leaves Fix is one line in `_parse_state_ctw3`: normalise any non-zero byte 19 to `1`. Future-proofs the field against further bit patterns (0x03/0x04 etc.). Tests: - New parser test exercising the captured detect=2 frame. - New parser test for the cleared (byte19=0x00) frame. - New end-to-end test feeding three CTW3 30-byte payloads through parser + `_track_drink_event_into` and asserting the count goes from 0 to 1. Diagnostics shipped in #68 (state_tail_hex sensor + byte-diff log) are left in place as a reusable debugging aid for future offset investigations. Refs #65, follows up #68. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes CTW3 drink-event counting by normalizing the detect_status byte from CMD 210 state payloads so the coordinator’s strict 0 → 1 edge detection works reliably on firmware 111.
Changes:
- Normalize CTW3
detect_statusto0/1(treat any non-zero value as detected). - Add regression tests covering
detect_status=0x02and an end-to-end parser→counter increment scenario using captured frames. - Bump integration version to
1.1.14.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
custom_components/petkit_ble/ble_client.py |
Normalizes CTW3 detect_status to 0/1 during CMD 210 parsing. |
tests/test_data_model.py |
Adds parser regression tests for detect_status normalization (0x02 → 1, 0x00 → 0). |
tests/test_drink_count.py |
Adds an end-to-end regression test feeding CTW3 payloads through parser + counter logic. |
custom_components/petkit_ble/manifest.json |
Version bump to 1.1.14. |
… test Replaces the synthesised IDLE_BEFORE (detected frame with byte 19 zeroed) with the actual frame captured at 13:58:00 in the ground-truth log, so the test faithfully matches the PR description's claim of three real frames. Addresses Copilot review on PR #69. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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
The CTW3 fountain firmware 111 emits
0x02on byte 19 of CMD 210 when the proximity sensor detects a pet, not0x01. The parser stored the raw byte, which had two effects:binary_sensor.pet_drinkstoggled correctly because it usesbool(detect_status)(truthy for2) — that's why the binary sensor did work in the UI.sensor.drink_events_todayrequires a strict0 → 1edge in the coordinator, so it never incremented.Ground-truth evidence
Captured by the user in
Logs/home-assistant_petkit_ble_2026-05-01T12-01-29.952Z.logwhile their cat Milka was drinking, with the diagnostic byte-diff log shipped in #68 enabled:byte[19]=0x00 -> 0x02byte[19]=0x02 -> 0x00Fix
One-line change in
_parse_state_ctw3(ble_client.py):python data.detect_status = 1 if payload[19] else 0Treating any non-zero byte 19 as "detected" also future-proofs the field against
0x03/0x04etc. (likely a multi-bit status flag).Tests
Three new tests, all using real frames from the captured log:
test_parse_state_ctw3_normalises_detect_status_value_2— parser test for thebyte[19]=0x02frame at 13:59:08.test_parse_state_ctw3_detect_status_zero_when_clear— counterpart for thebyte[19]=0x00frame at 14:00:17.test_real_drink_event_increments_counter— full parser-+-counter end-to-end run feeding three CTW3 30-byte payloads (idle → detected → idle), assertingstate.countgoes from 0 to 1.94 / 94 tests pass; ruff + ruff format clean.
Out of scope
Diagnostics from #68 (
state_tail_hexsensor and byte-diff log) are left in place as reusable instrumentation for future offset investigations.Closes #65, follows up #68.