Skip to content

Commit

Permalink
Add partial support for viewing service data
Browse files Browse the repository at this point in the history
  • Loading branch information
ztroop committed Mar 14, 2024
1 parent ed4f504 commit c266d2c
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub async fn bluetooth_scan(
properties.rssi,
properties.manufacturer_data,
properties.services,
properties.service_data,
));

// Send a clone of the accumulated device information so far
Expand Down
3 changes: 3 additions & 0 deletions src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct DeviceInfo {
pub manufacturer_data: HashMap<u16, Vec<u8>>,
pub services: Vec<Uuid>,
pub detected_at: String,
pub service_data: HashMap<Uuid, Vec<u8>>,
}

impl DeviceInfo {
Expand All @@ -25,6 +26,7 @@ impl DeviceInfo {
rssi: Option<i16>,
manufacturer_data: HashMap<u16, Vec<u8>>,
services: Vec<Uuid>,
service_data: HashMap<Uuid, Vec<u8>>,
) -> Self {
DeviceInfo {
id,
Expand All @@ -35,6 +37,7 @@ impl DeviceInfo {
manufacturer_data,
services,
detected_at: chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
service_data
}
}
}
16 changes: 16 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::collections::HashMap;

use ratatui::layout::Rect;

use crate::company_codes::COMPANY_CODE;

/// Extracts the manufacturer data from a `HashMap<u16, Vec<u8>>` and returns a tuple with the company name and the manufacturer data as a string.
Expand All @@ -26,3 +28,17 @@ pub fn extract_manufacturer_data(manufacturer_data: &HashMap<u16, Vec<u8>>) -> (
None => ("n/a".to_string(), m),
}
}

/// Returns a `Rect` with the provided percentage of the parent `Rect` and centered.
pub fn centered_rect(percent_x: u16, percent_y: u16, size: Rect) -> Rect {
let popup_size = Rect {
width: size.width * percent_x / 100,
height: size.height * percent_y / 100,
..Rect::default()
};
Rect {
x: (size.width - popup_size.width) / 2,
y: (size.height - popup_size.height) / 2,
..popup_size
}
}
19 changes: 18 additions & 1 deletion src/viewer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crossterm::event::{self, Event, KeyCode};
use ratatui::backend::Backend;
use ratatui::widgets::TableState;
use ratatui::widgets::{Clear, TableState};
use ratatui::{
layout::{Constraint, Direction, Layout},
Terminal,
Expand All @@ -12,9 +12,11 @@ use std::time::Duration;
use tokio::sync::mpsc;

use crate::structs::DeviceInfo;
use crate::utils::centered_rect;
use crate::widgets::detail_table::detail_table;
use crate::widgets::device_table::device_table;
use crate::widgets::info_table::info_table;
use crate::widgets::inspect_overlay::inspect_overlay;

/// Displays the detected Bluetooth devices in a table and handles the user input.
/// The user can navigate the table, pause the scanning, and quit the application.
Expand All @@ -27,6 +29,7 @@ pub async fn viewer<B: Backend>(
let mut table_state = TableState::default();
table_state.select(Some(0));
let mut devices = Vec::<DeviceInfo>::new();
let mut inspect_view = false;

loop {
// Draw UI
Expand Down Expand Up @@ -55,6 +58,17 @@ pub async fn viewer<B: Backend>(
// Draw the info table
let info_table = info_table(pause_signal.load(Ordering::SeqCst));
f.render_widget(info_table, chunks[2]);

let selected = table_state.selected();
if inspect_view && selected.is_some() {
let device: &DeviceInfo = &devices[selected.unwrap()];
if !device.service_data.is_empty() {
let inspect_overlay = inspect_overlay(device);
let area = centered_rect(60, 60, f.size());
f.render_widget(Clear, area);
f.render_widget(inspect_overlay, area);
}
}
})?;

// Event handling
Expand All @@ -66,6 +80,9 @@ pub async fn viewer<B: Backend>(
let current_state = pause_signal.load(Ordering::SeqCst);
pause_signal.store(!current_state, Ordering::SeqCst);
}
KeyCode::Enter => {
inspect_view = !inspect_view;
}
KeyCode::Down => {
let next = match table_state.selected() {
Some(selected) => {
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/info_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub fn info_table(signal: bool) -> Table<'static> {
let info_rows = vec![Row::new(vec![
"[q → quit]",
"[up/down → navigate]",
"[enter → inspection]",
if signal {
"[s → start scanning]"
} else {
Expand All @@ -22,6 +23,7 @@ pub fn info_table(signal: bool) -> Table<'static> {
Constraint::Length(10),
Constraint::Length(20),
Constraint::Length(20),
Constraint::Length(20),
],
)
.column_spacing(1);
Expand Down
39 changes: 39 additions & 0 deletions src/widgets/inspect_overlay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use ratatui::{
layout::Constraint,
style::{Color, Modifier, Style},
widgets::{Block, Borders, Row, Table},
};

use crate::structs::DeviceInfo;

/// Provides an overlay with the selected device's service data.
pub fn inspect_overlay(selected_device: &DeviceInfo) -> Table<'static> {
// Iterate through the selected device's service_data to create rows
let rows: Vec<Row> = selected_device
.service_data
.iter()
.map(|(uuid, data)| {
let data_str = data
.iter()
.map(|byte| format!("{:02x}", byte))
.collect::<Vec<String>>()
.join(" ");
// Create a row for each UUID and its corresponding data
Row::new(vec![uuid.to_string(), data_str])
})
.collect();

let table = Table::new(
rows,
[Constraint::Percentage(50), Constraint::Percentage(50)],
)
.header(Row::new(vec!["UUID", "Data"]).style(Style::default().fg(Color::Yellow)))
.block(
Block::default()
.borders(Borders::ALL)
.title("Data Overview"),
)
.highlight_style(Style::default().add_modifier(Modifier::BOLD));

table
}
1 change: 1 addition & 0 deletions src/widgets/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod detail_table;
pub mod device_table;
pub mod info_table;
pub mod inspect_overlay;

0 comments on commit c266d2c

Please sign in to comment.