Bevy+Ratutui powered Monitoring of Pico-Strike devices

No authoritative, use A/AAAA records for ip, Home view keybinds

+41 -26
+30 -23
src/net.rs
··· 15 15 use sachy_mdns::{ 16 16 GROUP_SOCK_V4, MDNS_PORT, 17 17 client::query_service, 18 - dns::{records::Record, reqres::Response, traits::DnsParse}, 18 + dns::{ 19 + records::{QType, Record}, 20 + reqres::Response, 21 + traits::DnsParse, 22 + }, 19 23 }; 20 24 use socket2::{Domain, Protocol, Socket, Type}; 21 25 ··· 56 60 57 61 while signal_rx.recv().await.is_ok() { 58 62 let send_fut = async { 63 + // Retry three times in case packets get lost coz UDP things 59 64 for _ in 0..3 { 60 65 udp_socket.send_to(query, GROUP_SOCK_V4).await.ok(); 61 66 Timer::after(Duration::from_millis(250)).await; ··· 63 68 }; 64 69 65 70 let recv_fut = async { 66 - while let Ok((read, socket)) = udp_socket.recv_from(&mut buf).await { 71 + while let Ok((read, _)) = udp_socket.recv_from(&mut buf).await { 67 72 let input = &buf[..read]; 68 73 let Ok(resp) = Response::parse(&mut &*input, input) else { 69 74 continue; 70 75 }; 71 76 72 - if resp.flags.is_authoritative() 73 - && resp.answers.iter().any(|answer| { 74 - if let Record::PTR(_) = &answer.record { 75 - answer.name == "_picostrike._tcp.local" 76 - } else { 77 - false 78 - } 79 - }) 80 - && let Some(instance) = resp.additional.iter().find_map(|answer| { 81 - if let Record::SRV(srv) = &answer.record { 82 - Some(InstanceDetails { 83 - host: answer.name.to_string(), 84 - address: srv.target.to_string(), 85 - port: srv.port, 86 - ip: socket.ip(), 87 - }) 88 - } else { 89 - None 90 - } 91 - }) 77 + if resp.answers.iter().any(|answer| { 78 + answer.atype == QType::PTR && answer.name == "_picostrike._tcp.local" 79 + }) && let Some(ip) = resp.additional.iter().find_map(|answer| match &answer 80 + .record 92 81 { 93 - resp_tx.send(instance).await.ok(); 82 + Record::A(a) => Some(IpAddr::V4(a.address)), 83 + Record::AAAA(aaaa) => Some(IpAddr::V6(aaaa.address)), 84 + _ => None, 85 + }) && let Some((name, srv)) = resp.additional.iter().find_map(|answer| { 86 + if let Record::SRV(srv) = &answer.record { 87 + Some((answer.name, srv)) 88 + } else { 89 + None 90 + } 91 + }) { 92 + resp_tx 93 + .send(InstanceDetails { 94 + host: name.to_string(), 95 + address: srv.target.to_string(), 96 + port: srv.port, 97 + ip, 98 + }) 99 + .await 100 + .ok(); 94 101 } 95 102 } 96 103 };
+11 -3
src/views/home.rs
··· 96 96 q_devices: Query<(&Name, &DeviceSocket), With<Device>>, 97 97 ) -> Result { 98 98 context.draw(|frame| { 99 - let [top, bottom] = 100 - Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).areas(frame.area()); 99 + let [top, mid, bottom] = 100 + Layout::vertical([Constraint::Length(3), Constraint::Fill(1), Constraint::Length(3)]).areas(frame.area()); 101 101 102 102 let searching = if is_searching.searching.is_some() { 103 103 "Searching..." ··· 119 119 )) 120 120 }); 121 121 122 + let help = Paragraph::new("Keys: 'q'/ESC Quit, 's' Toggle Search, UP/DOWN Choose Device(s), ENTER/SPACE Connect to Device").block( 123 + Block::bordered() 124 + .padding(Padding::horizontal(2)) 125 + .border_style(Color::LightBlue), 126 + ); 127 + 122 128 let list = List::new(items) 123 129 .highlight_symbol(">> ") 124 130 .direction(ListDirection::TopToBottom) 125 131 .block( 126 132 Block::bordered() 127 133 .title("Devices") 134 + .title_alignment(HorizontalAlignment::Center) 128 135 .padding(Padding::new(2, 2, 1, 1)) 129 136 .border_style(Color::LightBlue), 130 137 ); 131 138 132 139 frame.render_widget(paragraph, top); 133 - frame.render_stateful_widget(list, bottom, &mut list_state.0); 140 + frame.render_stateful_widget(list, mid, &mut list_state.0); 141 + frame.render_widget(help, bottom); 134 142 })?; 135 143 136 144 Ok(())