diff --git a/Cargo.toml b/Cargo.toml index 64a44a6..10576d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nut-client" -version = "0.0.3" +version = "0.0.4" authors = ["Aram Peres "] edition = "2018" description = "Network UPS Tools (NUT) client library" diff --git a/README.md b/README.md index 93912fd..b964161 100644 --- a/README.md +++ b/README.md @@ -49,15 +49,16 @@ fn main() -> nut_client::Result<()> { let mut conn = Connection::new(config)?; - // Print a list of all UPS devices and their variables + // Print a list of all UPS devices println!("Connected UPS devices:"); for (name, description) in conn.list_ups()? { println!("\t- Name: {}", name); println!("\t Description: {}", description); - println!("\t Variables:"); - for (var_name, var_val) in conn.list_vars(&name)? { - println!("\t\t- {} = {}", var_name, var_val); + // List UPS variables (key = val) + println!("\t Variables:"); + for var in conn.list_vars(&name)? { + println!("\t\t- {}", var); } } diff --git a/examples/blocking.rs b/examples/blocking.rs index 1b518d8..4256f71 100644 --- a/examples/blocking.rs +++ b/examples/blocking.rs @@ -23,15 +23,16 @@ fn main() -> nut_client::Result<()> { let mut conn = Connection::new(config)?; - // Print a list of all UPS devices and their variables + // Print a list of all UPS devices println!("Connected UPS devices:"); for (name, description) in conn.list_ups()? { println!("\t- Name: {}", name); println!("\t Description: {}", description); - println!("\t Variables:"); - for (var_name, var_val) in conn.list_vars(&name)? { - println!("\t\t- {} = {}", var_name, var_val); + // List UPS variables (key = val) + println!("\t Variables:"); + for var in conn.list_vars(&name)? { + println!("\t\t- {}", var); } } diff --git a/src/blocking/mod.rs b/src/blocking/mod.rs index 46dc01f..7a47e47 100644 --- a/src/blocking/mod.rs +++ b/src/blocking/mod.rs @@ -3,7 +3,7 @@ use std::io::{BufRead, BufReader, Write}; use std::net::{SocketAddr, TcpStream}; use crate::cmd::{Command, Response}; -use crate::{ClientError, Config, Host, NutError}; +use crate::{ClientError, Config, Host, NutError, Variable}; /// A blocking NUT client connection. pub enum Connection { @@ -29,9 +29,13 @@ impl Connection { } /// Queries the list of variables for a UPS device. - pub fn list_vars(&mut self, ups_name: &str) -> crate::Result> { + pub fn list_vars(&mut self, ups_name: &str) -> crate::Result> { match self { - Self::Tcp(conn) => conn.list_vars(ups_name), + Self::Tcp(conn) => Ok(conn + .list_vars(ups_name)? + .into_iter() + .map(|(key, val)| Variable::parse(key.as_str(), val)) + .collect()), } } } diff --git a/src/lib.rs b/src/lib.rs index 482462a..4fc0220 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub use config::*; pub use error::*; +pub use var::*; /// Blocking client implementation for NUT. pub mod blocking; @@ -14,3 +15,4 @@ pub mod blocking; mod cmd; mod config; mod error; +mod var; diff --git a/src/var.rs b/src/var.rs new file mode 100644 index 0000000..c50a773 --- /dev/null +++ b/src/var.rs @@ -0,0 +1,149 @@ +use core::fmt; +use std::time::Duration; + +/// Well-known variable keys for NUT UPS devices. +/// +/// List retrieved from: https://networkupstools.org/docs/user-manual.chunked/apcs01.html +pub mod key { + /// Device model. + pub const DEVICE_MODEL: &str = "device.model"; + /// Device manufacturer. + pub const DEVICE_MANUFACTURER: &str = "device.mfr"; + /// Device serial number. + pub const DEVICE_SERIAL: &str = "device.serial"; + /// Device type. + pub const DEVICE_TYPE: &str = "device.type"; + /// Device description. + pub const DEVICE_DESCRIPTION: &str = "device.description"; + /// Device administrator name. + pub const DEVICE_CONTACT: &str = "device.contact"; + /// Device physical location. + pub const DEVICE_LOCATION: &str = "device.location"; + /// Device part number. + pub const DEVICE_PART: &str = "device.part"; + /// Device MAC address. + pub const DEVICE_MAC_ADDRESS: &str = "device.macaddr"; + /// Device uptime. + pub const DEVICE_UPTIME: &str = "device.uptime"; +} + +/// Well-known variables for NUT UPS devices. +/// +/// List retrieved from: https://networkupstools.org/docs/user-manual.chunked/apcs01.html +#[derive(Debug, Clone)] +pub enum Variable { + /// Device model. + DeviceModel(String), + /// Device manufacturer. + DeviceManufacturer(String), + /// Device serial number. + DeviceSerial(String), + /// Device type. + DeviceType(DeviceType), + /// Device description. + DeviceDescription(String), + /// Device administrator name. + DeviceContact(String), + /// Device physical location. + DeviceLocation(String), + /// Device part number. + DevicePart(String), + /// Device MAC address. + DeviceMacAddress(String), + /// Device uptime. + DeviceUptime(Duration), + + /// Any other variable. Value is a tuple of (key, value). + Other((String, String)), +} + +impl Variable { + /// Parses a variable from its key and value. + pub fn parse(name: &str, value: String) -> Variable { + use self::key::*; + + match name { + DEVICE_MODEL => Self::DeviceModel(value), + DEVICE_MANUFACTURER => Self::DeviceManufacturer(value), + DEVICE_SERIAL => Self::DeviceSerial(value), + DEVICE_TYPE => Self::DeviceType(DeviceType::from(value)), + DEVICE_DESCRIPTION => Self::DeviceDescription(value), + DEVICE_CONTACT => Self::DeviceContact(value), + DEVICE_LOCATION => Self::DeviceLocation(value), + DEVICE_PART => Self::DevicePart(value), + DEVICE_MAC_ADDRESS => Self::DeviceMacAddress(value), + DEVICE_UPTIME => Self::DeviceUptime(Duration::from_secs( + value.parse().expect("invalid uptime value"), + )), + + _ => Self::Other((name.into(), value)), + } + } +} + +impl fmt::Display for Variable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use self::key::*; + + match self { + Self::DeviceModel(value) => write!(f, "{} = {}", DEVICE_MODEL, value), + Self::DeviceManufacturer(value) => write!(f, "{} = {}", DEVICE_MANUFACTURER, value), + Self::DeviceSerial(value) => write!(f, "{} = {}", DEVICE_SERIAL, value), + Self::DeviceType(value) => write!(f, "{} = {}", DEVICE_TYPE, value), + Self::DeviceDescription(value) => write!(f, "{} = {}", DEVICE_DESCRIPTION, value), + Self::DeviceContact(value) => write!(f, "{} = {}", DEVICE_CONTACT, value), + Self::DeviceLocation(value) => write!(f, "{} = {}", DEVICE_LOCATION, value), + Self::DevicePart(value) => write!(f, "{} = {}", DEVICE_PART, value), + Self::DeviceMacAddress(value) => write!(f, "{} = {}", DEVICE_MAC_ADDRESS, value), + Self::DeviceUptime(value) => { + write!(f, "{} = {} seconds", DEVICE_UPTIME, value.as_secs()) + } + + Self::Other((key, value)) => write!(f, "other({}) = {}", key, value), + } + } +} + +/// NUT device type. +#[derive(Debug, Clone)] +pub enum DeviceType { + /// UPS (Uninterruptible Power Supply) + Ups, + /// PDU (Power Distribution Unit) + Pdu, + /// SCD (Solar Controller Device) + Scd, + /// PSU (Power Supply Unit) + Psu, + /// ATS (Automatic Transfer Switch) + Ats, + /// Other device type. + Other(String), +} + +impl DeviceType { + /// Convert from string. + pub fn from(v: String) -> DeviceType { + match v.as_str() { + "ups" => Self::Ups, + "pdu" => Self::Pdu, + "scd" => Self::Scd, + "psu" => Self::Psu, + "ats" => Self::Ats, + _ => Self::Other(v), + } + } +} + +impl fmt::Display for DeviceType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Ups => write!(f, "ups"), + Self::Pdu => write!(f, "pdu"), + Self::Scd => write!(f, "scd"), + Self::Psu => write!(f, "psu"), + Self::Ats => write!(f, "ats"), + Self::Other(val) => write!(f, "other({})", val), + } + } +}