~tsdh/public-inbox

swayr: Add wifi module v1 PROPOSED

Add a wifi module which can display wifi network name and signal
strength.

It gathers this information by running nmcli.

Karl Eklund (1):
  Add wifi module

 Cargo.lock                  |   2 +-
 README.md                   |   7 ++
 swayrbar/Cargo.toml         |   2 +-
 swayrbar/src/bar.rs         |   1 +
 swayrbar/src/module.rs      |   1 +
 swayrbar/src/module/wifi.rs | 141 ++++++++++++++++++++++++++++++++++++
 6 files changed, 152 insertions(+), 2 deletions(-)
 create mode 100644 swayrbar/src/module/wifi.rs

-- 
2.34.5
#867892 arch.yml success
swayr/patches/arch.yml: SUCCESS in 4m5s

[Add wifi module][0] from [~ke][1]

[0]: https://lists.sr.ht/~tsdh/public-inbox/patches/36332
[1]: mailto:localpart@gmail.com

✓ #867892 SUCCESS swayr/patches/arch.yml https://builds.sr.ht/~tsdh/job/867892
Tassilo Horn <tsdh@gnu.org> writes:
Next
Karl Eklund <localpart@gmail.com> writes:

Hi Karl,
Next
Karl Eklund <localpart@gmail.com> writes:

Hi Karl,
Next
Tassilo Horn <tsdh@gnu.org> writes:

Hey Karl,
I've asked on #srht on libera.chat.  There is or was some issue where
cloning a repo using the "Clone repo to your account" button resulted in
a broken repo with exactly the above problem.  When did you clone the
repo to your account?

Anyway, I suggest you simply rebase your commits on the wifi branch and
squash them into a single commit and then send that as a new patch to
this list again.

Bye,
Tassilo
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~tsdh/public-inbox/patches/36332/mbox | git am -3
Learn more about email & git

Re: [PATCH swayr 0/1] Add wifi module Export this patch

Tassilo Horn <tsdh@gnu.org> writes:

>
> Whatever you prefer.  When I should merge from you, it's best you send
> the output of
>
>   $ git request-pull -p \
>       origin/master \
>       https://my.repo.com/my-fork \
>       wifi
>
> where origin is the name of the swayr upstream remote and the URL is the
> one of your fork.

OK, here goes...

The following changes since commit 1a0eec73e339ba166d17effab0551b61c3ca0595:

  Release swayrbar-0.2.4 (2022-09-27 16:56:18 +0200)

are available in the Git repository at:

  https://git.sr.ht/~ke/swayr wifi

for you to fetch changes up to f5a81f25c3646e704794bf3e6c132c2c3c692ca2:

  Make nmcli module prettier when there is no wifi (2022-10-24 15:49:12 +0200)
----------------------------------------------------------------
Karl Eklund (5):
      Add wifi module
      Add "bars" placeholder to wifi module
      If there is no connection, clear the state
      Rename wifi module to nmcli
      Make nmcli module prettier when there is no wifi

 Cargo.lock                   |   2 +-
 README.md                    |  10 +++
 swayrbar/Cargo.toml          |   2 +-
 swayrbar/src/bar.rs          |   1 +
 swayrbar/src/module.rs       |   1 +
 swayrbar/src/module/nmcli.rs | 153 +++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 167 insertions(+), 2 deletions(-)
 create mode 100644 swayrbar/src/module/nmcli.rs

diff --git a/Cargo.lock b/Cargo.lock
index 4e2c013..d4ba99d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -704,7 +704,7 @@ dependencies = [

[[package]]
name = "swayrbar"
version = "0.2.4"
version = "0.2.4-nmcli"
dependencies = [
 "battery",
 "chrono",
diff --git a/README.md b/README.md
index a21fe09..e626ecd 100644
--- a/README.md
+++ b/README.md
@@ -800,6 +800,16 @@ By default, it has the following click bindings:
* `WheelUp` and `WheelDown` increase/decrease the volume of the default sink.


#### The `nmcli` module

The `nmcli` module requires NetworkManager and the `nmcli` command line tool.
It can display information about the wi-fi connection.  It supports the
following placeholders:
* `{name}` wi-fi network name.
* `{signal}` wireless signal strength.
* `{bars}` a visualization of connection strength, like "▂▄▆_".


#### The `date` module

The `date` module shows the date and time by defining the `format` using
diff --git a/swayrbar/Cargo.toml b/swayrbar/Cargo.toml
index 942f2da..798e028 100644
--- a/swayrbar/Cargo.toml
+++ b/swayrbar/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "swayrbar"
version = "0.2.4"
version = "0.2.4-nmcli"
edition = "2021"
homepage = "https://sr.ht/~tsdh/swayr/#swayrbar"
repository = "https://git.sr.ht/~tsdh/swayr"
diff --git a/swayrbar/src/bar.rs b/swayrbar/src/bar.rs
index ea51ce4..e68c6d0 100644
--- a/swayrbar/src/bar.rs
+++ b/swayrbar/src/bar.rs
@@ -101,6 +101,7 @@ fn create_modules(config: config::Config) -> Vec<Box<dyn BarModuleFn>> {
            "battery" => module::battery::BarModuleBattery::create(mc),
            "date" => module::date::BarModuleDate::create(mc),
            "pactl" => module::pactl::BarModulePactl::create(mc),
            "nmcli" => module::nmcli::BarModuleNmcli::create(mc),
            unknown => {
                log::warn!("Unknown module name '{}'.  Ignoring...", unknown);
                continue;
diff --git a/swayrbar/src/module.rs b/swayrbar/src/module.rs
index 152cc45..74427cb 100644
--- a/swayrbar/src/module.rs
+++ b/swayrbar/src/module.rs
@@ -22,6 +22,7 @@ pub mod battery;
pub mod date;
pub mod pactl;
pub mod sysinfo;
pub mod nmcli;
pub mod window;

#[derive(Debug, PartialEq, Eq)]
diff --git a/swayrbar/src/module/nmcli.rs b/swayrbar/src/module/nmcli.rs
new file mode 100644
index 0000000..7bf2f1f
--- /dev/null
+++ b/swayrbar/src/module/nmcli.rs
@@ -0,0 +1,153 @@
use crate::config;
use crate::module::BarModuleFn;
use crate::shared::fmt::subst_placeholders;
use std::sync::Mutex;
use swaybar_types as s;

use super::RefreshReason;

const NAME: &str = "nmcli";

struct State {
    cached_text: String,
    signal: Option<String>,
    name: Option<String>,
    bars: Option<String>,
}

pub struct BarModuleNmcli {
    config: config::ModuleConfig,
    state: Mutex<State>,
}

fn run_nmcli() -> Result<String, String> {
    let cmd = "nmcli";
    let args = "-c no -g IN-USE,SSID,SIGNAL,BARS dev wifi".split(" ");
    let output = std::process::Command::new(cmd)
        .args(args)
        .output()
        .map_err(|e| format!("Failed to run nmcli: {}", e))?;

    if !output.status.success() {
        return Err(format!(
            "nmcli failed with status code {}",
            output.status.code().unwrap_or(-1)
        ));
    }

    Ok(String::from_utf8(output.stdout).unwrap())
}

fn subst_placeholders(fmt: &str, html_escape: bool, state: &State) -> String {
    subst_placeholders!(fmt, html_escape, {
        "name" => {
            match &state.name {
                None => "No wi-fi",
                Some(name) => name,
            }
        },
        "signal" => {
            match &state.signal {
                None => "".to_owned(),
                Some(signal) => " ".to_owned() + signal + "%",
            }
        },
        "bars" => {
            match &state.bars {
                None => "".to_owned(),
                Some(bars) => " ".to_owned() + bars,
            }
        },
    })
}

fn refresh_state(state: &mut State, fmt_str: &str, html_escape: bool) {
    if let Ok(output) = run_nmcli() {
        state.name = None;
        state.signal = None;
        state.bars = None;
        if let Some(line) = output.lines().find(|line| line.starts_with("*")) {
            let mut parts = line.split(":");
            parts.next();
            state.name = Some(parts.next().unwrap().to_string());
            state.signal = Some(parts.next().unwrap().to_string());
            state.bars = Some(parts.next().unwrap().to_string());
        }
    }
    state.cached_text = subst_placeholders(fmt_str, html_escape, state);
}

impl BarModuleFn for BarModuleNmcli {
    fn create(config: config::ModuleConfig) -> Box<dyn BarModuleFn>
    where
        Self: Sized,
    {
        Box::new(BarModuleNmcli {
            config,
            state: Mutex::new(State {
                cached_text: String::new(),
                signal: None,
                name: None,
                bars: None,
            }),
        })
    }

    fn default_config(instance: String) -> config::ModuleConfig
    where
        Self: Sized,
    {
        config::ModuleConfig {
            name: NAME.to_owned(),
            instance,
            format: "📡 Wi-fi: {name}{bars}{signal}".to_owned(),
            html_escape: Some(false),
            on_click: None,
        }
    }

    fn get_config(&self) -> &config::ModuleConfig {
        &self.config
    }

    fn build(&self, nai: &Option<super::NameInstanceAndReason>) -> s::Block {
        let mut state = self.state.lock().expect("Could not lock state.");

        if self.should_refresh(nai, true, &[RefreshReason::ClickEvent]) {
            refresh_state(
                &mut state,
                &self.config.format,
                self.config.is_html_escape(),
            );
        }

        s::Block {
            name: Some(NAME.to_owned()),
            instance: Some(self.config.instance.clone()),
            full_text: state.cached_text.to_owned(),
            align: Some(s::Align::Left),
            markup: Some(s::Markup::Pango),
            short_text: None,
            color: None,
            background: None,
            border: None,
            border_top: None,
            border_bottom: None,
            border_left: None,
            border_right: None,
            min_width: None,
            urgent: None,
            separator: Some(true),
            separator_block_width: None,
        }
    }

    fn subst_args<'a>(&'a self, cmd: &'a [String]) -> Option<Vec<String>> {
        let state = self.state.lock().expect("Could not lock state.");
        Some(
            cmd.iter()
                .map(|arg| subst_placeholders(arg, false, &state))
                .collect(),
        )
    }
}
Karl Eklund <localpart@gmail.com> writes:

Hi Karl,

[PATCH swayr 1/1] Add wifi module Export this patch

From: Karl Eklund <localpart@gmail.com>

---
 Cargo.lock                  |   2 +-
 README.md                   |   7 ++
 swayrbar/Cargo.toml         |   2 +-
 swayrbar/src/bar.rs         |   1 +
 swayrbar/src/module.rs      |   1 +
 swayrbar/src/module/wifi.rs | 141 ++++++++++++++++++++++++++++++++++++
 6 files changed, 152 insertions(+), 2 deletions(-)
 create mode 100644 swayrbar/src/module/wifi.rs

diff --git a/Cargo.lock b/Cargo.lock
index 4e2c013..bd61f2c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -704,7 +704,7 @@ dependencies = [

[[package]]
name = "swayrbar"
version = "0.2.4"
version = "0.2.4-wifi"
dependencies = [
 "battery",
 "chrono",
diff --git a/README.md b/README.md
index a21fe09..e33e84e 100644
--- a/README.md
+++ b/README.md
@@ -800,6 +800,13 @@ By default, it has the following click bindings:
* `WheelUp` and `WheelDown` increase/decrease the volume of the default sink.


#### The `wifi` module

The `wifi` module displays information about the wi-fi connection. It supports the following placeholders:
* `{name}` Wi-fi network name.
* `{signal}` wireless signal strength.


#### The `date` module

The `date` module shows the date and time by defining the `format` using
diff --git a/swayrbar/Cargo.toml b/swayrbar/Cargo.toml
index 942f2da..da2febd 100644
--- a/swayrbar/Cargo.toml
+++ b/swayrbar/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "swayrbar"
version = "0.2.4"
version = "0.2.4-wifi"
edition = "2021"
homepage = "https://sr.ht/~tsdh/swayr/#swayrbar"
repository = "https://git.sr.ht/~tsdh/swayr"
diff --git a/swayrbar/src/bar.rs b/swayrbar/src/bar.rs
index ea51ce4..2fa78a5 100644
--- a/swayrbar/src/bar.rs
+++ b/swayrbar/src/bar.rs
@@ -101,6 +101,7 @@ fn create_modules(config: config::Config) -> Vec<Box<dyn BarModuleFn>> {
            "battery" => module::battery::BarModuleBattery::create(mc),
            "date" => module::date::BarModuleDate::create(mc),
            "pactl" => module::pactl::BarModulePactl::create(mc),
            "wifi" => module::wifi::BarModuleWifi::create(mc),
            unknown => {
                log::warn!("Unknown module name '{}'.  Ignoring...", unknown);
                continue;
diff --git a/swayrbar/src/module.rs b/swayrbar/src/module.rs
index 152cc45..268cbc0 100644
--- a/swayrbar/src/module.rs
+++ b/swayrbar/src/module.rs
@@ -22,6 +22,7 @@ pub mod battery;
pub mod date;
pub mod pactl;
pub mod sysinfo;
pub mod wifi;
pub mod window;

#[derive(Debug, PartialEq, Eq)]
diff --git a/swayrbar/src/module/wifi.rs b/swayrbar/src/module/wifi.rs
new file mode 100644
index 0000000..d77be88
--- /dev/null
+++ b/swayrbar/src/module/wifi.rs
@@ -0,0 +1,141 @@
use crate::config;
use crate::module::BarModuleFn;
use crate::shared::fmt::subst_placeholders;
use std::sync::Mutex;
use swaybar_types as s;

use super::RefreshReason;

const NAME: &str = "wifi";

struct State {
    cached_text: String,
    signal: Option<String>,
    name: Option<String>,
}

pub struct BarModuleWifi {
    config: config::ModuleConfig,
    state: Mutex<State>,
}

fn run_nmcli() -> Result<String, String> {
    let cmd = "nmcli";
    let args = "-c no -g IN-USE,SSID,SIGNAL,BARS dev wifi".split(" ");
    let output = std::process::Command::new(cmd)
        .args(args)
        .output()
        .map_err(|e| format!("Failed to run nmcli: {}", e))?;

    if !output.status.success() {
        return Err(format!(
            "nmcli failed with status code {}",
            output.status.code().unwrap_or(-1)
        ));
    }

    Ok(String::from_utf8(output.stdout).unwrap())
}

fn subst_placeholders(fmt: &str, html_escape: bool, state: &State) -> String {
    subst_placeholders!(fmt, html_escape, {
        "name" => {
            match &state.name {
                None => "No wi-fi",
                Some(name) => name,
            }
        },
        "signal" => {
            match &state.signal {
                None => "None",
                Some(signal) => signal,
            }
        },
    })
}

fn refresh_state(state: &mut State, fmt_str: &str, html_escape: bool) {
    if let Ok(output) = run_nmcli() {
        if let Some(line) = output.lines().find(|line| line.starts_with("*")) {
            let mut parts = line.split(":");
            parts.next();
            state.name = Some(parts.next().unwrap().to_string());
            state.signal = Some(parts.next().unwrap().to_string());
        }
    }
    state.cached_text = subst_placeholders(fmt_str, html_escape, state);
}

impl BarModuleFn for BarModuleWifi {
    fn create(config: config::ModuleConfig) -> Box<dyn BarModuleFn>
    where
        Self: Sized,
    {
        Box::new(BarModuleWifi {
            config,
            state: Mutex::new(State {
                cached_text: String::new(),
                signal: None,
                name: None,
            }),
        })
    }

    fn default_config(instance: String) -> config::ModuleConfig
    where
        Self: Sized,
    {
        config::ModuleConfig {
            name: NAME.to_owned(),
            instance,
            format: "📡 Wi-fi {name} {signal}%".to_owned(),
            html_escape: Some(false),
            on_click: None,
        }
    }

    fn get_config(&self) -> &config::ModuleConfig {
        &self.config
    }

    fn build(&self, nai: &Option<super::NameInstanceAndReason>) -> s::Block {
        let mut state = self.state.lock().expect("Could not lock state.");

        if self.should_refresh(nai, true, &[RefreshReason::ClickEvent]) {
            refresh_state(
                &mut state,
                &self.config.format,
                self.config.is_html_escape(),
            );
        }

        s::Block {
            name: Some(NAME.to_owned()),
            instance: Some(self.config.instance.clone()),
            full_text: state.cached_text.to_owned(),
            align: Some(s::Align::Left),
            markup: Some(s::Markup::Pango),
            short_text: None,
            color: None,
            background: None,
            border: None,
            border_top: None,
            border_bottom: None,
            border_left: None,
            border_right: None,
            min_width: None,
            urgent: None,
            separator: Some(true),
            separator_block_width: None,
        }
    }

    fn subst_args<'a>(&'a self, cmd: &'a [String]) -> Option<Vec<String>> {
        let state = self.state.lock().expect("Could not lock state.");
        Some(
            cmd.iter()
                .map(|arg| subst_placeholders(arg, false, &state))
                .collect(),
        )
    }
}
-- 
2.34.5
swayr/patches/arch.yml: SUCCESS in 4m5s

[Add wifi module][0] from [~ke][1]

[0]: https://lists.sr.ht/~tsdh/public-inbox/patches/36332
[1]: mailto:localpart@gmail.com

✓ #867892 SUCCESS swayr/patches/arch.yml https://builds.sr.ht/~tsdh/job/867892