95 lines
3.6 KiB
Python
95 lines
3.6 KiB
Python
"""Config/options flow for a standalone cost sensor based on an existing energy sensor."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from homeassistant.components.sensor import SensorDeviceClass
|
|
from homeassistant.config_entries import ConfigFlowResult
|
|
from homeassistant.const import CONF_NAME
|
|
from homeassistant.data_entry_flow import section
|
|
from homeassistant.helpers import selector
|
|
from homeassistant.helpers.schema_config_entry_flow import SchemaFlowError
|
|
import voluptuous as vol
|
|
|
|
from custom_components.powercalc.const import (
|
|
CONF_ENERGY_PRICE,
|
|
CONF_ENERGY_PRICE_SENSOR,
|
|
CONF_ENERGY_SENSOR_ID,
|
|
DOMAIN,
|
|
DOMAIN_CONFIG,
|
|
SensorType,
|
|
)
|
|
from custom_components.powercalc.flow_helper.common import PowercalcFormStep, Step, flatten_sections
|
|
from custom_components.powercalc.flow_helper.schema import SCHEMA_COST_PRICING, SECTION_COST_PRICING
|
|
|
|
if TYPE_CHECKING:
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from custom_components.powercalc.config_flow import PowercalcConfigFlow, PowercalcOptionsFlow
|
|
|
|
SCHEMA_COST_OPTIONS = vol.Schema(
|
|
{
|
|
vol.Required(CONF_ENERGY_SENSOR_ID): selector.EntitySelector(
|
|
selector.EntitySelectorConfig(domain="sensor", device_class=SensorDeviceClass.ENERGY),
|
|
),
|
|
vol.Optional(SECTION_COST_PRICING): section(SCHEMA_COST_PRICING, {"collapsed": True}),
|
|
},
|
|
)
|
|
|
|
SCHEMA_COST = vol.Schema(
|
|
{
|
|
vol.Required(CONF_NAME): selector.TextSelector(),
|
|
**SCHEMA_COST_OPTIONS.schema,
|
|
},
|
|
)
|
|
|
|
|
|
def is_global_price_configured(hass: HomeAssistant) -> bool:
|
|
"""Check whether a global energy price (fixed or sensor) has been configured."""
|
|
global_config = hass.data.get(DOMAIN, {}).get(DOMAIN_CONFIG, {})
|
|
return bool(global_config.get(CONF_ENERGY_PRICE) or global_config.get(CONF_ENERGY_PRICE_SENSOR))
|
|
|
|
|
|
def validate_price_configured(hass: HomeAssistant, user_input: dict[str, Any]) -> dict[str, str] | None:
|
|
"""Ensure a price is available, either overridden on the sensor itself or globally."""
|
|
if user_input.get(CONF_ENERGY_PRICE) or user_input.get(CONF_ENERGY_PRICE_SENSOR):
|
|
return None
|
|
if is_global_price_configured(hass):
|
|
return None
|
|
return {"base": "cost_price_mandatory"}
|
|
|
|
|
|
class CostConfigFlow:
|
|
def __init__(self, flow: PowercalcConfigFlow) -> None:
|
|
self.flow: PowercalcConfigFlow = flow
|
|
|
|
async def async_step_cost(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
|
|
"""Handle the flow for a standalone cost sensor."""
|
|
self.flow.selected_sensor_type = SensorType.COST
|
|
return await self.flow.handle_form_step(
|
|
PowercalcFormStep(step=Step.COST, schema=SCHEMA_COST, validate_user_input=self.validate),
|
|
user_input,
|
|
)
|
|
|
|
def validate(self, user_input: dict[str, Any]) -> dict[str, Any]:
|
|
"""Flatten the pricing section and reject the input when no price is available."""
|
|
user_input = flatten_sections(user_input, SCHEMA_COST)
|
|
if validate_price_configured(self.flow.hass, user_input):
|
|
raise SchemaFlowError("cost_price_mandatory")
|
|
return user_input
|
|
|
|
|
|
class CostOptionsFlow:
|
|
def __init__(self, flow: PowercalcOptionsFlow) -> None:
|
|
self.flow: PowercalcOptionsFlow = flow
|
|
|
|
async def async_step_cost(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
|
|
"""Handle the cost sensor options flow."""
|
|
return await self.flow.async_handle_options_step(
|
|
user_input,
|
|
SCHEMA_COST_OPTIONS,
|
|
Step.COST,
|
|
validate=lambda flat_input: validate_price_configured(self.flow.hass, flat_input),
|
|
)
|