Initil after Upgrade

This commit is contained in:
2026-06-15 10:53:52 -04:00
parent 2fe9bf0dd6
commit 887feaa50a
143 changed files with 2288 additions and 881 deletions
+76 -39
View File
@@ -4,8 +4,9 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import Callable
from inspect import isawaitable
import logging
from typing import Any, cast
from typing import Any
import uuid
from homeassistant.config_entries import (
@@ -21,8 +22,7 @@ from homeassistant.const import (
CONF_NAME,
CONF_UNIQUE_ID,
)
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er, selector
from homeassistant.helpers.schema_config_entry_flow import SchemaFlowError
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
@@ -47,7 +47,12 @@ from .const import (
)
from .errors import ModelNotSupportedError, StrategyConfigurationError
from .flow_helper.common import FlowType, PowercalcFormStep, Step, fill_schema_defaults
from .flow_helper.flows.daily_energy import SCHEMA_DAILY_ENERGY_OPTIONS, DailyEnergyConfigFlow, DailyEnergyOptionsFlow, build_daily_energy_config
from .flow_helper.flows.daily_energy import (
SCHEMA_DAILY_ENERGY_OPTIONS,
DailyEnergyConfigFlow,
DailyEnergyOptionsFlow,
build_daily_energy_config,
)
from .flow_helper.flows.global_configuration import (
GlobalConfigurationConfigFlow,
GlobalConfigurationOptionsFlow,
@@ -65,6 +70,7 @@ from .flow_helper.flows.virtual_power import (
VirtualPowerConfigFlow,
VirtualPowerOptionsFlow,
)
from .flow_helper.profile_preview import async_setup_preview as async_setup_powercalc_preview
from .flow_helper.schema import (
SCHEMA_ENERGY_SENSOR_TOGGLE,
SCHEMA_SENSOR_ENERGY_OPTIONS,
@@ -158,9 +164,14 @@ class PowercalcCommonFlow(ABC, ConfigEntryBaseFlow):
super().__init__()
@staticmethod
async def async_setup_preview(hass: HomeAssistant) -> None:
"""Set up the config flow preview websocket command."""
await async_setup_powercalc_preview(hass)
@abstractmethod
@callback
def persist_config_entry(self) -> FlowResult:
def persist_config_entry(self) -> ConfigFlowResult:
pass # pragma: no cover
def _async_step(self, step: Step) -> Callable:
@@ -174,12 +185,12 @@ class PowercalcCommonFlow(ABC, ConfigEntryBaseFlow):
return _async_step
async def async_step(self, step: Step, user_input: dict[str, Any] | None = None) -> FlowResult:
async def async_step(self, step: Step, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
"""Handle a config flow step by delegating to specific handler."""
step_method = f"async_step_{step}"
for handler in self.flow_handlers.values():
if hasattr(handler, step_method):
return await getattr(handler, step_method)(user_input) # type:ignore
return await getattr(handler, step_method)(user_input) # type: ignore[no-any-return]
raise SchemaFlowError("No handler defined") # pragma: nocover
async def validate_strategy_config(self, user_input: dict[str, Any] | None = None) -> None:
@@ -188,8 +199,14 @@ class PowercalcCommonFlow(ABC, ConfigEntryBaseFlow):
self.sensor_config.get(CONF_MODE) or self.selected_profile.calculation_strategy, # type: ignore
)
factory = PowerCalculatorStrategyFactory(self.hass)
assert self.source_entity is not None
try:
await factory.create(user_input or self.sensor_config, strategy_name, self.selected_profile, self.source_entity) # type: ignore
await factory.create(
user_input or self.sensor_config,
strategy_name,
self.selected_profile,
self.source_entity,
)
except StrategyConfigurationError as error:
_LOGGER.error(str(error))
raise SchemaFlowError(error.get_config_flow_translate_key() or "unknown") from error
@@ -227,12 +244,13 @@ class PowercalcCommonFlow(ABC, ConfigEntryBaseFlow):
self,
form_step: PowercalcFormStep,
user_input: dict[str, Any] | None = None,
) -> FlowResult:
) -> ConfigFlowResult:
"""Handle the current step."""
if user_input is not None:
if form_step.validate_user_input is not None:
try:
user_input = await form_step.validate_user_input(user_input)
validated_input = form_step.validate_user_input(user_input)
user_input = await validated_input if isawaitable(validated_input) else validated_input
except SchemaFlowError as exc:
return await self._show_form(form_step, exc)
@@ -243,14 +261,15 @@ class PowercalcCommonFlow(ABC, ConfigEntryBaseFlow):
self.handled_steps.append(form_step.step)
next_step = form_step.next_step
if callable(form_step.next_step):
next_step = await form_step.next_step(user_input)
resolved_next_step = form_step.next_step(user_input)
next_step = await resolved_next_step if isawaitable(resolved_next_step) else resolved_next_step
if not next_step:
return await self.handle_final_steps(
skip_advanced=not form_step.continue_advanced_step,
skip_utility_meter_options=not form_step.continue_utility_meter_options_step,
)
return await getattr(self, f"async_step_{next_step}")() # type: ignore
return await getattr(self, f"async_step_{next_step}")() # type: ignore[no-any-return]
return await self._show_form(form_step)
@@ -258,15 +277,15 @@ class PowercalcCommonFlow(ABC, ConfigEntryBaseFlow):
self,
skip_advanced: bool = False,
skip_utility_meter_options: bool = False,
) -> FlowResult:
) -> ConfigFlowResult:
"""Handle the final steps of the flow if needed and persist the config entry."""
if not skip_advanced and self.selected_sensor_type == SensorType.VIRTUAL_POWER:
return await self.flow_handlers[FlowType.VIRTUAL_POWER].async_step_power_advanced() # type:ignore
return await self.flow_handlers[FlowType.VIRTUAL_POWER].async_step_power_advanced() # type: ignore[no-any-return]
if not skip_utility_meter_options and self.sensor_config.get(CONF_CREATE_UTILITY_METERS):
return await self.async_step_utility_meter_options()
return self.persist_config_entry()
async def _show_form(self, form_step: PowercalcFormStep, error: SchemaFlowError | None = None) -> FlowResult:
async def _show_form(self, form_step: PowercalcFormStep, error: SchemaFlowError | None = None) -> ConfigFlowResult:
# Show form for next step
last_step = None
if not callable(form_step.next_step):
@@ -288,9 +307,12 @@ class PowercalcCommonFlow(ABC, ConfigEntryBaseFlow):
async def _get_schema(self, form_step: PowercalcFormStep) -> vol.Schema:
if isinstance(form_step.schema, vol.Schema):
return form_step.schema
return await form_step.schema()
schema = await form_step.schema()
if schema is None:
return vol.Schema({})
return schema
async def async_step_utility_meter_options(self, user_input: dict[str, Any] | None = None) -> FlowResult:
async def async_step_utility_meter_options(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
"""Handle the flow for utility meter options."""
return await self.handle_form_step(
PowercalcFormStep(
@@ -300,7 +322,7 @@ class PowercalcCommonFlow(ABC, ConfigEntryBaseFlow):
user_input,
)
async def async_step_energy_options(self, user_input: dict[str, Any] | None = None) -> FlowResult:
async def async_step_energy_options(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
"""Handle the flow for utility meter options."""
return await self.handle_form_step(
PowercalcFormStep(
@@ -315,7 +337,7 @@ class PowercalcCommonFlow(ABC, ConfigEntryBaseFlow):
class PowercalcConfigFlow(PowercalcCommonFlow, ConfigFlow, domain=DOMAIN):
"""Handle a config flow for PowerCalc."""
VERSION = 7
VERSION = 8
def __init__(self) -> None:
"""Initialize options flow."""
@@ -367,13 +389,13 @@ class PowercalcConfigFlow(PowercalcCommonFlow, ConfigFlow, domain=DOMAIN):
self.is_library_flow = True
if discovery_info.get(CONF_MODE) == CalculationStrategy.WLED:
return await self.flow_handlers[FlowType.VIRTUAL_POWER].async_step_wled() # type:ignore
return await self.flow_handlers[FlowType.VIRTUAL_POWER].async_step_wled() # type: ignore[no-any-return]
if len(power_profiles) > 1:
return cast(ConfigFlowResult, await self.flow_handlers[FlowType.LIBRARY].async_step_library_multi_profile())
return await self.flow_handlers[FlowType.LIBRARY].async_step_library_multi_profile() # type: ignore[no-any-return]
self.selected_profile = power_profiles[0]
return cast(ConfigFlowResult, await self.flow_handlers[FlowType.LIBRARY].async_step_library())
return await self.flow_handlers[FlowType.LIBRARY].async_step_library() # type: ignore[no-any-return]
async def async_step_user(
self,
@@ -394,15 +416,15 @@ class PowercalcConfigFlow(PowercalcCommonFlow, ConfigFlow, domain=DOMAIN):
return self.async_show_menu(step_id=Step.USER, menu_options=menu)
async def async_step_menu_library(self, user_input: dict[str, Any] | None = None) -> FlowResult:
async def async_step_menu_library(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
"""Handle the Virtual power (library) step.
We forward to the virtual_power step, but without the strategy selector displayed.
"""
self.is_library_flow = True
return await self.flow_handlers[FlowType.VIRTUAL_POWER].async_step_virtual_power(user_input) # type:ignore
return await self.flow_handlers[FlowType.VIRTUAL_POWER].async_step_virtual_power(user_input) # type: ignore[no-any-return]
@callback
def persist_config_entry(self) -> FlowResult:
def persist_config_entry(self) -> ConfigFlowResult:
"""Create the config entry."""
self.sensor_config.update({CONF_SENSOR_TYPE: self.selected_sensor_type})
self.sensor_config.update({CONF_NAME: self.name})
@@ -442,18 +464,21 @@ class PowercalcOptionsFlow(PowercalcCommonFlow, OptionsFlow):
async def async_step_init(
self,
user_input: dict[str, Any] | None = None,
) -> FlowResult:
) -> ConfigFlowResult:
"""Handle options flow."""
self.selected_sensor_type = self.sensor_config.get(CONF_SENSOR_TYPE) or SensorType.VIRTUAL_POWER
self.source_entity_id = self.sensor_config.get(CONF_ENTITY_ID)
if self.config_entry.unique_id == ENTRY_GLOBAL_CONFIG_UNIQUE_ID:
self.global_config = get_global_powercalc_config(self)
return self.async_show_menu(step_id=Step.INIT, menu_options=self.flow_handlers[FlowType.GLOBAL_CONFIGURATION].build_global_config_menu())
return self.async_show_menu(
step_id=Step.INIT,
menu_options=self.flow_handlers[FlowType.GLOBAL_CONFIGURATION].build_global_config_menu(),
)
self.sensor_config = dict(self.config_entry.data)
if self.source_entity_id:
self.source_entity = await create_source_entity(
self.source_entity = create_source_entity(
self.source_entity_id,
self.hass,
)
@@ -463,7 +488,7 @@ class PowercalcOptionsFlow(PowercalcCommonFlow, OptionsFlow):
return self.async_show_menu(step_id=Step.INIT, menu_options=self.build_menu())
async def initialize_library_profile(self) -> FlowResult | None:
async def initialize_library_profile(self) -> ConfigFlowResult | None:
"""Initialize the library profile, when manufacturer and model are set."""
manufacturer: str | None = self.sensor_config.get(CONF_MANUFACTURER)
model: str | None = self.sensor_config.get(CONF_MODEL)
@@ -519,19 +544,29 @@ class PowercalcOptionsFlow(PowercalcCommonFlow, OptionsFlow):
return True
async def async_step_basic_options(self, user_input: dict[str, Any] | None = None) -> FlowResult:
async def async_step_basic_options(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
"""Handle the basic options flow."""
return await self.async_handle_options_step(user_input, self.build_basic_options_schema(), Step.BASIC_OPTIONS)
async def async_step_advanced_options(self, user_input: dict[str, Any] | None = None) -> FlowResult:
async def async_step_advanced_options(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
"""Handle the basic options flow."""
return await self.async_handle_options_step(user_input, SCHEMA_POWER_ADVANCED, Step.ADVANCED_OPTIONS)
async def async_step_utility_meter_options(self, user_input: dict[str, Any] | None = None) -> FlowResult:
async def async_step_utility_meter_options(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
"""Handle the basic options flow."""
return await self.async_handle_options_step(user_input, SCHEMA_UTILITY_METER_OPTIONS, Step.UTILITY_METER_OPTIONS)
return await self.async_handle_options_step(
user_input,
SCHEMA_UTILITY_METER_OPTIONS,
Step.UTILITY_METER_OPTIONS,
)
async def async_handle_options_step(self, user_input: dict[str, Any] | None, schema: vol.Schema, step: Step) -> FlowResult:
async def async_handle_options_step(
self,
user_input: dict[str, Any] | None,
schema: vol.Schema,
step: Step,
form_kwarg: dict[str, Any] | None = None,
) -> ConfigFlowResult:
"""
Generic handler for all the option steps.
processes user input against the select schema.
@@ -543,11 +578,13 @@ class PowercalcOptionsFlow(PowercalcCommonFlow, OptionsFlow):
errors = await self.process_all_options(user_input, schema)
if not errors:
return self.persist_config_entry()
return self.async_show_form(step_id=step, data_schema=schema, errors=errors)
return self.async_show_form(step_id=step, data_schema=schema, errors=errors, **(form_kwarg or {}))
def persist_config_entry(self) -> FlowResult:
def persist_config_entry(self) -> ConfigFlowResult:
"""Persist changed options on the config entry."""
data = (self.config_entry.unique_id == ENTRY_GLOBAL_CONFIG_UNIQUE_ID and self.global_config) or self.sensor_config
data = (
self.config_entry.unique_id == ENTRY_GLOBAL_CONFIG_UNIQUE_ID and self.global_config
) or self.sensor_config
self.hass.config_entries.async_update_entry(
self.config_entry,
@@ -597,7 +634,7 @@ class PowercalcOptionsFlow(PowercalcCommonFlow, OptionsFlow):
) -> None:
"""
Process the provided user input against the schema.
Update the current_config dictionary with the new options. We use that to save the data to config entry later on.
Update current_config with the new options. Used to save the data to the config entry later.
"""
for key in schema.schema:
if isinstance(key, vol.Marker):
@@ -632,7 +669,7 @@ class PowercalcOptionsFlow(PowercalcCommonFlow, OptionsFlow):
{vol.Optional(CONF_STANDBY_POWER): vol.Coerce(float)},
)
return schema.extend( # type: ignore
return schema.extend( # type: ignore[no-any-return]
{
**SCHEMA_ENERGY_SENSOR_TOGGLE.schema,
**SCHEMA_UTILITY_METER_TOGGLE.schema,