Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ history below starts at that fork point.

### Added

- `capture()` and `capture_rf()`, async generators that own the arm, poll,
timeout and re-arm loop of a learning session and yield each signal as a
`CapturedSignal` (device packet, decoded pulses at the correct tick,
kind, repeat count, and for RF the carrier frequency). They re-arm on a
timer, because the device leaves learning mode silently, and after any
`send_data`, because a transmission ends the session; both intervals and
the poll cadence were set from a bench on an RM4 Pro. Only one window can
be open per device. `capture_rf()` (Pro models only) takes the carrier
frequency directly and falls back to the on-device sweep when it is not
given.
- Packet helpers: `pulses_to_data` takes `kind` and `repeat`, `parse_packet`
is its inverse, and `SignalKind` names the IR, 433 MHz and 315 MHz bands.
A device's 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.
- Devices, carried over from pull requests against the original repository
with their authors' commits intact: RM Max 0xAF8B (#838, Alexey Masolov);
RM5 plus 0x5224 with a new `rm5plus` class (#831, Anil Daoud); RM mini 3
Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,46 @@ You can exit the learning mode in the middle of the process by calling this meth
await device.cancel_sweep_frequency()
```

### Capturing signals

`capture()` wraps the arm, poll, timeout and re-arm dance above into one
async generator that yields each signal it hears as a `CapturedSignal`:

```python3
from contextlib import aclosing

async with aclosing(device.capture(window=30)) as signals:
async for signal in signals:
print(signal.kind, len(signal.pulses), "pulses")
await other_device.send_data(signal.packet)
```

By default the window closes after the first signal. Pass
`stop_after_first=False` to keep it open for the whole `window` (in seconds;
`window=0` runs until the generator is closed), re-arming after each signal
because the device holds only one code per learning session. A universal
remote has a single receiver, so only one capture window can be open on a
device at a time.

`CapturedSignal` carries the device's own `packet` bytes (ready for
`send_data`), the decoded `pulses` in microseconds at the correct tick, the
`kind` (`SignalKind.IR`, `RF_433` or `RF_315`), the `repeat` count, and for
RF the `frequency_mhz` the packet itself does not record.

RF works the same way on the Pro models, with the carrier as the one extra
input:

```python3
async with aclosing(device.capture_rf(window=30, frequency=433.92)) as signals:
async for signal in signals:
...
```

Pass `frequency` whenever you know it. Without it the device first sweeps
for the carrier while you hold a button down, then learns the code from a
fresh press; the sweep is unreliable on some firmware and can report a
carrier it never really locked, so the known-frequency path is preferred.

### Sending IR/RF packets
```python3
await device.send_data(packet)
Expand Down
9 changes: 9 additions & 0 deletions broadlink/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ class StorageError(BroadlinkException):
"""Storage error."""


class CaptureInProgressError(BroadlinkException):
"""A capture window is already open on this device.

A universal remote has one receiver, so only one ``capture`` or
``capture_rf`` window can be open at a time. Close the running one
before opening another.
"""


class WriteError(BroadlinkException):
"""Write error."""

Expand Down
Loading
Loading