From 73a1490860db6fd43f8653cfbf7f3d45253d7015 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 ++++++++++++ result | 1 + rustup-toolchain.toml | 4 + src/bluetooth.rs | 48 ++++++++++++ src/main.rs | 18 +++++ 10 files changed, 393 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 120000 result 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/result b/result new file mode 120000 index 0000000..d63ecac --- /dev/null +++ b/result @@ -0,0 +1 @@ +/nix/store/63q4lpxwgnldx6f6d72kyjl1y4iz8c6l-pumo-system-info-0.1.0 \ No newline at end of file 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(()) +}