Coder Social home page Coder Social logo

aramperes / nut-rs Goto Github PK

View Code? Open in Web Editor NEW
21.0 3.0 3.0 121 KB

rups: A Network UPS Tools (NUT) implementation in Rust.

Home Page: https://crates.io/crates/rups

License: MIT License

Rust 100.00%
nut ups network-ups-tools rust rust-library tokio async hacktoberfest

nut-rs's Introduction

rups

crates.io Documentation MIT licensed CI

A Network UPS Tools (NUT) client library for Rust.

  • Connect to upsd/nut-server using TCP
  • Login with username and password
  • List UPS devices
  • List variables for a UPS device
  • Connect securely with SSL (optional feature)
  • Supports blocking and async (Tokio)

Getting Started

You'll need a running instance of the NUT daemon (upsd, version >= 2.6.4) and a compatible UPS device to use this library:

  1. Install NUT
  2. Configure and launch upsd

Verify that your UPS is connected using the built-in upsc tool:

upsc myupsname@localhost ups.status

Example

The rupsc CLI is written using this library, and is a clone of NUT's built-in upsc tool.

Below is a sample program using this library (cargo run --example blocking).

You can also run the async version of this code using cargo run --example async --features async-rt (source: rups/examples/async.rs).

// rups/examples/blocking.rs

use std::env;

use rups::blocking::Connection;
use rups::{Auth, ConfigBuilder};
use std::convert::TryInto;

fn main() -> nut_client::Result<()> {
    let host = env::var("NUT_HOST").unwrap_or_else(|_| "localhost".into());
    let port = env::var("NUT_PORT")
        .ok()
        .and_then(|s| s.parse::<u16>().ok())
        .unwrap_or(3493);

    let username = env::var("NUT_USER").ok();
    let password = env::var("NUT_PASSWORD").ok();
    let auth = username.map(|username| Auth::new(username, password));

    let config = ConfigBuilder::new()
        .with_host((host, port).try_into().unwrap_or_default())
        .with_auth(auth)
        .with_debug(false) // Turn this on for debugging network chatter
        .build();

    let mut conn = Connection::new(&config)?;

    // 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);

        // List UPS variables (key = val)
        println!("\t  Variables:");
        for var in conn.list_vars(&name)? {
            println!("\t\t- {}", var);
        }
    }

    Ok(())
}

SSL

You can turn on SSL support by adding .with_ssl(true) in the ConfigBuilder. This requires the ssl feature, which uses rustls under the hood.

Note that, by default, .with_ssl(true) will enable strict verification. This means it will verify the server certificate's DNS entries, check for revocation, and verify the chain using the local root trust. You must also ensure that the connection hostname is a valid DNS name (e.g. localhost, not 127.0.0.1).

If the server is using a self-signed certificate, and you'd like to ignore the strict validation, you can add .with_insecure_ssl(true) along with .with_ssl(true).

Async (Tokio)

The rups library supports async network requests. This requires the async feature, which uses Tokio v1 under the hood.

For SSL support, you must use the async-ssl feature as well.

Pronunciation

r-oops

License

The crates in this repository are licensed as such:

  • rups: MIT License, see ./LICENSE
  • rupsc: GPL-3.0 or later, see ./rupsc/LICENSE

nut-rs's People

Contributors

aramperes avatar avranju avatar hubertpawlak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

nut-rs's Issues

Unit tests for the network stack

Add tests for the network implementations (e.g. nut_client::blocking::TcpConnection) by crafting packets like a NUT server would send.

Support `GET TYPE`

https://networkupstools.org/docs/developer-guide.chunked/ar01s09.html

Form:
	GET TYPE <upsname> <varname>
	GET TYPE su700 input.transfer.low

Response:
	TYPE <upsname> <varname> <type>...
	TYPE su700 input.transfer.low ENUM

<type> can be several values, and multiple words may be returned:

	RW: this variable may be set to another value with SET
	ENUM: an enumerated type, which supports a few specific values
	STRING:n: this is a string of maximum length n
	RANGE: this is an numeric, either integer or float, comprised in the range (see LIST RANGE)
	NUMBER: this is a simple numeric value, either integer or float

ENUM, STRING and RANGE are usually associated with RW, but not always. The default <type>, when omitted, is numeric, so either integer or float. Each driver is then responsible for handling values as either integer or float.

Note that float values are expressed using decimal (base 10) english-based representation, so using a dot, in non-scientific notation. So hexadecimal, exponents, and comma for thousands separator are forbidden. For example: "1200.20" is valid, while "1,200.20" and "1200,20" are invalid.

Async support

Since this library is entirely I/O bound, I think it makes sense to support async (tokio).

I'm not familiar with supporting both a blocking implementation and an async one. Should maybe take inspiration from reqwest.

Connection refused

Even though all the connection settings are correct and I can use upsc to query my UPS I get Connection refused when creating a new connection using the example code.

use dotenvy::dotenv;

use rups::blocking::Connection;
use rups::{Auth, ConfigBuilder};

use std::convert::TryInto;
use std::env;

fn main() -> rups::Result<()> {
    // load environment variables from .env file
    dotenv().expect(".env file not found");

    let host = env::var("NUT_HOST").unwrap_or_else(|_| "localhost".into());
    let port = env::var("NUT_PORT")
        .ok()
        .map(|s| s.parse::<u16>().ok())
        .flatten()
        .unwrap_or(3493);

    let username = env::var("NUT_USER").ok();
    let password = env::var("NUT_PASSWORD").ok();
    let auth = username.map(|username| Auth::new(username, password));

    let config = ConfigBuilder::new()
        .with_host((host, port).try_into().unwrap_or_default())
        .with_auth(auth)
        .with_debug(true) // Turn this on for debugging network chatter
        .build();

    let mut conn = Connection::new(&config)?;

    // 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);

        // List UPS variables (key = val)
        println!("\t  Variables:");
        for var in conn.list_vars(&name)? {
            println!("\t\t- {}", var);
        }
    }

    Ok(())
}

To be specific I receive the following error:

Error: Io(Os { code: 111, kind: ConnectionRefused, message: "Connection refused" })

Support `LIST CMD`

Form:
	LIST CMD <upsname>

Response:
	BEGIN LIST CMD <upsname>
	CMD <upsname> <cmdname>
	...
	END LIST CMD <cmdname>

Support `GET DESC`

https://networkupstools.org/docs/developer-guide.chunked/ar01s09.html

Form:
	GET DESC <upsname> <varname>
	GET DESC su700 ups.status

Response:
	DESC <upsname> <varname> "<description>"
	DESC su700 ups.status "UPS status"

	<description> is a string that gives a brief explanation of the named variable. upsd may return "Unavailable" if the file which provides this description is not installed.

Different versions of this file may be used in some situations to provide for localization and internationalization.

Write a clone of `upsd`

The idea is to make a drop-in replacement of upsd, with only the ups drivers having to be installed.

Note: license as GPL-3.0

Strict SSL verification

When implemented for #1, any sort of SSL certificate validation was turned off (cert chain, DNS name, OCSP). Proper verification should be enabled by default, and disabled using .with_insecure_ssl(true).

For rupsc, -S should turn on strict validation, and --insecure-ssl should keep unverified SSL on.

Support `LIST RANGE`

Form:
	LIST RANGE <upsname> <varname>

Response:
	BEGIN LIST RANGE <upsname> <varname>
	RANGE <upsname> <varname> "<min>" "<max>"
	...
	END LIST RANGE <upsname> <varname>

Bi-directional protocol and serverbound API

Right now the network implementation and API are entirely client-sided. In order to re-implement server-side components (#26) and implement unit testing (#2), the network stack should support crafting packets for both directions.

The public API needs to be reworked a bit to support this.

Support `LIST ENUM`

Form:
	LIST ENUM <upsname> <varname>
	LIST ENUM su700 input.transfer.low

Response:
	BEGIN LIST ENUM <upsname> <varname>
	ENUM <upsname> <varname> "<value>"
	...
	END LIST ENUM <upsname> <varname>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.