ambient_weather_api/lib.rs
1//! # Ambient Weather API
2//!
3//! `ambient_weather_api` is a collection of functions for downloading current and historical data from the Ambient Weather API. It features built in support for choosing which device you want to pull data from, and has (some) safety measures built in to avoid hitting Ambient Weather's rate limits.
4//!
5//! To learn more about how the Ambient Weather API works, and to obtain the required API and Application keys to use this creat, check out the [Ambient Weather API Documentation](https://ambientweather.docs.apiary.io).
6//!
7//! In order to use this API, you will need to look over the [list of device parameters](https://github.com/ambient-weather/api-docs/wiki/Device-Data-Specs) that Ambient Weather offers. Not all device parameters may be used, so make sure you are calling one that is associated with your device.
8//!
9//! Currently, this Rust crate is only capable of utilizing the Ambient Weather REST API. Support for their Realtime Socket.IO API will come at a later date.
10//!
11//! # Getting Started
12//!
13//! To get started with pulling in the latest weather data from your Ambient Weather device, simply follow the example below:
14//!
15//! ```
16//! use ambient_weather_api::*;
17//!
18//! fn main() {
19//!
20//! let api_credentials = AmbientWeatherAPICredentials {
21//! api_key: String::from("Your API Key"),
22//! app_key: String::from("Your Application Key"),
23//! device_id: 0,
24//! use_new_api_endpoint: false,
25//! };
26//!
27//! // Get the current temperature
28//! let latest_data = get_latest_device_data(&api_credentials);
29//! println!("The current temp is: {}F", latest_data.tempf.unwrap());
30//!
31//! // Get the historic temperatures and loop through them going back in time
32//! let historic_data = get_historic_device_data(&api_credentials);
33//! for i in 0..historic_data.len() {
34//! println!("The historic temp was: {}F", historic_data[i].tempf.unwrap());
35//! }
36//! }
37//! ```
38
39use serde_json::{self, json, Value};
40use std::{thread, time::Duration};
41
42mod weather_data_struct;
43
44#[derive(Clone)]
45
46/// The struct for holding the API and App keys, the device idea, and whether or not to use the new API endpoint or not.
47pub struct AmbientWeatherAPICredentials {
48 /// The API key received from Ambient Weather.
49 pub api_key: String,
50 /// The Application key received from Ambient Weather.
51 pub app_key: String,
52 /// The device id, which for a user with a single station will be 0.
53 pub device_id: usize,
54 /// A bool to determine if the new API endpoint should be used. Due to problematic behavior, I recommend leaving this set to false.
55 pub use_new_api_endpoint: bool,
56}
57
58/// A private function for crafting the appropriate Ambient Weather API URL.
59fn get_aw_api_url(
60 api_credentials: &AmbientWeatherAPICredentials,
61 device_mac_address: &str,
62) -> String {
63 let url_endpoint = if api_credentials.use_new_api_endpoint {
64 "rt"
65 } else {
66 "api"
67 };
68
69 format!("https://{url_endpoint}.ambientweather.net/v1/devices/{device_mac_address}?applicationKey={}&apiKey={}", api_credentials.app_key, api_credentials.api_key)
70}
71
72/// A private function that gets the raw device data from the Ambient Weather REST API, and then returns either the latest or the historical data for a device
73#[tokio::main]
74async fn get_raw_device_data(
75 api_credentials: &AmbientWeatherAPICredentials,
76 device_mac_address: String,
77 retrieve_history: bool,
78) -> Result<Value, reqwest::Error> {
79 let device_id = api_credentials.device_id;
80
81 let response: Value = reqwest::get(get_aw_api_url(api_credentials, &device_mac_address))
82 .await?
83 .json()
84 .await?;
85
86 thread::sleep(Duration::from_millis(1000));
87
88 // If True, this will get and return the historic data for a given device
89 if retrieve_history {
90 let mut device_mac_address =
91 response[device_id].as_object().unwrap()["macAddress"].to_string();
92
93 device_mac_address.pop();
94 device_mac_address.remove(0);
95
96 let historical_response: Value =
97 reqwest::get(get_aw_api_url(api_credentials, &device_mac_address))
98 .await?
99 .json()
100 .await?;
101
102 thread::sleep(Duration::from_millis(1000));
103
104 return Ok(json!(historical_response));
105 }
106
107 Ok(json!(response[device_id]))
108}
109
110/// Gets the latest device data from the Ambient Weather API.
111///
112/// In order to use this API, you will need to look over the [list of device parameters](https://github.com/ambient-weather/api-docs/wiki/Device-Data-Specs) that Ambient Weather offers. Not all device parameters may be used, so make sure you are calling one that is associated with your device.
113///
114/// When calling the `get_latest_device_data` function, you must pass the api_credentials as a reference (`&api_credentials`), as this allows for it to be called multiple times elsewhere in a program if necessary.
115///
116/// # Examples
117///
118/// ```
119/// use ambient_weather_api::*;
120///
121/// fn main() {
122///
123/// let api_credentials = AmbientWeatherAPICredentials {
124/// api_key: String::from("Your API Key"),
125/// app_key: String::from("Your Application Key"),
126/// device_id: 0,
127/// use_new_api_endpoint: false,
128/// };
129///
130/// // Get the current temperature
131/// let latest_data = get_latest_device_data(&api_credentials);
132/// println!("The current temp is: {}F", latest_data.tempf.unwrap());
133///
134/// }
135/// ```
136pub fn get_latest_device_data(
137 api_credentials: &AmbientWeatherAPICredentials,
138) -> weather_data_struct::WeatherData {
139 let raw_device_data =
140 get_raw_device_data(api_credentials, "".to_string(), false).unwrap();
141
142 serde_json::from_value(json!(raw_device_data["lastData"])).unwrap_or(weather_data_struct::WeatherData::default())
143}
144
145/// Gets the historic device data from the Ambient Weather API.
146///
147/// As of version 0.3.0 this now functions in an asynchronous manner.
148///
149/// In order to use this API, you will need to look over the [list of device parameters](https://github.com/ambient-weather/api-docs/wiki/Device-Data-Specs) that Ambient Weather offers. Not all device parameters may be used, so make sure you are calling one that is associated with your device.
150///
151/// # Examples
152///
153/// ```
154/// use ambient_weather_api::*;
155///
156/// fn main() {
157///
158/// let api_credentials = AmbientWeatherAPICredentials {
159/// api_key: String::from("Your API Key"),
160/// app_key: String::from("Your Application Key"),
161/// device_id: 0,
162/// use_new_api_endpoint: false,
163/// };
164///
165/// // Get the historic temperatures and loop through them going back in time
166/// let historic_data = get_historic_device_data(&api_credentials);
167/// for i in 0..historic_data.len() {
168/// println!("The historic temp was: {}F", historic_data[i].tempf.unwrap());
169/// }
170///
171/// }
172/// ```
173pub fn get_historic_device_data(
174 api_credentials: &AmbientWeatherAPICredentials,
175) -> Vec<weather_data_struct::WeatherData> {
176 let raw_device_data =
177 get_raw_device_data(api_credentials, "".to_string(), true).unwrap();
178
179 let weather_data_array: Vec<Value> = raw_device_data
180 .as_array()
181 .unwrap()
182 .to_vec();
183
184 weather_data_array
185 .into_iter()
186 .map(|data| serde_json::from_value(data)
187 .unwrap())
188 .collect()
189}