| 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 | |
| 6 | use crate::bluez::PairedDevice; |
| 7 | use crate::bt::Uuid; |
| 8 | use crate::error::{Error, Result}; |
| 9 | use crate::status::{NoiseControl, NoiseMode, Status, Switch, TouchChange, TouchControls}; |
| 10 | use crate::transport::Transport; |
| 11 | |
| 12 | pub mod realme_t310; |
| 13 | |
| 14 | /// Every model budctl supports. |
| 15 | pub static REGISTRY: &[&dyn Device] = &[&realme_t310::RealmeT310]; |
| 16 | |
| 17 | /// One supported model. |
| 18 | pub 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. |
| 33 | pub trait Session: Send { |
| 34 | /// Battery, noise control, EQ, switches and whatever else the model can read. |
| 35 | fn status(&mut self) -> Result<Status>; |
| 36 | |
| 37 | /// The touch-control (gesture) settings of each bud. |
| 38 | fn touch_controls(&mut self) -> Result<TouchControls> { |
| 39 | Err(Error::Unsupported( |
| 40 | "these buds have no touch-control settings budctl can read".to_owned(), |
| 41 | )) |
| 42 | } |
| 43 | |
| 44 | /// Set the noise-control mode, then read it back. Returns the mode the buds now report. |
| 45 | fn set_noise_control(&mut self, _mode: NoiseMode) -> Result<NoiseControl> { |
| 46 | Err(Error::Unsupported( |
| 47 | "these buds have no noise-control setting budctl can change".to_owned(), |
| 48 | )) |
| 49 | } |
| 50 | |
| 51 | /// Select a built-in EQ preset by name, then read it back. Returns the preset now in use. |
| 52 | fn set_eq(&mut self, _preset: &str) -> Result<String> { |
| 53 | Err(Error::Unsupported( |
| 54 | "budctl cannot change the EQ on these buds".to_owned(), |
| 55 | )) |
| 56 | } |
| 57 | |
| 58 | /// Start (`true`) or stop ringing the buds so they can be found. Only the ack can be |
| 59 | /// checked: there is no state to read back. |
| 60 | fn find(&mut self, _ring: bool) -> Result<()> { |
| 61 | Err(Error::Unsupported("these buds cannot ring through budctl".to_owned())) |
| 62 | } |
| 63 | |
| 64 | /// Turn an on/off setting ([`Switch::key`]) on or off, then read it back. |
| 65 | fn set_switch(&mut self, key: &str, _on: bool) -> Result<Switch> { |
| 66 | Err(Error::Unsupported(format!("budctl cannot change {key} on these buds"))) |
| 67 | } |
| 68 | |
| 69 | /// Set the noise-control modes a hold steps through (`off`, `transparency`, `anc`), then |
| 70 | /// read it back. Returns the modes the buds now report. |
| 71 | fn set_hold_cycle(&mut self, _modes: &[&str]) -> Result<Vec<String>> { |
| 72 | Err(Error::Unsupported( |
| 73 | "budctl cannot change the hold cycle on these buds".to_owned(), |
| 74 | )) |
| 75 | } |
| 76 | |
| 77 | /// Give a touch gesture a new action, on `side` (`left`, `right` or `both`), then read the |
| 78 | /// table back. With `dry_run`, read the table and work out the write, but send nothing. |
| 79 | fn set_touch(&mut self, _side: &str, _gesture: &str, _action: &str, _dry_run: bool) -> Result<TouchChange> { |
| 80 | Err(Error::Unsupported( |
| 81 | "budctl cannot change the touch controls on these buds".to_owned(), |
| 82 | )) |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | /// The supported model a paired device is, if any. |
| 87 | pub fn identify(device: &PairedDevice) -> Option<&'static dyn Device> { |
| 88 | REGISTRY.iter().copied().find(|model| model.matches(device)) |
| 89 | } |