Python client for downloading electricity usage data from Enova Power
Project description
enovapower
⚠️ Unofficial project — not affiliated with, endorsed by, or supported by Enova Power Corp. "Enova Power" is a trademark of its respective owner. Use at your own risk under the Apache-2.0 license.
A Python library for downloading electricity usage data from the Enova Power customer portal.
Enova Power serves residential and commercial customers in the Kitchener-Waterloo region of Ontario, Canada. Their My Account portal provides smart meter data exports, but only through a web UI. This library automates that process so you can pull your usage data into scripts, notebooks, dashboards, or data pipelines.
Quick start
Install
Using uv (recommended):
uv add enovapower
Using pip:
pip install enovapower
Environment variables
Credentials can be set via environment variables instead of passing them directly:
export ENOVA_USERNAME="user@example.com"
export ENOVA_PASSWORD="your_password"
Async client (primary)
The AsyncEnovaClient is the primary interface.
from datetime import date
from enovapower import AsyncEnovaClient
async with AsyncEnovaClient() as client:
await client.login("user@example.com", "your_password") # Do not pass credentials if they are set via environment variables
readings = await client.download_usage(date(2026, 2, 25), date(2026, 3, 26))
for r in readings:
print(f"{r.date}: {r.total:.2f} kWh")
Sync client (convenience)
The EnovaClient is a thin synchronous wrapper for scripts and non-async contexts. It runs a dedicated background event loop.
from datetime import date
from enovapower import EnovaClient
client = EnovaClient()
client.login("user@example.com", "your_password")
readings = client.download_usage(date(2026, 2, 25), date(2026, 3, 26))
for r in readings:
print(f"{r.date}: {r.total:.2f} kWh")
Features
- Authenticate with the Enova Power My Account portal
- Download hourly smart meter usage data as
UsageReadingdataclasses - Convert hourly readings to timezone-aware UTC
(timestamp, kWh)intervals viaUsageReading.intervals() - Distinguish missing hours (
None) from real zero-consumption hours - Download Green Button XML exports and parse them with
parse_green_button_xml()(safedefusedxmlparsing) - Multi-meter accounts via
meter_ids/select_meter() - Optional re-authentication callback to avoid retaining credentials
- Download tariff rates for all pricing plans (Time-of-Use, Ultra-Low Overnight, Tiered)
- Automatically chunk requests for date ranges exceeding 90 days
- Store and incrementally update usage history in a local SQLite database
- Automatic retry with exponential backoff on transient errors
- Session expiry detection with automatic re-login
- Configurable
base_urlfor custom portal endpoints - Built-in logging with customizable logger support
Local storage
from enovapower import EnovaClient, UsageStore
client = EnovaClient()
client.login("user@example.com", "your_password")
with UsageStore("usage.db") as store:
store.seed(client, months=12) # initial backfill
store.update(client) # incremental update
readings = store.load("111111", from_date, to_date)
Polling interval
The Enova portal is a utility web UI, not a high-throughput API. Avoid polling more frequently than every 15 minutes. A 30-minute interval is recommended for regular updates.
Logging
The library uses Python's standard logging module. The logger name is "enovapower".
Basic usage
import logging
logging.basicConfig(level=logging.DEBUG)
# Now all enovapower logs will appear
Custom logger
You can inject a custom logger to both clients and storage:
import logging
my_logger = logging.getLogger("my_app")
my_logger.setLevel(logging.INFO)
client = AsyncEnovaClient(logger=my_logger)
store = UsageStore("usage.db", logger=my_logger)
Built-in configuration
The library provides a convenience function to set up default handlers:
from enovapower.logger import configure_logging
# Configure with default format and DEBUG level
configure_logging(level=logging.DEBUG)
# Or with custom format
configure_logging(
level=logging.INFO,
format_string="%(asctime)s - %(levelname)s - %(message)s"
)
Security
Credentials passed to login() are stored in memory to enable automatic re-authentication on session expiry. They are never written to disk.
The library requires HTTPS by default. Use allow_insecure_http=True only for local testing against a development endpoint.
UsageStore databases are created with owner-only permissions (0600), since hourly usage data can reveal household occupancy patterns.
Documentation
See docs/usage.md for detailed API documentation and examples.
Development
See CONTRIBUTING.md for setup instructions and development workflow.
License
Apache-2.0. See LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file enovapower-0.11.0.tar.gz.
File metadata
- Download URL: enovapower-0.11.0.tar.gz
- Upload date:
- Size: 60.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e68827ce47d9059fa3c14dc1f48b269097ebde948a089f237ff639c2e7dac5b2
|
|
| MD5 |
460be6cabec7853b32ec2759c15ecb7f
|
|
| BLAKE2b-256 |
564aedec1c981f8a01ca4621da23ec01c4461a82592ca5a5a6ff65a3e56bba84
|
Provenance
The following attestation bundles were made for enovapower-0.11.0.tar.gz:
Publisher:
publish.yml on mojo17/enovapower
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
enovapower-0.11.0.tar.gz -
Subject digest:
e68827ce47d9059fa3c14dc1f48b269097ebde948a089f237ff639c2e7dac5b2 - Sigstore transparency entry: 2073248613
- Sigstore integration time:
-
Permalink:
mojo17/enovapower@b086c0e3f2b4f3fcccbf649f32ee3f63d2625330 -
Branch / Tag:
refs/tags/v0.11.0 - Owner: https://github.com/mojo17
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b086c0e3f2b4f3fcccbf649f32ee3f63d2625330 -
Trigger Event:
push
-
Statement type:
File details
Details for the file enovapower-0.11.0-py3-none-any.whl.
File metadata
- Download URL: enovapower-0.11.0-py3-none-any.whl
- Upload date:
- Size: 34.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a612c1778edab0c69819f08cc722cbafc1cfd76028d9c57ae9f36f398003671
|
|
| MD5 |
0bef5b9ec6a0c5f581b236a87a1f87cd
|
|
| BLAKE2b-256 |
b3fc329ede6f2b10f3791a30ba3dbab6cdf2bfe89aaf856e902326b519bfb01d
|
Provenance
The following attestation bundles were made for enovapower-0.11.0-py3-none-any.whl:
Publisher:
publish.yml on mojo17/enovapower
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
enovapower-0.11.0-py3-none-any.whl -
Subject digest:
4a612c1778edab0c69819f08cc722cbafc1cfd76028d9c57ae9f36f398003671 - Sigstore transparency entry: 2073248622
- Sigstore integration time:
-
Permalink:
mojo17/enovapower@b086c0e3f2b4f3fcccbf649f32ee3f63d2625330 -
Branch / Tag:
refs/tags/v0.11.0 - Owner: https://github.com/mojo17
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b086c0e3f2b4f3fcccbf649f32ee3f63d2625330 -
Trigger Event:
push
-
Statement type: