diff --git a/Cargo.lock b/Cargo.lock index 3168770..b9013c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -400,6 +400,8 @@ dependencies = [ "embedded-plots", "linux-embedded-hal", "profont", + "serde", + "serde_json", "ssd1675", "tinybmp", ] @@ -422,6 +424,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + [[package]] name = "jpeg-decoder" version = "0.1.22" @@ -635,6 +643,15 @@ dependencies = [ "miniz_oxide 0.3.7", ] +[[package]] +name = "proc-macro2" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +dependencies = [ + "unicode-ident", +] + [[package]] name = "profont" version = "0.5.0" @@ -644,6 +661,15 @@ dependencies = [ "embedded-graphics", ] +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.6.5" @@ -839,6 +865,12 @@ dependencies = [ "semver 1.0.13", ] +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + [[package]] name = "scoped_threadpool" version = "0.1.9" @@ -896,6 +928,37 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "serde" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590c437916fb6b221e1d00df6e3294f3fccd70ca7e92541c475d6ed6ef5fee2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b5b8d809babe02f538c2cfec6f2c1ed10804c0e5a6a041a049a4f5588ccc2e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serial-core" version = "0.4.0" @@ -953,6 +1016,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "syn" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sysfs_gpio" version = "0.6.1" @@ -992,6 +1066,12 @@ dependencies = [ "nom", ] +[[package]] +name = "unicode-ident" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" + [[package]] name = "vcell" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 54ae595..8a19611 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ profont = "0.5.0" ssd1675 = { git = "https://github.com/Feliix42/ssd1675" } #ssd1675 = { path = "../ssd1675" } +# for parsing the weather api data +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + linux-embedded-hal = { version = "0.3.2", optional = true } embedded-graphics-simulator = { version = "0.3.0", optional = true } diff --git a/src/weather/data.rs b/src/weather/data.rs index e69de29..10b89fd 100644 --- a/src/weather/data.rs +++ b/src/weather/data.rs @@ -0,0 +1,177 @@ +//! Rust data types representing a subset of the weather data returned by the OpenWeather [One +//! Call](https://openweathermap.org/api/one-call-3) API. +//! +//! The response fields are described in detail +//! [here](https://openweathermap.org/api/one-call-3#parameter). +#![allow(dead_code)] + +use serde::Deserialize; + + +/// Response from the OpenWeather API with both current and forecast data for a given location. +#[derive(Deserialize)] +pub struct WeatherData { + /// Latitude + lat: f32, + /// Longitude + lon: f32, + /// Name of the current timezone + timezone: String, + /// Timezone offset from UTC + timezone_offset: i32, + /// Current weather situation + current: CurrentWeather, + /// Minutely wheather information (optional; unused and excluded by API call + #[serde(skip_deserializing)] + minutely: Vec<()>, + /// Weather Information for the coming hours + hourly: Vec, + /// Weather information for the next days _(unused at the moment)_ + #[serde(skip_deserializing)] + daily: Vec<()>, + /// Currently active wheather alerts + #[serde(default)] + alerts: Vec +} + +/// Current weather data. +#[derive(Deserialize)] +pub struct CurrentWeather { + #[serde(flatten)] + data: CommonWeatherInfo, + /// Sunrise time (Unix Timestamp, UTC) + sunrise: u64, + /// Sunset time (Unix Timestamp, UTC) + sunset: u64, +} + +/// Weather Forecast for a specific hour in the future. +#[derive(Deserialize)] +pub struct HourlyWeather { + #[serde(flatten)] + data: CommonWeatherInfo, + /// Probability of precipitation (values range from 0 to 1). + pop: f32 +} + +/// Common weather information shared among different forecast types. +/// +/// This struct is not instantiated by the API but merely introduced by the `Deserializer`. +#[derive(Deserialize)] +pub struct CommonWeatherInfo { + /// Current time (Unix Timestamp, UTC) + dt: u64, + /// Current temperature + temp: f32, + /// Temperature according to human perception + feels_like: f32, + /// Atmospheric Pressure at sea level (hPa) + pressure: u32, + /// Air Humidity in % + humidity: u8, + /// Dew Point (temperature below which water droplets begin to form) + dew_point: f32, + /// Cloudiness in % + clouds: u8, + /// Current UV Index + uvi: f32, + /// Average Visibility in metres (max: 10.000m) + visibility: u16, + /// Wind Speed + wind_speed: f32, + /// Wind Gust (optional) + wind_gust: f32, + /// Wind direction in degrees + wind_deg: u16, + /// Rain + rain: Option, + /// Snow + snow: Option, + /// Weather + weather: Vec, +} + +#[derive(Deserialize)] +pub struct WeatherAlert { + /// Name of the alert source. + sender_name: String, + /// Name of the Event + event: String, + /// Date and time of the start of the alert (Unix Timestamp, UTC) + start: u64, + /// Date and time of the end of the alert (Unix Timestamp, UTC) + end: u64, + /// Description of the alert (localized). + description: String, + /// Type(s) of severe weather. + tags: Vec, +} + +#[derive(Deserialize)] +pub struct Precipitation { + /// Precipitation volume in the last hour in mm + #[serde(rename = "1h")] + hour: u16, +} + +#[derive(Deserialize)] +pub struct WeatherDetails { + #[serde(rename = "id")] + condition: WeatherCondition, + main: String, + description: String, + icon: String +} + +/// Weather Condition +/// +/// This information is deserialized from a unsigned int returned by the Open Weather API according +/// to [this +/// specification](https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2). +/// It ignores the fine details within weather conditions (e.g. heavy thunderstorm and light +/// thunderstorm both map to `Thunderstorm`). +#[derive(Deserialize)] //Debug)] +#[serde(from = "u16")] +pub enum WeatherCondition { + Thunderstorm, + Drizzle, + Rain, + Snow, + Mist, + Smoke, + Haze, + SandDust, + Fog, + Sand, + Dust, + Ash, + Squall, + Tornado, + Clear, + Clouds, +} + +impl From for WeatherCondition { + fn from(value: u16) -> Self { + match value { + 200 ..= 299 => Self::Thunderstorm, + 300 ..= 399 => Self::Drizzle, + 500 ..= 599 => Self::Rain, + 600 ..= 699 => Self::Snow, + 701 => Self::Mist, + 711 => Self::Smoke, + 721 => Self::Haze, + 731 => Self::SandDust, + 741 => Self::Fog, + 751 => Self::Sand, + 761 => Self::Dust, + 762 => Self::Ash, + 771 => Self::Squall, + 781 => Self::Tornado, + 800 => Self::Clear, + 801..=804 => Self::Clouds, + // NOTE(feliix42): There's no `Error` mapping or something like that. + _ => Self::Clear, + } + } +}