Krolyxon
Commit 9dea828

Branches

Refs →

budctlcratesbudctl-coresrcdevicesmod.rs

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