Krolyxon
Commit 047794f

Branches

Refs →

budctl047794f

Spec: deleting a custom EQ preset works on the owner's T310 (W11e)

eq delete custom-5 went out byte for byte and was acked with 00 05; the
list read back is empty. The EQ in use (deep-bass, not the deleted
preset) did not change; deleting the preset in use is still untested.
The owner had deleted Custom1 from budctl-gui before.

budctl-gui: with no custom preset left, a new one in --demo gets the
T310's layout as the buds' create does, and the "use the pencil" hint
shows only when there is a custom preset to open.
Author
krolyxon
Date
2026-10-28 01:53 +0530
Parent
4276e2b
Commit
047794fd39868ff53436f72d24be7b5676a36154

+37 −18

3 files changed

  1. M crates/budctl-gui/src/equalizer.rs +10 −7
  2. M crates/budctl-gui/src/main.rs +25 −11
  3. M docs/protocol/realme_t310.md +2 −0
M crates/budctl-gui/src/equalizer.rs View
@@ -116,12 +116,15 @@ pub fn view<'a>(
116116 )
117117 })
118118 .collect();
119− let editor: Element<'a, Message> = match edited(buds, editing) {
120− Some(preset) => bands(look, preset, draft, marks),
121− None => text("Use the pencil on one of your presets to change its bands.")
122− .size(13)
123− .color(p.dim)
124− .into(),
119+ let editor: Option<Element<'a, Message>> = match edited(buds, editing) {
120+ Some(preset) => Some(bands(look, preset, draft, marks)),
121+ None if eq.presets.iter().any(|p| p.custom) => Some(
122+ text("Use the pencil on one of your presets to change its bands.")
123+ .size(13)
124+ .color(p.dim)
125+ .into(),
126+ ),
127+ None => None,
125128 };
126129 column![title, ui::card(look, rows), new_preset(look, marks), editor]
127130 .spacing(18)
@@ -134,7 +137,7 @@ fn new_preset<'a>(look: Look, marks: Marks) -> Element<'a, Message> {
134137 .pending
135138 .contains(&Item::NewPreset)
136139 .then(|| text("Creating…").size(13).color(look.p.icon));
137− let about = text("A new preset starts flat, with the bands of your other presets, and is put to use.")
140+ let about = text("A new preset starts flat and is put to use.")
138141 .size(13)
139142 .color(look.p.dim)
140143 .width(Length::Fill);
M crates/budctl-gui/src/main.rs View
@@ -545,18 +545,20 @@ impl App {
545545 .flat_map(|eq| eq.presets.iter())
546546 .filter(|p| p.custom)
547547 };
548− let model = customs().next().cloned();
548+ // As the buds do: the bands of another preset, else the T310's own, all flat.
549+ let (bands, range) = customs().next().map_or_else(
550+ || (vec![50, 250, 1000, 4000, 8000, 16000], Some((-6, 6))),
551+ |model| (model.bands.iter().map(|b| b.hz).collect(), model.range),
552+ );
549553 let id = customs().map(|p| p.id).max().map_or(4, |id| id.saturating_add(1));
550− if let Some(model) = model {
551− self.editing = Some(id);
552− self.buds.set_custom(EqPreset {
553− name,
554− id,
555− custom: true,
556− bands: model.bands.iter().map(|b| EqBand { hz: b.hz, gain: 0 }).collect(),
557− range: model.range,
558− });
559− }
554+ self.editing = Some(id);
555+ self.buds.set_custom(EqPreset {
556+ name,
557+ id,
558+ custom: true,
559+ bands: bands.into_iter().map(|hz| EqBand { hz, gain: 0 }).collect(),
560+ range,
561+ });
560562 }
561563 Command::DeleteEq(_) => {
562564 let id = item_preset(item);
@@ -2094,6 +2096,18 @@ mod tests {
20942096 let _ = app.update(Message::RenamePreset(gym));
20952097 press(&mut app, "Esc");
20962098 assert_eq!(app.prompt, None);
2099+
2100+ // With every custom preset gone, a new one still gets bands.
2101+ for id in [4, gym] {
2102+ let _ = app.update(Message::DeletePreset(id));
2103+ let _ = app.update(Message::ConfirmDelete(true));
2104+ }
2105+ assert_eq!(app.buds.custom_names().count(), 0);
2106+ let _ = app.update(Message::NewPreset);
2107+ let _ = app.update(Message::NameInput("Fresh".to_owned()));
2108+ let _ = app.update(Message::SubmitName);
2109+ let fresh = equalizer::in_use(&app.buds).unwrap();
2110+ assert_eq!((fresh.name.as_str(), fresh.bands.len()), ("Fresh", 6));
20972111 }
20982112
20992113 #[test]
M docs/protocol/realme_t310.md View
@@ -518,6 +518,7 @@ Evidence: *vendor*
518518 - <a id="custom-eq-delete"></a>**W11e, delete** (`03`), wanted by the owner (2026-10-28): `budctl eq delete custom-<id>` reads the list and sends the preset as listed, with action `03`: the write has the same layout for every action. A preset the buds do not have is refused before anything is sent.
519519 - **Check:** read the list again, as for [read-back](#write-read-back). It must have one preset fewer, and none left with the deleted one's name and bands. Ids can be renumbered after a delete, so they prove nothing.
520520 - **Unknown:** which EQ the buds use after the preset in use is deleted. budctl reads the EQ again after a delete.
521+ - **On the owner's T310** (*ours-2026-10-28*, W11e): the delete of Gym (id `05`, not in use) went out byte for byte as the expected frame below, and the buds answered `0x8418 00 05` within 50 ms. The list read 150 ms after the ack was `00 00`: no custom preset left. The EQ in use (deep-bass) did not change. The owner had deleted Custom1 (id `04`) before, from budctl-gui.
521522 - **Expected frame** (*derived*), for `eq delete custom-5` on the owner's list after W11d (Gym, flat), with seq `06`: `AA 22 00 00 18 04 06 1B 00 03 FA 06 05 03 47 79 6D 06 32 00 00 FA 00 00 E8 03 00 A0 0F 00 40 1F 00 80 3E 00`.
522523 - **With no custom preset left**, a create has nothing to copy. It then uses the layout of the owner's T310 (*ours-2026-10-27*): range `FA 06` and bands at 50, 250, 1000, 4000, 8000 and 16000 Hz.
523524
@@ -575,6 +576,7 @@ The owner will not install realme Link (2026-10-27), so there is no app capture.
575576
576577 | Date | Checkpoint | Result | Notes |
577578 |---|---|---|---|
579+| 2026-10-28 | W11e `eq delete custom-5` | pass | The delete matched the derived frame byte for byte, and was acked with `00 05`. The list read back at once is empty. Deleting the preset in use is still untested. See [§10.9](#custom-eq-delete) |
578580 | 2026-10-28 | W11d `eq rename custom-5 Gym` | pass | The save with the new name matched the derived frame byte for byte, and was acked with `00 05`. The list read back at once has id `05` named Gym, bands unchanged, in use. See [§10.9](#custom-eq-rename) |
579581 | 2026-10-28 | W11c `eq new Test` | pass | The create frame matched the derived one byte for byte, and was acked with `00 05`. The list read back at once has Test as id `05`, flat, and in use. See [§10.9](#custom-eq-create) |
580582 | 2026-10-27 | G5 `budctl-gui --debug`, Gestures page: left triple tap by dropdown and by `h`/`l`, transparency into the hold cycle and out | pass (protocol) | Every `0x0401` write (`06`, `03`, `0B`, `03`, `06`, then `05` to restore it) and both `0x0404 02 01` writes (`0B`, `09`) were acked and read back on the first try. Keys pressed during a write went out one by one as each write finished, and the last one was read back. A session closed after 10 s idle; the next pick opened a new one and wrote at once |