Thank you for your interest in contributing! This document provides guidelines for contributing to this Home Assistant integration development template.
- Getting Started
- Development Setup
- Making Changes
- Pull Request Process
- Quality Standards
- Code Style
- Testing Requirements
- Documentation
This repository is a template for developing Home Assistant custom integrations. Contributions should focus on:
- Improving the template structure
- Enhancing documentation and guides
- Adding example patterns and code
- Fixing bugs in template code
- Updating dependencies and tooling
- Integration-specific features (this is a template, not a specific integration)
- Major architectural changes without discussion
- Breaking changes without migration path
- Python 3.12.3 or later
- Git
- Home Assistant 2026.2.0 (installed in venv)
# 1. Fork and clone the repository
git clone https://github.com/L3DigitalNet/HA-Dev-Template.git
cd ha-template
# 2. Activate the virtual environment
source venv/bin/activate
# 3. Verify environment
python scripts/verify_environment.py# Activate venv first
source venv/bin/activate
# Run all tests
pytest tests/ -v
# Run with coverage
pytest tests/ --cov=custom_components --cov-report=html
# Run specific test file
pytest tests/test_config_flow.py -v# Lint and auto-fix
ruff check . --fix
# Format code
ruff format .
# Type check
mypy custom_components/
# Run all pre-commit hooks
pre-commit run --all-filesUse descriptive branch names:
docs/add-security-guide
feat/oauth2-example
fix/coordinator-error-handling
chore/update-dependencies
Follow conventional commit format:
feat: add OAuth2 example integration
fix: correct coordinator error handling
docs: update HACS integration guide
test: add config flow tests
chore: update pre-commit hooks
Format:
<type>: <description>
[optional body]
[optional footer]
Types:
feat- New featurefix- Bug fixdocs- Documentation changestest- Adding or updating testschore- Maintenance tasksrefactor- Code refactoringstyle- Code style changes (formatting)
-
Create a feature branch
git checkout -b feat/your-feature
-
Make your changes
- Write code
- Add/update tests
- Update documentation
-
Test your changes
pytest tests/ -v ruff check . --fix mypy custom_components/ -
Update CHANGELOG.md
## [Unreleased] ### Added - New OAuth2 example integration
-
Commit your changes
git add . git commit -m "feat: add OAuth2 example integration"
-
Push to your fork
git push origin feat/your-feature
-
Open a Pull Request
- All tests pass (
pytest tests/) - Code passes linting (
ruff check .) - Code is formatted (
ruff format .) - Type checking passes (
mypy custom_components/) - CHANGELOG.md updated
- Documentation updated (if applicable)
- Pre-commit hooks pass (
pre-commit run --all-files)
Use conventional commit format:
feat: add OAuth2 authentication example
fix: correct coordinator timeout handling
docs: improve HACS integration guide
Use the provided template and include:
- What changed - Brief description
- Why it changed - Motivation and context
- Testing - How you tested the changes
- Checklist - Complete all items
- Automated checks run on every PR
- Maintainer review typically within 7 days
- Address feedback by pushing new commits
- Approval required before merge
- Squash and merge for clean history
All code must meet these standards:
- Ruff linting: Zero errors
- Type hints: All functions have return type hints
- Docstrings: All public functions documented
- Mypy: Passes in strict mode
- Minimum 80% coverage for new code
- All new features have tests
- Bug fixes include regression tests
- Integration tests for config flows
- New features documented in relevant guides
- Code comments explain WHY, not WHAT
- README updated for user-facing changes
- CHANGELOG updated for all changes
Follow PEP 8 and Home Assistant's style guidelines:
"""Module docstring."""
from __future__ import annotations
from datetime import timedelta
from typing import Any
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
# Constants
DEFAULT_TIMEOUT: Final[int] = 30
class MyCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Coordinator description.
Longer explanation of what this coordinator does.
"""
def __init__(self, hass: HomeAssistant, host: str) -> None:
"""Initialize coordinator.
Args:
hass: Home Assistant instance.
host: Device hostname or IP.
"""
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=30),
)
self._host = host
async def _async_update_data(self) -> dict[str, Any]:
"""Fetch data from device.
Returns:
Device data dictionary.
Raises:
UpdateFailed: When device is unreachable.
"""
try:
return await self._fetch_data()
except ConnectionError as err:
raise UpdateFailed(f"Connection failed: {err}") from errUse modern Python 3.13+ syntax:
# ✅ CORRECT
from typing import Any
def process_data(data: dict[str, Any]) -> list[str]:
"""Process data."""
items: list[str] = []
return items
# ❌ WRONG (old syntax)
from typing import Dict, List, Any
def process_data(data: Dict[str, Any]) -> List[str]:
"""Process data."""
items: List[str] = []
return itemsOrder imports as:
- Future imports (
from __future__ import annotations) - Standard library
- Third-party libraries
- Home Assistant core
- Local imports
from __future__ import annotations
import asyncio
from datetime import timedelta
from typing import Any
import aiohttp
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN"""Test the example integration."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST
from custom_components.example_integration import async_setup_entry
async def test_setup_entry(hass, mock_api):
"""Test successful setup."""
entry = ConfigEntry(
version=1,
domain="example_integration",
title="Test Device",
data={CONF_HOST: "192.168.1.100"},
source="user",
)
assert await async_setup_entry(hass, entry)
await hass.async_block_till_done()
# Verify setup
assert hass.data["example_integration"][entry.entry_id] is not NoneUse pytest fixtures for mocking:
@pytest.fixture
def mock_api():
"""Mock API client."""
with patch("custom_components.example_integration.APIClient") as mock:
mock.return_value.fetch_data.return_value = {"value": 42}
yield mockCheck coverage after adding tests:
pytest tests/ --cov=custom_components --cov-report=term-missing
# Open HTML report
pytest tests/ --cov=custom_components --cov-report=html
open htmlcov/index.html # macOS
xdg-open htmlcov/index.html # Linux- Clear and concise - Get to the point
- Code examples - Show, don't just tell
- Why, not just what - Explain rationale
- User-focused - Write for developers using the template
When adding features, update relevant documentation:
README.md- User-facing changesCLAUDE.md- AI assistant instructionsdocs/guides - Implementation patternsCHANGELOG.md- All changes- Code comments - Complex logic
We use an automated documentation audit system to ensure docs stay in sync with code:
# Run the documentation audit
make audit-docs
# Or directly
python scripts/audit_documentation.py --verboseWhen to run the audit:
- Before committing documentation changes
- After updating code that affects documentation
- Before releases
What it checks:
- Version numbers (Python, HA, packages) match actual versions
- File and directory references exist
- Code examples are syntactically correct
- Manifest files are valid
- Skills directory structure matches docs
Fixing audit issues:
- Review the audit report
- Fix errors (❌) immediately - these block functionality
- Address warnings (
⚠️ ) if they're legitimate issues - Consider info (ℹ️) suggestions for improvements
See docs/DOCUMENTATION_AUDIT.md for complete audit tool documentation.
Provide complete, runnable examples:
# ✅ GOOD - Complete example
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
class ExampleCoordinator(DataUpdateCoordinator):
"""Example coordinator."""
async def _async_update_data(self):
"""Fetch data."""
return await self.api.fetch_data()
# ❌ BAD - Incomplete snippet
async def _async_update_data(self):
return await self.api.fetch_data()- GitHub Issues - Bug reports, feature requests
- GitHub Discussions - General questions, ideas
- Home Assistant Community - Integration development help
By contributing, you agree that your contributions will be licensed under the same license as this project.
Thank you for contributing! Your efforts help developers worldwide create better Home Assistant integrations.