feat: Added wan info, status info and devices info

Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
Louis Vallat 2022-06-28 22:20:16 +02:00
parent 65f1b0bc5e
commit ffc315f3c8
No known key found for this signature in database
GPG Key ID: 0C87282F76E61283
5 changed files with 128 additions and 29 deletions

View File

@ -8,7 +8,7 @@ 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; use crate::{status::Status, wan::WANConfiguration, devices::Device};
/// 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 {
@ -112,6 +112,76 @@ impl Client {
return status; return status;
} }
/// Get WAN configuration data from the router.
pub async fn get_wan_config(&self) -> WANConfiguration {
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": "NMC",
"method": "getWANStatus",
"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() && json["status"].as_bool().unwrap(),
"Router answered with something else than a success code.");
let wan_config: WANConfiguration = serde_json::from_value(json["data"].clone())
.expect("Looks like the deserialized data is incomplete.");
debug!("Deserialized settings is: {:?}", wan_config);
return wan_config;
}
/// Get various settings data from the router.
pub async fn get_devices(&self) -> Vec<Device> {
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": "Devices",
"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() && json["status"].is_array(),
"Router answered with something else than a success code.");
let devices: Vec<Device> = serde_json::from_value(json["status"].clone())
.expect("Looks like the deserialized data is incomplete.");
debug!("Deserialized settings is: {:?}", devices);
return devices;
}
/// Logout from the router. /// Logout from the router.
pub async fn logout(&mut self) { pub async fn logout(&mut self) {
trace!("Logging out."); trace!("Logging out.");

22
src/devices.rs Normal file
View File

@ -0,0 +1,22 @@
///! All the necessary struct elements to store the router devices information.
use serde::Deserialize;
/// The Device structure is used to store status data from the devices that are
/// or have been connected to the router at some point.
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
#[serde(rename_all="PascalCase")]
pub struct Device {
pub key: String,
pub name: String,
pub discovery_source: String,
pub active: bool,
pub device_type: String,
pub tags: String,
#[serde(rename(deserialize = "IPAddress"))]
pub ip_address: Option<String>,
#[serde(rename(deserialize = "SSID"))]
pub ssid: Option<String>,
pub channel: Option<u32>,
}

View File

@ -2,4 +2,6 @@
//! 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; pub mod status;
pub mod wan;
pub mod devices;

View File

@ -6,60 +6,37 @@ use serde::Deserialize;
/// The Status structure is used to store status data from the router. /// The Status structure is used to store status data from the router.
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] #[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
#[serde(rename_all="PascalCase")]
pub struct Status { pub struct Status {
#[serde(rename(deserialize = "Manufacturer"))]
pub manufacturer: String, pub manufacturer: String,
#[serde(rename(deserialize = "ManufacturerOUI"))] #[serde(rename(deserialize = "ManufacturerOUI"))]
pub manufacturer_oui: String, pub manufacturer_oui: String,
#[serde(rename(deserialize = "ModelName"))]
pub model_name: String, pub model_name: String,
#[serde(rename(deserialize = "Description"))]
pub description: String, pub description: String,
#[serde(rename(deserialize = "ProductClass"))]
pub product_class: String, pub product_class: String,
#[serde(rename(deserialize = "SerialNumber"))]
pub serial_number: String, pub serial_number: String,
#[serde(rename(deserialize = "HardwareVersion"))]
pub hardware_version: String, pub hardware_version: String,
#[serde(rename(deserialize = "SoftwareVersion"))]
pub software_version: String, pub software_version: String,
#[serde(rename(deserialize = "RescueVersion"))]
pub rescue_version: String, pub rescue_version: String,
#[serde(rename(deserialize = "ModemFirmwareVersion"))]
pub modem_firmware_version: String, pub modem_firmware_version: String,
#[serde(rename(deserialize = "EnabledOptions"))]
pub enabled_options: String, pub enabled_options: String,
#[serde(rename(deserialize = "AdditionalHardwareVersion"))]
pub additional_hardware_version: String, pub additional_hardware_version: String,
#[serde(rename(deserialize = "AdditionalSoftwareVersion"))]
pub additional_software_version: String, pub additional_software_version: String,
#[serde(rename(deserialize = "SpecVersion"))]
pub spec_version: String, pub spec_version: String,
#[serde(rename(deserialize = "ProvisioningCode"))]
pub provisioning_code: String, pub provisioning_code: String,
#[serde(rename(deserialize = "UpTime"))] pub up_time: u32,
pub uptime: u32,
#[serde(rename(deserialize = "FirstUseDate"))]
pub first_use_date: String, pub first_use_date: String,
#[serde(rename(deserialize = "DeviceLog"))]
pub device_log: String, pub device_log: String,
#[serde(rename(deserialize = "VendorConfigFileNumberOfEntries"))] pub vendor_config_file_number_of_entries: u32,
pub vendor_configfile_number_of_entries: u32,
#[serde(rename(deserialize = "ManufacturerURL"))] #[serde(rename(deserialize = "ManufacturerURL"))]
pub manufacturer_url: String, pub manufacturer_url: String,
#[serde(rename(deserialize = "Country"))]
pub country: String, pub country: String,
#[serde(rename(deserialize = "ExternalIPAddress"))] #[serde(rename(deserialize = "ExternalIPAddress"))]
pub external_ip_address: String, pub external_ip_address: String,
#[serde(rename(deserialize = "DeviceStatus"))]
pub device_status: String, pub device_status: String,
#[serde(rename(deserialize = "NumberOfReboots"))]
pub number_of_reboots: u32, pub number_of_reboots: u32,
#[serde(rename(deserialize = "UpgradeOccurred"))] pub upgrade_occurred: bool,
pub upgrade_occured: bool,
#[serde(rename(deserialize = "ResetOccurred"))]
pub reset_occurred: bool, pub reset_occurred: bool,
#[serde(rename(deserialize = "RestoreOccurred"))]
pub restore_occurred: bool, pub restore_occurred: bool,
#[serde(rename(deserialize = "X_SOFTATHOME-COM_AdditionalSoftwareVersions"))] #[serde(rename(deserialize = "X_SOFTATHOME-COM_AdditionalSoftwareVersions"))]
pub softathome_additional_software_versions: String, pub softathome_additional_software_versions: String,

28
src/wan.rs Normal file
View File

@ -0,0 +1,28 @@
///! The WAN configuration that you can read from the router.
use serde::Deserialize;
/// The WANConfiguration structure is used to store WAN configuration data from
/// the router.
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
#[serde(rename_all="PascalCase")]
pub struct WANConfiguration {
pub wan_state: String,
pub link_type: String,
pub link_state: String,
#[serde(rename(deserialize = "MACAddress"))]
pub mac_address: String,
pub protocol: String,
pub connection_state: String,
pub last_connection_error: String,
#[serde(rename(deserialize = "IPAddress"))]
pub ip_address: String,
pub remote_gateway: String,
#[serde(rename(deserialize = "DNSServers"))]
pub dns_servers: String,
#[serde(rename(deserialize = "IPv6Address"))]
pub ipv6_address: String,
#[serde(rename(deserialize = "IPv6DelegatedPrefix"))]
pub ipv6_delegated_prefix: String
}