use std::{sync::Arc, time::Duration}; use common::{Endpoint, Inbox, WeatherMsg, WeatherPayload, WeatherResponse, Wire}; use iced::{ futures::{channel::mpsc, SinkExt}, Subscription, }; use tokio::time::interval; pub const NAMESPACE: &str = "weather."; const FORECAST_URL: &str = "https://api.open-meteo.com/v1/forecast\ ?latitude=51.3714&longitude=-0.2302\ &hourly=temperature_2m,rain,showers,precipitation,relative_humidity_2m"; pub struct WeatherService { client: reqwest::Client, } impl WeatherService { fn new() -> Self { Self { client: reqwest::Client::default(), } } pub fn run(inbox: Inbox) -> Subscription { Subscription::run_with(inbox, |inbox| { let key = inbox.key(); let mut _rx = inbox.take().expect("cant take"); iced::stream::channel(0, move |mut sender: mpsc::Sender| async move { let weather = WeatherService::new(); let mut interval = interval(Duration::from_mins(30)); tracing::info!(key = key, "weather service started"); loop { tokio::select! { _ = interval.tick() => { if let Some(resp) = weather.get_weather().await { let env = WeatherPayload { kind: WeatherMsg::State, payload: Arc::new(resp), }; let wire = Wire::topic(Endpoint::service(key), key, env); if sender.send(wire).await.is_err() { tracing::warn!(key, "weather send failed; stopping service"); return; } } } } } }) }) } async fn get_weather(&self) -> Option { tracing::debug!(url = FORECAST_URL, "fetching weather"); match self.client.get(FORECAST_URL).send().await { Ok(resp) => match resp.json::().await { Ok(parsed) => { tracing::debug!("weather fetched"); Some(parsed) } Err(e) => { tracing::warn!(error = %e, "weather response decode failed"); None } }, Err(e) => { tracing::warn!(error = %e, "weather request failed"); None } } } }