Configuration Flow Development¶
Guide to HAEO's ConfigSubentry-based configuration flow implementation.
Overview¶
HAEO uses Home Assistant's ConfigSubentry architecture where each element is managed as a subentry:
- Hub flow (in
custom_components/haeo/flows/hub.py): Creates main hub entry with optimization settings - Element flows (in
custom_components/haeo/flows/element.py): Creates element ConfigSubentries usingConfigSubentryFlow - Network subentry: Automatically created representing the optimization network itself
This architecture follows Home Assistant's native subentry pattern. Elements appear as subentries under the main hub in the UI with proper parent-child management.
For general information on config flows, see the Home Assistant Config Flow documentation and Data Entry Flow.
Architecture Benefits¶
- Native Home Assistant subentry UI integration
- Automatic parent-child relationship management
- Independent configuration of each element
- Proper device registry association via
config_subentry_id - Easy addition/removal through native UI
- No manual
parent_entry_idtracking required
Architecture¶
graph TD
A[Add Integration] --> B{Entry Type?}
B -->|First Time| C[Hub Flow]
B -->|Hub Exists| D[Element Type Selection]
C --> E[Optimization Settings]
E --> F[Create Hub Entry]
D --> G{Element Type}
G -->|Battery| H[Battery Flow]
G -->|Grid| I[Grid Flow]
G -->|PV| J[PV Flow]
G -->|Load| K[Load Flow]
H --> L[Select Parent Hub]
I --> L
J --> L
K --> L
L --> M[Configure Element]
M --> N[Create Subentry]
Hub Flow¶
The hub flow creates the main integration entry that acts as a parent for element subentries and hosts the optimization coordinator.
Hub entry structure¶
Hub entries are identified by the presence of integration_type: "hub" in their data:
{
"entry_id": "abc123...",
"domain": "haeo",
"title": "Home Energy System",
"data": {
"integration_type": "hub" # Marker to identify hub entries
},
"options": {
"horizon_hours": 48,
"period_minutes": 5,
},
}
Optimization settings are stored in options (user-editable), while the hub marker is stored in data (immutable).
The hub flow implementation is in custom_components/haeo/flows/hub.py.
Key implementation points¶
- Hub flow uses standard config flow pattern with user step
- Prevents duplicate hub names by checking existing entries
- Stores optimization settings in
optionsfor later editing via options flow - Hub marker in
dataallows coordinator to identify hub entries
Element Flows¶
Element subentries are created through separate config flows, one per element type. All element flows inherit from a common base class that handles parent hub selection and entry creation.
Element entry structure¶
Element entries link to their parent hub via parent_entry_id:
{
"entry_id": "def456...",
"domain": "haeo",
"title": "Home Battery",
"data": {
"element_type": "battery",
"parent_entry_id": "abc123...", # Links to hub entry
"capacity": 13500,
"charge_power": 5000,
# ... element-specific configuration
},
}
Base element flow pattern¶
All element flows extend ElementConfigFlow which provides:
- Parent hub selection (auto-selects if only one hub exists)
- Entry creation with proper parent linkage
- Duplicate prevention
- Standard error handling
The element flow base class is in custom_components/haeo/flows/element.py.
Connection endpoint filtering¶
Connection elements require selecting source and target endpoints from other configured elements. The element flow filters available elements based on connectivity level and Advanced Mode setting.
Each element type in the ELEMENT_TYPES registry defines a connectivity level that controls when it appears in connection selectors.
The ConnectivityLevel enum has three values:
ALWAYS: Always shown in connection selectorsADVANCED: Only shown when Advanced Mode is enabledNEVER: Never shown in connection selectors
This filtering ensures connection endpoints are appropriate for the user's configuration level.
It prevents invalid connection topologies by excluding elements that shouldn't be connection endpoints.
See custom_components/haeo/elements/__init__.py for the connectivity level assigned to each element type.
Element-specific implementations¶
Each element type has its own flow class in custom_components/haeo/flows/:
BatteryConfigFlow- Battery element configurationGridConfigFlow- Grid configurationSolarConfigFlow- Solar system configurationConstantLoadConfigFlow- Constant load configurationForecastLoadConfigFlow- Forecast-based load configurationNodeConfigFlow- Network node configuration
Each flow defines element-specific schema fields, defaults, and validation logic.
Two-Step Config Flow Pattern¶
Some element types use a two-step configuration flow that separates mode selection from value entry. This pattern provides a cleaner user experience when fields can be configured in different ways.
Flow Steps¶
Step 1 (user): User enters the element name, connection target, and selects an input mode for each configurable field.
Step 2 (values): Based on the mode selections, the UI shows appropriate inputs for each field.
Input Modes¶
The InputMode enum defines how each field receives its value:
| Mode | Description | UI Widget |
|---|---|---|
CONSTANT |
User enters a fixed numeric value | NumberSelector |
ENTITY_LINK |
Value comes from one or more Home Assistant sensors | EntitySelector (multi) |
NONE |
Field is disabled (only available for optional fields) | No input shown |
The NONE option only appears for optional fields (those marked with NotRequired in the TypedDict schema).
Required fields only show CONSTANT and ENTITY_LINK options.
Implementation Pattern¶
The two-step flow utilities are in custom_components/haeo/flows/field_schema.py:
build_mode_schema_entry(): Creates the mode selector for step 1build_value_schema_entry(): Creates the value input for step 2 based on selected modeget_mode_defaults(): Provides default mode selections based on field typesget_value_defaults(): Extracts current values for reconfigure flows
Flow handlers store step 1 data and use it to build the step 2 schema dynamically.
When mode is NONE, no value entry is shown and no input entity is created for that field.
Entity Creation¶
Input entities are only created for fields that are actually configured.
If a user selects NONE for an optional field, no entity is created for that field.
This keeps the entity list clean and focused on configured functionality.
Field Schema System¶
HAEO uses a typed schema system to define element configuration fields. The schema system provides type safety, validation, and data loading from Home Assistant entities.
ELEMENT_TYPES Registry¶
All element types are registered in custom_components/haeo/elements/__init__.py:
ELEMENT_TYPES: dict[ElementType, ElementRegistryEntry] = {
"battery": ElementRegistryEntry(
schema=BatteryConfigSchema, # TypedDict for UI configuration
data=BatteryConfigData, # TypedDict for loaded values
defaults={...}, # Default field values
translation_key="battery", # For UI localization
adapter=create_battery, # Creates model elements
extract=extract_battery, # Extracts optimization results
),
# ... other element types
}
The registry provides:
- Schema/Data class pairs: Dual TypedDict pattern for type safety
- Default values: Pre-populated fields in the UI
- Adapter functions: Convert loaded data to model elements
- Result extractors: Convert optimization results to sensor data
Schema vs Data Mode¶
Each element type has two TypedDict definitions:
Schema mode (*ConfigSchema): What the user enters in the UI
- Contains entity IDs as strings
- Used during config flow form display and validation
Data mode (*ConfigData): What the optimizer uses
- Contains loaded numeric values
- Produced by the
load()function during coordinator updates
# Schema mode: entity IDs with Annotated metadata
class BatteryConfigSchema(TypedDict):
capacity: EnergySensorFieldSchema # Annotated[str, EntitySelect(...), TimeSeries(...)]
# Data mode: loaded values
class BatteryConfigData(TypedDict):
capacity: EnergySensorFieldData # Annotated[list[float], ...]
Field Metadata with Annotated¶
Fields use Annotated types with composable metadata markers:
from typing import Annotated
# Define field type with composed metadata
EnergySensorFieldSchema = Annotated[
str,
EntitySelect(accepted_units=ENERGY_UNITS),
TimeSeries(accepted_units=ENERGY_UNITS),
]
class BatteryConfigSchema(TypedDict):
capacity: EnergySensorFieldSchema # Entity ID with attached metadata
The composable metadata types are:
- Validator subclasses: Define schema validation and UI selectors (e.g.,
PositiveKW,Percentage,EntitySelect) - LoaderMeta subclasses: Specify how values are loaded at runtime (e.g.,
ConstantFloat,TimeSeries) - Default: Provides default values for config flow UI forms
Available Field Types¶
Field types are defined in custom_components/haeo/schema/fields.py:
| Validator Class | Purpose | Base Type |
|---|---|---|
PositiveKW |
Positive power values in kW | float |
AnyKW |
Power flow (pos or neg) in kW | float |
PositiveKWH |
Positive energy values in kWh | float |
Price |
Price values in $/kWh | float |
Percentage |
Percentage values (0-100) | float |
BatterySOC |
Battery SOC percentage | float |
Boolean |
Boolean flags | bool |
Name |
Free-form text names | str |
ElementName |
References to other elements | str |
EntitySelect |
Entity sensor references | str |
Data Loading Flow¶
The schema system integrates with data loading:
- User enters entity IDs in config flow (Schema mode)
- Voluptuous validators ensure valid entity selection
- On coordinator update,
load()converts Schema → Data mode - LoaderMeta markers determine which loader extracts values from Home Assistant
- Adapter functions receive Data mode config to create model elements
For details on loaders, see Data Loading.
Options Flow¶
The options flow allows users to edit hub optimization settings after initial setup. Elements are managed as separate config entries (added/edited/removed through the main integration flow), not through the options flow.
The options flow implementation is in custom_components/haeo/flows/hub.py.
Key points¶
- Options flow only edits hub-level optimization settings (horizon, period, solver)
- Element configuration happens via separate config entries
- Settings stored in
config_entry.options(notdata) - Changes trigger coordinator reload to apply new parameters
Element Management¶
Elements are not managed through the hub's options flow. Instead, users add/edit/remove elements as independent config entries through the main "Add Integration" flow.
User workflow¶
Adding elements:
- Navigate to Settings → Devices & Services
- Click Add Integration
- Search for "HAEO"
- Select element type (battery, grid, etc.)
- Choose parent hub
- Configure element parameters
Editing elements:
- Find the element entry in Devices & Services
- Click Configure on the element entry
- Modify parameters
- Submit changes
Removing elements:
- Find the element entry in Devices & Services
- Click the three-dot menu
- Select Delete
The hub coordinator automatically detects element changes on the next update cycle. See user configuration guide for end-user instructions.
Testing Config Flow¶
Config flow testing uses Home Assistant's testing fixtures and follows standard patterns.
Comprehensive test coverage is in tests/test_config_flow.py, including:
- Hub flow success and duplicate prevention
- Element flow with hub selection
- Options flow for editing settings
- Error handling scenarios
- Validation logic
Example test pattern:
async def test_hub_flow_success(hass: HomeAssistant) -> None:
"""Test successful hub creation."""
result = await hass.config_entries.flow.async_init(DOMAIN, context={"source": config_entries.SOURCE_USER})
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_NAME: "Test Hub", CONF_HORIZON_HOURS: 48},
)
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["data"] == {INTEGRATION_TYPE: INTEGRATION_TYPE_HUB}
Related Documentation¶
-
Architecture
Overall system design.
-
Data Loading
Field types and data validation.
-
Energy Models
Element type implementations.
-
Testing
Testing patterns for config flows.
-
User Configuration Guide
End-user configuration instructions.
-
Home Assistant Config Flow
Upstream pattern documentation.
-
Data Entry Flow
Flow framework reference.