Adaptive Cover Pro is a Home Assistant custom integration that automatically controls blinds, awnings, and venetian blinds based on sun position, using the Data Coordinator Pattern for state management.
┌─────────────────────────────────────────────────────────────────┐
│ Home Assistant Core │
└─────────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────────────┐
│ Config Flow (UI Setup) │
│ - Multi-step wizard for vertical/horizontal/tilt covers │
│ - Options flow for configuration updates │
└─────────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────────────┐
│ AdaptiveDataUpdateCoordinator │
│ - Central state management hub │
│ - Tracks sun position, temperature, weather, presence │
│ - Orchestrates position calculations │
│ - Detects manual overrides │
│ - Calls cover services │
└─────┬───────────────────┬───────────────────┬───────────────────┘
│ │ │
┌─────▼─────┐ ┌─────────▼──────┐ ┌───────▼────────┐
│ Services │ │ Calculation │ │ Entities │
│ Layer │ │ Engine │ │ (Platform) │
└───────────┘ └────────────────┘ └────────────────┘
Role: Central orchestrator for state management and entity updates
Responsibilities:
- Registers listeners on sun, temperature, weather, presence entities
- Triggers calculations when state changes
- Detects manual override (user manually moved cover)
- Calls cover services to move blinds
- Manages automation schedules (start/end times)
- Provides diagnostic data
Data Flow:
Entity State Change → Listener → coordinator._async_update_data()
→ Calculate Position → Update coordinator.data
→ Entities auto-refresh → Call cover service (if enabled)
Purpose: Extract focused responsibilities from coordinator
Services:
- ConfigurationService - Parses config entries, extracts parameters
- Handles vertical/horizontal/tilt-specific configuration
- Converts units (cm → meters for tilt slats)
- Provides climate mode configuration
(Note: Additional services planned for future phases: temperature, presence, weather, capability detection, position verification)
Purpose: Calculate optimal cover positions based on sun geometry
Classes:
- Shared sun position calculations
- Field of view (FOV) validation
- Elevation limits
- Blind spot detection
- Direct sun validity checks
- Purpose: Up/down blinds (vertical movement)
- Algorithm: Projects sun rays to calculate required blind height
- Features:
- Enhanced geometric accuracy with safety margins
- Edge case handling (extreme angles)
- Optional window depth support
- Output: Blind height in meters → converted to percentage
- Purpose: In/out awnings (horizontal projection)
- Algorithm: Uses vertical calculation + trigonometry for horizontal extension
- Output: Awning extension length → converted to percentage
- Purpose: Slat rotation for venetian blinds
- Algorithm: Calculates optimal slat angle to block sun while allowing light
- Output: Slat angle in degrees → converted to percentage
- NormalCoverState - Basic sun position mode
- ClimateCoverState - Climate-aware mode (temperature, presence, weather)
- Winter: Open for solar heating
- Summer: Close for heat blocking
- Presence-aware: Different strategies when home vs away
- PositionConverter: Unified percentage conversion and limit application
- Eliminates code duplication across cover types
- Handles min/max position constraints
- SafetyMarginCalculator: Angle-dependent safety margins
- EdgeCaseHandler: Safe fallbacks for extreme sun angles
- 100% test coverage
- Type-safe enumerations (CoverType, TiltMode, ClimateStrategy, etc.)
- Replaces string comparisons
- Provides display names and utility methods
- Named constants for all magic numbers
- Geometric thresholds (2°, 85°, 88°)
- Safety margin multipliers (0.2, 0.15, 0.1)
- Climate defaults (45°, 80° tilt angles)
Purpose: Eliminate duplication across platform files
Classes:
- AdaptiveCoverBaseEntity - Common device_info, coordinator handling
- AdaptiveCoverSensorBase - Base for sensors
- AdaptiveCoverDiagnosticSensorBase - Base for diagnostic sensors
Benefit: Single source of truth for device information and coordinator updates
Sensor Platform (sensor.py):
- Cover Position (0-100%)
- Start/End Sun Times
- Control Method (direct/summer/winter/default)
- Diagnostic sensors (P0: sun azimuth/elevation, P1: advanced diagnostics)
Switch Platform (switch.py):
- Automatic Control (on/off)
- Climate Mode (on/off)
- Manual Override (on/off) + reset button
Binary Sensor Platform (binary_sensor.py):
- Sun Visibility (in window FOV)
- Position Mismatch (for diagnostics)
Button Platform (button.py):
- Manual Override Reset
Config Entry Created
→ coordinator.__init__()
→ ConfigurationService created
→ Register state listeners
→ Initial calculation
Sun moves OR Temperature changes OR Weather changes
→ Entity state change event
→ coordinator.async_check_entity_state_change()
→ coordinator._async_update_data()
→ Extract config with ConfigurationService
→ Calculate position with AdaptiveXXXCover
→ Apply limits with PositionConverter
→ Build diagnostic data
→ Return AdaptiveCoverData
→ coordinator.data updated
→ All entities notified via _handle_coordinator_update()
→ Entities call async_write_ha_state()
coordinator._async_update_data() completes
→ Check if automatic control enabled
→ Check if position delta sufficient
→ Check if time delta sufficient
→ Check if manual override active
→ If all checks pass:
→ coordinator.async_set_position()
→ Call cover.set_cover_position service
name- Instance namesensor_type- cover_blind/cover_awning/cover_tilt
Window Properties:
set_azimuth- Window facing direction (0-360°)fov_left,fov_right- Field of viewmin_elevation,max_elevation- Sun elevation limitswindow_height,distance_shaded_area- Dimensionswindow_depth- Optional reveal/frame depth (Phase 1 v2.7.0+)
Position Limits:
min_position,max_position- Absolute boundaries (0-100%)enable_min_position,enable_max_position- When limits apply
Automation:
delta_position- Minimum position change to trigger movementdelta_time- Minimum time between movementsstart_time,end_time- Operational time windowsmanual_override_duration- How long to pause after manual changemanual_threshold- Position difference to detect manual override
Climate Mode:
temp_entity,presence_entity,weather_entity- Sensor entitiestemp_low,temp_high- Temperature thresholdsweather_state- Sunny weather conditions listlux_entity,lux_threshold- Light level sensorsirradiance_entity,irradiance_threshold- Solar irradiance sensors
Blind Spots:
blind_spot_left,blind_spot_right- Area to ignore within FOVblind_spot_elevation- Maximum elevation for blind spot
- Create new dataclass extending
AdaptiveGeneralCover - Implement
calculate_position()method - Implement
calculate_percentage()method - Add CoverType enum value
- Update coordinator to handle new type
- Create strategy class (future: when strategy pattern is extracted)
- Implement
calculate_position()logic - Register in ClimateStrategy enum
- Create service class in
services/directory - Inject into coordinator.init()
- Update coordinator to use service methods
- Add tests for service
- calculation.py: 91% coverage, 146 tests
- Position calculations
- Percentage conversions
- Safety margins
- Edge cases
- geometry.py: 100% coverage
- position_utils.py: 100% coverage
- helpers.py: 100% coverage (inverse state tests)
- Manual override detection
- Climate mode behavior
- Time window validation
- Entity state updates
- Calculation Efficiency: Numpy operations for fast trigonometry
- State Caching: Coordinator caches configuration to avoid repeated parsing
- Delta Checking: Prevents unnecessary cover movements
- Async Architecture: Non-blocking I/O for all state changes
Extract remaining responsibilities into focused services:
- Cover capability detection
- Position verification
- Time window management
- State change handling
Split ClimateCoverData into:
- Temperature service
- Presence service
- Weather/light service
- Climate facade
Core:
homeassistant- Home Assistant frameworkpandas- Solar data calculationsnumpy- Fast mathematical operationsastral- Sun position/timing
Development:
pytest- Testing frameworkruff- Linting and formatting
adaptive-cover/
├── custom_components/adaptive_cover_pro/
│ ├── __init__.py # Integration entry point
│ ├── coordinator.py # Data coordinator (central hub)
│ ├── calculation.py # Position calculation engine
│ ├── config_flow.py # Configuration UI
│ │
│ ├── services/ # Service layer (Phase 6+)
│ │ ├── __init__.py
│ │ └── configuration_service.py
│ │
│ ├── entity_base.py # Base entity classes (Phase 2)
│ ├── position_utils.py # Position utilities (Phase 3)
│ ├── geometry.py # Geometric utilities (Phase 3)
│ ├── enums.py # Type-safe enumerations (Phase 1)
│ ├── const.py # Constants (Phase 1)
│ │
│ ├── sensor.py # Sensor platform
│ ├── switch.py # Switch platform
│ ├── binary_sensor.py # Binary sensor platform
│ ├── button.py # Button platform
│ │
│ ├── sun.py # Solar calculations
│ ├── helpers.py # Utility functions
│ └── manifest.json # Integration metadata
│
├── tests/ # Unit tests (239 tests)
├── docs/ # Documentation directory
│ ├── ARCHITECTURE.md # This file
│ ├── CONTRIBUTING.md # Contributing guidelines
│ ├── DEVELOPMENT.md # Developer documentation
│ ├── UNIT_TESTS.md # Testing documentation
│ └── VSCODE_TESTING_GUIDE.md # VS Code testing guide
├── release_notes/ # Historical release notes
└── scripts/ # Development scripts
- v2.7.0+: Enhanced geometric accuracy, window depth support
- v2.6.x: Climate mode, manual override detection
- v2.0.0: Initial release with vertical/horizontal/tilt support
For more information, see:
- DEVELOPMENT.md - Developer guide
- UNIT_TESTS.md - Testing documentation
- README.md - User documentation