EyeOnWater client library.
Project description
pyonwater
EyeOnWater client library
Features
- Async/await - Built on aiohttp for efficient async operations
- Type-safe - Full type hints and Pydantic v2 validation
- Production-ready - Configurable timeouts, automatic retries with exponential backoff
- Comprehensive - Access meter readings, historical data, and account information
- Flexible units - Support for gallons, cubic feet, liters, cubic meters, and more
- Data processing - Utilities for monotonic enforcement, filtering, and unit conversion
- Well-tested - 97% test coverage with extensive validation
Installation
pip install pyonwater
Basic Usage
"""Example showing the EOW Client usage."""
import asyncio
import aiohttp
from pyonwater import Account, Client
async def main() -> None:
"""Main."""
account = Account(
eow_hostname="eyeonwater.com",
username="your EOW login",
password="your EOW password",
)
async with aiohttp.ClientSession() as websession:
client = Client(websession=websession, account=account)
await client.authenticate()
meters = await account.fetch_meters(client=client)
print(f"{len(meters)} meters found")
for meter in meters:
# Read meter info
await meter.read_meter_info(client=client)
print(f"meter {meter.meter_uuid} shows {meter.reading}")
print(f"meter {meter.meter_uuid} info {meter.meter_info}")
# Read historical data (default: 3 days, hourly aggregation)
await meter.read_historical_data(client=client, days_to_load=3)
for d in meter.last_historical_data:
print(d)
asyncio.run(main())
Advanced Usage
Configuring Request Timeouts
The client includes robust timeout configuration to prevent hung requests:
from aiohttp import ClientTimeout
# Custom timeout configuration
timeout = ClientTimeout(
total=30, # Maximum time for entire request (seconds)
connect=10, # Maximum time to establish connection
sock_read=20 # Maximum time to read data from socket
)
client = Client(websession=websession, account=account, timeout=timeout)
Default timeout values are: total=30s, connect=10s, sock_read=20s.
The client automatically retries on authentication expiration and rate limiting with exponential backoff (max 3 attempts).
Error Handling
The library provides specific exceptions for different error scenarios:
from pyonwater import (
EyeOnWaterAuthError, # Invalid username/password
EyeOnWaterAuthExpired, # Token expired (auto-retried)
EyeOnWaterRateLimitError, # Rate limit hit (auto-retried)
EyeOnWaterAPIError, # Unknown API error
EyeOnWaterResponseIsEmpty, # Valid response but no data
EyeOnWaterUnitError, # Unit conversion error
)
try:
await client.authenticate()
meters = await account.fetch_meters(client=client)
except EyeOnWaterAuthError:
print("Invalid credentials")
except EyeOnWaterRateLimitError:
print("Rate limit exceeded - retry with backoff")
except EyeOnWaterAPIError as e:
print(f"API error: {e}")
Note: EyeOnWaterAuthExpired and EyeOnWaterRateLimitError are automatically retried with exponential backoff.
Specifying Units and Aggregation
You can customize the units and time granularity when reading historical data:
from pyonwater.models.units import AggregationLevel, RequestUnits
# Read 7 days of data with daily aggregation in gallons
await meter.read_historical_data(
client=client,
days_to_load=7,
aggregation=AggregationLevel.DAILY,
units=RequestUnits.GALLONS
)
# Read 1 day with 15-minute intervals in cubic meters
await meter.read_historical_data(
client=client,
days_to_load=1,
aggregation=AggregationLevel.QUARTER_HOURLY,
units=RequestUnits.CUBIC_METERS
)
Available Options
Aggregation Levels:
AggregationLevel.QUARTER_HOURLY- 15-minute intervalsAggregationLevel.HOURLY- 1-hour intervals (default)AggregationLevel.DAILY- 1-day intervalsAggregationLevel.WEEKLY- 7-day intervalsAggregationLevel.MONTHLY- 1-month intervalsAggregationLevel.YEARLY- 1-year intervals
Units:
RequestUnits.GALLONS- US gallonsRequestUnits.CUBIC_FEET- Cubic feetRequestUnits.CCF- Centum cubic feet (100 ft³)RequestUnits.LITERS- LitersRequestUnits.CUBIC_METERS- Cubic meters (default)RequestUnits.IMPERIAL_GALLONS- Imperial gallonsRequestUnits.OIL_BARRELS- Oil barrelsRequestUnits.FLUID_BARRELS- Fluid barrels
Data Processing Utilities
The library includes helper functions for processing historical data:
Monotonic Total Enforcement
Ensures cumulative meter readings never decrease (useful for handling resets or rounding errors):
from pyonwater import enforce_monotonic_total
# Normalize historical data to be monotonically increasing
normalized = enforce_monotonic_total(
meter.last_historical_data,
clamp_min=0.0 # Optional: enforce minimum value
)
Time-Based Filtering
Filter data points to avoid duplicates when importing to statistics engines:
from datetime import datetime
from pyonwater import filter_points_after
# Only get data after a specific time
since = datetime(2026, 1, 1, tzinfo=timezone.utc)
recent_data = filter_points_after(meter.last_historical_data, since=since)
Unit Conversion
Convert between meter native units and display units:
from pyonwater import convert_to_native, deduce_native_units, EOWUnits, NativeUnits
# Deduce native units from reading unit
native = deduce_native_units(EOWUnits.UNIT_KGAL) # Returns NativeUnits.GAL
# Convert reading to native units
gallons = convert_to_native(
NativeUnits.GAL,
EOWUnits.UNIT_KGAL,
value=5.0 # 5 kGal = 5000 gallons
)
Quick Reference
Common patterns for everyday use:
from pyonwater import (
Account, Client,
AggregationLevel, RequestUnits,
EyeOnWaterAuthError,
)
from aiohttp import ClientSession, ClientTimeout
# Initialize with custom timeout
async with ClientSession() as session:
timeout = ClientTimeout(total=30, connect=10, sock_read=20)
client = Client(session, Account(...), timeout=timeout)
# Authenticate
await client.authenticate()
# Get all meters
meters = await account.fetch_meters(client)
# Get current reading
await meters[0].read_meter_info(client)
print(meters[0].reading)
# Get 30 days of hourly data in gallons
await meters[0].read_historical_data(
client,
days_to_load=30,
aggregation=AggregationLevel.HOURLY,
units=RequestUnits.GALLONS
)
API Documentation
For complete API parameter requirements, validation details, and endpoint documentation, see docs/API_VALIDATION.md.
Development
This library uses comprehensive input validation and type-safe enums to ensure API requests are always valid. All API parameters are validated before making requests to prevent silent failures.
See the test suite for examples of proper usage and parameter validation.
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 pyonwater-0.3.18.tar.gz.
File metadata
- Download URL: pyonwater-0.3.18.tar.gz
- Upload date:
- Size: 18.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e03d60c2f4e54f2a7db0138a70cb1879478ce59c24800b6742378dbace96bb6
|
|
| MD5 |
af4c1bb38c1d4a90a034c3da86b4e5fa
|
|
| BLAKE2b-256 |
496ecb6fe586f30f055e89539d66ff963eaa073d4c8a941dfe9d79a257c80557
|
Provenance
The following attestation bundles were made for pyonwater-0.3.18.tar.gz:
Publisher:
pypi.yaml on kdeyev/pyonwater
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyonwater-0.3.18.tar.gz -
Subject digest:
9e03d60c2f4e54f2a7db0138a70cb1879478ce59c24800b6742378dbace96bb6 - Sigstore transparency entry: 952333314
- Sigstore integration time:
-
Permalink:
kdeyev/pyonwater@51057e565a2977c6f87303f9f8d2a8be735439f3 -
Branch / Tag:
refs/tags/v0.3.18 - Owner: https://github.com/kdeyev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@51057e565a2977c6f87303f9f8d2a8be735439f3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyonwater-0.3.18-py3-none-any.whl.
File metadata
- Download URL: pyonwater-0.3.18-py3-none-any.whl
- Upload date:
- Size: 21.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7937c46e91586ef20520ef3de5bbff3a6a756f4e79c2e868ce76f6c6beebcf8c
|
|
| MD5 |
9b13efefe2774277020611bddea40c47
|
|
| BLAKE2b-256 |
46f7ad721500d2e43431115b135b3324236358c303fa00bc8b6bc93b00cdce57
|
Provenance
The following attestation bundles were made for pyonwater-0.3.18-py3-none-any.whl:
Publisher:
pypi.yaml on kdeyev/pyonwater
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyonwater-0.3.18-py3-none-any.whl -
Subject digest:
7937c46e91586ef20520ef3de5bbff3a6a756f4e79c2e868ce76f6c6beebcf8c - Sigstore transparency entry: 952333315
- Sigstore integration time:
-
Permalink:
kdeyev/pyonwater@51057e565a2977c6f87303f9f8d2a8be735439f3 -
Branch / Tag:
refs/tags/v0.3.18 - Owner: https://github.com/kdeyev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@51057e565a2977c6f87303f9f8d2a8be735439f3 -
Trigger Event:
push
-
Statement type: