Initial version of weather microservice
This commit is contained in:
3
microservices/readme.md
Normal file
3
microservices/readme.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Microservices
|
||||||
|
|
||||||
|
TODO
|
||||||
14
microservices/weather/.gitignore
vendored
Normal file
14
microservices/weather/.gitignore
vendored
Normal file
@@ -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
|
||||||
18
microservices/weather/Cargo.toml
Normal file
18
microservices/weather/Cargo.toml
Normal file
@@ -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"] }
|
||||||
6
microservices/weather/Settings.toml
Normal file
6
microservices/weather/Settings.toml
Normal file
@@ -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"
|
||||||
3
microservices/weather/readme.md
Normal file
3
microservices/weather/readme.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Weather microservice
|
||||||
|
|
||||||
|
This microservice will control all weather related actions around the house.
|
||||||
1
microservices/weather/rustfmt.toml
Normal file
1
microservices/weather/rustfmt.toml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
edition="2021"
|
||||||
62
microservices/weather/src/fetch.rs
Normal file
62
microservices/weather/src/fetch.rs
Normal file
@@ -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<Weather>,
|
||||||
|
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<Utc>,
|
||||||
|
) -> 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<Utc>,
|
||||||
|
) -> Result<WeatherResponse, reqwest::Error> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
microservices/weather/src/input.rs
Normal file
15
microservices/weather/src/input.rs
Normal file
@@ -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
|
||||||
|
}
|
||||||
47
microservices/weather/src/main.rs
Normal file
47
microservices/weather/src/main.rs
Normal file
@@ -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<input::Input, config::ConfigError> {
|
||||||
|
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::<input::Input>()
|
||||||
|
}
|
||||||
7
microservices/weather/src/mode.rs
Normal file
7
microservices/weather/src/mode.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Default)]
|
||||||
|
pub enum Mode {
|
||||||
|
#[default]
|
||||||
|
ReadWeatherData,
|
||||||
|
}
|
||||||
0
microservices/weather/src/publish.rs
Normal file
0
microservices/weather/src/publish.rs
Normal file
21
microservices/weather/src/write.rs
Normal file
21
microservices/weather/src/write.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use influxdb::{Client, InfluxDbWriteable};
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
#[derive(InfluxDbWriteable)]
|
||||||
|
pub struct WeatherReading {
|
||||||
|
pub time: DateTime<Utc>,
|
||||||
|
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(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user