Krolyxon
Commit 828a2c7

Branches

Refs →

budctlcratesbudctl-coresrcdevicesmod.rs

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