switchbot_api2/
switchbot.rs

1use anyhow::{Context, Result, bail, ensure};
2use reqwest::Client;
3use reqwest::header::{AUTHORIZATION, HeaderMap};
4use serde::Deserialize;
5
6/// SwitchBot client.
7pub struct SwitchBot {
8    /// HTTP client.
9    client: Client,
10}
11
12impl SwitchBot {
13    /// Create a new SwitchBot client from environment variables.
14    pub fn new_from_env() -> Result<Self> {
15        let token = match std::env::var("SWITCHBOT_TOKEN") {
16            Ok(token) => token,
17            Err(_) => bail!("environment variable SWITCHBOT_TOKEN is not set"),
18        };
19
20        Self::new_from_token(&token)
21    }
22
23    /// Create a new SwitchBot client using the given token.
24    pub fn new_from_token(token: &str) -> Result<Self> {
25        let mut headers = HeaderMap::new();
26        headers.insert(AUTHORIZATION, token.parse()?);
27
28        let client = Client::builder().default_headers(headers).build()?;
29
30        Ok(Self { client })
31    }
32
33    /// Issue HTTP GET request and parse response.
34    pub async fn get<T: for<'de> Deserialize<'de>>(&self, path: &str) -> Result<T> {
35        let url = format!("https://api.switch-bot.com/{}", path);
36
37        let res = self
38            .client
39            .get(&url)
40            .send()
41            .await
42            .with_context(|| format!("failed to GET {}", url))?
43            .text()
44            .await
45            .with_context(|| format!("failed to read GET {}", url))?;
46
47        let msg = serde_json::from_str::<ApiError>(&res)
48            .with_context(|| format!("invalid SwitchBot API response for GET {}:\n{}", url, res))?
49            .message;
50        ensure!(
51            msg == "success",
52            "SwitchBot API returned an error for GET {}: {}",
53            url,
54            msg
55        );
56
57        let res = serde_json::from_str::<ApiResponse<T>>(&res).with_context(|| {
58            format!(
59                "failed to parse SwitchBot API response for GET {}:\n{}",
60                url, res
61            )
62        })?;
63
64        Ok(res.body)
65    }
66}
67
68/// A response from the SwitchBot API.
69#[derive(Clone, Debug, Deserialize, PartialEq)]
70struct ApiResponse<T> {
71    /// Response body.
72    body: T,
73}
74
75/// An error returned by the SwitchBot API.
76#[derive(Clone, Debug, Deserialize, PartialEq)]
77struct ApiError {
78    /// Error message.
79    message: String,
80}