Skip to content

fix(ctw3): normalize detect_status so drink-counter increments (Closes #65)#69

Merged
aavdberg merged 2 commits intodevfrom
fix/ctw3-detect-status-value-2
May 1, 2026
Merged

fix(ctw3): normalize detect_status so drink-counter increments (Closes #65)#69
aavdberg merged 2 commits intodevfrom
fix/ctw3-detect-status-value-2

Conversation

@aavdberg
Copy link
Copy Markdown
Owner

@aavdberg aavdberg commented May 1, 2026

Summary

The CTW3 fountain firmware 111 emits 0x02 on byte 19 of CMD 210 when the proximity sensor detects a pet, not 0x01. The parser stored the raw byte, which had two effects:

  • binary_sensor.pet_drinks toggled correctly because it uses bool(detect_status) (truthy for 2) — that's why the binary sensor did work in the UI.
  • sensor.drink_events_today requires a strict 0 → 1 edge 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.log while their cat Milka was drinking, with the diagnostic byte-diff log shipped in #68 enabled:

Time Diff Meaning
13:59:13 byte[19]=0x00 -> 0x02 cat starts drinking
14:00:23 byte[19]=0x02 -> 0x00 cat leaves

Fix

One-line change in _parse_state_ctw3 (ble_client.py):

python data.detect_status = 1 if payload[19] else 0

Treating any non-zero byte 19 as "detected" also future-proofs the field against 0x03/0x04 etc. (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 the byte[19]=0x02 frame at 13:59:08.
  • test_parse_state_ctw3_detect_status_zero_when_clear — counterpart for the byte[19]=0x00 frame 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), asserting state.count goes from 0 to 1.

94 / 94 tests pass; ruff + ruff format clean.

Out of scope

Diagnostics from #68 (state_tail_hex sensor and byte-diff log) are left in place as reusable instrumentation for future offset investigations.

Closes #65, follows up #68.

)

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>
Copilot AI review requested due to automatic review settings May 1, 2026 12:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_status to 0/1 (treat any non-zero value as detected).
  • Add regression tests covering detect_status=0x02 and 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 (0x021, 0x000).
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.

Comment thread tests/test_drink_count.py Outdated
… 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>
@aavdberg aavdberg merged commit 7621368 into dev May 1, 2026
3 checks passed
@aavdberg aavdberg deleted the fix/ctw3-detect-status-value-2 branch May 1, 2026 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants