ADR-0011: Power-code sweep for unknown devices

Status

Accepted

Area

Infrared

Context

Phone IR is transmit-only, so "learn from the original remote" is not buildable. A phone IR blaster is an emitter LED with no demodulator, and Android’s ConsumerIrManager exposes only transmit() — no receive path. (A front camera without an IR filter can see a remote LED flicker but can’t resolve a 38 kHz carrier at 30–60 fps, so it can confirm "the remote fires" and nothing more.) The only way to get a code for a device we don’t have is to try the ones we do.

Decision

tv_core::ir::power_sweep(device_type) returns one code set per DISTINCT power waveform, deduped so the phone never blasts the same code twice, ordered by how many sets share each waveform (most-shared first = most likely to hit a rebadged no-name unit). None sweeps the whole DB; the UI always scopes to a category.

The sweep reuses the existing IrWizard. The sweep is just a different source of Vec<IrCodeSet> fed to the same trial-and-save component — send POWER, "it reacted" / "try next", save the working set. Entry point is a footer on the brand picker ("Try every code for this device", BrandSearch::on_sweep), so a device whose brand isn’t listed still has a path through. The category is passed as the wizard’s brand, which titles the screen and seeds the default remote name.

Command ir_power_sweep is async like the other DB queries, for the same reason (keep the CPU walk off Tauri’s main thread).

Consequences

The DB’s ~3.6k POWER codes collapse to ~2.2k distinct waveforms overall, and per category much harder: Projector 115 sets → ~68 waveforms (~80 s to sweep all), TV 383 → ~98 (~2 min).

Parsing a category’s sets is lazy and one-shot; a whole-DB sweep would parse a big slice of the DB, which is why the UI never offers the unscoped form.

Do not add a "capture the remote" feature; it can’t work on this hardware. Revisit only for a paired external receiver (Flipper-class).