Files
hemma/backend/src/collector/weatherapi.rs

152 lines
3.5 KiB
Rust

use crate::collector::Collector;
use chrono::{serde::ts_seconds, DateTime, Utc};
use reqwest::get;
use serde::Deserialize;
use std::fmt::{self, Display, Formatter};
#[derive(Deserialize)]
struct Response {
current: CurrentBody,
}
#[derive(Deserialize)]
#[allow(dead_code)]
struct Condition {
text: String,
icon: String,
code: i64,
}
#[derive(Deserialize)]
#[allow(dead_code)]
struct CurrentBody {
#[serde(with = "ts_seconds")]
last_updated_epoch: DateTime<Utc>,
last_updated: String,
temp_c: f64,
is_day: i8,
condition: Condition,
wind_kph: f64,
wind_degree: f64,
wind_dir: String,
pressure_mb: f64,
precip_mm: f64,
humidity: f64,
cloud: f64,
feelslike_c: f64,
vis_km: f64,
uv: f64,
gust_kph: f64,
}
pub struct WeatherApi {
pub api_key: String,
pub location: String,
}
#[async_trait::async_trait]
impl Collector for WeatherApi {
async fn collect(&mut self) -> anyhow::Result<String> {
let path = "https://api.weatherapi.com/v1/current.json";
let api_key = &self.api_key;
let location = &self.location;
let url = format!("{path}?key={api_key}&q={location}&aqi=no");
let Response {
current:
CurrentBody {
last_updated_epoch,
temp_c,
feelslike_c,
wind_kph,
precip_mm,
humidity,
cloud,
..
},
..
} = get(&url).await?.json().await?;
let wind_mps = (wind_kph / 3.6).round();
let wind = wind_speed_to_beaufort(wind_mps);
let temp = if feelslike_c != temp_c {
format!("**{temp_c}°** (känns som {feelslike_c}°)")
} else {
format!("**{temp_c}°**")
};
let time_of_day = last_updated_epoch.naive_local().format("%H:%M");
let markdown = format!(
r#"
# Väder
## {location} kl {time_of_day}
{temp}. **{wind}** ({wind_mps} m/s).
{cloud}% molntäcke, {precip_mm} mm regn, {humidity}% luftfuktighet.
"#
);
let html = markdown::to_html(markdown.trim());
Ok(html)
}
}
/// Wind speed expressed on the beaufort scale
struct BeaufortScale(u8);
impl Display for BeaufortScale {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let name = match self {
BeaufortScale(0) => "Lugnt",
BeaufortScale(1 | 2) => "Svag vind",
BeaufortScale(3 | 4) => "Måttlig vind",
BeaufortScale(5 | 6) => "Frisk vind",
BeaufortScale(7 | 8) => "Hård vind",
BeaufortScale(9) => "Mycket hård vind",
BeaufortScale(10) => "Storm",
BeaufortScale(11) => "Svår storm",
BeaufortScale(12..) => "Orkan",
};
write!(f, "{name}")
}
}
fn wind_speed_to_beaufort(mps: f64) -> BeaufortScale {
let beaufort_wind_speeds = [
0.0..0.3,
0.3..1.6,
1.6..3.4,
3.4..5.5,
5.5..8.8,
8.0..10.8,
10.8..13.9,
13.9..17.2,
17.2..20.8,
20.8..24.5,
24.5..28.5,
28.5..32.7,
32.7..37.0,
37.0..41.5,
41.5..46.2,
46.2..51.0,
51.0..56.1,
56.1..61.3,
];
let mps = mps.max(0.0);
let index = beaufort_wind_speeds
.iter()
.enumerate()
.find_map(|(i, range)| range.contains(&mps).then(|| i))
.unwrap_or(beaufort_wind_speeds.len());
BeaufortScale(index as u8)
}