From f1698db0e22bd14eafb71d33f401aac07fe4bb9c Mon Sep 17 00:00:00 2001 From: wserr Date: Sun, 7 May 2023 11:51:01 +0200 Subject: [PATCH] Initial version of weather microservice --- microservices/readme.md | 3 ++ microservices/weather/.gitignore | 14 +++++++ microservices/weather/Cargo.toml | 18 ++++++++ microservices/weather/Settings.toml | 6 +++ microservices/weather/readme.md | 3 ++ microservices/weather/rustfmt.toml | 1 + microservices/weather/src/fetch.rs | 62 ++++++++++++++++++++++++++++ microservices/weather/src/input.rs | 15 +++++++ microservices/weather/src/main.rs | 47 +++++++++++++++++++++ microservices/weather/src/mode.rs | 7 ++++ microservices/weather/src/publish.rs | 0 microservices/weather/src/write.rs | 21 ++++++++++ 12 files changed, 197 insertions(+) create mode 100644 microservices/readme.md create mode 100644 microservices/weather/.gitignore create mode 100644 microservices/weather/Cargo.toml create mode 100644 microservices/weather/Settings.toml create mode 100644 microservices/weather/readme.md create mode 100644 microservices/weather/rustfmt.toml create mode 100644 microservices/weather/src/fetch.rs create mode 100644 microservices/weather/src/input.rs create mode 100644 microservices/weather/src/main.rs create mode 100644 microservices/weather/src/mode.rs create mode 100644 microservices/weather/src/publish.rs create mode 100644 microservices/weather/src/write.rs diff --git a/microservices/readme.md b/microservices/readme.md new file mode 100644 index 0000000..d357649 --- /dev/null +++ b/microservices/readme.md @@ -0,0 +1,3 @@ +# Microservices + +TODO diff --git a/microservices/weather/.gitignore b/microservices/weather/.gitignore new file mode 100644 index 0000000..6985cf1 --- /dev/null +++ b/microservices/weather/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/microservices/weather/Cargo.toml b/microservices/weather/Cargo.toml new file mode 100644 index 0000000..99d8736 --- /dev/null +++ b/microservices/weather/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "weather" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.71" +chrono = "0.4.24" +config = "0.13.3" +env_logger = "0.10.0" +influxdb = { version = "0.6.0", features = ["derive"] } +log = "0.4.17" +reqwest = { version = "0.11.17", features = ["json", "serde_json"] } +serde = { version = "1.0.162", features = ["serde_derive"] } +serde_json = "1.0.96" +tokio = { version = "1.28.0", features = ["macros", "rt-multi-thread"] } diff --git a/microservices/weather/Settings.toml b/microservices/weather/Settings.toml new file mode 100644 index 0000000..e885a66 --- /dev/null +++ b/microservices/weather/Settings.toml @@ -0,0 +1,6 @@ +weather_api_base_url="https://api.openweathermap.org/data/2.5/weather" +weather_api_key="4ce897f7ce77f84919fd990cc68d9d20" +influx_db_base_url="http://jumpbox.io:8092" +influx_db_token="R1BGVaxWrX_ySaovweRz6ZTBDvHa5omQkricJGmn3PrI44jpfU7411PSmHOCjGcNR7OcBjmtE9gA62boHHXoKg==" +latitude="50.854019" +longitude="3.355230" diff --git a/microservices/weather/readme.md b/microservices/weather/readme.md new file mode 100644 index 0000000..d296156 --- /dev/null +++ b/microservices/weather/readme.md @@ -0,0 +1,3 @@ +# Weather microservice + +This microservice will control all weather related actions around the house. diff --git a/microservices/weather/rustfmt.toml b/microservices/weather/rustfmt.toml new file mode 100644 index 0000000..eec13eb --- /dev/null +++ b/microservices/weather/rustfmt.toml @@ -0,0 +1 @@ +edition="2021" diff --git a/microservices/weather/src/fetch.rs b/microservices/weather/src/fetch.rs new file mode 100644 index 0000000..a74edbd --- /dev/null +++ b/microservices/weather/src/fetch.rs @@ -0,0 +1,62 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct WeatherResponse { + pub id: usize, + pub weather: Vec, + pub wind: Wind, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Weather { + pub id: usize, + pub main: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Wind { + pub speed: f32, +} + +fn construct_weather_data_url( + base_url: &str, + latitude: &str, + longitude: &str, + api_key: &str, + current_datetime: &DateTime, +) -> String { + format!( + "{}?lat={}&lon={}&dt={}&appid={}", + base_url, latitude, longitude, current_datetime.timestamp(), api_key + ) +} + +pub async fn fetch_weather_data( + base_url: &str, + latitude: &str, + longitude: &str, + api_key: &str, + current_datetime: &DateTime, +) -> Result { + let url = construct_weather_data_url(base_url, latitude, longitude, api_key, current_datetime); + reqwest::get(url).await?.json().await +} + +#[cfg(test)] +mod test { + use super::*; + use chrono::prelude::*; + + #[test] + fn should_construct_url() { + let result = construct_weather_data_url( + "http://test", + "1.14", + "1.12", + "abc", + &Utc.with_ymd_and_hms(2023, 1, 2, 1, 0, 0).unwrap(), + ); + assert_eq!("http://test?lat=1.14&lon=1.12&dt=1672621200&appid=abc", result); + } +} diff --git a/microservices/weather/src/input.rs b/microservices/weather/src/input.rs new file mode 100644 index 0000000..8015d37 --- /dev/null +++ b/microservices/weather/src/input.rs @@ -0,0 +1,15 @@ +use serde::Deserialize; +use crate::mode::Mode; + +#[derive(Deserialize, Debug)] +pub struct Input +{ + #[serde(default)] + pub program_mode: Mode, + pub weather_api_base_url: String, + pub weather_api_key: String, + pub influx_db_base_url: String, + pub influx_db_token: String, + pub latitude: String, + pub longitude: String +} diff --git a/microservices/weather/src/main.rs b/microservices/weather/src/main.rs new file mode 100644 index 0000000..3e12289 --- /dev/null +++ b/microservices/weather/src/main.rs @@ -0,0 +1,47 @@ +mod fetch; +mod input; +mod mode; +mod write; + +use anyhow::Result; +use config::Config; +use log::info; +use chrono::Utc; + +#[tokio::main] +async fn main() -> Result<()> { + env_logger::init(); + + let settings: input::Input = fetch_settings()?; + + match settings.program_mode + { + mode::Mode::ReadWeatherData => read_weather_data(&settings).await? + }; + + Ok(()) +} + +async fn read_weather_data(settings: &input::Input) -> Result<()> { + info!("Start fetching weather data..."); + let result = fetch::fetch_weather_data(&settings.weather_api_base_url, &settings.latitude, &settings.longitude, &settings.weather_api_key, &Utc::now()).await?; + + info!("Start writing weather data..."); + let map = write::WeatherReading { + wind_speed: result.wind.speed, + time: Utc::now(), + }; + write::write_weather_data(map, &settings.influx_db_base_url, "homeassistant", &settings.influx_db_token).await?; + Ok(()) +} + +fn fetch_settings() -> Result { + let settings = Config::builder() + // Add in `./Settings.toml` + .add_source(config::File::with_name("./Settings").required(false)) + .add_source(config::Environment::with_prefix("weather")) + .build() + .unwrap(); + + settings.try_deserialize::() +} diff --git a/microservices/weather/src/mode.rs b/microservices/weather/src/mode.rs new file mode 100644 index 0000000..8187edc --- /dev/null +++ b/microservices/weather/src/mode.rs @@ -0,0 +1,7 @@ +use serde::Deserialize; + +#[derive(Deserialize, Debug, Default)] +pub enum Mode { + #[default] + ReadWeatherData, +} diff --git a/microservices/weather/src/publish.rs b/microservices/weather/src/publish.rs new file mode 100644 index 0000000..e69de29 diff --git a/microservices/weather/src/write.rs b/microservices/weather/src/write.rs new file mode 100644 index 0000000..54c1006 --- /dev/null +++ b/microservices/weather/src/write.rs @@ -0,0 +1,21 @@ +use chrono::{DateTime, Utc}; +use influxdb::{Client, InfluxDbWriteable}; +use anyhow::Result; + +#[derive(InfluxDbWriteable)] +pub struct WeatherReading { + pub time: DateTime, + pub wind_speed: f32, +} + +pub async fn write_weather_data( + reading: WeatherReading, + base_url: &str, + database: &str, + token: &str, +) -> Result<()> { + let client = Client::new(base_url, database).with_token(token); + let query = reading.into_query("WeatherReading"); + client.query(query).await?; + Ok(()) +}