Crates.io | unifi-client |
lib.rs | unifi-client |
version | 0.4.0 |
created_at | 2025-04-26 18:41:39.34145+00 |
updated_at | 2025-09-18 22:28:47.168716+00 |
description | Rust client library for UniFi Controller API |
homepage | |
repository | https://github.com/fedibtc/unifi-client |
max_upload_size | |
id | 1650486 |
size | 393,100 |
UniFiClient is a third party Ubiquiti UniFi API client, allowing you to easily build your own UniFi integrations in Rust. UniFiClient comes with two primary sets of APIs for communicating with UniFi controllers, a high level strongly typed semantic API, and a lower level HTTP API for extending behaviour.
Note: This crate is not officially associated with or endorsed by Ubiquiti Inc.
Run this command in your terminal to add the latest version of UniFiClient.
cargo add unifi-client
The easiest way to use unifi-client is with the singleton pattern. This provides a single, globally accessible client instance.
use unifi_client::{UniFiClient, UniFiError};
#[tokio::main]
async fn main() -> Result<(), UniFiError> {
// 1. Initialize the singleton client (ONCE, at the start).
// Get your credentials securely (e.g., from environment variables).
let client = UniFiClient::builder()
.controller_url("https://your-unifi-controller:8443") // Replace!
.username("your_username") // Replace!
.password_from_env("UNIFI_PASSWORD") // Best practice
.site("default") // Or your site ID
.accept_invalid_certs(false) // Set to `true` if you don't have valid SSL certificates
.build()
.await?;
unifi_client::initialize(client);
// 2. Access the client anywhere using `unifi_client::instance()`:
let guests = unifi_client::instance().guests().list().send().await?;
println!("Guests: {:?}", guests);
Ok(())
}
The semantic API is a high level API that provides a type-safe way to
interact with the UniFi controller. It is built around a set of models
that map to the UniFi controller's API. Currently the following modules are
available.
guest
- Guest access management.unifi-client
uses a builder pattern for constructing API requests.
All methods with multiple optional parameters are built as Builder structs, allowing you to easily specify parameters.
use unifi_client::{UniFiClient, UniFiError};
#[tokio::main]
async fn main() -> Result<(), UniFiError> {
let client = UniFiClient::builder()
.controller_url("https://your-controller:8443")
.username("your_username")
.password_from_env("UNIFI_PASSWORD")
.build()
.await?;
unifi_client::initialize(client);
let unifi_client = unifi_client::instance();
// Authorize a guest:
let new_guest = unifi_client.guests()
.authorize("00:11:22:33:44:55")
.duration_minutes(60)
.up(1024)
.down(2048)
.data_quota_megabytes(1024)
.send() // *MUST* call .send()
.await?;
println!("Authorized Guest: {:?}", new_guest);
// List guests (with optional filtering):
let guests = unifi_client.guests().list().within(24).send().await?;
// Unauthorize a guest:
unifi_client.guests().unauthorize("00:11:22:33:44:55").send().await?;
//Unathorize all guests
unifi_client.guests().unauthorize_all().send().await?;
Ok(())
}
All API methods return a Result<T, UniFiError>
.
use unifi_client::{UniFiClient, UniFiError};
#[tokio::main]
async fn main() -> Result<(), UniFiError> {
let client = UniFiClient::builder()
.controller_url("https://your-controller:8443")
.username("your_username")
.password_from_env("UNIFI_PASSWORD")
.build()
.await?;
unifi_client::initialize(client);
let result = unifi_client::instance().guests().list().send().await;
match result {
Ok(guests) => println!("Guests: {:?}", guests),
Err(UniFiError::ApiError(msg)) => eprintln!("API Error: {}", msg),
Err(UniFiError::AuthenticationError(msg)) => eprintln!("Auth Error: {}", msg),
Err(e) => eprintln!("Other Error: {:?}", e),
}
Ok(())
}
UniFiClient supports two patterns for client creation and access:
Builder (UniFiClient::builder()
):
Create new, independent client instances with custom configurations.
Use when:
Singleton (initialize()
/ instance()
):
Set up a global client for simplified, centralized access.
Use when:
Best Practices:
initialize()
early (typically in main
) and use instance()
anywhere else.
initialize()
isnβt called, instance()
refers to an inert default client; any requests will return a configuration error.default-client
(enabled by default):
initialize()
/ instance()
), implemented with a
thread-safe Arc
and ArcSwap
.Disable the global client if you want explicit dependency injection only:
[dependencies]
unifi-client = { version = "*", default-features = false }
When disabled, call UniFiClient::builder()
and pass the client handle through your application.
If you need to connect to multiple UniFi Controllers, or you need different client configurations
within the same application, create independent client instances using UniFiClient::builder()
without calling initialize()
:
use unifi_client::{UniFiClient, UniFiError};
#[tokio::main]
async fn main() -> Result<(), UniFiError> {
let client1 = UniFiClient::builder()
.controller_url("https://controller1.example.com:8443")
.username("user1")
.password("password1")
.build()
.await?;
let client2 = UniFiClient::builder()
.controller_url("https://controller2.example.com:8443")
.username("user2")
.password("password2")
.build()
.await?;
// Use client1 and client2 independently.
let guests1 = client1.guests().list().send().await?;
let guests2 = client2.guests().list().send().await?;
println!("Guests on controller 1: {:?}", guests1);
println!("Guests on controller 2: {:?}", guests2);
Ok(())
}
use unifi_client::{UniFiClient, UniFiClientBuilder};
use reqwest::Client as ReqwestClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let custom_http_client = ReqwestClient::builder()
.timeout(std::time::Duration::from_secs(60))
.redirect(Policy::none())
.cookie_store(true)
.build()?;
let client = UniFiClient::builder()
.controller_url("https://your-controller:8443")
.username("your_username")
.password_from_env("UNIFI_PASSWORD")
.http_client(custom_http_client)
.build()
.await?;
Ok(())
}
This library has been tested with:
We set rust-version = "1.74"
in Cargo.toml
. Downstream crates can remain on older editions (e.g., 2018/2021); they just need a toolchain new enough to compile this crate.
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature
)git commit -m 'Add some amazing feature'
)git push origin feature/amazing-feature
)This project is licensed under the MIT license.