From c316ba80c916565727961c370a1166769423b7aa Mon Sep 17 00:00:00 2001 From: sq4ind Date: Sun, 28 Sep 2025 12:47:05 +0000 Subject: [PATCH] Add Development --- Development.md | 526 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 526 insertions(+) create mode 100644 Development.md diff --git a/Development.md b/Development.md new file mode 100644 index 0000000..7f789a2 --- /dev/null +++ b/Development.md @@ -0,0 +1,526 @@ +# ๐Ÿ› ๏ธ 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 + +```bash +# 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 + +```bash +# 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 + +```bash +# 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 + +```bash +# 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 + +```bash +# 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 + +```bash +# Start Home Assistant in development mode +hass --open-ui --verbose + +# Or with specific config directory +hass -c ~/.homeassistant --open-ui --verbose +``` + +## ๐Ÿงช Testing + +### Running Tests + +```bash +# 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 + +```python +# 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 + +```python +# 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 + +```bash +# Install pre-commit +pip install pre-commit + +# Set up git hooks +pre-commit install + +# Run manually +pre-commit run --all-files +``` + +### Code Formatting + +```bash +# 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 + +```bash +# 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 +```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 +```ini +[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: + +1. **quality-check.yml** - Code quality and security +2. **integration-test.yml** - Integration testing +3. **release.yml** - Automated releases + +### Local CI Testing + +```bash +# 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`: + +```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 + +```python +# 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 + +```yaml +# 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 + +```python +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: + +1. Update relevant wiki pages +2. Add examples to documentation +3. Update troubleshooting guide if needed +4. Add API documentation for new endpoints + +## ๐Ÿ”ง Adding New Features + +### Feature Development Process + +1. **Plan**: Create issue describing the feature +2. **Branch**: Create feature branch from main +3. **Develop**: Implement feature with tests +4. **Test**: Run full test suite +5. **Document**: Update documentation +6. **Review**: Submit pull request +7. **Merge**: Merge after approval + +### Adding New Service + +```python +# 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 + +```python +# 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 + +```bash +# 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](Contributing) for submission guidelines and code standards! \ No newline at end of file