Clone
1
Development
sq4ind edited this page 2025-09-28 12:47:05 +00:00
Table of Contents
- 🛠️ Development Guide
- 🚀 Development Environment Setup
- Prerequisites
- 1. Clone the Repository
- 2. Set Up Python Environment
- 3. Install Home Assistant Core
- 4. Set Up Test AdGuard Home
- 🔧 Development Workflow
- 🧪 Testing
- 🔍 Code Quality Tools
- 🔄 Continuous Integration
- 🐛 Debugging
- 📝 Documentation
- 🔧 Adding New Features
- 🚀 Release Process
🛠️ Development Guide
Set up a development environment for AdGuard Control Hub and learn how to contribute to the project.
🚀 Development Environment Setup
Prerequisites
- Python 3.11+ (Home Assistant requirement)
- Git for version control
- Home Assistant Core (development installation)
- AdGuard Home instance for testing
- VS Code (recommended) with Python extension
1. Clone the Repository
# Clone your fork or the main repository
git clone https://your-gitea-domain.com/your-username/adguard-control-hub.git
cd adguard-control-hub
# Set up remote for upstream (if fork)
git remote add upstream https://your-gitea-domain.com/original-user/adguard-control-hub.git
2. Set Up Python Environment
# Create virtual environment
python3 -m venv venv
source venv/bin/activate # Linux/Mac
# or
venv\Scripts\activate # Windows
# Upgrade pip
python -m pip install --upgrade pip
# Install development dependencies
pip install -r requirements-dev.txt
3. Install Home Assistant Core
# Install Home Assistant for development
pip install homeassistant
# Or use development version
git clone https://github.com/home-assistant/core.git
cd core
pip install -e .
4. Set Up Test AdGuard Home
# Download and run AdGuard Home for testing
wget https://github.com/AdguardTeam/AdGuardHome/releases/latest/download/AdGuardHome_linux_amd64.tar.gz
tar -xzf AdGuardHome_linux_amd64.tar.gz
cd AdGuardHome
# Initial setup
./AdGuardHome -s install
./AdGuardHome -s start
# Access web interface at http://localhost:3000
# Set up admin user: admin/development
🔧 Development Workflow
Project Structure
adguard-control-hub/
├── custom_components/adguard_hub/ # Main integration code
│ ├── __init__.py # Integration setup
│ ├── api.py # API wrapper
│ ├── config_flow.py # Configuration flow
│ ├── const.py # Constants
│ ├── manifest.json # Integration metadata
│ ├── services.py # Custom services
│ ├── strings.json # UI strings
│ └── switch.py # Switch platform
├── tests/ # Test suite
│ ├── __init__.py
│ ├── conftest.py # Test configuration
│ ├── test_api.py # API tests
│ ├── test_config_flow.py # Config flow tests
│ └── test_switch.py # Switch tests
├── .gitea/workflows/ # CI/CD pipelines
├── docs/ # Documentation
├── requirements-dev.txt # Development dependencies
├── pyproject.toml # Tool configuration
├── .flake8 # Linting configuration
└── README.md # Project documentation
Development Installation
# Link integration to Home Assistant config
mkdir -p ~/.homeassistant/custom_components
ln -s $(pwd)/custom_components/adguard_hub ~/.homeassistant/custom_components/
# Or copy files during development
cp -r custom_components/adguard_hub ~/.homeassistant/custom_components/
Running Home Assistant
# Start Home Assistant in development mode
hass --open-ui --verbose
# Or with specific config directory
hass -c ~/.homeassistant --open-ui --verbose
🧪 Testing
Running Tests
# Run all tests
pytest
# Run specific test file
pytest tests/test_api.py
# Run with coverage
pytest --cov=custom_components.adguard_hub --cov-report=html
# Run integration tests only
pytest tests/test_integration.py -v
Test Configuration
# tests/conftest.py
import pytest
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
@pytest.fixture
async def hass(event_loop):
"""Create Home Assistant instance for testing."""
hass = HomeAssistant()
await async_setup_component(hass, "homeassistant", {})
yield hass
await hass.async_stop()
@pytest.fixture
def mock_adguard_api():
"""Mock AdGuard Home API for testing."""
with patch("custom_components.adguard_hub.api.AdGuardHomeAPI") as mock:
mock.return_value.test_connection.return_value = True
mock.return_value.get_status.return_value = {"protection_enabled": True}
yield mock.return_value
Writing Tests
# Example test
async def test_protection_switch_toggle(hass, mock_adguard_api):
"""Test protection switch can be toggled."""
# Set up integration
config_entry = MockConfigEntry(
domain=DOMAIN,
data={"host": "localhost", "port": 3000, "username": "admin", "password": "test"}
)
config_entry.add_to_hass(hass)
# Load integration
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
# Test switch toggle
await hass.services.async_call(
"switch", "turn_off", {"entity_id": "switch.adguard_protection"}
)
await hass.async_block_till_done()
# Verify API was called
mock_adguard_api.set_protection.assert_called_with(False)
🔍 Code Quality Tools
Pre-commit Setup
# Install pre-commit
pip install pre-commit
# Set up git hooks
pre-commit install
# Run manually
pre-commit run --all-files
Code Formatting
# Format code with Black
black custom_components/ tests/
# Sort imports with isort
isort custom_components/ tests/
# Check formatting
black --check custom_components/ tests/
isort --check-only custom_components/ tests/
Linting
# Lint with flake8
flake8 custom_components/ tests/
# Type checking with mypy
mypy custom_components/
# Security scanning with bandit
bandit -r custom_components/
Configuration Files
pyproject.toml
[tool.black]
line-length = 127
target-version = ['py311']
include = '\.pyi?$'
[tool.isort]
profile = "black"
line_length = 127
multi_line_output = 3
include_trailing_comma = true
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
ignore_missing_imports = true
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
addopts = "-v --tb=short"
.flake8
[flake8]
max-line-length = 127
exclude = .git,__pycache__,.venv,venv,.pytest_cache
ignore = E203,W503,E501
🔄 Continuous Integration
Gitea Actions Workflow
The project uses Gitea Actions for CI/CD. Key workflows:
- quality-check.yml - Code quality and security
- integration-test.yml - Integration testing
- release.yml - Automated releases
Local CI Testing
# Run quality checks locally
black --check .
isort --check-only .
flake8 .
mypy custom_components/
bandit -r custom_components/
safety check
# Run tests
pytest --cov=custom_components.adguard_hub
🐛 Debugging
VS Code Configuration
Create .vscode/launch.json
:
{
"version": "0.2.0",
"configurations": [
{
"name": "Home Assistant",
"type": "python",
"request": "launch",
"program": "/path/to/venv/bin/hass",
"args": [
"-c",
"/home/user/.homeassistant",
"--debug",
"--verbose"
],
"console": "integratedTerminal",
"justMyCode": false
}
]
}
Debug Logging
# In your code
import logging
_LOGGER = logging.getLogger(__name__)
# Debug statements
_LOGGER.debug("Debugging info: %s", variable)
_LOGGER.info("General info: %s", status)
_LOGGER.warning("Warning: %s", issue)
_LOGGER.error("Error occurred: %s", error)
Home Assistant Debug Mode
# configuration.yaml
logger:
default: info
logs:
custom_components.adguard_hub: debug
custom_components.adguard_hub.api: debug
# Enable Home Assistant debug mode
homeassistant:
debug: true
📝 Documentation
Code Documentation
class AdGuardHomeAPI:
"""API wrapper for AdGuard Home.
Provides methods to interact with AdGuard Home's REST API,
including client management and service blocking.
Args:
host: AdGuard Home hostname or IP address
port: AdGuard Home web interface port
username: Admin username for authentication
password: Admin password for authentication
ssl: Whether to use HTTPS
session: aiohttp session for HTTP requests
Example:
>>> api = AdGuardHomeAPI("192.168.1.100", 3000, "admin", "password")
>>> await api.test_connection()
True
"""
async def get_clients(self) -> dict:
"""Get all configured clients.
Returns:
Dictionary containing client list and settings.
Raises:
aiohttp.ClientError: If API request fails
Example:
>>> clients = await api.get_clients()
>>> print(clients["clients"][0]["name"])
"Kids iPad"
"""
Wiki Updates
When adding new features:
- Update relevant wiki pages
- Add examples to documentation
- Update troubleshooting guide if needed
- Add API documentation for new endpoints
🔧 Adding New Features
Feature Development Process
- Plan: Create issue describing the feature
- Branch: Create feature branch from main
- Develop: Implement feature with tests
- Test: Run full test suite
- Document: Update documentation
- Review: Submit pull request
- Merge: Merge after approval
Adding New Service
# 1. Define service schema in const.py
NEW_SERVICE_SCHEMA = vol.Schema({
vol.Required("client_name"): cv.string,
vol.Required("new_parameter"): cv.string,
})
# 2. Implement service in services.py
async def new_service_handler(call):
"""Handle new service call."""
client_name = call.data["client_name"]
new_parameter = call.data["new_parameter"]
try:
# Implement service logic
await api.new_api_method(client_name, new_parameter)
except Exception as err:
_LOGGER.error("New service failed: %s", err)
raise HomeAssistantError(f"Service failed: {err}")
# 3. Register service
hass.services.async_register(
DOMAIN,
"new_service",
new_service_handler,
schema=NEW_SERVICE_SCHEMA,
)
# 4. Add to strings.json
{
"services": {
"new_service": {
"name": "New Service",
"description": "Description of new service",
"fields": {
"client_name": {
"name": "Client Name",
"description": "Name of the client"
}
}
}
}
}
# 5. Write tests
async def test_new_service(hass, mock_api):
"""Test new service functionality."""
# Test implementation
Adding New Platform
# 1. Create new platform file (e.g., sensor.py)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up sensors."""
coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"]
entities = []
# Create sensor entities
entities.append(NewSensor(coordinator))
async_add_entities(entities)
# 2. Add to PLATFORMS in const.py
PLATFORMS = ["switch", "binary_sensor", "sensor", "new_platform"]
# 3. Update manifest.json if needed
{
"requirements": ["aiohttp>=3.8.0", "new_dependency>=1.0.0"]
}
🚀 Release Process
Version Management
# Update version in manifest.json
{
"version": "1.1.0"
}
# Update CHANGELOG.md
## [1.1.0] - 2025-01-XX
### Added
- New feature description
### Fixed
- Bug fix description
# Commit changes
git add .
git commit -m "Bump version to 1.1.0"
git push
# Create release tag
git tag -a v1.1.0 -m "Release version 1.1.0"
git push origin v1.1.0
Release Checklist
- All tests pass
- Documentation updated
- Version bumped in manifest.json
- CHANGELOG.md updated
- Git tag created
- Release notes written
- Tested with multiple Home Assistant versions
Ready to contribute? Check out the Contributing Guide for submission guidelines and code standards!