enphase_local/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright 2024 the octopower authors.
// This project is dual-licensed under Apache 2.0 and MIT terms.
// See LICENSE-APACHE and LICENSE-MIT for details.

//! A client library for the Enphase Envoy local API.
//!
//! # Example
//!
//! ```
//! use enphase_local::Envoy;
//! use reqwest::Url;
//!
//! const AUTH_TOKEN: &str = "...";
//!
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let envoy = Envoy::new(Url::parse("https://envoy.local/")?, AUTH_TOKEN);
//! let production = envoy.production().await?;
//! println!("Production: {:?}", production);
//! # Ok(()) }
//! ```

pub mod home;
pub mod inventory;
pub mod meters;
pub mod production;
mod timestamp_string;

use home::Home;
use inventory::Inventory;
use meters::{Reading, Report};
use production::Production;
use reqwest::{Client, Error, Url};

/// Client for the Enphase Envoy local API.
#[derive(Clone, Debug)]
pub struct Envoy {
    base_url: Url,
    auth_token: String,
    client: Client,
}

impl Envoy {
    /// Constructs a new Enphase Envoy local API client with the given base URL and auth token.
    pub fn new(base_url: Url, auth_token: &str) -> Self {
        Self {
            base_url,
            auth_token: auth_token.to_owned(),
            client: Client::builder()
                .danger_accept_invalid_certs(true)
                .build()
                .unwrap(),
        }
    }

    /// Returns a summary of the gateway status.
    pub async fn home(&self) -> Result<Home, Error> {
        self.client
            .get(self.base_url.join("home.json").unwrap())
            .send()
            .await?
            .error_for_status()?
            .json()
            .await
    }

    /// Returns an inventory of devices in the system.
    pub async fn inventory(&self, include_deleted: bool) -> Result<Inventory, Error> {
        let mut url = self.base_url.join("inventory.json").unwrap();
        url.set_query(Some(&format!(
            "deleted={}",
            if include_deleted { 1 } else { 0 }
        )));
        self.client
            .get(url)
            .bearer_auth(&self.auth_token)
            .send()
            .await?
            .error_for_status()?
            .json()
            .await
    }

    /// Returns statistics about current and past production and consumption.
    pub async fn production(&self) -> Result<Production, Error> {
        self.client
            .get(self.base_url.join("production.json?details=1").unwrap())
            .bearer_auth(&self.auth_token)
            .send()
            .await?
            .error_for_status()?
            .json()
            .await
    }

    /// Gets readings from all meters.
    pub async fn meter_readings(&self) -> Result<Vec<Reading>, Error> {
        self.client
            .get(self.base_url.join("ivp/meters/readings").unwrap())
            .bearer_auth(&self.auth_token)
            .send()
            .await?
            .error_for_status()?
            .json()
            .await
    }

    /// Gets reports from all meters.
    pub async fn meter_reports(&self) -> Result<Vec<Report>, Error> {
        self.client
            .get(self.base_url.join("ivp/meters/reports").unwrap())
            .bearer_auth(&self.auth_token)
            .send()
            .await?
            .error_for_status()?
            .json()
            .await
    }
}