Awair API client and data collection system with AWS Lambda automation
Project description
Awair Air Quality Dashboard
A Python CLI tool and automated data collection system for Awair air quality sensors. Provides real-time data fetching using the Awair API, historical analysis, automated S3 storage via AWS Lambda (per-device), and a web dashboard for visualization.
Features
- Web Dashboard: Real-time visualization at awair.runsascoded.com
- CLI Interface: Raw data fetching, analysis, and export from Awair sensors
- Automated Collection: AWS Lambda functions that collect data every 2 minutes per device
- Multi-Device Support: Separate Lambda stacks and Parquet files per device
- S3 Storage: Efficient Parquet format with incremental updates
- Data Analysis: Built-in tools for gaps analysis, histograms, and data summaries
- Flexible Storage: Works with local files or S3 (configurable default paths)
- AWS Deployment: One-command CDK deployment with automatic IAM permissions
Installation
From PyPI (Recommended)
# Basic installation
pip install awair
# With Lambda deployment support
pip install awair[lambda]
# Development installation
pip install awair[dev]
From Source
git clone https://github.com/runsascoded/awair.git
cd awair
pip install -e .
Configuration
API Token
Set your Awair API token via:
- Environment variable:
export AWAIR_TOKEN="your-token" - Local file:
echo "your-token" > .token - User config:
echo "your-token" > ~/.awair/token
API Rate Limits:
- Hobbyist tier (default): Advertised 500/day, observed up to 1,280/day
- Enterprise tier: 86,400/day (60 requests/minute)
- Contact
developer@getawair.comto request a rate limit increase - Thanks to Steve at Awair for upgrading this project to Enterprise tier!
Device Configuration
Configure your Awair device via:
- Environment variables:
export AWAIR_DEVICE_TYPE="awair-element" AWAIR_DEVICE_ID="12345" - Local file:
echo "awair-element,12345" > .awair-device - User config:
echo "awair-element,12345" > ~/.awair/device - Auto-discovery: If not configured, the CLI will automatically detect your device on first use
- Command-line flag: Use
-i/--device-idwith numeric ID or name pattern (regex):awair data info -i 17617(numeric ID)awair data info -i "Ryan"(name pattern)awair data info -i "^Awair 2$"(exact regex match)
Data Storage Location
Configure default data file path via:
- Environment variable:
export AWAIR_DATA_PATH="s3://your-bucket/awair-17617.parquet"(explicit path) - Local file:
echo "s3://your-bucket/awair-17617.parquet" > .awair-data-path - User config:
echo "s3://your-bucket/awair-17617.parquet" > ~/.awair/data-path - Path template:
export AWAIR_DATA_PATH_TEMPLATE="s3://your-bucket/awair-{device_id}.parquet"- Automatically interpolates
{device_id}from device configuration - Default template:
s3://380nwk/awair-{device_id}.parquet - Useful for multi-device setups where you switch between devices
- Automatically interpolates
Usage
Data Collection
# Fetch raw API data and save to configured data file
awair api raw --from-dt 250710T10 --to-dt 250710T11
# Fetch raw API data and output as JSONL to stdout
awair api raw --from-dt 250710T10 --to-dt 250710T11 -d /dev/null
# Fetch only new data since latest timestamp in storage
awair api raw --recent-only
# Check your account info
awair api self
# List your devices (cached, 1 hour TTL)
awair api devices
# Force refresh device list from API
awair api devices --refresh
Data Analysis
# Show data file summary
awair data info
awair data info -d s3://your-bucket/data.parquet
# Daily histogram of record counts
awair data hist
awair data hist --from-dt 250710 --to-dt 250712
# Find timing gaps in data
awair data gaps -n 5 -m 300 # Top 5 gaps over 5 minutes
AWS Lambda Deployment
# Deploy automated data collector for a device
awair lambda deploy -s awair-updater-17617 -r 2
# Deploy for multiple devices (separate stacks)
AWAIR_DEVICE_ID=17617 AWAIR_DATA_PATH=s3://bucket/awair-17617.parquet \
awair lambda deploy -s awair-updater-17617 -r 2
AWAIR_DEVICE_ID=137496 AWAIR_DATA_PATH=s3://bucket/awair-137496.parquet \
awair lambda deploy -s awair-updater-137496 -r 2
# View CloudFormation template
awair lambda synth
# Monitor logs (specify function name)
aws logs tail /aws/lambda/awair-updater-17617 --follow
# Test locally
awair lambda test
Data Format
Sensor data is stored in Parquet format with these fields:
| Field | Type | Description |
|---|---|---|
timestamp |
datetime | UTC timestamp |
temp |
float | Temperature (°F) |
co2 |
int | CO2 (ppm) |
pm10 |
int | PM10 particles |
pm25 |
int | PM2.5 particles |
humid |
float | Humidity (%) |
voc |
int | Volatile Organic Compounds |
Example Data
{"timestamp":"2025-07-05T22:22:06.331Z","temp":73.36,"co2":563,"pm10":3,"pm25":2,"humid":52.31,"voc":96}
{"timestamp":"2025-07-05T22:21:06.063Z","temp":73.33,"co2":562,"pm10":3,"pm25":2,"humid":52.23,"voc":92}
Architecture
Automated Data Collection
The system uses AWS Lambda for automated data collection:
- Schedule: Runs every 2 minutes via EventBridge
- Multi-Device: Separate Lambda stack per device
- Storage: Updates device-specific S3 Parquet file incrementally
- Efficiency: Only fetches data since last update
- Reliability: Uses
utz.s3.atomic_editfor safe concurrent updates - Scalability: Each device has its own schedule and S3 path
CLI Integration
The CLI seamlessly works with both local files and S3:
# Both work the same way
storage = ParquetStorage('local-file.parquet')
storage = ParquetStorage('s3://bucket/file.parquet')
Configurable Deployments
Lambda deployments respect user configuration:
- IAM permissions generated dynamically per S3 bucket
- Environment variables passed to Lambda runtime
- Support for any S3 bucket/key combination
Development
Setup
pip install -e ".[dev]"
Code Style
ruff check
ruff format
Testing
pytest
Lambda Deployment
Deploy AWS Lambda function for automated data collection:
# Deploy latest PyPI version for a device (recommended)
AWAIR_DATA_PATH=s3://bucket/awair-17617.parquet \
awair lambda deploy -s awair-updater-17617 -r 2
# Deploy specific PyPI version
awair lambda deploy -v 0.0.1 -s awair-updater-17617 -r 2
# Deploy from local source (development)
awair lambda deploy -v source -s awair-updater-17617 -r 2
# Build package only (no deploy)
awair lambda deploy --dry-run
Multi-Device Deployment: Each device gets its own Lambda stack with independent:
- EventBridge schedule (default: 2 minutes)
- S3 Parquet file (
awair-{device_id}.parquet) - CloudWatch logs
- IAM permissions
PyPI Deployment (Default):
- ✅ Exact Versions: Deploy specific, tested releases
- ✅ Immutable: Consistent across environments
- ✅ Traceable: Clear version tracking in Lambda
- ✅ Production Ready: Uses published releases
Source Deployment (-v source):
- 🔧 Development: Test local changes before publishing
- 🚀 Latest Features: Access unreleased functionality
AWS Infrastructure
Each Lambda deployment creates:
- Lambda Function:
awair-updater-{device_id}(e.g.,awair-updater-17617) - EventBridge Rule: Configurable schedule (default: 2 minutes)
- IAM Role: S3 permissions for device-specific target path
- CloudWatch Logs: 2-week retention
- Environment Variables:
AWAIR_TOKEN,AWAIR_DATA_PATH,AWAIR_DEVICE_ID
Example: Two devices
Stack: awair-updater-17617
├─ Lambda: awair-updater-17617
├─ EventBridge: rate(2 minutes)
└─ S3: s3://380nwk/awair-17617.parquet
Stack: awair-updater-137496
├─ Lambda: awair-updater-137496
├─ EventBridge: rate(2 minutes)
└─ S3: s3://380nwk/awair-137496.parquet
Required AWS Permissions
For deployment, you need permissions to create:
- Lambda functions and layers
- IAM roles and policies
- EventBridge rules
- CloudWatch log groups
- S3 bucket access (for your target bucket)
Date/Time Format
The CLI uses a compact date format for convenience:
250710→ July 10, 2025250710T16→ July 10, 2025 at 4 PM20250710T1630→ July 10, 2025 at 4:30 PM
License
MIT License - see LICENSE file for details.
Project details
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 awair-0.0.6.tar.gz.
File metadata
- Download URL: awair-0.0.6.tar.gz
- Upload date:
- Size: 32.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94cab5c25d27c13bc57544ae0fb00d0cc486071a2272a1049ca596089edaaebb
|
|
| MD5 |
b5a7b94e42a36d3f982910fb9909ddba
|
|
| BLAKE2b-256 |
25f16c05edc8a4627ae7006333edbcc4f675c4f7ae03d063f0651df9e24a105b
|
File details
Details for the file awair-0.0.6-py3-none-any.whl.
File metadata
- Download URL: awair-0.0.6-py3-none-any.whl
- Upload date:
- Size: 31.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b94f8f22e3a1943150e5a5a3c2d2f2d205334412ef68c45a2fb868d04a2df57
|
|
| MD5 |
e8455fff71ed1e491429351e319a96a8
|
|
| BLAKE2b-256 |
be95449c562ff0bc8dabaedb68159b6966de5d67b77bf54a739f79773699275d
|