ambient_weather_progenitor/lib.rs
1pub use progenitor_client::{ByteStream, Error, ResponseValue};
2#[allow(unused_imports)]
3use progenitor_client::{encode_path, RequestBuilderExt};
4#[allow(unused_imports)]
5use reqwest::header::{HeaderMap, HeaderValue};
6/// Types used as operation parameters and responses.
7#[allow(clippy::all)]
8pub mod types {
9 use serde::{Deserialize, Serialize};
10 #[allow(unused_imports)]
11 use std::convert::TryFrom;
12 /// Error types.
13 pub mod error {
14 /// Error from a TryFrom or FromStr implementation.
15 pub struct ConversionError(std::borrow::Cow<'static, str>);
16 impl std::error::Error for ConversionError {}
17 impl std::fmt::Display for ConversionError {
18 fn fmt(
19 &self,
20 f: &mut std::fmt::Formatter<'_>,
21 ) -> Result<(), std::fmt::Error> {
22 std::fmt::Display::fmt(&self.0, f)
23 }
24 }
25 impl std::fmt::Debug for ConversionError {
26 fn fmt(
27 &self,
28 f: &mut std::fmt::Formatter<'_>,
29 ) -> Result<(), std::fmt::Error> {
30 std::fmt::Debug::fmt(&self.0, f)
31 }
32 }
33 impl From<&'static str> for ConversionError {
34 fn from(value: &'static str) -> Self {
35 Self(value.into())
36 }
37 }
38 impl From<String> for ConversionError {
39 fn from(value: String) -> Self {
40 Self(value.into())
41 }
42 }
43 }
44 ///ListUsersDevicesResponseItem
45 ///
46 /// <details><summary>JSON schema</summary>
47 ///
48 /// ```json
49 ///{
50 /// "type": "object",
51 /// "properties": {
52 /// "info": {
53 /// "type": "object",
54 /// "properties": {
55 /// "location": {
56 /// "type": "string"
57 /// },
58 /// "name": {
59 /// "type": "string"
60 /// }
61 /// }
62 /// },
63 /// "lastData": {
64 /// "type": "object",
65 /// "properties": {
66 /// "baromabsin": {
67 /// "type": "number"
68 /// },
69 /// "baromrelin": {
70 /// "type": "number"
71 /// },
72 /// "dailyrainin": {
73 /// "type": "number"
74 /// },
75 /// "date": {
76 /// "type": "string",
77 /// "format": "date-time"
78 /// },
79 /// "dateutc": {
80 /// "type": "number"
81 /// },
82 /// "dewPoint": {
83 /// "type": "number"
84 /// },
85 /// "feelsLike": {
86 /// "type": "number"
87 /// },
88 /// "hourlyrainin": {
89 /// "type": "number"
90 /// },
91 /// "humidity": {
92 /// "type": "number"
93 /// },
94 /// "humidityin": {
95 /// "type": "number"
96 /// },
97 /// "maxdailygust": {
98 /// "type": "number"
99 /// },
100 /// "monthlyrainin": {
101 /// "type": "number"
102 /// },
103 /// "tempf": {
104 /// "type": "number"
105 /// },
106 /// "tempinf": {
107 /// "type": "number"
108 /// },
109 /// "winddir": {
110 /// "type": "number"
111 /// },
112 /// "winddir_avg10m": {
113 /// "type": "number"
114 /// },
115 /// "winddir_avg2m": {
116 /// "type": "number"
117 /// },
118 /// "windgustdir": {
119 /// "type": "number"
120 /// },
121 /// "windgustmph": {
122 /// "type": "number"
123 /// },
124 /// "windspdmph_avg10m": {
125 /// "type": "number"
126 /// },
127 /// "windspdmph_avg2m": {
128 /// "type": "number"
129 /// },
130 /// "windspeedmph": {
131 /// "type": "number"
132 /// },
133 /// "yearlyrainin": {
134 /// "type": "number"
135 /// }
136 /// }
137 /// },
138 /// "macAddress": {
139 /// "type": "string"
140 /// }
141 /// }
142 ///}
143 /// ```
144 /// </details>
145 #[derive(Clone, Debug, Deserialize, Serialize)]
146 pub struct ListUsersDevicesResponseItem {
147 #[serde(default, skip_serializing_if = "Option::is_none")]
148 pub info: Option<ListUsersDevicesResponseItemInfo>,
149 #[serde(rename = "lastData", default, skip_serializing_if = "Option::is_none")]
150 pub last_data: Option<ListUsersDevicesResponseItemLastData>,
151 #[serde(rename = "macAddress", default, skip_serializing_if = "Option::is_none")]
152 pub mac_address: Option<String>,
153 }
154 impl From<&ListUsersDevicesResponseItem> for ListUsersDevicesResponseItem {
155 fn from(value: &ListUsersDevicesResponseItem) -> Self {
156 value.clone()
157 }
158 }
159 ///ListUsersDevicesResponseItemInfo
160 ///
161 /// <details><summary>JSON schema</summary>
162 ///
163 /// ```json
164 ///{
165 /// "type": "object",
166 /// "properties": {
167 /// "location": {
168 /// "type": "string"
169 /// },
170 /// "name": {
171 /// "type": "string"
172 /// }
173 /// }
174 ///}
175 /// ```
176 /// </details>
177 #[derive(Clone, Debug, Deserialize, Serialize)]
178 pub struct ListUsersDevicesResponseItemInfo {
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub location: Option<String>,
181 #[serde(default, skip_serializing_if = "Option::is_none")]
182 pub name: Option<String>,
183 }
184 impl From<&ListUsersDevicesResponseItemInfo> for ListUsersDevicesResponseItemInfo {
185 fn from(value: &ListUsersDevicesResponseItemInfo) -> Self {
186 value.clone()
187 }
188 }
189 ///ListUsersDevicesResponseItemLastData
190 ///
191 /// <details><summary>JSON schema</summary>
192 ///
193 /// ```json
194 ///{
195 /// "type": "object",
196 /// "properties": {
197 /// "baromabsin": {
198 /// "type": "number"
199 /// },
200 /// "baromrelin": {
201 /// "type": "number"
202 /// },
203 /// "dailyrainin": {
204 /// "type": "number"
205 /// },
206 /// "date": {
207 /// "type": "string",
208 /// "format": "date-time"
209 /// },
210 /// "dateutc": {
211 /// "type": "number"
212 /// },
213 /// "dewPoint": {
214 /// "type": "number"
215 /// },
216 /// "feelsLike": {
217 /// "type": "number"
218 /// },
219 /// "hourlyrainin": {
220 /// "type": "number"
221 /// },
222 /// "humidity": {
223 /// "type": "number"
224 /// },
225 /// "humidityin": {
226 /// "type": "number"
227 /// },
228 /// "maxdailygust": {
229 /// "type": "number"
230 /// },
231 /// "monthlyrainin": {
232 /// "type": "number"
233 /// },
234 /// "tempf": {
235 /// "type": "number"
236 /// },
237 /// "tempinf": {
238 /// "type": "number"
239 /// },
240 /// "winddir": {
241 /// "type": "number"
242 /// },
243 /// "winddir_avg10m": {
244 /// "type": "number"
245 /// },
246 /// "winddir_avg2m": {
247 /// "type": "number"
248 /// },
249 /// "windgustdir": {
250 /// "type": "number"
251 /// },
252 /// "windgustmph": {
253 /// "type": "number"
254 /// },
255 /// "windspdmph_avg10m": {
256 /// "type": "number"
257 /// },
258 /// "windspdmph_avg2m": {
259 /// "type": "number"
260 /// },
261 /// "windspeedmph": {
262 /// "type": "number"
263 /// },
264 /// "yearlyrainin": {
265 /// "type": "number"
266 /// }
267 /// }
268 ///}
269 /// ```
270 /// </details>
271 #[derive(Clone, Debug, Deserialize, Serialize)]
272 pub struct ListUsersDevicesResponseItemLastData {
273 #[serde(default, skip_serializing_if = "Option::is_none")]
274 pub baromabsin: Option<f64>,
275 #[serde(default, skip_serializing_if = "Option::is_none")]
276 pub baromrelin: Option<f64>,
277 #[serde(default, skip_serializing_if = "Option::is_none")]
278 pub dailyrainin: Option<f64>,
279 #[serde(default, skip_serializing_if = "Option::is_none")]
280 pub date: Option<chrono::DateTime<chrono::offset::Utc>>,
281 #[serde(default, skip_serializing_if = "Option::is_none")]
282 pub dateutc: Option<f64>,
283 #[serde(rename = "dewPoint", default, skip_serializing_if = "Option::is_none")]
284 pub dew_point: Option<f64>,
285 #[serde(rename = "feelsLike", default, skip_serializing_if = "Option::is_none")]
286 pub feels_like: Option<f64>,
287 #[serde(default, skip_serializing_if = "Option::is_none")]
288 pub hourlyrainin: Option<f64>,
289 #[serde(default, skip_serializing_if = "Option::is_none")]
290 pub humidity: Option<f64>,
291 #[serde(default, skip_serializing_if = "Option::is_none")]
292 pub humidityin: Option<f64>,
293 #[serde(default, skip_serializing_if = "Option::is_none")]
294 pub maxdailygust: Option<f64>,
295 #[serde(default, skip_serializing_if = "Option::is_none")]
296 pub monthlyrainin: Option<f64>,
297 #[serde(default, skip_serializing_if = "Option::is_none")]
298 pub tempf: Option<f64>,
299 #[serde(default, skip_serializing_if = "Option::is_none")]
300 pub tempinf: Option<f64>,
301 #[serde(default, skip_serializing_if = "Option::is_none")]
302 pub winddir: Option<f64>,
303 #[serde(default, skip_serializing_if = "Option::is_none")]
304 pub winddir_avg10m: Option<f64>,
305 #[serde(default, skip_serializing_if = "Option::is_none")]
306 pub winddir_avg2m: Option<f64>,
307 #[serde(default, skip_serializing_if = "Option::is_none")]
308 pub windgustdir: Option<f64>,
309 #[serde(default, skip_serializing_if = "Option::is_none")]
310 pub windgustmph: Option<f64>,
311 #[serde(default, skip_serializing_if = "Option::is_none")]
312 pub windspdmph_avg10m: Option<f64>,
313 #[serde(default, skip_serializing_if = "Option::is_none")]
314 pub windspdmph_avg2m: Option<f64>,
315 #[serde(default, skip_serializing_if = "Option::is_none")]
316 pub windspeedmph: Option<f64>,
317 #[serde(default, skip_serializing_if = "Option::is_none")]
318 pub yearlyrainin: Option<f64>,
319 }
320 impl From<&ListUsersDevicesResponseItemLastData>
321 for ListUsersDevicesResponseItemLastData {
322 fn from(value: &ListUsersDevicesResponseItemLastData) -> Self {
323 value.clone()
324 }
325 }
326 ///QueryDeviceDataResponseItem
327 ///
328 /// <details><summary>JSON schema</summary>
329 ///
330 /// ```json
331 ///{
332 /// "type": "object",
333 /// "properties": {
334 /// "baromabsin": {
335 /// "type": "number"
336 /// },
337 /// "baromrelin": {
338 /// "type": "number"
339 /// },
340 /// "dailyrainin": {
341 /// "type": "number"
342 /// },
343 /// "date": {
344 /// "type": "string",
345 /// "format": "date-time"
346 /// },
347 /// "dateutc": {
348 /// "type": "number"
349 /// },
350 /// "dewPoint": {
351 /// "type": "number"
352 /// },
353 /// "feelsLike": {
354 /// "type": "number"
355 /// },
356 /// "hourlyrainin": {
357 /// "type": "number"
358 /// },
359 /// "humidity": {
360 /// "type": "number"
361 /// },
362 /// "humidityin": {
363 /// "type": "number"
364 /// },
365 /// "maxdailygust": {
366 /// "type": "number"
367 /// },
368 /// "monthlyrainin": {
369 /// "type": "number"
370 /// },
371 /// "tempf": {
372 /// "type": "number"
373 /// },
374 /// "tempinf": {
375 /// "type": "number"
376 /// },
377 /// "winddir": {
378 /// "type": "number"
379 /// },
380 /// "winddir_avg10m": {
381 /// "type": "number"
382 /// },
383 /// "winddir_avg2m": {
384 /// "type": "number"
385 /// },
386 /// "windgustdir": {
387 /// "type": "number"
388 /// },
389 /// "windgustmph": {
390 /// "type": "number"
391 /// },
392 /// "windspdmph_avg10m": {
393 /// "type": "number"
394 /// },
395 /// "windspdmph_avg2m": {
396 /// "type": "number"
397 /// },
398 /// "windspeedmph": {
399 /// "type": "number"
400 /// },
401 /// "yearlyrainin": {
402 /// "type": "number"
403 /// }
404 /// }
405 ///}
406 /// ```
407 /// </details>
408 #[derive(Clone, Debug, Deserialize, Serialize)]
409 pub struct QueryDeviceDataResponseItem {
410 #[serde(default, skip_serializing_if = "Option::is_none")]
411 pub baromabsin: Option<f64>,
412 #[serde(default, skip_serializing_if = "Option::is_none")]
413 pub baromrelin: Option<f64>,
414 #[serde(default, skip_serializing_if = "Option::is_none")]
415 pub dailyrainin: Option<f64>,
416 #[serde(default, skip_serializing_if = "Option::is_none")]
417 pub date: Option<chrono::DateTime<chrono::offset::Utc>>,
418 #[serde(default, skip_serializing_if = "Option::is_none")]
419 pub dateutc: Option<f64>,
420 #[serde(rename = "dewPoint", default, skip_serializing_if = "Option::is_none")]
421 pub dew_point: Option<f64>,
422 #[serde(rename = "feelsLike", default, skip_serializing_if = "Option::is_none")]
423 pub feels_like: Option<f64>,
424 #[serde(default, skip_serializing_if = "Option::is_none")]
425 pub hourlyrainin: Option<f64>,
426 #[serde(default, skip_serializing_if = "Option::is_none")]
427 pub humidity: Option<f64>,
428 #[serde(default, skip_serializing_if = "Option::is_none")]
429 pub humidityin: Option<f64>,
430 #[serde(default, skip_serializing_if = "Option::is_none")]
431 pub maxdailygust: Option<f64>,
432 #[serde(default, skip_serializing_if = "Option::is_none")]
433 pub monthlyrainin: Option<f64>,
434 #[serde(default, skip_serializing_if = "Option::is_none")]
435 pub tempf: Option<f64>,
436 #[serde(default, skip_serializing_if = "Option::is_none")]
437 pub tempinf: Option<f64>,
438 #[serde(default, skip_serializing_if = "Option::is_none")]
439 pub winddir: Option<f64>,
440 #[serde(default, skip_serializing_if = "Option::is_none")]
441 pub winddir_avg10m: Option<f64>,
442 #[serde(default, skip_serializing_if = "Option::is_none")]
443 pub winddir_avg2m: Option<f64>,
444 #[serde(default, skip_serializing_if = "Option::is_none")]
445 pub windgustdir: Option<f64>,
446 #[serde(default, skip_serializing_if = "Option::is_none")]
447 pub windgustmph: Option<f64>,
448 #[serde(default, skip_serializing_if = "Option::is_none")]
449 pub windspdmph_avg10m: Option<f64>,
450 #[serde(default, skip_serializing_if = "Option::is_none")]
451 pub windspdmph_avg2m: Option<f64>,
452 #[serde(default, skip_serializing_if = "Option::is_none")]
453 pub windspeedmph: Option<f64>,
454 #[serde(default, skip_serializing_if = "Option::is_none")]
455 pub yearlyrainin: Option<f64>,
456 }
457 impl From<&QueryDeviceDataResponseItem> for QueryDeviceDataResponseItem {
458 fn from(value: &QueryDeviceDataResponseItem) -> Self {
459 value.clone()
460 }
461 }
462}
463#[derive(Clone, Debug)]
464/**Client for Ambient Weather REST API
465
466Access an Ambient Weather user's weather station data programmatically using our REST API
467
468##### Authentication
469
470Two API Keys are required for all REST API requests:
471
472+ `applicationKey` - identifies the developer / application. To create an application key please login to your AmbientWeather.net account page (https://ambientweather.net/account)
473
474+ `apiKey` - grants access to past/present data for a given user's devices. A typical consumer-facing application will initially ask the user to create an `apiKey` on their AmbientWeather.net account page (https://ambientweather.net/account) and paste it into the app. Developers for personal or in-house apps will also need to create an apiKey on their own account page.
475
476##### Rate Limiting
477
478API requests are capped at 1 request/second for each user's apiKey and 3 requests/second per applicationKey. When this limit is exceeded, the API will return a 429 response code. Please be kind to our servers :)
479
480##### Helper Libraries
481
482+ Node.js - https://github.com/owise1/ambient-weather-api
483
484+ PHP (Laravel) - https://github.com/Jafo232/ambient_api
485
486+ Rust - https://github.com/JoshuaKimsey/ambient-weather-api
487
488+ Go - https://github.com/lrosenman/ambient
489
490+ Python - https://github.com/avryhof/ambient_api
491
492+ Python (asyncio) - https://github.com/bachya/aioambient
493
494+ R - https://github.com/andrewflack/ambientweatheR
495
496+ Java - https://github.com/rsv-code/ambient-weather-java
497
498+ Swift - https://github.com/MikeManzo/SwiftyWeatherKit
499
500+ .NET 5 - https://github.com/ChaseDRedmon/Cirrus
501
502##### Other Resources
503
504+ API Wiki - https://github.com/ambient-weather/api-docs/wiki
505
506+ This documentation's repository - https://github.com/ambient-weather/api-docs
507
508Version: 1.0.0*/
509pub struct Client {
510 pub(crate) baseurl: String,
511 pub(crate) client: reqwest::Client,
512}
513impl Client {
514 /// Create a new client.
515 ///
516 /// `baseurl` is the base URL provided to the internal
517 /// `reqwest::Client`, and should include a scheme and hostname,
518 /// as well as port and a path stem if applicable.
519 pub fn new(baseurl: &str) -> Self {
520 #[cfg(not(target_arch = "wasm32"))]
521 let client = {
522 let dur = std::time::Duration::from_secs(15);
523 reqwest::ClientBuilder::new().connect_timeout(dur).timeout(dur)
524 };
525 #[cfg(target_arch = "wasm32")]
526 let client = reqwest::ClientBuilder::new();
527 Self::new_with_client(baseurl, client.build().unwrap())
528 }
529 /// Construct a new client with an existing `reqwest::Client`,
530 /// allowing more control over its configuration.
531 ///
532 /// `baseurl` is the base URL provided to the internal
533 /// `reqwest::Client`, and should include a scheme and hostname,
534 /// as well as port and a path stem if applicable.
535 pub fn new_with_client(baseurl: &str, client: reqwest::Client) -> Self {
536 Self {
537 baseurl: baseurl.to_string(),
538 client,
539 }
540 }
541 /// Get the base URL to which requests are made.
542 pub fn baseurl(&self) -> &String {
543 &self.baseurl
544 }
545 /// Get the internal `reqwest::Client` used to make requests.
546 pub fn client(&self) -> &reqwest::Client {
547 &self.client
548 }
549 /// Get the version of this API.
550 ///
551 /// This string is pulled directly from the source OpenAPI
552 /// document and may be in any format the API selects.
553 pub fn api_version(&self) -> &'static str {
554 "1.0.0"
555 }
556}
557#[allow(clippy::all)]
558impl Client {
559 /**List User's Devices
560
561Provides a list of the user's available devices along with each device's most recent data.
562
563Sends a `GET` request to `/v1/devices`
564
565Arguments:
566- `api_key`: API Key for user account
567- `application_key`: Application Key
568*/
569 pub async fn list_users_devices<'a>(
570 &'a self,
571 api_key: &'a str,
572 application_key: &'a str,
573 ) -> Result<ResponseValue<Vec<types::ListUsersDevicesResponseItem>>, Error<()>> {
574 let url = format!("{}/v1/devices", self.baseurl,);
575 let mut query = Vec::with_capacity(2usize);
576 query.push(("apiKey", api_key.to_string()));
577 query.push(("applicationKey", application_key.to_string()));
578 #[allow(unused_mut)]
579 let mut request = self
580 .client
581 .get(url)
582 .header(
583 reqwest::header::ACCEPT,
584 reqwest::header::HeaderValue::from_static("application/json"),
585 )
586 .query(&query)
587 .build()?;
588 let result = self.client.execute(request).await;
589 let response = result?;
590 match response.status().as_u16() {
591 200u16 => ResponseValue::from_response(response).await,
592 429u16 => Err(Error::ErrorResponse(ResponseValue::empty(response))),
593 _ => Err(Error::UnexpectedResponse(response)),
594 }
595 }
596 /**Query Device Data
597
598Fetch data for a given device. Data is stored in 5 or 30 minute increments. A list of all possible fields is here: https://github.com/ambient-weather/api-docs/wiki/Device-Data-Specs
599
600Sends a `GET` request to `/v1/devices/{macAddress}`
601
602Arguments:
603- `mac_address`: device Mac Address
604- `api_key`: API Key for user account
605- `application_key`: Application Key
606- `end_date`: The most recent datetime. Results descend from there. If left blank, the most recent results will be returned. Date format should be in milliseconds since the epoch or string representations outlined here: https://momentjs.com/docs/#/parsing/string/. Note: datetimes are stored in UTC.
607- `limit`: The maximum number of results to return (max: 288)
608*/
609 pub async fn query_device_data<'a>(
610 &'a self,
611 mac_address: &'a str,
612 api_key: &'a str,
613 application_key: &'a str,
614 end_date: Option<&'a chrono::DateTime<chrono::offset::Utc>>,
615 limit: Option<f64>,
616 ) -> Result<ResponseValue<Vec<types::QueryDeviceDataResponseItem>>, Error<()>> {
617 let url = format!(
618 "{}/v1/devices/{}", self.baseurl, encode_path(& mac_address.to_string()),
619 );
620 let mut query = Vec::with_capacity(4usize);
621 query.push(("apiKey", api_key.to_string()));
622 query.push(("applicationKey", application_key.to_string()));
623 if let Some(v) = &end_date {
624 query.push(("endDate", v.to_string()));
625 }
626 if let Some(v) = &limit {
627 query.push(("limit", v.to_string()));
628 }
629 #[allow(unused_mut)]
630 let mut request = self
631 .client
632 .get(url)
633 .header(
634 reqwest::header::ACCEPT,
635 reqwest::header::HeaderValue::from_static("application/json"),
636 )
637 .query(&query)
638 .build()?;
639 let result = self.client.execute(request).await;
640 let response = result?;
641 match response.status().as_u16() {
642 200u16 => ResponseValue::from_response(response).await,
643 429u16 => Err(Error::ErrorResponse(ResponseValue::empty(response))),
644 _ => Err(Error::UnexpectedResponse(response)),
645 }
646 }
647}
648/// Items consumers will typically use such as the Client.
649pub mod prelude {
650 pub use super::Client;
651}