Laptop battery level monitor for Linux
You could replace your get_str_from_file function with the standard read_to_string function:
For instance like so:
use std::fs;

if fs::read_to_string(STATUS).unwrap().trim() == CHARGING_STR) {
    return; //return should be equivalent to std::process::exit(0)
}

This is your implementation:
fn get_str_from_file(file_path: &amp;str) -&gt; String {
    let mut file = File::open(file_path)
        .expect(&quot;file not found&quot;);
    let mut ret = String::new();
    file.read_to_string(&amp;mut ret)
        .expect(&quot;failed to read file&quot;);

    ret
}

This is the implementation of std::fs::read_to_string
pub fn read_to_string&lt;P: AsRef&lt;Path&gt;&gt;(path: P) -&gt; io::Result&lt;String&gt; {
    let mut file = File::open(path)?;
    let mut string = String::with_capacity(initial_buffer_size(&amp;file));
    file.read_to_string(&amp;mut string)?;
    Ok(string)
}

Almost identical, except the std lib version is more generic and leaves error handling to the caller. It also uses more efficient memory allocation.
New Type Idiom
The following is not necessarily better, but it is certainly a valid alternative approach.
Instead of relying on using an f32 you could use a wrapper struct around it:
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)] //not necessary but nice to have
struct ChargeLevel(f32);

This is called New Type Idiom. You could further go ahead and put most of the logic surrounding the charge level into special functions in the ChargeLevel's impl block. Some suggestions:
impl ChargeLevel {
    const WARNING_LEVEL: f32 = 15.0;
    const CRITICAL_LEVEL: f32 = 5.0;

    fn new(now: f32, full: f32) -&gt; Self {
        ChargeLevel((now / full) * 100.0)
    }

    fn is_warning(&amp;self) -&gt; bool {
        self.0 &lt; Self::WARNING_LEVEL
    }

    fn is_critical(&amp;self) -&gt; bool {
        self.0 &lt; Self::CRITICAL_LEVEL
    }
}

You could also combine this struct with the external library stuff:
impl From&lt;ChargeLevel&gt; for NotificationUrgency {
    fn from(level: ChargeLevel) -&gt; Self {
        if level.is_critical() {
            NotificationUrgency::Critical
        } else {
            NotificationUrgency::Normal
        }
    }
}

This would allow you to replace
if charge_level &lt; CHARGE_WARN {
    Notification::new()
        .summary(&quot;Battery level low!&quot;)
        .body(&amp;format!(&quot;Battery level: {0:.1$}%&quot;, charge_level, 2))
        .icon(&quot;dialog-information&quot;)
        .urgency(
            if charge_level &lt; CHARGE_CRIT { 
                NotificationUrgency::Critical
            } else { 
                NotificationUrgency::Normal
            }
        )
        .timeout(NOTIFICATION_TIMEOUT)
        .show().unwrap();
}

with
if charge_level.is_warning() {
    Notification::new()
        .summary(&quot;Battery level low!&quot;)
        .body(&amp;format!(&quot;Battery level: {0:.1$}%&quot;, charge_level.0, 2))
        .icon(&quot;dialog-information&quot;)
        .urgency(NotificationUrgency::from(charge_level))
        .timeout(NOTIFICATION_TIMEOUT)
        .show()
        .unwrap();
}

A little much for such a small application perhaps, but might give you an example how to write nice abstractions in Rust that don't cost any runtime overhead.
Implementing std::fmt::Display would also make sense for the string formatting, but you get the point.