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" }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0.81"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
hyper = { version = "0.14", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-test = "0.4.2"
|
||||
log = "0.4.17"
|
||||
env_logger = "0.9.0"
|
||||
cookie = "0.16"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4.2"
|
||||
|
@ -4,10 +4,12 @@
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
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 log::{trace, debug};
|
||||
|
||||
use crate::router_info::Status;
|
||||
|
||||
/// A structure to hold all the necessary data to connect to the router.
|
||||
pub struct Client {
|
||||
ip: String,
|
||||
@ -21,9 +23,9 @@ pub struct Client {
|
||||
impl Client {
|
||||
/// Create a new Client using only the password, and using `192.168.1.1` as
|
||||
/// 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.");
|
||||
return Client {
|
||||
return Self {
|
||||
ip: Ipv4Addr::new(192, 168, 1, 1).to_string(),
|
||||
username: "admin".to_string(),
|
||||
password: password.to_string(),
|
||||
@ -57,8 +59,10 @@ impl Client {
|
||||
.expect("There was an issue contacting the router.").into_parts();
|
||||
|
||||
debug!("Status is {}.", parts.status.as_str());
|
||||
debug!("Body was: '{}'.", std::str::from_utf8(
|
||||
&hyper::body::to_bytes(body).await.unwrap()).unwrap());
|
||||
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.");
|
||||
|
||||
@ -68,6 +72,44 @@ impl Client {
|
||||
}
|
||||
assert!(!self.cookies.is_empty(),
|
||||
"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.
|
||||
|
@ -2,3 +2,4 @@
|
||||
//! A crate aimed to gather (read only) data from a Livebox 4 or more recent.
|
||||
|
||||
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