Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
71 changes: 0 additions & 71 deletions .github/workflows/codeql-analysis.yml

This file was deleted.

24 changes: 13 additions & 11 deletions .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@ jobs:
build-and-publish:
name: Builds and publishes releases to PyPI
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
Comment thread
coderabbitai[bot] marked this conversation as resolved.
steps:
- uses: actions/checkout@v3.1.0
- name: Set up Python 3.9
uses: actions/setup-python@v4.3.0
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
python-version: 3.9
- name: Install wheel
persist-credentials: false
- name: Set up Python 3.12
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
- name: Install build
run: >-
pip install wheel
pip install build
- name: Build
run: >-
python3 setup.py sdist bdist_wheel
python3 -m build
- name: Publish release to PyPI
uses: pypa/gh-action-pypi-publish@v1.13.0
with:
user: __token__
password: ${{ secrets.PYPI_TOKEN }}
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
5 changes: 4 additions & 1 deletion .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ on:
jobs:
update_release_draft:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
# Drafts your next Release notes as Pull Requests are merged into "main"
- uses: release-drafter/release-drafter@v5
- uses: release-drafter/release-drafter@c2e2804cc59f45f57076a99af580d0fedb697927 # v7.3.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26 changes: 15 additions & 11 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,49 @@ on:
jobs:
tests:
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
python-version: ["3.12"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: 📥 Checkout the repository
uses: actions/checkout@v4
- name: 🛠️ Set up Python
uses: actions/setup-python@v5
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 2
fetch-depth: 2
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: ${{ matrix.python-version }}
- name: 📦 Install requirements
run: |
pip install tox tox-gh-actions
pip install tox tox-gh-actions
- name: 🏃 Test with tox
run: tox
- name: 📤 Upload coverage to Codecov
uses: "actions/upload-artifact@v4"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: coverage-data
path: "coverage.xml"

coverage:
runs-on: ubuntu-latest
needs: tests
permissions:
contents: read
steps:
- name: 📥 Checkout the repository
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 2
persist-credentials: false
- name: 📥 Download coverage data
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: coverage-data
- name: 📤 Upload coverage report
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.14
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-added-large-files
- id: debug-statements
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"files.associations": {
"*.yaml": "home-assistant"
}
}
}
139 changes: 137 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,139 @@
# python-openei
Python Library for OpenEI.org Rest data

A python library for consuming OpenEI.org rest data and outputting it into an easy to use format.
[![PyPI version](https://img.shields.io/pypi/v/python-openei.svg)](https://pypi.org/project/python-openei/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

An asynchronous Python library for consuming utility rate data from the [OpenEI.org](https://openei.org) API and outputting it into an easy-to-use format.

## Features

- **Asynchronous API**: Fully built on `aiohttp` for non-blocking network calls.
- **Auto Caching**: Automatically caches API responses locally (24-hour expiration) to stay within rate limits.
- **Utility Plan Lookup**: Find utility rate plans by coordinates (latitude/longitude) or street address.
- **Rate Schedule Queries**: Calculates current and upcoming energy rates, demand rates, adjustments, and tier/sell rates for any given date and time.

---

## Installation

Install using `pip`:

```bash
pip install python-openei
```

---

## Quick Start

You will need an API key from [OpenEI.org](https://openei.org/wiki/Special:Register).

### Basic Usage

Here is a quick example of how to retrieve and query energy rates for a specific plan:

```python
import asyncio
from openeihttp import Rates

async def main():
# Initialize Rates helper
# Retrieve a specific plan (e.g. "539fca56ec12157c50403bf6")
api = Rates(
api="YOUR_OPENEI_API_KEY",
plan="539fca56ec12157c50403bf6",
cache_file="my_rate_cache.json" # Optional local cache file
)

# Fetch/update the rate plan details
await api.update()

print(f"Rate Plan Name: {api.rate_name}")
print(f"Current Energy Rate: ${api.current_rate}/kWh")
print(f"Current Sell Rate: ${api.current_sell_rate}/kWh")

# Check what the next rate will be and when it changes
next_time = api.next_energy_rate_structure_time
next_rate = api.next_energy_rate_structure
print(f"Next rate change at: {next_time} (structure ID: {next_rate})")

asyncio.run(main())
```

---

## Plan Lookup

If you do not know the plan ID, you can look up available plans using a latitude/longitude pair or a physical address:

```python
import asyncio
from openeihttp import Rates

async def lookup():
# Set up lookup using latitude and longitude
api = Rates(
api="YOUR_OPENEI_API_KEY",
lat=37.7749,
lon=-122.4194,
radius=5.0 # Optional search radius in miles
)

plans = await api.lookup_plans()

# plans will be grouped by utility name
for utility, plan_list in plans.items():
print(f"\nUtility: {utility}")
for plan in plan_list:
print(f" - {plan['name']} (Plan Label: {plan['label']})")

asyncio.run(lookup())
```

---

## API Reference

### Properties

The `Rates` object exposes the following properties after a successful `update()`:

| Property | Return Type | Description |
| :--- | :--- | :--- |
| `rate_name` | `str` | Name of the utility rate plan. |
| `approval` | `bool` | Approval status of the rate plan on OpenEI. |
| `current_rate` | `float \| None` | Current active energy rate in $/kWh. |
| `current_sell_rate` | `float \| None` | Current net-metering / sell rate in $/kWh. |
| `current_adjustment` | `float \| None` | Current rate adjustment value in $/kWh. |
| `next_energy_rate_structure` | `int \| None` | Upcoming energy rate structure ID. |
| `next_energy_rate_structure_time` | `datetime \| None` | The time at which the next energy rate structure starts. |
| `current_demand_rate` | `float \| None` | Current demand rate. |
| `current_demand_adjustment`| `float \| None` | Current demand rate adjustment. |
| `demand_unit` | `str \| None` | The unit of the demand rate. |
| `monthly_tier_rate` | `float \| None` | Current tier rate based on monthly meter reading. |
| `distributed_generation` | `str \| None` | Distributed generation rules / net-metering type. |
| `mincharge` | `tuple[float, str] \| None` | Minimum charge amount and units (e.g. `(10.0, "$/month")`). |
| `fixedchargefirstmeter` | `tuple[float, str] \| None` | Fixed charge amount and units for the first meter. |

### Methods

- `await api.update()`: Updates the internal data. Loads from cache if fresh, otherwise fetches from API and caches locally.
- `await api.update_data()`: Forces a fresh API call (bypassing cache) and rewrites the cache file.
- `await api.clear_cache()`: Deletes the cache file if one was configured.
- `api.rate(date: datetime)`: Look up the energy rate for a specific date and time.
- `api.sell_rate(date: datetime)`: Look up the sell/net-metering rate for a specific date and time.
- `api.demand_rate(date: datetime)`: Look up the demand rate for a specific date and time.

---

## Development

This project uses `tox` to run checks and tests across Python versions.

### Run Tests and Linters

Make sure you have `tox` installed, then run:

```bash
tox
```
Loading
Loading