From a0df4e1810c645f0b80dd628d1014c7c22ceb758 Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Fri, 23 May 2025 21:09:36 +0200 Subject: [PATCH] initial commit This commit contains a functionning data collector for bluetooth devices. --- .envrc | 1 + .gitignore | 1 + Cargo.lock | 174 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 14 ++++ flake.lock | 82 ++++++++++++++++++++ flake.nix | 50 ++++++++++++ rustup-toolchain.toml | 4 + src/bluetooth.rs | 48 ++++++++++++ src/main.rs | 18 +++++ 9 files changed, 392 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 rustup-toolchain.toml create mode 100644 src/bluetooth.rs create mode 100644 src/main.rs diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..8392d15 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5a1f9c9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,174 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "cc" +version = "1.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +dependencies = [ + "shlex", +] + +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pumo-system-info" +version = "0.1.0" +dependencies = [ + "anyhow", + "dbus", + "serde", + "serde_json", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3f3573a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "pumo-system-info" +version = "0.1.0" +edition = "2024" +authors = ["Lucien Cartier-Tilet "] +license = "GPL-3.0-or-later" +description = "Gather system information in JSON format" +publish = false + +[dependencies] +anyhow = "1.0.98" +dbus = { version = "0.9.7", features = ["vendored"] } +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..be5ef2d --- /dev/null +++ b/flake.lock @@ -0,0 +1,82 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1747744144, + "narHash": "sha256-W7lqHp0qZiENCDwUZ5EX/lNhxjMdNapFnbErcbnP11Q=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "2795c506fe8fb7b03c36ccb51f75b6df0ab2553f", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1747967795, + "narHash": "sha256-76s4jDRbQzxRO+5y8ilMp5V30qVgY9R6n8U7aOap8ig=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "f1d5bfa8c692cacd798a3e1fb93d54c1b9ac701a", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..60b8c34 --- /dev/null +++ b/flake.nix @@ -0,0 +1,50 @@ +{ + description = "System information in JSON format for eww"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-utils = { + url = "github:numtide/flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, flake-utils, rust-overlay }: + flake-utils.lib.eachSystem ["x86_64-linux"] (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { inherit system overlays; }; + rustVersion = (pkgs.rust-bin.fromRustupToolchainFile ./rustup-toolchain.toml); + rustPlatform = pkgs.makeRustPlatform { + cargo = rustVersion; + rustc = rustVersion; + }; + appName = "pumo-system-info"; + appRustBuild = rustPlatform.buildRustPackage { + pname = appName; + version = "0.1.0"; + src = ./.; + cargoLock.lockFile = ./Cargo.lock; + doCheck = true; + }; + in { + packages.rustPackage = appRustBuild; + defaultPackage = appRustBuild; + devShell = with pkgs; mkShell { + buildInputs = [ + bacon + cargo + cargo-deny + cargo-msrv + cargo-tarpaulin + just + rustVersion + ]; + }; + } + ); +} diff --git a/rustup-toolchain.toml b/rustup-toolchain.toml new file mode 100644 index 0000000..37e258d --- /dev/null +++ b/rustup-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.87.0" +components = [ "rustfmt", "rust-src", "clippy", "rust-analyzer" ] +profile = "default" \ No newline at end of file diff --git a/src/bluetooth.rs b/src/bluetooth.rs new file mode 100644 index 0000000..da582ce --- /dev/null +++ b/src/bluetooth.rs @@ -0,0 +1,48 @@ +use anyhow::Result; +use dbus::arg; +use std::collections::HashMap; +use std::time::Duration; + +#[derive(Default, serde::Serialize)] +pub struct BluetoothInfo { + pub name: String, + pub address: String, + pub alias: String, + pub connected: bool, + pub icon: String, + pub battery: Option, +} + +impl BluetoothInfo { + fn new(from: HashMap) -> Option { + let device = from.get("org.bluez.Device1")?; + let mut result = Self { + name: device.get("Name")?.0.as_str()?.to_owned(), + address: device.get("Address")?.0.as_str()?.to_owned(), + alias: device.get("Alias")?.0.as_str()?.to_owned(), + connected: matches!(&device.get("Connected")?.0.as_u64(), Some(1)), + icon: device.get("Icon")?.0.as_str()?.to_owned(), + battery: None + }; + if let Some(battery) = from.get("org.bluez.Battery1") { + result.battery = Some(battery.get("Percentage")?.0.as_u64()?); + } + Some(result) + } +} + +pub fn get_all_bluetooth_devices(connection: dbus::blocking::Connection) -> Result> { + let proxy = connection.with_proxy("org.bluez", "/", Duration::from_millis(5000)); + let (result,): (HashMap, HashMap>,) = proxy + .method_call( + "org.freedesktop.DBus.ObjectManager", + "GetManagedObjects", + (), + )?; + let result: Vec = result + .into_iter() + .filter(|(_, value)| value.contains_key("org.bluez.Device1")) + .flat_map(|(_, value)| BluetoothInfo::new(value)) + .collect(); + Ok(result) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..53c20e1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,18 @@ +use anyhow::Result; +use bluetooth::BluetoothInfo; +use dbus::blocking::Connection; + +mod bluetooth; + +#[derive(serde::Serialize)] +struct SystemInfo { + bluetooth: Vec, +} + +fn main() -> Result<()> { + let connection = Connection::new_system()?; + let bluetooth = bluetooth::get_all_bluetooth_devices(connection)?; + let info = SystemInfo { bluetooth }; + println!("{}", serde_json::to_string(&info)?); + Ok(()) +}