use super::{GatewayModel, GatewayClient, parse_json, ClientError, ClientResult, JSONUnwrappable, parse_login_json};
use std::{
time::Duration,
collections::HashMap,
fmt::Write
};
use json::JsonValue;
use reqwest::{
blocking::Client,
header::{CONTENT_LENGTH, CONTENT_TYPE}
};
use rust_utils::logging::LogLevel;
use crate::{
LOG,
devices::DeviceList,
config::{DeviceConfig, GlobalConfig, Config},
stats::{AdvancedGatewayStats, CellInfo, GatewayInfo, NetworkInfo},
};
pub struct SagemcomClient(Client);
#[allow(clippy::new_without_default)]
impl SagemcomClient {
pub fn new() -> SagemcomClient {
SagemcomClient(
Client::builder()
.timeout(Duration::from_secs(10))
.user_agent("homeisp/android/2.10")
.build()
.unwrap()
)
}
fn get_token(&self) -> ClientResult<String> {
let config = GlobalConfig::load();
let mut login_json = HashMap::new();
login_json.insert("username", config.username);
login_json.insert("password", config.password);
let req = self.0.post("http://192.168.12.1/TMI/v1/auth/login").json(&login_json);
match req.send() {
Ok(response) => {
let json_text = response.text().unwrap();
let json = parse_login_json(&json_text)?;
match json["auth"]["token"].as_str() {
Some(token) => Ok(token.to_string()),
None => Err(ClientError::LoginFailed)
}
}
Err(why) => {
LOG.line(LogLevel::Error, format!("Error logging into gateway: {why}"), false);
Err(ClientError::GatewayUnreachable(why.to_string()))
}
}
}
fn post(&self, res: &str, body: Option<String>) -> ClientResult<()> {
let config = GlobalConfig::load();
let addr = format!("http://{}/TMI/v1/{res}", config.gateway_ip);
let mut req = self.0
.post(addr)
.bearer_auth(self.get_token()?)
.timeout(Duration::from_secs(1));
if let Some(json_str) = body {
req = req.body(json_str).header(CONTENT_TYPE, "application/json");
}
else {
req = req.header(CONTENT_LENGTH, 0);
}
match req.send() {
Ok(_) => Ok(()),
Err(why) => Err(ClientError::GatewayUnreachable(why.to_string()))
}
}
fn json_get(&self, res: &str, auth: bool) -> ClientResult<JsonValue> {
let config = GlobalConfig::load();
let addr = format!("http://{}/TMI/v1/{res}", config.gateway_ip);
let mut req = self.0.get(addr);
if auth {
req = req.bearer_auth(self.get_token()?);
}
match req.send() {
Ok(response) => {
let text = response.text().unwrap();
Ok(parse_json(&text)?)
},
Err(why) => Err(ClientError::GatewayUnreachable(why.to_string()))
}
}
}
impl GatewayClient for SagemcomClient {
fn reboot_gateway_no_log(&self) -> ClientResult<()> {
self.post("gateway/reset?set=reboot", None).unwrap_or(());
Ok(())
}
fn get_devices(&self) -> ClientResult<DeviceList> {
let dev_config = DeviceConfig::load();
let json = self.json_get("network/telemetry?get=clients", true)?;
DeviceList::get_sgmcm(json, &dev_config)
}
fn get_5g_stats(&self) -> ClientResult<CellInfo> {
let json = self.json_get("gateway?get=all", false)?;
let bars = json["signal"]["5g"]["bars"].as_u8().unwrap_json("bars", &json)?;
let strength = json["signal"]["5g"]["rsrp"].as_i32().unwrap_json("rsrp", &json)?;
let band = json["signal"]["5g"]["bands"][0].as_str().unwrap_json("bands", &json)?.to_string();
Ok(CellInfo {
band,
bars,
strength
})
}
fn get_4g_stats(&self) -> ClientResult<CellInfo> {
let json = self.json_get("gateway?get=all", false)?;
let bars = json["signal"]["4g"]["bars"].as_u8().unwrap_json("bars", &json)?;
let strength = json["signal"]["4g"]["rsrp"].as_i32().unwrap_json("rsrp", &json)?;
let band = json["signal"]["4g"]["bands"][0].as_str().unwrap_json("bands", &json)?.to_string();
Ok(CellInfo {
band,
bars,
strength
})
}
fn get_conn_status(&self) -> bool {
let json_res = self.json_get("gateway?get=all", false);
if let Ok(json) = json_res {
let val = json["device"]["isEnabled"].as_bool();
return val.unwrap_or(false);
}
false
}
fn get_info(&self) -> ClientResult<GatewayInfo> {
let json = self.json_get("gateway?get=all", false)?;
let sim_json = self.json_get("network/telemetry?get=sim", true)?;
let vendor = json["device"]["manufacturer"].as_str().unwrap_json("manufacturer", &json)?.to_string();
let ser_num = json["device"]["serial"].as_str().unwrap_json("serial", &json)?.to_string();
let hw_ver = json["device"]["hardwareVersion"].as_str().unwrap_json("hardwareVersion", &json)?.to_string();
let sw_ver = json["device"]["softwareVersion"].as_str().unwrap_json("softwareVersion", &json)?.to_string();
let uptime = json["time"]["upTime"].as_usize().unwrap_json("upTime", &json)?;
let imei = sim_json["sim"]["imei"].as_str().unwrap_json("imei", &sim_json)?.parse().unwrap();
let mut line_num_raw = sim_json["sim"]["msisdn"].as_str().unwrap_json(",msisdn", &sim_json)?.to_string();
line_num_raw.remove(0);
while line_num_raw.chars().count() < 10 { line_num_raw.push(' '); }
let mut line_num = format!("{}-{}-{}", &line_num_raw[0..3], &line_num_raw[3..6], &line_num_raw[6..10]);
line_num.retain(|c| c != ' ');
Ok(GatewayInfo {
vendor,
ser_num,
hw_ver,
sw_ver,
uptime,
imei,
line_num
})
}
fn get_adv_stats(&self) -> ClientResult<AdvancedGatewayStats> {
let json = self.json_get("gateway?get=all", false)?;
let cell_json = self.json_get("network/telemetry?get=cell", true)?;
let sim_json = self.json_get("network/telemetry?get=sim", true)?;
let apn_name = cell_json["cell"]["generic"]["apn"].as_str().unwrap_json("apn", &cell_json)?.to_string();
let band_5g = json["signal"]["5g"]["bands"][0].as_str().unwrap_json("0", &json)?.to_string();
let rsrp_5g = cell_json["cell"]["5g"]["sector"]["rsrp"].as_i32().unwrap_json("rsrp", &cell_json)?;
let snr_5g = cell_json["cell"]["5g"]["sector"]["sinr"].as_i32().unwrap_json("sinr", &cell_json)?;
let rsrq_5g = cell_json["cell"]["5g"]["sector"]["rsrq"].as_i32().unwrap_json("rsrq", &cell_json)?;
let rssi = cell_json["cell"]["5g"]["sector"]["rssi"].as_i32().unwrap_json("rssi", &cell_json)?;
let band_4g = json["signal"]["4g"]["bands"][0].as_str().unwrap_json("0", &json)?.to_string();
let rsrp_4g = cell_json["cell"]["4g"]["sector"]["rsrp"].as_i32().unwrap_json("rsrp", &cell_json)?;
let snr_4g = cell_json["cell"]["4g"]["sector"]["sinr"].as_i32().unwrap_json("sinr", &cell_json)?;
let rsrq_4g = cell_json["cell"]["4g"]["sector"]["rsrq"].as_i32().unwrap_json("rsrq", &cell_json)?;
let imei = sim_json["sim"]["imei"].as_str().unwrap_json("imei", &sim_json)?.parse().unwrap();
let imsi = sim_json["sim"]["imsi"].as_str().unwrap_json("imsi", &sim_json)?.parse().unwrap();
let iccid = sim_json["sim"]["iccId"].as_str().unwrap_json("iccId", &sim_json)?.parse().unwrap();
Ok(AdvancedGatewayStats {
apn_name,
apn_ip4: "N/A".to_string(),
apn_ip6: "N/A".to_string(),
band_5g,
rsrp_5g,
snr_5g,
rsrq_5g,
rssi,
band_4g,
rsrp_4g,
snr_4g,
rsrq_4g,
imei,
imsi,
iccid
})
}
fn get_networks(&self) -> ClientResult<Vec<NetworkInfo>> {
let json = self.json_get("network/configuration/v2?get=ap", true)?;
let mut networks = Vec::new();
let net_json = json["ssids"].members();
for (i, net) in net_json.enumerate() {
networks.push(NetworkInfo {
id: format!("WLAN{i}"),
name: net["ssidName"].as_str().unwrap_json("ssidName", net)?.to_string(),
password: net["wpaKey"].as_str().unwrap_json("wpaKey", net)?.to_string(),
net_security: net["encryptionVersion"].as_str().unwrap_json("encryptionVersion", net)?.to_string(),
enabled: net["isBroadcastEnabled"].as_bool().unwrap_json("isBroadcastEnabled", net)?,
freq2_4g: net["2.4ghzSsid"].as_bool().unwrap_json("2.4ghzSsid", net)?,
freq5g: net["5.0ghzSsid"].as_bool().unwrap_json("5.0ghzSsid", net)?,
#[cfg(feature = "nokia")]
download: 0,
#[cfg(feature = "nokia")]
upload: 0
})
}
Ok(networks)
}
fn webui_pic(&self) -> &'static str { "sagemcom" }
fn model(&self) -> GatewayModel { GatewayModel::Sagemcom }
fn set_networks(&self, networks: &[NetworkInfo]) -> ClientResult<()> {
let mut net_json_list = String::new();
for network in networks {
write!(net_json_list,
"{{\
\"encryptionMode\":\"AES\",\
\"encryptionVersion\":\"WPA2\",\
\"guest\":false,\
\"2.4ghzSsid\":{},\
\"5.0ghzSsid\":{},\
\"isBroadcastEnabled\":true,\
\"ssidName\":\"{}\",
\"wpaKey\":\"{}\"
}},",
network.freq2_4g,
network.freq5g,
network.name,
network.password
).unwrap();
}
net_json_list.pop();
let cfg_json = format!(
"{{\
\"2.4ghz\":{{
\"airtimeFairness\":true,\
\"channel\":\"Auto\",\
\"channelBandwidth\":\"Auto\",\
\"isMUMIMOEnabled\":true,\
\"isRadioEnabled\":true,\
\"isWMMEnabled\":true,\
\"maxClients\":64,\
\"mode\":\"auto\",\
\"transmissionPower\":\"100%\"\
}},\
\"5.0ghz\":{{\
\"airtimeFairness\":true,\
\"channel\":\"Auto\",\
\"channelBandwidth\":\"Auto\",\
\"isMUMIMOEnabled\":true,\
\"isRadioEnabled\":true,\
\"isWMMEnabled\":true,\
\"maxClients\":64,\
\"mode\":\"auto\",\
\"transmissionPower\":\"100%\"\
}},\
\"bandSteering\":{{\
\"isEnabled\":true\
}},\
\"ssids\":[\
{net_json_list}\
]\
}}"
);
self.post("network/configuration/v2?set=ap", Some(cfg_json))
}
fn set_admin_pswd(&self, password: &str) -> ClientResult<()> {
let auth_json = format!(
"{{\
\"passwordNew\":\"{password}\",\
\"usernameNew\":\"admin\"\
}}"
);
self.post("auth/admin/reset", Some(auth_json))
}
}