switchbot_api2/
switchbot.rs1use anyhow::{Context, Result, bail, ensure};
2use reqwest::Client;
3use reqwest::header::{AUTHORIZATION, HeaderMap};
4use serde::Deserialize;
5
6pub struct SwitchBot {
8 client: Client,
10}
11
12impl SwitchBot {
13 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 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 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#[derive(Clone, Debug, Deserialize, PartialEq)]
70struct ApiResponse<T> {
71 body: T,
73}
74
75#[derive(Clone, Debug, Deserialize, PartialEq)]
77struct ApiError {
78 message: String,
80}