feat: support Tmobile TMOHS1 hotspot

Add support for the Tmobile TMOHS1, a Wingtech CT2MHS01-based hotspot
with a Qualcomm mdm9607. The TMOHS1 has no screen, only 5 LEDs, two of
which are RGB.
This commit is contained in:
oopsbagel
2025-06-12 20:51:59 -07:00
committed by Cooper Quintin
parent f23cc07652
commit b7636386fc
7 changed files with 307 additions and 0 deletions

View File

@@ -1,5 +1,11 @@
#[cfg(any(feature = "orbic", feature = "tplink", feature = "wingtech"))]
mod generic_framebuffer;
#[cfg(feature = "tmobile")]
mod tmobile;
#[cfg(feature = "tmobile")]
pub use tmobile::update_ui;
#[cfg(feature = "tplink")]
mod tplink;
#[cfg(feature = "tplink")]
@@ -20,6 +26,7 @@ mod wingtech;
#[cfg(feature = "wingtech")]
pub use wingtech::update_ui;
#[derive(Clone, Copy, PartialEq)]
pub enum DisplayState {
Recording,
Paused,

View File

@@ -0,0 +1,85 @@
/// Display module for Tmobile TMOHS1, blink LEDs on the front of the device.
/// DisplayState::Recording => Signal LED slowly blinks blue.
/// DisplayState::Paused => WiFi LED blinks white.
/// DisplayState::WarningDetected => Signal LED slowly blinks red.
use log::{error, info};
use tokio::sync::mpsc;
use tokio::sync::oneshot;
use tokio_util::task::TaskTracker;
use std::fs::write;
use std::thread::sleep;
use std::time::Duration;
use crate::config;
use crate::display::DisplayState;
macro_rules! led {
($l:expr) => {{
format!("/sys/class/leds/led:{}/blink", $l)
}};
}
fn start_blinking(path: String) {
write(&path, "1").ok();
}
fn stop_blinking(path: String) {
write(&path, "0").ok();
}
pub fn update_ui(
task_tracker: &TaskTracker,
config: &config::Config,
mut ui_shutdown_rx: oneshot::Receiver<()>,
mut ui_update_rx: mpsc::Receiver<DisplayState>,
) {
let mut invisible: bool = false;
if config.ui_level == 0 {
info!("Invisible mode, not spawning UI.");
invisible = true;
}
task_tracker.spawn_blocking(move || {
let mut state = DisplayState::Recording;
let mut last_state = DisplayState::Paused;
loop {
match ui_shutdown_rx.try_recv() {
Ok(_) => {
info!("received UI shutdown");
break;
}
Err(oneshot::error::TryRecvError::Empty) => {}
Err(e) => panic!("error receiving shutdown message: {e}"),
}
match ui_update_rx.try_recv() {
Ok(new_state) => state = new_state,
Err(mpsc::error::TryRecvError::Empty) => {}
Err(e) => error!("error receiving ui update message: {e}"),
};
if invisible || state == last_state {
sleep(Duration::from_secs(1));
continue;
}
match state {
DisplayState::Paused => {
stop_blinking(led!("signal_blue"));
stop_blinking(led!("signal_red"));
start_blinking(led!("wlan_white"));
}
DisplayState::Recording => {
stop_blinking(led!("wlan_white"));
stop_blinking(led!("signal_red"));
start_blinking(led!("signal_blue"));
}
DisplayState::WarningDetected => {
stop_blinking(led!("wlan_white"));
stop_blinking(led!("signal_blue"));
start_blinking(led!("signal_red"));
}
}
last_state = state;
sleep(Duration::from_secs(1));
}
});
}