feat: Added get_status for client and the Status data structure for deserializing API response
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
parent
8576f252ba
commit
65f1b0bc5e
@ -11,10 +11,13 @@ license = "GPL-3.0"
|
|||||||
maintenance = { status = "experimental" }
|
maintenance = { status = "experimental" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json = "1.0.81"
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
hyper = { version = "0.14", features = ["full"] }
|
hyper = { version = "0.14", features = ["full"] }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tokio-test = "0.4.2"
|
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
cookie = "0.16"
|
cookie = "0.16"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio-test = "0.4.2"
|
||||||
|
@ -4,10 +4,12 @@
|
|||||||
|
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use cookie::Cookie;
|
use cookie::Cookie;
|
||||||
use serde_json::json;
|
use serde_json::{json, Value};
|
||||||
use hyper::{Method, Request, body::Body, header::{CONTENT_TYPE, SET_COOKIE, AUTHORIZATION, COOKIE}, client::HttpConnector};
|
use hyper::{Method, Request, body::Body, header::{CONTENT_TYPE, SET_COOKIE, AUTHORIZATION, COOKIE}, client::HttpConnector};
|
||||||
use log::{trace, debug};
|
use log::{trace, debug};
|
||||||
|
|
||||||
|
use crate::router_info::Status;
|
||||||
|
|
||||||
/// A structure to hold all the necessary data to connect to the router.
|
/// A structure to hold all the necessary data to connect to the router.
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
ip: String,
|
ip: String,
|
||||||
@ -21,9 +23,9 @@ pub struct Client {
|
|||||||
impl Client {
|
impl Client {
|
||||||
/// Create a new Client using only the password, and using `192.168.1.1` as
|
/// Create a new Client using only the password, and using `192.168.1.1` as
|
||||||
/// target IP, and `admin` as the default username.
|
/// target IP, and `admin` as the default username.
|
||||||
pub fn new(password: &str) -> Client {
|
pub fn new(password: &str) -> Self {
|
||||||
trace!("Creating a new client.");
|
trace!("Creating a new client.");
|
||||||
return Client {
|
return Self {
|
||||||
ip: Ipv4Addr::new(192, 168, 1, 1).to_string(),
|
ip: Ipv4Addr::new(192, 168, 1, 1).to_string(),
|
||||||
username: "admin".to_string(),
|
username: "admin".to_string(),
|
||||||
password: password.to_string(),
|
password: password.to_string(),
|
||||||
@ -57,8 +59,10 @@ impl Client {
|
|||||||
.expect("There was an issue contacting the router.").into_parts();
|
.expect("There was an issue contacting the router.").into_parts();
|
||||||
|
|
||||||
debug!("Status is {}.", parts.status.as_str());
|
debug!("Status is {}.", parts.status.as_str());
|
||||||
debug!("Body was: '{}'.", std::str::from_utf8(
|
let body_bytes = hyper::body::to_bytes(body).await.unwrap();
|
||||||
&hyper::body::to_bytes(body).await.unwrap()).unwrap());
|
let json: Value = serde_json::from_slice(&body_bytes)
|
||||||
|
.expect("Could not parse JSON.");
|
||||||
|
debug!("Body was: '{}'.", std::str::from_utf8(&body_bytes).unwrap());
|
||||||
assert!(parts.status.is_success(), "Router answered with something else
|
assert!(parts.status.is_success(), "Router answered with something else
|
||||||
than a success code.");
|
than a success code.");
|
||||||
|
|
||||||
@ -68,6 +72,44 @@ impl Client {
|
|||||||
}
|
}
|
||||||
assert!(!self.cookies.is_empty(),
|
assert!(!self.cookies.is_empty(),
|
||||||
"No cookie detected on login, there should be an error.");
|
"No cookie detected on login, there should be an error.");
|
||||||
|
assert!(json["status"].as_u64().unwrap() == 0, "Status wasn't 0.");
|
||||||
|
self.context_id =
|
||||||
|
Some(json["data"]["contextID"].as_str().unwrap().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get various status data from the router.
|
||||||
|
pub async fn get_status(&self) -> Status {
|
||||||
|
assert!(!self.cookies.is_empty() && self.context_id.is_some(),
|
||||||
|
"Cannot get status without logging in beforehand.");
|
||||||
|
trace!("Getting router status.");
|
||||||
|
let post_data = json!({
|
||||||
|
"service": "DeviceInfo",
|
||||||
|
"method": "get",
|
||||||
|
"parameters": {}
|
||||||
|
});
|
||||||
|
|
||||||
|
let req = Request::builder()
|
||||||
|
.method(Method::POST)
|
||||||
|
.uri(format!("http://{}/ws", self.ip))
|
||||||
|
.header(CONTENT_TYPE, "application/x-sah-ws-4-call+json")
|
||||||
|
.header("X-Context", self.context_id.clone().unwrap())
|
||||||
|
.header(COOKIE, self.cookies.join("; "))
|
||||||
|
.body(Body::from(post_data.to_string()))
|
||||||
|
.expect("Could not build request.");
|
||||||
|
let (parts, body) = self.client.request(req).await
|
||||||
|
.expect("There was an issue contacting the router.").into_parts();
|
||||||
|
|
||||||
|
debug!("Status is {}.", parts.status.as_str());
|
||||||
|
let body_bytes = hyper::body::to_bytes(body).await.unwrap();
|
||||||
|
let json: Value = serde_json::from_slice(&body_bytes)
|
||||||
|
.expect("Could not parse JSON.");
|
||||||
|
debug!("Body was: '{}'.", std::str::from_utf8(&body_bytes).unwrap());
|
||||||
|
assert!(parts.status.is_success(), "Router answered with something else
|
||||||
|
than a success code.");
|
||||||
|
let status: Status = serde_json::from_value(json["status"].clone())
|
||||||
|
.expect("Looks like the deserialized data is incomplete.");
|
||||||
|
debug!("Deserialized status is: {:?}", status);
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Logout from the router.
|
/// Logout from the router.
|
||||||
|
@ -2,3 +2,4 @@
|
|||||||
//! A crate aimed to gather (read only) data from a Livebox 4 or more recent.
|
//! A crate aimed to gather (read only) data from a Livebox 4 or more recent.
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
pub mod router_info;
|
||||||
|
68
src/router_info.rs
Normal file
68
src/router_info.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
|
||||||
|
///! All the necessary struct elements to store the router status.
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
/// The Status structure is used to store status data from the router.
|
||||||
|
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Status {
|
||||||
|
#[serde(rename(deserialize = "Manufacturer"))]
|
||||||
|
pub manufacturer: String,
|
||||||
|
#[serde(rename(deserialize = "ManufacturerOUI"))]
|
||||||
|
pub manufacturer_oui: String,
|
||||||
|
#[serde(rename(deserialize = "ModelName"))]
|
||||||
|
pub model_name: String,
|
||||||
|
#[serde(rename(deserialize = "Description"))]
|
||||||
|
pub description: String,
|
||||||
|
#[serde(rename(deserialize = "ProductClass"))]
|
||||||
|
pub product_class: String,
|
||||||
|
#[serde(rename(deserialize = "SerialNumber"))]
|
||||||
|
pub serial_number: String,
|
||||||
|
#[serde(rename(deserialize = "HardwareVersion"))]
|
||||||
|
pub hardware_version: String,
|
||||||
|
#[serde(rename(deserialize = "SoftwareVersion"))]
|
||||||
|
pub software_version: String,
|
||||||
|
#[serde(rename(deserialize = "RescueVersion"))]
|
||||||
|
pub rescue_version: String,
|
||||||
|
#[serde(rename(deserialize = "ModemFirmwareVersion"))]
|
||||||
|
pub modem_firmware_version: String,
|
||||||
|
#[serde(rename(deserialize = "EnabledOptions"))]
|
||||||
|
pub enabled_options: String,
|
||||||
|
#[serde(rename(deserialize = "AdditionalHardwareVersion"))]
|
||||||
|
pub additional_hardware_version: String,
|
||||||
|
#[serde(rename(deserialize = "AdditionalSoftwareVersion"))]
|
||||||
|
pub additional_software_version: String,
|
||||||
|
#[serde(rename(deserialize = "SpecVersion"))]
|
||||||
|
pub spec_version: String,
|
||||||
|
#[serde(rename(deserialize = "ProvisioningCode"))]
|
||||||
|
pub provisioning_code: String,
|
||||||
|
#[serde(rename(deserialize = "UpTime"))]
|
||||||
|
pub uptime: u32,
|
||||||
|
#[serde(rename(deserialize = "FirstUseDate"))]
|
||||||
|
pub first_use_date: String,
|
||||||
|
#[serde(rename(deserialize = "DeviceLog"))]
|
||||||
|
pub device_log: String,
|
||||||
|
#[serde(rename(deserialize = "VendorConfigFileNumberOfEntries"))]
|
||||||
|
pub vendor_configfile_number_of_entries: u32,
|
||||||
|
#[serde(rename(deserialize = "ManufacturerURL"))]
|
||||||
|
pub manufacturer_url: String,
|
||||||
|
#[serde(rename(deserialize = "Country"))]
|
||||||
|
pub country: String,
|
||||||
|
#[serde(rename(deserialize = "ExternalIPAddress"))]
|
||||||
|
pub external_ip_address: String,
|
||||||
|
#[serde(rename(deserialize = "DeviceStatus"))]
|
||||||
|
pub device_status: String,
|
||||||
|
#[serde(rename(deserialize = "NumberOfReboots"))]
|
||||||
|
pub number_of_reboots: u32,
|
||||||
|
#[serde(rename(deserialize = "UpgradeOccurred"))]
|
||||||
|
pub upgrade_occured: bool,
|
||||||
|
#[serde(rename(deserialize = "ResetOccurred"))]
|
||||||
|
pub reset_occurred: bool,
|
||||||
|
#[serde(rename(deserialize = "RestoreOccurred"))]
|
||||||
|
pub restore_occurred: bool,
|
||||||
|
#[serde(rename(deserialize = "X_SOFTATHOME-COM_AdditionalSoftwareVersions"))]
|
||||||
|
pub softathome_additional_software_versions: String,
|
||||||
|
#[serde(rename(deserialize = "BaseMAC"))]
|
||||||
|
pub base_mac: String,
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user