Skip to main content

Crate provident

Crate provident 

Source
Expand description

§provident

crates.io docs.rs CI License: GPL-3.0-only

Unofficial Rust client for the Provident (MeterConnex) utility metering portal: sign in, list your meters, and fetch consumption history and billing-period reports for electricity, water, heating, cooling, and gas.

Not affiliated with or endorsed by Provident or QMC. The portal has no public API; this crate talks to the same endpoints its web pages use, which may change without notice.

Python bindings are published on PyPI as provident (pip install provident); see python/README.md.

§Installation

[dependencies]
provident = "0.1"

The client is async by default and runtime-agnostic (any executor that can drive reqwest). For a blocking client, disable default features:

[dependencies]
provident = { version = "0.1", default-features = false, features = ["blocking"] }

async and blocking are mutually exclusive: enable exactly one. TLS uses the platform’s native stack (OpenSSL on Linux), because the portal only offers cipher suites rustls does not support.

§Usage

use chrono::{Days, NaiveDate};
use provident::Client;
use provident::models::api::internal::graphs::quick_graphs::QuickGraphsRequest;
use provident::models::common::Utility;
use provident::models::login::login_service::process_login::ProcessLoginRequest;

#[tokio::main]
async fn main() -> provident::Result<()> {
    let client = Client::new()?;

    // Sign in once; the session cookie is shared by every call on this client and its clones.
    let credentials = ProcessLoginRequest::builder()
        .username("your-login")
        .password("your-password")
        .build();
    client.auth.login(&credentials).await?;

    // Meters on the account.
    let meters = client.account.meters().await?;
    for meter in &meters {
        println!("{:?} meter {} ({})", meter.utility, meter.serial, meter.unit);
    }

    // Hourly electricity use for one day (ranges up to 7 days are hourly, longer ones daily).
    let meter = meters
        .iter()
        .find(|m| m.utility == Utility::Electricity)
        .expect("account has an electricity meter");
    let day = NaiveDate::from_ymd_opt(2026, 10, 18).unwrap();
    let query = QuickGraphsRequest::builder()
        .meters(meter.id)
        .start(day)
        .end(day.checked_add_days(Days::new(1)).unwrap()) // end is exclusive
        .build();
    let series = client.usage.series(&query).await?;
    println!("{} per hour: {:?}", series[0].units, series[0].data);
    Ok(())
}

With the blocking feature the same calls exist without .await.

§What’s available

AreaCallsPortal endpoint
client.authlogin, verifyLoginService.aspx
client.accountmeters, meter, meter_by_serial, utilities, location, details, api_keymeter tree, AccountInfo.aspx
client.usageseries (hourly/daily consumption), report (start/end readings and totals)Quick Graphs, Quick Reports

Request and response types live in provident::models, laid out to mirror the portal’s URLs. Things worth knowing:

  • Time zones. Dates are calendar days with no zone. Hourly buckets for electricity behave like local time; water meters appear to use a fixed offset about two hours earlier in winter. See the quick_graphs module docs for details.
  • Missing readings. Hourly series report 0.0 for missing hours; reports flag them with error and leave values empty.
  • Deliberately not wrapped. The dashboard’s GetUtilities, UpdateCard, and GetChartData page methods intermittently answer with another account’s data, so this crate derives utilities from the per-account meter tree instead.

§Minimum supported Rust version

Rust 1.99.1 (edition 2024).

§License

GPL-3.0-only.

Re-exports§

pub use http::ClientConfig;

Modules§

account
The signed-in account. Requires crate::auth::Auth::login first; without a session these calls fail with HTTP 401. Wire endpoints: crate::models::api::internal::meter_tree and crate::models::secure::account_info.
auth
Authentication. Wire endpoints: crate::models::login::login_service.
http
JSON-over-HTTP transport shared by every endpoint: client configuration, request building, the {"d": ...} page-method envelope, and HTTP-level errors.
models
Typed data returned by the Provident (meterconnex) API.
usage
Consumption history for meters and groups. Requires crate::auth::Auth::login first. Unlike the rest of the API these endpoints do not answer 401 without a session: they return an empty result, or HTTP 500 for ids that exist. Wire endpoints: crate::models::api::internal::{graphs, reports}.

Structs§

Client
Entry point. Cheap to clone; clones share connections and the session.

Enums§

Error
Top-level error returned by Client operations.

Type Aliases§

Result
std::result::Result defaulting its error type to Error.