use std::error::Error; use chrono::Timelike; use common::{ BarModule, ClockKind, ClockPayload, Endpoint, Hourly, ModuleEffect, Service, WeatherKind, WeatherMsg, WeatherPayload, WeatherResponse, Wire, }; use iced::{ widget::{container, mouse_area, text}, Element, Task, }; pub struct WeatherModule { text: String, /// Cached hourly forecast from the last weather update. hourly: Option, /// Current hour, mirrored from the datetime service's clock ticks. hour: Option, } impl WeatherModule { /// Shows the forecast temperature for the tracked current hour. fn refresh(&mut self) { let (Some(hourly), Some(hour)) = (&self.hourly, self.hour) else { return; }; if let Some(i) = hourly.time.iter().position(|t| t.hour() == hour) { if let Some(&temp) = hourly.temperature_2m.get(i) { let value = format!("{}°", temp as i32); if self.text != value { self.text = value; tracing::info!(hour, temp, "weather: current hour"); } } } } } impl Default for WeatherModule { fn default() -> Self { Self { text: "waiting ".into(), hourly: None, hour: None, } } } impl BarModule for WeatherModule { fn id(&self) -> &'static str { "weather" } fn view(&self, _window_id: Option) -> Element<'_, Wire> { let me = Endpoint::module(self.id()); container(mouse_area(text(&self.text).size(16)).on_press(Wire::module( me, self.id(), WeatherKind::Poke, ))) .into() } fn update(&mut self, msg: Wire) -> Task { // Weather state: cache the hourly forecast, then show the current hour. if let Some(env) = msg.downcast::() { match env.kind { WeatherMsg::State => match env.payload.downcast_ref::() { Some(resp) => { self.hourly = Some(resp.hourly.clone()); self.refresh(); } None => tracing::warn!("weather payload was not a WeatherResponse"), }, } } // Clock tick from the datetime service: track the current hour. if let Some(payload) = msg.downcast::() { self.hour = payload.value.split(':').next().and_then(|h| h.parse().ok()); self.refresh(); } match msg.downcast::() { Some(WeatherKind::Poke) => { tracing::debug!("weather poked"); self.text = "poked".into(); } None => (), } Task::none() } /// Route keys this module subscribes to. fn services(&self) -> Vec { vec![WeatherMsg::State.key(), ClockKind::Seconds.key()] } /// Optional: read `module.weather` from the config table. fn config(&mut self, _config: toml::Table) -> Result<(), Box> { Ok(()) } }