Skip to content

Add capture() and capture_rf() - #4

Merged
DAB-LABS merged 1 commit into
masterfrom
capture
Sep 5, 2026
Merged

Add capture() and capture_rf()#4
DAB-LABS merged 1 commit into
masterfrom
capture

Conversation

@DAB-LABS

@DAB-LABS DAB-LABS commented Sep 5, 2026

Copy link
Copy Markdown
Owner

A learning session on these remotes is arm, poll, wait, re-arm, one code at a time, on a device that leaves learning mode without saying so and whose own transmissions end the session. capture() and capture_rf() own that loop and hand back clean signals, so a consumer subscribes and reads instead of reimplementing the dance the remote platform and the receiver PR each did separately.

capture(window, stop_after_first, poll_interval, rearm_interval) is an async generator yielding CapturedSignal. It re-arms on a timer (15 s, under the 25 to 40 s the device was measured to hold a session) and after any send_data, since a send ends the session. One window per device. capture_rf(window, frequency, ...) on the Pro models takes the carrier directly, or sweeps for it when not given; the sweep is unreliable on some firmware, so the known-frequency path is primary. CapturedSignal carries the device packet, the pulses at the corrected tick, the kind, the repeat count, and for RF the carrier the packet does not itself record.

Packet helpers: pulses_to_data gains kind and repeat, parse_packet is the inverse, SignalKind names the bands. A returned RF packet does not always use the canonical type byte (an RM4 Pro answers a 433 MHz capture with 0xB1, not 0xB2), so the kind is read by band and a capture is tagged from what it armed rather than the byte.

Benched live on an RM4 Pro: IR single and multi-capture confirmed, the 25 to 40 s session limit and the send-ends-session behaviour measured, the 0xB1 RF byte and the flaky sweep observed. 242 tests on 3.13 and 3.14; the transport oracle fixtures are unchanged.

A learning session is arm, poll, wait, re-arm, one code at a time, with a
device that leaves learning mode without saying so and a send that ends the
session. capture() and capture_rf() own that loop and hand back clean
signals, so a consumer subscribes and reads instead of reimplementing the
dance (as the remote platform, the receiver PR and others each did).

- capture(window, stop_after_first, poll_interval, rearm_interval): async
  generator yielding CapturedSignal. Re-arms on a timer (default 15 s, under
  the 25-40 s the device was measured to hold a session) and after any
  send_data (a send ends the session; both from the bench). One window per
  device; a second raises CaptureInProgressError.
- capture_rf(window, frequency, ...) on the Pro classes: takes the carrier
  directly, or sweeps for it when not given. The sweep is unreliable on some
  firmware, so the known-frequency path is primary.
- CapturedSignal: device packet, pulses at the corrected tick, kind, repeat,
  and the RF carrier the packet does not itself record.
- pulses_to_data gains kind and repeat; parse_packet is the inverse;
  SignalKind names the bands. A returned RF packet does not always carry the
  canonical type byte (an RM4 Pro sends 0xB1 for 433 MHz), so kind is read by
  band and a capture is tagged from what it armed, never dropped on the byte.
- One shared front-end lock and a transmit generation counter already live on
  the device; capture reads the counter so a concurrent send re-arms the
  window.

Tests drive the loops against a scripted device that models the bench
findings; the transport oracle fixtures are unchanged.
@DAB-LABS
DAB-LABS merged commit 5f136a3 into master Sep 5, 2026
3 checks passed
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.

1 participant