Krolyxon
Commit 047794f

Branches

Refs →

budctlcratesbudctl-coresrcdevicesmod.rs

149 lines · 6.0 KB · Rust

1//! Device plugins.
2//!
3//! To support a new pair of buds, add one module here and list it in [`REGISTRY`]. Transport,
4//! CLI and GUI code do not change.
5
6use crate::bluez::PairedDevice;
7use crate::bt::Uuid;
8use crate::error::{Error, Result};
9use crate::status::{EqBand, EqPreset, EqPresets, NoiseControl, NoiseMode, Status, Switch, TouchChange, TouchControls};
10use crate::transport::Transport;
11
12pub mod realme_t310;
13
14/// Every model budctl supports.
15pub static REGISTRY: &[&dyn Device] = &[&realme_t310::RealmeT310];
16
17/// One supported model.
18pub trait Device: Sync {
19 /// Model name, e.g. `realme Buds T310`.
20 fn name(&self) -> &'static str;
21
22 /// Whether a paired device is this model, judged from BlueZ data alone.
23 fn matches(&self, device: &PairedDevice) -> bool;
24
25 /// The control service(s), in the order to try. SDP turns each into an RFCOMM channel.
26 fn rfcomm_uuids(&self) -> &'static [Uuid];
27
28 /// Run the model's init sequence on a fresh link and return a ready session.
29 fn open(&self, link: Box<dyn Transport>) -> Result<Box<dyn Session>>;
30}
31
32/// An open control link to one pair of buds. Dropping it closes the link.
33pub trait Session: Send {
34 /// Answer what the buds sent since the last call, without waiting. Some buds drop the link
35 /// when their own requests (the time request, for one) go unanswered, so a front end that
36 /// keeps a session open between commands calls this every so often. An error means the link
37 /// is gone.
38 fn serve(&mut self) -> Result<()> {
39 Ok(())
40 }
41
42 /// Battery, noise control, EQ, switches and whatever else the model can read.
43 fn status(&mut self) -> Result<Status>;
44
45 /// The touch-control (gesture) settings of each bud.
46 fn touch_controls(&mut self) -> Result<TouchControls> {
47 Err(Error::Unsupported(
48 "these buds have no touch-control settings budctl can read".to_owned(),
49 ))
50 }
51
52 /// Set the noise-control mode, then read it back. Returns the mode the buds now report.
53 fn set_noise_control(&mut self, _mode: NoiseMode) -> Result<NoiseControl> {
54 Err(Error::Unsupported(
55 "these buds have no noise-control setting budctl can change".to_owned(),
56 ))
57 }
58
59 /// The EQ presets on the buds, built-in and custom, and the one in use.
60 fn eq_presets(&mut self) -> Result<EqPresets> {
61 Err(Error::Unsupported(
62 "budctl cannot list the EQ presets of these buds".to_owned(),
63 ))
64 }
65
66 /// Select a built-in EQ preset by name, then read it back. Returns the preset now in use.
67 fn set_eq(&mut self, _preset: &str) -> Result<String> {
68 Err(Error::Unsupported(
69 "budctl cannot change the EQ on these buds".to_owned(),
70 ))
71 }
72
73 /// Change the gains of some bands of a custom EQ preset (`custom-<id>`), save it and select
74 /// it, then read it back. With no bands, it only selects the preset.
75 fn set_custom_eq(&mut self, preset: &str, _bands: &[EqBand]) -> Result<EqPreset> {
76 Err(Error::Unsupported(format!(
77 "budctl cannot change {preset} on these buds"
78 )))
79 }
80
81 /// The lowest and the highest level [`Session::set_bass_level`] takes, when the buds have a
82 /// bass level to set.
83 fn bass_range(&self) -> Option<(i8, i8)> {
84 None
85 }
86
87 /// Create a custom EQ preset called `name`, flat, with the bands of the buds' other custom
88 /// presets. Returns it as the buds list it afterwards.
89 fn create_custom_eq(&mut self, name: &str) -> Result<EqPreset> {
90 Err(Error::Unsupported(format!(
91 "budctl cannot create the preset {name} on these buds"
92 )))
93 }
94
95 /// Give a custom EQ preset (`custom-<id>`) a new name. The buds save it, which also selects
96 /// it. Returns it as the buds list it afterwards.
97 fn rename_custom_eq(&mut self, preset: &str, _name: &str) -> Result<EqPreset> {
98 Err(Error::Unsupported(format!(
99 "budctl cannot rename {preset} on these buds"
100 )))
101 }
102
103 /// Delete a custom EQ preset (`custom-<id>`). Returns the custom presets left, as the buds
104 /// list them afterwards: their ids can change.
105 fn delete_custom_eq(&mut self, preset: &str) -> Result<Vec<EqPreset>> {
106 Err(Error::Unsupported(format!(
107 "budctl cannot delete {preset} on these buds"
108 )))
109 }
110
111 /// Set the bass level. Only the ack can be checked when the buds cannot report the level.
112 fn set_bass_level(&mut self, _level: i8) -> Result<()> {
113 Err(Error::Unsupported(
114 "budctl cannot change the bass level on these buds".to_owned(),
115 ))
116 }
117
118 /// Start (`true`) or stop ringing the buds so they can be found. Only the ack can be
119 /// checked: there is no state to read back.
120 fn find(&mut self, _ring: bool) -> Result<()> {
121 Err(Error::Unsupported("these buds cannot ring through budctl".to_owned()))
122 }
123
124 /// Turn an on/off setting ([`Switch::key`]) on or off, then read it back.
125 fn set_switch(&mut self, key: &str, _on: bool) -> Result<Switch> {
126 Err(Error::Unsupported(format!("budctl cannot change {key} on these buds")))
127 }
128
129 /// Set the noise-control modes a hold steps through (`off`, `transparency`, `anc`), then
130 /// read it back. Returns the modes the buds now report.
131 fn set_hold_cycle(&mut self, _modes: &[&str]) -> Result<Vec<String>> {
132 Err(Error::Unsupported(
133 "budctl cannot change the hold cycle on these buds".to_owned(),
134 ))
135 }
136
137 /// Give a touch gesture a new action, on `side` (`left`, `right` or `both`), then read the
138 /// table back. With `dry_run`, read the table and work out the write, but send nothing.
139 fn set_touch(&mut self, _side: &str, _gesture: &str, _action: &str, _dry_run: bool) -> Result<TouchChange> {
140 Err(Error::Unsupported(
141 "budctl cannot change the touch controls on these buds".to_owned(),
142 ))
143 }
144}
145
146/// The supported model a paired device is, if any.
147pub fn identify(device: &PairedDevice) -> Option<&'static dyn Device> {
148 REGISTRY.iter().copied().find(|model| model.matches(device))
149}