Read-only monitoring for Eero mesh networks
Guide for setting up a development environment and contributing to eeroVista.
git clone https://github.com/yeraze/eerovista.git
cd eerovista
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
pip install -r requirements-dev.txt # Development dependencies
mkdir data
python -m src.models.database # Initialize schema
# Set environment variables
export DATABASE_PATH=./data/eerovista.db
export LOG_LEVEL=DEBUG
export COLLECTION_INTERVAL_DEVICES=30
export COLLECTION_INTERVAL_NETWORK=60
# Run FastAPI with auto-reload
uvicorn src.main:app --reload --host 0.0.0.0 --port 8080
Access the development server at http://localhost:8080
eerovista/
├── src/ # Source code
│ ├── main.py # FastAPI app entry
│ ├── config.py # Configuration
│ ├── models/ # Database models
│ ├── collectors/ # Data collectors
│ ├── eero_client/ # Eero API wrapper
│ ├── api/ # API endpoints
│ ├── scheduler/ # Background jobs
│ ├── utils/ # Utilities
│ └── templates/ # HTML templates
├── static/ # Static assets
│ ├── css/
│ ├── js/
│ └── img/
├── docs/ # Documentation
├── tests/ # Test suite
├── Dockerfile # Container definition
├── docker-compose.yml # Docker composition
├── requirements.txt # Python dependencies
└── requirements-dev.txt # Dev dependencies
The requirements-dev.txt includes:
# Testing
pytest>=7.4.0
pytest-asyncio>=0.21.0
pytest-cov>=4.1.0
httpx>=0.24.0 # For testing FastAPI
# Code quality
black>=23.7.0 # Code formatter
ruff>=0.0.280 # Fast linter
mypy>=1.4.0 # Type checker
# Development tools
ipython>=8.14.0
Install with:
pip install -r requirements-dev.txt
# Format all Python files
black src/ tests/
# Check without modifying
black --check src/ tests/
Configuration in pyproject.toml:
[tool.black]
line-length = 100
target-version = ['py311']
# Lint all files
ruff check src/ tests/
# Auto-fix issues
ruff check --fix src/ tests/
# Type check
mypy src/
pytest
pytest --cov=src --cov-report=html
open htmlcov/index.html # View coverage report
# Single test file
pytest tests/test_collectors.py
# Single test function
pytest tests/test_collectors.py::test_device_collector
# Tests matching pattern
pytest -k "device"
tests/
├── conftest.py # Pytest fixtures
├── test_collectors.py # Collector tests
├── test_eero_client.py # Eero client tests
├── test_api.py # API endpoint tests
├── test_database.py # Database model tests
└── test_scheduler.py # Scheduler tests
import pytest
from httpx import AsyncClient
from src.main import app
@pytest.mark.asyncio
async def test_health_endpoint():
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.get("/api/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "healthy"
Common fixtures in conftest.py:
import pytest
from sqlalchemy import create_engine
from src.models.database import Base
@pytest.fixture
def db_session():
"""Create test database session."""
engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
# ... return session
@pytest.fixture
def mock_eero_client():
"""Mock Eero API client."""
# ... return mock
When modifying database models, create a migration:
# Generate migration
alembic revision --autogenerate -m "Add new table"
# Apply migration
alembic upgrade head
Located in alembic/versions/:
alembic/
├── env.py
├── script.py.mako
└── versions/
└── 001_initial_schema.py
docker build -t eerovista:dev .
docker compose -f docker-compose.dev.yml up
docker-compose.dev.yml:
version: '3.8'
services:
eerovista:
build: .
volumes:
- ./src:/app/src # Mount source for live reload
- ./data:/data
environment:
- LOG_LEVEL=DEBUG
command: uvicorn src.main:app --reload --host 0.0.0.0 --port 8080
.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "FastAPI",
"type": "python",
"request": "launch",
"module": "uvicorn",
"args": [
"src.main:app",
"--reload",
"--host", "0.0.0.0",
"--port", "8080"
],
"jinja": true,
"env": {
"DATABASE_PATH": "./data/eerovista.db",
"LOG_LEVEL": "DEBUG"
}
}
]
}
Use structured logging:
import logging
logger = logging.getLogger(__name__)
logger.debug("Device collected", extra={"mac": device.mac, "connected": True})
logger.info("Collector started", extra={"interval": 30})
logger.warning("API rate limited", extra={"retry_after": 60})
logger.error("Database error", exc_info=True)
git checkout -b feature/my-featurepytestblack src/ && ruff check src/git push origin feature/my-featureFollow conventional commits:
feat: Add device filtering to API
fix: Correct signal strength calculation
docs: Update installation guide
test: Add collector unit tests
refactor: Simplify database queries
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Manual testing completed
- [ ] All tests pass
## Checklist
- [ ] Code follows style guidelines (black, ruff)
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] No new warnings
Follow Semantic Versioning (SemVer):
src/__init__.py:
__version__ = "1.2.0"
git add src/__init__.py CHANGELOG.md
git commit -m "chore: Release v1.2.0"
git tag -a v1.2.0 -m "Release v1.2.0"
git push origin main --tags
View SQLite database:
sqlite3 data/eerovista.db
.tables
SELECT * FROM devices LIMIT 10;
Or use DB Browser for SQLite.
Use the auto-generated docs:
http://localhost:8080/docshttp://localhost:8080/redocOr use curl:
curl -X GET http://localhost:8080/api/devices | jq
# Add to endpoint for profiling
import cProfile
import pstats
profiler = cProfile.Profile()
profiler.enable()
# ... code to profile ...
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
stats.print_stats(20)
src/collectors/my_collector.pyBaseCollectorcollect() methodsrc/scheduler/jobs.pytests/test_collectors.pysrc/api/my_routes.pysrc/main.pytests/test_api.pydocs/api-reference.mdsrc/models/database.pyalembic revision --autogeneratealembic upgrade headtests/test_database.pyEnsure you’re in the virtual environment:
which python # Should show venv path
pip list | grep fastapi
SQLite doesn’t handle concurrent writes well:
# Kill any running instances
pkill -f uvicorn
# Remove lock
rm data/eerovista.db-journal
# Find process using port 8080
lsof -i :8080
# Kill it
kill -9 <PID>
For development questions: