diff --git a/Cargo.toml b/Cargo.toml
index 9a0d36061cd968a5a691b676b8e944ec9ce74d7c..e46aeff60880fe22944adbaef1c353c6cbe0e36e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,7 +10,7 @@ fern = "0.7.1"
 log = "0.4.25"
 meshtastic = "0.1.6"
 tokio = "1.42.0"
-rusqlite = { version = "0.32.1", features = ["bundled"] }
+rusqlite = { version = "0.32.1", features = ["bundled", "backup"] }
 rusqlite_migration = "1.3.1"
 lazy_static = "1.5.0"
 figment =  {version = "0.10.19", features = ["toml", "env"]}
diff --git a/src/config.rs b/src/config.rs
index 7deb82fc96bf3a525d48f5d0ee5e275c7abf989c..4ff19cb8d17b1cafc1fd47cee7c1d7fb74a4bf44 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -53,6 +53,21 @@ pub struct GuiConfig {
     pub units: String
 }
 
+impl GuiConfig {
+    pub fn get_unit_shorthand(&self) -> Option<&'static str> {
+        match self.units.to_lowercase().as_str() {
+            "centimeters" => Some("cm"),
+            "meters" => Some("m"),
+            "kilometers" => Some("km"),
+            "inches" => Some("inch"),
+            "feet" => Some("ft"),
+            "yards" => Some("yd"),
+            "miles" => Some("mi"),
+            _ => None,
+        }
+    }
+}
+
 // Defaults
 
 impl Default for ServerConfig {
diff --git a/src/db.rs b/src/db.rs
index 35fab6ac9f21dcfa9ff55884993c6be87be0ee8e..fdf80b4982c196da514b116f8582e5e46b71f464 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -12,6 +12,10 @@ use meshtastic::protobufs::{Channel, DeviceMetrics, MeshPacket, MyNodeInfo, Node
 
 use crate::CONFIG;
 
+// General TODO:
+// Spin this off into a thread? Have some form of caching? reading from the DB every single frame seems a little... retarded.
+// Do we have separate dbs for different types of MeshPackets? (table for each type? each node? )
+
 pub(crate) struct Database {
     conn: Connection,
 }
@@ -58,85 +62,99 @@ impl Database {
         // Open it up, and run migrations!
         let mut conn = Connection::open(&db_path)?;
         // Apply migrations
-        MIGRATIONS.to_latest(&mut conn).expect("Migration failed");
+        if let Err(e) = MIGRATIONS.to_latest(&mut conn) {
+            error!("DB Migration failed: {}", e);
+        }
         Ok(Database { conn })
     }
 
     /// Updates the database with a new MyNodeInfo packet
     pub fn update_mynodeinfo(my_node_info: &MyNodeInfo) -> Result<bool> {
         // Open DB
-        let db = Database::open().expect("Failure to open database");
-
-        db.conn.execute(
-            "INSERT INTO MyNodeInfo (my_node_num, reboot_count, min_app_version) VALUES (?1, ?2, ?3)",
-            rusqlite::params![my_node_info.my_node_num, my_node_info.reboot_count, my_node_info.min_app_version],
-        )?;
-        debug!("Saved MyNodeInfo to database");
-        Ok(true)
+        match Database::open() {
+            Ok(db) => {
+                db.conn.execute(
+                    "INSERT INTO MyNodeInfo (my_node_num, reboot_count, min_app_version) VALUES (?1, ?2, ?3)",
+                    rusqlite::params![my_node_info.my_node_num, my_node_info.reboot_count, my_node_info.min_app_version],
+                )?;
+                debug!("Saved MyNodeInfo to database");
+                Ok(true)
+            },
+            Err(e) => {
+                error!("Failed to open database: {}", e);
+                Ok(false)
+            },
+        }
     }
 
     /// Updates the database with a new NodeInfo packet
     pub fn update_nodeinfo(node_info: &NodeInfo) -> Result<bool> {
         // Open DB
-        let db = Database::open().expect("Failure to open database");
-
-        db.conn.execute(
-            "INSERT INTO Node (
-                num, user_id, user_lname, user_sname, hw_model, is_licensed, role, 
-                latitude_i, longitude_i, altitude, location_source, gps_timestamp, 
-                altitude_source, pdop, hdop, vdop, gps_accuracy, ground_speed, 
-                ground_track, fix_quality, fix_type, sats_in_view, sensor_id, 
-                next_update, seq_number, precision_bits, snr, last_heard, 
-                battery_level, voltage, channel_utilization, air_util_tx, 
-                channel, via_mqtt, hops_away
-            ) VALUES (
-                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, 
-                ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, 
-                ?27, ?28, ?29, ?30, ?31, ?32, ?33, ?34, ?35
-            )",
-            rusqlite::params![
-                node_info.num,
-                // This is gibberish to me, so lets explain.
-                // We're mapping the value user.id as the value, but chucking a NULL in if it's a None type.
-                node_info.user.as_ref().map(|user| user.id.clone()),
-                node_info.user.as_ref().map(|user| user.long_name.clone()),
-                node_info.user.as_ref().map(|user| user.short_name.clone()),
-                node_info.user.as_ref().map(|user| user.hw_model),
-                node_info.user.as_ref().map(|user| user.is_licensed),
-                node_info.user.as_ref().map(|user| user.role),
-                node_info.position.as_ref().map(|position| position.latitude_i),
-                node_info.position.as_ref().map(|position| position.longitude_i),
-                node_info.position.as_ref().map(|position| position.altitude),
-                node_info.position.as_ref().map(|position| position.location_source),
-                node_info.position.as_ref().map(|position| position.timestamp),
-                node_info.position.as_ref().map(|position| position.altitude_source),
-                node_info.position.as_ref().map(|position| position.pdop),
-                node_info.position.as_ref().map(|position| position.hdop),
-                node_info.position.as_ref().map(|position| position.vdop),
-                node_info.position.as_ref().map(|position| position.gps_accuracy),
-                node_info.position.as_ref().map(|position| position.ground_speed),
-                node_info.position.as_ref().map(|position| position.ground_track),
-                node_info.position.as_ref().map(|position| position.fix_quality),
-                node_info.position.as_ref().map(|position| position.fix_type),
-                node_info.position.as_ref().map(|position| position.sats_in_view),
-                node_info.position.as_ref().map(|position| position.sensor_id),
-                node_info.position.as_ref().map(|position| position.next_update),
-                node_info.position.as_ref().map(|position| position.seq_number),
-                node_info.position.as_ref().map(|position| position.precision_bits),
-                node_info.snr,
-                node_info.last_heard,
-                node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.battery_level),
-                node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.voltage),
-                node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.channel_utilization),
-                node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.air_util_tx),
-                node_info.channel,
-                node_info.via_mqtt,
-                node_info.hops_away
-            ]
-        )?;
-
-        debug!("Saved NodeInfo to database");
-        Ok(true)
+        match Database::open() {
+            Ok(db) => {
+                db.conn.execute(
+                    "INSERT INTO Node (
+                        num, user_id, user_lname, user_sname, hw_model, is_licensed, role, 
+                        latitude_i, longitude_i, altitude, location_source, gps_timestamp, 
+                        altitude_source, pdop, hdop, vdop, gps_accuracy, ground_speed, 
+                        ground_track, fix_quality, fix_type, sats_in_view, sensor_id, 
+                        next_update, seq_number, precision_bits, snr, last_heard, 
+                        battery_level, voltage, channel_utilization, air_util_tx, 
+                        channel, via_mqtt, hops_away
+                    ) VALUES (
+                        ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, 
+                        ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, 
+                        ?27, ?28, ?29, ?30, ?31, ?32, ?33, ?34, ?35
+                    )",
+                    rusqlite::params![
+                        node_info.num,
+                        // This is gibberish to me, so lets explain.
+                        // We're mapping the value user.id as the value, but chucking a NULL in if it's a None type.
+                        node_info.user.as_ref().map(|user| user.id.clone()),
+                        node_info.user.as_ref().map(|user| user.long_name.clone()),
+                        node_info.user.as_ref().map(|user| user.short_name.clone()),
+                        node_info.user.as_ref().map(|user| user.hw_model),
+                        node_info.user.as_ref().map(|user| user.is_licensed),
+                        node_info.user.as_ref().map(|user| user.role),
+                        node_info.position.as_ref().map(|position| position.latitude_i),
+                        node_info.position.as_ref().map(|position| position.longitude_i),
+                        node_info.position.as_ref().map(|position| position.altitude),
+                        node_info.position.as_ref().map(|position| position.location_source),
+                        node_info.position.as_ref().map(|position| position.timestamp),
+                        node_info.position.as_ref().map(|position| position.altitude_source),
+                        node_info.position.as_ref().map(|position| position.pdop),
+                        node_info.position.as_ref().map(|position| position.hdop),
+                        node_info.position.as_ref().map(|position| position.vdop),
+                        node_info.position.as_ref().map(|position| position.gps_accuracy),
+                        node_info.position.as_ref().map(|position| position.ground_speed),
+                        node_info.position.as_ref().map(|position| position.ground_track),
+                        node_info.position.as_ref().map(|position| position.fix_quality),
+                        node_info.position.as_ref().map(|position| position.fix_type),
+                        node_info.position.as_ref().map(|position| position.sats_in_view),
+                        node_info.position.as_ref().map(|position| position.sensor_id),
+                        node_info.position.as_ref().map(|position| position.next_update),
+                        node_info.position.as_ref().map(|position| position.seq_number),
+                        node_info.position.as_ref().map(|position| position.precision_bits),
+                        node_info.snr,
+                        node_info.last_heard,
+                        node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.battery_level),
+                        node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.voltage),
+                        node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.channel_utilization),
+                        node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.air_util_tx),
+                        node_info.channel,
+                        node_info.via_mqtt,
+                        node_info.hops_away
+                    ]
+                )?;
+        
+                debug!("Saved NodeInfo to database");
+                Ok(true)
+            },
+            Err(e) => {
+                error!("Failed to open database: {}", e);
+                Ok(false)
+            },
+        }
     }
 
     /// Updates the database with a new Channel packet
diff --git a/src/gui/widgets/mod.rs b/src/gui/widgets/mod.rs
index f048c5894a291642f4bdbc0d15ca323caa1215eb..6f593f5c2c39faee394ffbda19d2fc177a7894fb 100644
--- a/src/gui/widgets/mod.rs
+++ b/src/gui/widgets/mod.rs
@@ -40,7 +40,9 @@ pub fn render_widget(widget_type: &WidgetType, ui: &mut egui::Ui) {
         WidgetType::NodeList => {
             node_list::nodelist_ui(ui);
         },
-        WidgetType::NodeInfo => todo!(),
+        WidgetType::NodeInfo => {
+            node_info::nodeinfo_ui(ui);
+        },
         WidgetType::Log => {
             logging::log_ui(ui);
         },
diff --git a/src/gui/widgets/node_info.rs b/src/gui/widgets/node_info.rs
index b8d10d3e2248f47313a3481d6bf33e83e5b91d20..f9da0f3d8d4bca115e8b7e7e948bb0ccc39288ca 100644
--- a/src/gui/widgets/node_info.rs
+++ b/src/gui/widgets/node_info.rs
@@ -1,10 +1,13 @@
 use chrono::DateTime;
 use chrono_humanize::HumanTime;
+use egui::Label;
 use egui_extras::{Column, TableBuilder};
+use log::info;
 use longitude::{DistanceUnit, Location};
-use meshtastic::protobufs::{channel::Role, HardwareModel, Position};
+use meshtastic::{protobufs::{channel::Role, HardwareModel, Position}, types::NodeId};
+use meshtastic::protobufs::{Channel, DeviceMetrics, MeshPacket, MyNodeInfo, NodeInfo, User};
 
-use crate::{db::Database, CONFIG};
+use crate::{db::Database, gui::widgets::node_list::calc_distance_to_node, nodes, CONFIG};
 
 use super::node_list;
 
@@ -22,122 +25,84 @@ use super::node_list;
 /// UI Elements for the Node List
 pub fn nodeinfo_ui(ui: &mut egui::Ui) {
     // Get nodes from DB
-    let mut nodes = Database::read_node_list().expect("Failed to read nodes");
-    
-    // Sort nodes by `last_heard` in descending order
-    nodes.sort_by(|a, b| b.last_heard.cmp(&a.last_heard));
-    
-    TableBuilder::new(ui)
-    // TODO for the table:
-    // - Clickable Table Sorting
-    // - Add more info
-    // - Auto hide/show scrollbar on smaller devices
-    // - Add to a panel that we can move around / shitty windowing.
-        .column(Column::remainder())            // node id
-        .column(Column::remainder().clip(true)) // node name
-        .column(Column::remainder())            // snr
-        .column(Column::remainder())            // dist
-        .column(Column::remainder())            // hops
-        .column(Column::remainder().clip(true))            // hw model
-        .column(Column::remainder())            // role
-        .column(Column::remainder())            // battery
-        .column(Column::remainder().clip(true))            // last heard
-        .striped(true)
-        .header(15.0, |mut header| {
-            header.col(|ui| {
-                ui.heading("Node ID");
-            });
-            header.col(|ui| {
-                ui.heading("Name");
-            });
-            header.col(|ui| {
-                ui.heading("SNR");
-            });
-            header.col(|ui| {
-            
-                // Get distance units
-                let unit = match CONFIG.gui.units.as_ref() {
-                    "Centimeters" => "cm",
-                    "Meters" => "m",
-                    "Kilometers" => "km",
-                    "Inches" => "in",
-                    "Feet" => "ft",
-                    "Yards" => "yd",
-                    "Miles" => "mi",
-                    _ => "km", // Default to km if no match found.
-                };
+    let nodes: Vec<NodeInfo> = Database::read_node_list().expect("Failed to read nodes");
+
+    info!("Nodes: {:?}", nodes);
+
+    // Set the first node in the db to be selected
+    // This needs to be set elsewhere, but for testing... meh.
+    let mut selected_node = nodes.first().unwrap().num;
+
+    // Add a dropdown selector for changing the node
+    egui::ComboBox::from_label("Selected Node")
+        .selected_text(format!("{:?}", selected_node))
+        .show_ui(ui, |ui| {
+            for node in &nodes {
+                ui.selectable_value(&mut selected_node, node.num, format!("{:?}", node.user.clone().unwrap().id));
+            }
+        }
+    );
+
+    // Iterate through the list until we find the selected node.
+    let mut found_selected_node = false;
+    for node in &nodes {
+        if node.num == selected_node {
+            // Render Info when found
+            ui.label(format!("Node: {:?}", node));
+            ui.label(format!("User ID: {}", node.user.clone().unwrap().id));
+
+            // Compute First Heard
+            // How will we do this? the database doesn't store first heard. TODO!
+
+            // Compute Last Heard
+            let user = node.user.clone().unwrap();
+            let device = node.device_metrics.clone().unwrap();
+            // Convert the last heard to a time, then make it nice to read.
+            let now = chrono::Local::now().to_utc();
+            let humanised = if node.last_heard == 0 {
+                String::from("Unknown")
+            } else {
+                let last_heard = DateTime::from_timestamp(node.last_heard.into(), 0).unwrap();
+                let duration = last_heard - now;
+                HumanTime::from(duration).to_string()
+            };
+            ui.label(format!("Last Heard: {}", humanised));
+
+            // Find Messages to or from said node
+            // TODO: Requires parsing all MeshPackets in the DB
             
-                ui.heading(format!("Distance ({})", unit));
-            });
-            header.col(|ui| {
-                ui.heading("Hops Away");
-            });
-            header.col(|ui| {
-                ui.heading("HW Model");
-            });
-            header.col(|ui| {
-                ui.heading("Role");
-            });
-            header.col(|ui| {
-                ui.heading("Battery");
-            });
-            header.col(|ui| {
-                ui.heading("Last Heard");
-            });
-        })
-        .body(|mut body| {
-            for node in nodes {
-                let user = node.user.unwrap();
-                let device = node.device_metrics.unwrap();
-                // Convert the last heard to a time, then make it nice to read.
-                let now = chrono::Local::now().to_utc();
-                let humanised = if node.last_heard == 0 {
-                    String::from("Unknown")
+            // Find all Battery stats from said node
+            // TODO: Requires parsing all MeshPackets in the DB
+
+            // Find all temperature stats from said node
+            // TODO: Requires parsing all MeshPackets in the DB
+
+            // Find Location history from said node
+            // TODO: Requires parsing all MeshPackets in the DB
+            // Perhaps with the 3 above, we parse all packets to or from the node, and then filter depending on the above 4
+
+            // Find distance to node.
+            if let Some(distance) = calc_distance_to_node(node.position.clone().unwrap()) {
+                if distance.is_nan() {
+                    ui.label("-");
                 } else {
-                    let last_heard = DateTime::from_timestamp(node.last_heard.into(), 0).unwrap();
-                    let duration = last_heard - now;
-                    HumanTime::from(duration).to_string()
-                };
-            
-                body.row(15.0, |mut row| {
-                    row.col(|ui| {
-                        ui.label(user.id);
-                    });
-                    row.col(|ui| {
-                        ui.label(format!("{} ({})", user.long_name, user.short_name));
-                    });
-                    row.col(|ui| {
-                        ui.label(node.snr.to_string());
-                    });
-                    row.col(|ui| {
-                        if let Some(distance) = node_list::calc_distance_to_node(node.position.unwrap()) {
-                            if distance.is_nan() {
-                                ui.label("-");
-                            } else {
-                                ui.label(format!("{:.2}", distance));
-                            }
-                        } else {
-                            ui.label("-");
-                        }
-                    });
-                    row.col(|ui| {
-                        ui.label(node.hops_away.to_string());
-                    });
-                    row.col(|ui| {
-                        // Convert the i32 to a HardwareModel object, then convert to string
-                        ui.label(HardwareModel::from_i32(user.hw_model).unwrap_or_default().as_str_name());
-                    });
-                    row.col(|ui| {
-                        // Convert the i32 to a Role object, convert to string.
-                        ui.label(Role::from_i32(user.role).unwrap_or_default().as_str_name());
-                    });
-                    row.col(|ui| {
-                        ui.label(device.battery_level.to_string());
-                    });
-                    row.col(|ui| {
-                        ui.label(humanised);
-                    });
-                });
+                    // Get the unit shorthand from the GuiConfig struct
+                    let unit_shorthand = CONFIG.gui.get_unit_shorthand().unwrap_or("");
+                    ui.label(format!("Distance to Node: {:.2}{}", distance, unit_shorthand));
+                }
+            } else {
+                ui.label("-");
             }
-        });
+
+            // Find Direct (radio) Neighbours of said node
+            
+            found_selected_node = true;
+            break;  // Exit loop once the selected node is found
+        }
+    }
+
+    if !found_selected_node {
+        ui.label("Selected node not found.");
+    }
+
 }
\ No newline at end of file