Python library for interacting with DropCountr.com water usage monitoring
Project description
PyDropCountr 💧
A Python library for interacting with DropCountr.com water usage monitoring systems.
Features
- 🔐 Authentication: Secure login with session management
- 🏠 Service Discovery: List and get details for your service connections
- 📊 Usage Data: Fetch water usage data with flexible date ranges
- 🐍 Pythonic API: Clean, type-safe interface with Pydantic models
- ⏰ Smart Dates: Accepts both Python
datetimeobjects and ISO strings - 🖥️ CLI Tool: Command-line interface for quick usage reports
Installation
pip install pydropcountr
For development:
git clone https://github.com/yourusername/pydropcountr.git
cd pydropcountr
uv install --dev
CLI Usage
PyDropCountr includes a command-line tool for quick usage reports:
Quick Start
# Set credentials as environment variables (recommended)
export DROPCOUNTR_EMAIL="your@email.com"
export DROPCOUNTR_PASSWORD="yourpassword"
# Get yesterday's usage + last 7 days (default behavior)
dropcountr usage
# Get last 30 days
dropcountr usage --days=30
# Get specific date range
dropcountr usage --start_date=2025-06-01 --end_date=2025-06-15
# List all service connections
dropcountr services
CLI Options
# All commands support these authentication options:
--email=your@email.com --password=yourpass
# Usage command options:
dropcountr usage [options]
--service_id=1234567 # Use specific service (defaults to first)
--start_date=YYYY-MM-DD # Start date
--end_date=YYYY-MM-DD # End date
--days=30 # Days back from today
--period=day # Granularity: 'day' or 'hour'
# Get help for any command:
dropcountr usage --help
dropcountr services --help
Environment Variables
export DROPCOUNTR_EMAIL="your@email.com"
export DROPCOUNTR_PASSWORD="yourpassword"
Python API Usage
Quick Start
from pydropcountr import DropCountrClient
from datetime import datetime
# Create client and login
client = DropCountrClient()
success = client.login('your@email.com', 'yourpassword')
if success:
# Discover your service connections
services = client.list_service_connections()
print(f"Found {len(services)} service connections:")
for service in services:
print(f" {service.id}: {service.name} at {service.address}")
# Get usage data for a service
service_id = services[0].id
start_date = datetime(2025, 6, 1)
end_date = datetime(2025, 6, 30, 23, 59, 59)
usage = client.get_usage(service_id, start_date, end_date)
if usage:
print(f"\\nUsage data: {usage.total_items} records")
for record in usage.usage_data[:5]: # Show first 5 days
print(f" {record.start_date.date()}: {record.total_gallons:.1f} gallons")
List Service Connections
# Get all service connections for the authenticated user
services = client.list_service_connections()
if services:
print(f"Found {len(services)} service connections:")
for service in services:
print(f" {service.id}: {service.name} at {service.address}")
Get Service Connection Details
# Get details for a specific service connection
service = client.get_service_connection(1064520)
if service:
print(f"Service: {service.name}")
print(f"Address: {service.address}")
print(f"Account: {service.account_number}")
print(f"Status: {service.status}")
Fetch Usage Data
from datetime import datetime
# Get daily usage data for June 2025 using Python datetime objects
usage = client.get_usage(
service_connection_id=1258809,
start_date=datetime(2025, 6, 1), # Python datetime object
end_date=datetime(2025, 6, 30, 23, 59, 59), # Python datetime object
period='day' # Can be 'day', 'hour', etc.
)
if usage:
print(f"Total records: {usage.total_items}")
for record in usage.usage_data[:3]:
print(f"{record.start_date.date()}: {record.total_gallons} gallons")
# Alternative: You can still use ISO datetime strings
usage = client.get_usage(
service_connection_id=1258809,
start_date='2025-06-01T00:00:00.000Z',
end_date='2025-06-30T23:59:59.000Z',
period='day'
)
API Reference
DropCountrClient
The main client for interacting with DropCountr.
Initialization
# Default: Pacific timezone (America/Los_Angeles)
client = DropCountrClient()
# Custom timezone
client = DropCountrClient(timezone="America/New_York")
client = DropCountrClient(timezone="UTC")
# Using ZoneInfo object
from zoneinfo import ZoneInfo
client = DropCountrClient(timezone=ZoneInfo("Europe/London"))
Authentication
success = client.login(email: str, password: str) -> bool
client.logout() -> None
client.is_logged_in() -> bool
Service Connections
# List all service connections
services = client.list_service_connections() -> List[ServiceConnection] | None
# Get specific service details
service = client.get_service_connection(service_id: int) -> ServiceConnection | None
Usage Data
# Get usage data with datetime objects (recommended)
usage = client.get_usage(
service_connection_id: int,
start_date: datetime,
end_date: datetime,
period: str = "day" # or "hour"
) -> UsageResponse | None
# Alternative: Use ISO datetime strings
usage = client.get_usage(
service_connection_id=1234,
start_date='2025-06-01T00:00:00.000Z',
end_date='2025-06-30T23:59:59.000Z'
)
Data Models
All response data is validated using Pydantic models:
ServiceConnection
class ServiceConnection(BaseModel):
id: int # Service connection ID
name: str # Service name
address: str # Service address
account_number: str | None # Account number
service_type: str | None # Service type
status: str | None # Service status
meter_serial: str | None # Meter serial number
api_id: str | None # API identifier
UsageData
class UsageData(BaseModel):
during: str # Time period (ISO interval)
total_gallons: float # Total usage in gallons (≥0)
irrigation_gallons: float # Irrigation usage (≥0)
irrigation_events: float # Number of irrigation events (≥0)
is_leaking: bool # Leak detection status
# Convenience properties
start_date: datetime # Parsed start date (timezone-aware)
end_date: datetime # Parsed end date (timezone-aware)
UsageResponse
class UsageResponse(BaseModel):
usage_data: List[UsageData] # List of usage records
total_items: int # Total number of items (≥0)
api_id: str # API response identifier
consumed_via_id: str # Service connection identifier
Error Handling
The library raises clear exceptions for common issues:
try:
usage = client.get_usage(service_id, start_date, end_date)
except ValueError as e:
print(f"Authentication or parameter error: {e}")
except requests.RequestException as e:
print(f"Network error: {e}")
Common errors:
ValueError: Not logged in or invalid parametersrequests.RequestException: Network connectivity issues- Returns
None: API returned unexpected data format
Date and Timezone Handling
The library accepts both Python datetime objects and ISO 8601 strings:
from datetime import datetime
# Recommended: Python datetime objects
start_date = datetime(2025, 6, 1)
end_date = datetime(2025, 6, 30, 23, 59, 59)
# Alternative: ISO datetime strings
start_date = '2025-06-01T00:00:00.000Z'
end_date = '2025-06-30T23:59:59.000Z'
Timezone Behavior
⚠️ Breaking Change in v0.1.3: Timezone handling has been fixed to correctly represent local time.
PyDropCountr now properly handles timezone-aware datetime objects:
- Default timezone: Pacific Time (
America/Los_Angeles) - configurable during client initialization - API timestamps: Despite having 'Z' suffix, timestamps are actually in local time (not UTC)
- Returned datetimes: All
start_dateandend_dateproperties are timezone-aware - Standard library: Uses Python 3.12+
zoneinfomodule (no external dependencies)
from datetime import datetime
from zoneinfo import ZoneInfo
# Default Pacific timezone
client = DropCountrClient()
usage = client.get_usage(service_id, start_date, end_date)
# Returned datetimes are timezone-aware in Pacific time
for record in usage.usage_data:
print(record.start_date) # 2025-06-01 08:00:00-07:00 (PDT)
print(record.start_date.tzinfo) # America/Los_Angeles
# Custom timezone for other regions
client = DropCountrClient(timezone="America/New_York")
# or
client = DropCountrClient(timezone=ZoneInfo("UTC"))
Migration from v0.1.2: If you were previously working around the incorrect UTC timestamps, you'll need to update your code as datetimes are now correctly timezone-aware in local time.
Development
Setup
git clone https://github.com/yourusername/pydropcountr.git
cd pydropcountr
uv install --dev
Testing
uv run python test_login.py
Linting
uv run ruff check
uv run ruff format
uv run mypy pydropcountr.py
Project Structure
pydropcountr/
├── pydropcountr.py # Main library
├── cli.py # Command-line interface
├── test_login.py # Test suite
├── pyproject.toml # Project configuration
├── README.md # This file
└── CLAUDE.md # Development notes
Requirements
- Python 3.12+
- requests >= 2.31.0
- pydantic >= 2.0.0
- fire >= 0.7.0
License
MIT License - see LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests and linting
- Submit a pull request
Disclaimer
This is an unofficial library for DropCountr.com. Use at your own risk and respect the service's terms of use.
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 pydropcountr-1.1.0.tar.gz.
File metadata
- Download URL: pydropcountr-1.1.0.tar.gz
- Upload date:
- Size: 48.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 |
2e25d3a3d314f283af079cf31c9afaa0b480e35cc7bc1e5fc1f78c750b3228fb
|
|
| MD5 |
275c54426c5d7bdc96272f054583a704
|
|
| BLAKE2b-256 |
ae467e4c80f3073652c972e9d4f8cc2d2407ad9c47cfca20d782997555404810
|
Provenance
The following attestation bundles were made for pydropcountr-1.1.0.tar.gz:
Publisher:
publish.yml on mcolyer/pydropcountr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydropcountr-1.1.0.tar.gz -
Subject digest:
2e25d3a3d314f283af079cf31c9afaa0b480e35cc7bc1e5fc1f78c750b3228fb - Sigstore transparency entry: 1818190592
- Sigstore integration time:
-
Permalink:
mcolyer/pydropcountr@cec7f43874dbe47badfbbfe11c07aad1d9151df7 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/mcolyer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cec7f43874dbe47badfbbfe11c07aad1d9151df7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pydropcountr-1.1.0-py3-none-any.whl.
File metadata
- Download URL: pydropcountr-1.1.0-py3-none-any.whl
- Upload date:
- Size: 14.9 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 |
ed2e637168ba9d5c4936e825c0b4e647584fcc87f9aabbf9fb9653dde1e7f585
|
|
| MD5 |
c303ce3c57e7bc38dd29a75b74e0640b
|
|
| BLAKE2b-256 |
1db104a96c3e93d57d67abb95cbe61ee673fac95cb2df382ba7e6f8bda69554c
|
Provenance
The following attestation bundles were made for pydropcountr-1.1.0-py3-none-any.whl:
Publisher:
publish.yml on mcolyer/pydropcountr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydropcountr-1.1.0-py3-none-any.whl -
Subject digest:
ed2e637168ba9d5c4936e825c0b4e647584fcc87f9aabbf9fb9653dde1e7f585 - Sigstore transparency entry: 1818190610
- Sigstore integration time:
-
Permalink:
mcolyer/pydropcountr@cec7f43874dbe47badfbbfe11c07aad1d9151df7 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/mcolyer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cec7f43874dbe47badfbbfe11c07aad1d9151df7 -
Trigger Event:
push
-
Statement type: