enphase_local/
lib.rs

1// Copyright 2024 the octopower authors.
2// This project is dual-licensed under Apache 2.0 and MIT terms.
3// See LICENSE-APACHE and LICENSE-MIT for details.
4
5//! A client library for the Enphase Envoy local API.
6//!
7//! # Example
8//!
9//! ```
10//! use enphase_local::Envoy;
11//! use reqwest::Url;
12//!
13//! const AUTH_TOKEN: &str = "...";
14//!
15//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
16//! let envoy = Envoy::new(Url::parse("https://envoy.local/")?, AUTH_TOKEN);
17//! let production = envoy.production().await?;
18//! println!("Production: {:?}", production);
19//! # Ok(()) }
20//! ```
21
22pub mod home;
23pub mod inventory;
24pub mod inverters;
25pub mod meters;
26pub mod production;
27mod timestamp_string;
28
29use home::Home;
30use inventory::Inventory;
31use inverters::Inverter;
32use meters::{Reading, Report};
33use production::Production;
34use reqwest::{Client, Error, Url};
35
36/// Client for the Enphase Envoy local API.
37#[derive(Clone, Debug)]
38pub struct Envoy {
39    base_url: Url,
40    auth_token: String,
41    client: Client,
42}
43
44impl Envoy {
45    /// Constructs a new Enphase Envoy local API client with the given base URL and auth token.
46    pub fn new(base_url: Url, auth_token: &str) -> Self {
47        Self {
48            base_url,
49            auth_token: auth_token.to_owned(),
50            client: Client::builder()
51                .danger_accept_invalid_certs(true)
52                .build()
53                .unwrap(),
54        }
55    }
56
57    /// Returns a summary of the gateway status.
58    pub async fn home(&self) -> Result<Home, Error> {
59        self.client
60            .get(self.base_url.join("home.json").unwrap())
61            .send()
62            .await?
63            .error_for_status()?
64            .json()
65            .await
66    }
67
68    /// Returns an inventory of devices in the system.
69    pub async fn inventory(&self, include_deleted: bool) -> Result<Inventory, Error> {
70        let mut url = self.base_url.join("inventory.json").unwrap();
71        url.set_query(Some(&format!(
72            "deleted={}",
73            if include_deleted { 1 } else { 0 }
74        )));
75        self.client
76            .get(url)
77            .bearer_auth(&self.auth_token)
78            .send()
79            .await?
80            .error_for_status()?
81            .json()
82            .await
83    }
84
85    /// Returns statistics about current and past production and consumption.
86    pub async fn production(&self) -> Result<Production, Error> {
87        self.client
88            .get(self.base_url.join("production.json?details=1").unwrap())
89            .bearer_auth(&self.auth_token)
90            .send()
91            .await?
92            .error_for_status()?
93            .json()
94            .await
95    }
96
97    /// Gets readings from all meters.
98    pub async fn meter_readings(&self) -> Result<Vec<Reading>, Error> {
99        self.client
100            .get(self.base_url.join("ivp/meters/readings").unwrap())
101            .bearer_auth(&self.auth_token)
102            .send()
103            .await?
104            .error_for_status()?
105            .json()
106            .await
107    }
108
109    /// Gets reports from all meters.
110    pub async fn meter_reports(&self) -> Result<Vec<Report>, Error> {
111        self.client
112            .get(self.base_url.join("ivp/meters/reports").unwrap())
113            .bearer_auth(&self.auth_token)
114            .send()
115            .await?
116            .error_for_status()?
117            .json()
118            .await
119    }
120
121    /// Gets individual inverter production data from the v1 API.
122    pub async fn inverters(&self) -> Result<Vec<Inverter>, Error> {
123        self.client
124            .get(self.base_url.join("api/v1/production/inverters").unwrap())
125            .bearer_auth(&self.auth_token)
126            .send()
127            .await?
128            .error_for_status()?
129            .json()
130            .await
131    }
132}