awair


Nameawair JSON
Version 0.0.4 PyPI version JSON
download
home_pageNone
SummaryAwair API client and data collection system with AWS Lambda automation
upload_time2025-07-15 14:48:03
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords awair air-quality iot sensors aws lambda data-collection
VCS
bugtrack_url
requirements click requests
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # [Awair] Air Quality Dashboard

[![PyPI version](https://badge.fury.io/py/awair.svg)](https://badge.fury.io/py/awair)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Dashboard](https://img.shields.io/badge/Dashboard-awair.runsascoded.com-blue)][awair.runsascoded.com]

A Python CLI tool and automated data collection system for [Awair] air quality sensors. Provides real-time data fetching using [the Awair API][API], historical analysis, automated S3 storage via AWS Lambda, and a web dashboard for visualization.

[![Awair Dashboard](https://raw.githubusercontent.com/runsascoded/awair/v0.0.4/www/public/awair.png)][awair.runsascoded.com]

## 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 function that collects data every 5 minutes
- **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)

```bash
# Basic installation
pip install awair

# With Lambda deployment support
pip install awair[lambda]

# Development installation
pip install awair[dev]
```

### From Source

```bash
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`

### 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

### Data Storage Location
Configure default data file path via:
- Environment variable: `export AWAIR_DATA_PATH="s3://your-bucket/data.parquet"`
- Local file: `echo "s3://your-bucket/data.parquet" > .awair-data-path`
- User config: `echo "s3://your-bucket/data.parquet" > ~/.awair/data-path`
- Default: `s3://380nwk/awair.parquet`

## Usage

### Data Collection

```bash
# Fetch raw API data and save to configured data file
awair raw --from-dt 250710T10 --to-dt 250710T11

# Fetch raw API data and output as JSONL to stdout
awair raw --from-dt 250710T10 --to-dt 250710T11 -d /dev/null

# Fetch only new data since latest timestamp in storage
awair raw --recent-only

# Check your account info
awair self

# List your devices
awair devices
```

### Data Analysis

```bash
# Show data file summary
awair data-info
awair data-info -d s3://your-bucket/data.parquet

# Daily histogram of record counts
awair hist
awair hist --from-dt 250710 --to-dt 250712

# Find timing gaps in data
awair gaps -n 5 -m 300  # Top 5 gaps over 5 minutes
```

### AWS Lambda Deployment

```bash
# Deploy automated data collector
awair lambda deploy

# View CloudFormation template
awair lambda synth

# Monitor logs
awair lambda logs --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

```json
{"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 5 minutes via EventBridge
- **Storage**: Updates S3 Parquet file incrementally
- **Efficiency**: Only fetches data since last update
- **Reliability**: Uses `utz.s3.atomic_edit` for safe concurrent updates

### CLI Integration

The CLI seamlessly works with both local files and S3:

```python
# 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

```bash
pip install -e ".[dev]"
```

### Code Style

```bash
ruff check
ruff format
```

### Testing

```bash
pytest
```

## Lambda Deployment

Deploy AWS Lambda function for automated data collection:

```bash
# Deploy latest PyPI version (recommended)
awair lambda deploy

# Deploy specific PyPI version
awair lambda deploy -v 0.0.1

# Deploy from local source (development)
awair lambda deploy -v source

# Build package only (no deploy)
awair lambda deploy --dry-run
awair lambda deploy -v 0.0.1 --dry-run
```

**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

The Lambda deployment creates:

- **Lambda Function**: `awair-data-updater`
- **EventBridge Rule**: 5-minute schedule
- **IAM Role**: S3 permissions for target bucket
- **CloudWatch Logs**: 2-week retention
- **Environment Variables**: `AWAIR_TOKEN`, `AWAIR_DATA_PATH`

### 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, 2025
- `250710T16` → July 10, 2025 at 4 PM
- `20250710T1630` → July 10, 2025 at 4:30 PM

## License

MIT License - see LICENSE file for details.

[Awair]: https://www.getawair.com/
[API]: https://docs.developer.getawair.com/
[awair.runsascoded.com]: https://awair.runsascoded.com

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "awair",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "Ryan Williams <ryan@runsascoded.com>",
    "keywords": "awair, air-quality, iot, sensors, aws, lambda, data-collection",
    "author": null,
    "author_email": "Ryan Williams <ryan@runsascoded.com>",
    "download_url": "https://files.pythonhosted.org/packages/ce/ac/fb4b9af7c4971192565c998b2376f2141b35b2a6722179036a1c4656a0a4/awair-0.0.4.tar.gz",
    "platform": null,
    "description": "# [Awair] Air Quality Dashboard\n\n[![PyPI version](https://badge.fury.io/py/awair.svg)](https://badge.fury.io/py/awair)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Dashboard](https://img.shields.io/badge/Dashboard-awair.runsascoded.com-blue)][awair.runsascoded.com]\n\nA Python CLI tool and automated data collection system for [Awair] air quality sensors. Provides real-time data fetching using [the Awair API][API], historical analysis, automated S3 storage via AWS Lambda, and a web dashboard for visualization.\n\n[![Awair Dashboard](https://raw.githubusercontent.com/runsascoded/awair/v0.0.4/www/public/awair.png)][awair.runsascoded.com]\n\n## Features\n\n- **Web Dashboard**: Real-time visualization at [awair.runsascoded.com]\n- **CLI Interface**: Raw data fetching, analysis, and export from Awair sensors\n- **Automated Collection**: AWS Lambda function that collects data every 5 minutes\n- **S3 Storage**: Efficient Parquet format with incremental updates\n- **Data Analysis**: Built-in tools for gaps analysis, histograms, and data summaries\n- **Flexible Storage**: Works with local files or S3 (configurable default paths)\n- **AWS Deployment**: One-command CDK deployment with automatic IAM permissions\n\n## Installation\n\n### From PyPI (Recommended)\n\n```bash\n# Basic installation\npip install awair\n\n# With Lambda deployment support\npip install awair[lambda]\n\n# Development installation\npip install awair[dev]\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/runsascoded/awair.git\ncd awair\npip install -e .\n```\n\n## Configuration\n\n### API Token\nSet your Awair API token via:\n- Environment variable: `export AWAIR_TOKEN=\"your-token\"`\n- Local file: `echo \"your-token\" > .token`\n- User config: `echo \"your-token\" > ~/.awair/token`\n\n### Device Configuration\nConfigure your Awair device via:\n- Environment variables: `export AWAIR_DEVICE_TYPE=\"awair-element\" AWAIR_DEVICE_ID=\"12345\"`\n- Local file: `echo \"awair-element,12345\" > .awair-device`\n- User config: `echo \"awair-element,12345\" > ~/.awair/device`\n- **Auto-discovery**: If not configured, the CLI will automatically detect your device on first use\n\n### Data Storage Location\nConfigure default data file path via:\n- Environment variable: `export AWAIR_DATA_PATH=\"s3://your-bucket/data.parquet\"`\n- Local file: `echo \"s3://your-bucket/data.parquet\" > .awair-data-path`\n- User config: `echo \"s3://your-bucket/data.parquet\" > ~/.awair/data-path`\n- Default: `s3://380nwk/awair.parquet`\n\n## Usage\n\n### Data Collection\n\n```bash\n# Fetch raw API data and save to configured data file\nawair raw --from-dt 250710T10 --to-dt 250710T11\n\n# Fetch raw API data and output as JSONL to stdout\nawair raw --from-dt 250710T10 --to-dt 250710T11 -d /dev/null\n\n# Fetch only new data since latest timestamp in storage\nawair raw --recent-only\n\n# Check your account info\nawair self\n\n# List your devices\nawair devices\n```\n\n### Data Analysis\n\n```bash\n# Show data file summary\nawair data-info\nawair data-info -d s3://your-bucket/data.parquet\n\n# Daily histogram of record counts\nawair hist\nawair hist --from-dt 250710 --to-dt 250712\n\n# Find timing gaps in data\nawair gaps -n 5 -m 300  # Top 5 gaps over 5 minutes\n```\n\n### AWS Lambda Deployment\n\n```bash\n# Deploy automated data collector\nawair lambda deploy\n\n# View CloudFormation template\nawair lambda synth\n\n# Monitor logs\nawair lambda logs --follow\n\n# Test locally\nawair lambda test\n```\n\n## Data Format\n\nSensor data is stored in Parquet format with these fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `timestamp` | datetime | UTC timestamp |\n| `temp` | float | Temperature (\u00b0F) |\n| `co2` | int | CO2 (ppm) |\n| `pm10` | int | PM10 particles |\n| `pm25` | int | PM2.5 particles |\n| `humid` | float | Humidity (%) |\n| `voc` | int | Volatile Organic Compounds |\n\n### Example Data\n\n```json\n{\"timestamp\":\"2025-07-05T22:22:06.331Z\",\"temp\":73.36,\"co2\":563,\"pm10\":3,\"pm25\":2,\"humid\":52.31,\"voc\":96}\n{\"timestamp\":\"2025-07-05T22:21:06.063Z\",\"temp\":73.33,\"co2\":562,\"pm10\":3,\"pm25\":2,\"humid\":52.23,\"voc\":92}\n```\n\n## Architecture\n\n### Automated Data Collection\n\nThe system uses AWS Lambda for automated data collection:\n\n- **Schedule**: Runs every 5 minutes via EventBridge\n- **Storage**: Updates S3 Parquet file incrementally\n- **Efficiency**: Only fetches data since last update\n- **Reliability**: Uses `utz.s3.atomic_edit` for safe concurrent updates\n\n### CLI Integration\n\nThe CLI seamlessly works with both local files and S3:\n\n```python\n# Both work the same way\nstorage = ParquetStorage('local-file.parquet')\nstorage = ParquetStorage('s3://bucket/file.parquet')\n```\n\n### Configurable Deployments\n\nLambda deployments respect user configuration:\n\n- IAM permissions generated dynamically per S3 bucket\n- Environment variables passed to Lambda runtime\n- Support for any S3 bucket/key combination\n\n## Development\n\n### Setup\n\n```bash\npip install -e \".[dev]\"\n```\n\n### Code Style\n\n```bash\nruff check\nruff format\n```\n\n### Testing\n\n```bash\npytest\n```\n\n## Lambda Deployment\n\nDeploy AWS Lambda function for automated data collection:\n\n```bash\n# Deploy latest PyPI version (recommended)\nawair lambda deploy\n\n# Deploy specific PyPI version\nawair lambda deploy -v 0.0.1\n\n# Deploy from local source (development)\nawair lambda deploy -v source\n\n# Build package only (no deploy)\nawair lambda deploy --dry-run\nawair lambda deploy -v 0.0.1 --dry-run\n```\n\n**PyPI Deployment (Default):**\n- \u2705 **Exact Versions**: Deploy specific, tested releases\n- \u2705 **Immutable**: Consistent across environments\n- \u2705 **Traceable**: Clear version tracking in Lambda\n- \u2705 **Production Ready**: Uses published releases\n\n**Source Deployment (`-v source`):**\n- \ud83d\udd27 **Development**: Test local changes before publishing\n- \ud83d\ude80 **Latest Features**: Access unreleased functionality\n\n## AWS Infrastructure\n\nThe Lambda deployment creates:\n\n- **Lambda Function**: `awair-data-updater`\n- **EventBridge Rule**: 5-minute schedule\n- **IAM Role**: S3 permissions for target bucket\n- **CloudWatch Logs**: 2-week retention\n- **Environment Variables**: `AWAIR_TOKEN`, `AWAIR_DATA_PATH`\n\n### Required AWS Permissions\n\nFor deployment, you need permissions to create:\n- Lambda functions and layers\n- IAM roles and policies\n- EventBridge rules\n- CloudWatch log groups\n- S3 bucket access (for your target bucket)\n\n## Date/Time Format\n\nThe CLI uses a compact date format for convenience:\n\n- `250710` \u2192 July 10, 2025\n- `250710T16` \u2192 July 10, 2025 at 4 PM\n- `20250710T1630` \u2192 July 10, 2025 at 4:30 PM\n\n## License\n\nMIT License - see LICENSE file for details.\n\n[Awair]: https://www.getawair.com/\n[API]: https://docs.developer.getawair.com/\n[awair.runsascoded.com]: https://awair.runsascoded.com\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Awair API client and data collection system with AWS Lambda automation",
    "version": "0.0.4",
    "project_urls": {
        "Documentation": "https://github.com/runsascoded/awair#readme",
        "Homepage": "https://github.com/runsascoded/awair",
        "Issues": "https://github.com/runsascoded/awair/issues",
        "Repository": "https://github.com/runsascoded/awair.git"
    },
    "split_keywords": [
        "awair",
        " air-quality",
        " iot",
        " sensors",
        " aws",
        " lambda",
        " data-collection"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "fefeac516231e2f971e17df81c627af694b1322aac0528ac3542d0dc9663dccb",
                "md5": "70e9d32b8ea01c3ec170cd1198d2f23a",
                "sha256": "026a1d04461710707e46ec1d7f8eb75e2f534dd69e67b894e4794b1f3b6ced8e"
            },
            "downloads": -1,
            "filename": "awair-0.0.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "70e9d32b8ea01c3ec170cd1198d2f23a",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 27666,
            "upload_time": "2025-07-15T14:48:02",
            "upload_time_iso_8601": "2025-07-15T14:48:02.753501Z",
            "url": "https://files.pythonhosted.org/packages/fe/fe/ac516231e2f971e17df81c627af694b1322aac0528ac3542d0dc9663dccb/awair-0.0.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ceacfb4b9af7c4971192565c998b2376f2141b35b2a6722179036a1c4656a0a4",
                "md5": "1ad2a75a4558cb1d6a40dcf9f1a22791",
                "sha256": "e58aff5f1b6afc6792fea672ce63977af239dc527f55126911daf30705ed7bae"
            },
            "downloads": -1,
            "filename": "awair-0.0.4.tar.gz",
            "has_sig": false,
            "md5_digest": "1ad2a75a4558cb1d6a40dcf9f1a22791",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 27511,
            "upload_time": "2025-07-15T14:48:03",
            "upload_time_iso_8601": "2025-07-15T14:48:03.987987Z",
            "url": "https://files.pythonhosted.org/packages/ce/ac/fb4b9af7c4971192565c998b2376f2141b35b2a6722179036a1c4656a0a4/awair-0.0.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-15 14:48:03",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "runsascoded",
    "github_project": "awair#readme",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "click",
            "specs": []
        },
        {
            "name": "requests",
            "specs": []
        }
    ],
    "lcname": "awair"
}
        
Elapsed time: 0.43278s