updated apps
This commit is contained in:
@@ -3,24 +3,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
import copy
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
import uuid
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN, PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.components.utility_meter import max_28_days
|
||||
from homeassistant.components.utility_meter.const import METER_TYPES
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN, SensorEntity
|
||||
from homeassistant.components.utility_meter.sensor import UtilityMeterSensor
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_ENTITIES,
|
||||
CONF_ENTITY_ID,
|
||||
CONF_ID,
|
||||
CONF_NAME,
|
||||
CONF_PATH,
|
||||
CONF_UNIQUE_ID,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, SupportsResponse, callback
|
||||
@@ -35,7 +29,6 @@ from homeassistant.helpers.entity_registry import (
|
||||
RegistryEntryDisabler,
|
||||
)
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.template import Template
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -46,69 +39,22 @@ from .common import (
|
||||
create_source_entity,
|
||||
get_merged_sensor_configuration,
|
||||
validate_is_number,
|
||||
validate_name_pattern,
|
||||
)
|
||||
from .configuration.config_entry_conversion import convert_config_entry_to_sensor_config
|
||||
from .configuration.discovery_info import convert_discovery_info_to_sensor_config
|
||||
from .configuration.sensor_config import PLATFORM_SCHEMA as PLATFORM_SCHEMA
|
||||
from .const import (
|
||||
CONF_AND,
|
||||
CONF_AVAILABILITY_ENTITY,
|
||||
CONF_CALCULATION_ENABLED_CONDITION,
|
||||
CONF_COMPOSITE,
|
||||
CONF_COST,
|
||||
CONF_CREATE_ENERGY_SENSOR,
|
||||
CONF_CREATE_GROUP,
|
||||
CONF_CREATE_UTILITY_METERS,
|
||||
CONF_CUSTOM_MODEL_DIRECTORY,
|
||||
CONF_DAILY_FIXED_ENERGY,
|
||||
CONF_DELAY,
|
||||
CONF_DISABLE_STANDBY_POWER,
|
||||
CONF_ENERGY_FILTER_OUTLIER_ENABLED,
|
||||
CONF_ENERGY_FILTER_OUTLIER_MAX,
|
||||
CONF_ENERGY_INTEGRATION_METHOD,
|
||||
CONF_ENERGY_SENSOR_CATEGORY,
|
||||
CONF_ENERGY_SENSOR_ID,
|
||||
CONF_ENERGY_SENSOR_NAMING,
|
||||
CONF_ENERGY_SENSOR_UNIT_PREFIX,
|
||||
CONF_FILTER,
|
||||
CONF_FIXED,
|
||||
CONF_FORCE_CALCULATE_GROUP_ENERGY,
|
||||
CONF_FORCE_ENERGY_SENSOR_CREATION,
|
||||
CONF_GROUP_ENERGY_START_AT_ZERO,
|
||||
CONF_GROUP_TYPE,
|
||||
CONF_HIDE_MEMBERS,
|
||||
CONF_IGNORE_UNAVAILABLE_STATE,
|
||||
CONF_INCLUDE,
|
||||
CONF_INCLUDE_NON_POWERCALC_SENSORS,
|
||||
CONF_LINEAR,
|
||||
CONF_MANUFACTURER,
|
||||
CONF_MODE,
|
||||
CONF_MODEL,
|
||||
CONF_MULTI_SWITCH,
|
||||
CONF_MULTIPLY_FACTOR,
|
||||
CONF_MULTIPLY_FACTOR_STANDBY,
|
||||
CONF_NOT,
|
||||
CONF_ON_TIME,
|
||||
CONF_OR,
|
||||
CONF_PLAYBOOK,
|
||||
CONF_PLAYBOOKS,
|
||||
CONF_POWER,
|
||||
CONF_POWER_SENSOR_CATEGORY,
|
||||
CONF_POWER_SENSOR_ID,
|
||||
CONF_POWER_SENSOR_NAMING,
|
||||
CONF_POWER_TEMPLATE,
|
||||
CONF_SENSOR_TYPE,
|
||||
CONF_SLEEP_POWER,
|
||||
CONF_STANDBY_POWER,
|
||||
CONF_STATE,
|
||||
CONF_STATES_POWER,
|
||||
CONF_SUBTRACT_ENTITIES,
|
||||
CONF_UNAVAILABLE_POWER,
|
||||
CONF_UTILITY_METER_NET_CONSUMPTION,
|
||||
CONF_UTILITY_METER_OFFSET,
|
||||
CONF_UTILITY_METER_TARIFFS,
|
||||
CONF_UTILITY_METER_TYPES,
|
||||
CONF_VALUE,
|
||||
CONF_VALUE_TEMPLATE,
|
||||
CONF_VARIABLES,
|
||||
CONF_WLED,
|
||||
DATA_CONFIG_TYPES,
|
||||
DATA_CONFIGURED_ENTITIES,
|
||||
DATA_DOMAIN_ENTITIES,
|
||||
@@ -122,157 +68,49 @@ from .const import (
|
||||
DOMAIN,
|
||||
DOMAIN_CONFIG,
|
||||
DUMMY_ENTITY_ID,
|
||||
ENERGY_INTEGRATION_METHODS,
|
||||
ENTITY_CATEGORIES,
|
||||
ENTRY_DATA_ENERGY_ENTITY,
|
||||
ENTRY_DATA_POWER_ENTITY,
|
||||
ENTRY_GLOBAL_CONFIG_UNIQUE_ID,
|
||||
SERVICE_ACTIVATE_PLAYBOOK,
|
||||
SERVICE_CALIBRATE_COST,
|
||||
SERVICE_CALIBRATE_ENERGY,
|
||||
SERVICE_CALIBRATE_UTILITY_METER,
|
||||
SERVICE_DEBUG_GROUP,
|
||||
SERVICE_GET_ACTIVE_PLAYBOOK,
|
||||
SERVICE_GET_GROUP_ENTITIES,
|
||||
SERVICE_INCREASE_DAILY_ENERGY,
|
||||
SERVICE_RESET_COST,
|
||||
SERVICE_RESET_ENERGY,
|
||||
SERVICE_STOP_PLAYBOOK,
|
||||
SERVICE_SWITCH_SUB_PROFILE,
|
||||
CalculationStrategy,
|
||||
EntityType,
|
||||
GroupType,
|
||||
PowercalcDiscoveryType,
|
||||
SensorType,
|
||||
UnitPrefix,
|
||||
)
|
||||
from .device_binding import attach_entities_to_source_device
|
||||
from .device_binding import attach_entities_to_resolved_device
|
||||
from .errors import (
|
||||
PowercalcSetupError,
|
||||
SensorAlreadyConfiguredError,
|
||||
SensorConfigurationError,
|
||||
)
|
||||
from .group_include.filter import FILTER_CONFIG, FilterOperator, create_composite_filter
|
||||
from .group_include.filter import FilterOperator, create_composite_filter
|
||||
from .group_include.include import find_entities
|
||||
from .sensors.cost import CostSensor, create_cost_sensor_for_energy_entity
|
||||
from .sensors.daily_energy import (
|
||||
DAILY_FIXED_ENERGY_SCHEMA,
|
||||
create_daily_fixed_energy_power_sensor,
|
||||
create_daily_fixed_energy_sensor,
|
||||
)
|
||||
from .sensors.energy import EnergySensor, create_energy_sensor
|
||||
from .sensors.energy_related import create_energy_related_sensors
|
||||
from .sensors.group.config_entry_utils import add_to_associated_groups
|
||||
from .sensors.group.custom import GroupedSensor
|
||||
from .sensors.group.factory import create_group_sensors
|
||||
from .sensors.group.standby import StandbyPowerSensor
|
||||
from .sensors.power import PowerSensor, VirtualPowerSensor, create_power_sensor
|
||||
from .sensors.utility_meter import create_utility_meters
|
||||
from .strategy.composite import CONFIG_SCHEMA as COMPOSITE_SCHEMA
|
||||
from .strategy.fixed import CONFIG_SCHEMA as FIXED_SCHEMA
|
||||
from .strategy.linear import CONFIG_SCHEMA as LINEAR_SCHEMA
|
||||
from .strategy.multi_switch import CONFIG_SCHEMA as MULTI_SWITCH_SCHEMA
|
||||
from .strategy.playbook import CONFIG_SCHEMA as PLAYBOOK_SCHEMA
|
||||
from .strategy.wled import CONFIG_SCHEMA as WLED_SCHEMA
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MAX_GROUP_NESTING_LEVEL = 5
|
||||
|
||||
SENSOR_CONFIG = {
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Optional(CONF_ENTITY_ID): cv.entity_id,
|
||||
vol.Optional(CONF_AVAILABILITY_ENTITY): cv.entity_id,
|
||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||
vol.Optional(CONF_MODEL): cv.string,
|
||||
vol.Optional(CONF_MANUFACTURER): cv.string,
|
||||
vol.Optional(CONF_MODE): vol.In([cls.value for cls in CalculationStrategy]),
|
||||
vol.Optional(CONF_STANDBY_POWER): vol.Any(vol.Coerce(float), cv.template),
|
||||
vol.Optional(CONF_DISABLE_STANDBY_POWER): cv.boolean,
|
||||
vol.Optional(CONF_CUSTOM_MODEL_DIRECTORY): cv.string,
|
||||
vol.Optional(CONF_POWER_SENSOR_ID): cv.entity_id,
|
||||
vol.Optional(CONF_FORCE_ENERGY_SENSOR_CREATION): cv.boolean,
|
||||
vol.Optional(CONF_FORCE_CALCULATE_GROUP_ENERGY): cv.boolean,
|
||||
vol.Optional(CONF_FIXED): FIXED_SCHEMA,
|
||||
vol.Optional(CONF_LINEAR): LINEAR_SCHEMA,
|
||||
vol.Optional(CONF_MULTI_SWITCH): MULTI_SWITCH_SCHEMA,
|
||||
vol.Optional(CONF_WLED): WLED_SCHEMA,
|
||||
vol.Optional(CONF_PLAYBOOK): PLAYBOOK_SCHEMA,
|
||||
vol.Optional(CONF_DAILY_FIXED_ENERGY): DAILY_FIXED_ENERGY_SCHEMA,
|
||||
vol.Optional(CONF_CREATE_ENERGY_SENSOR): cv.boolean,
|
||||
vol.Optional(CONF_CREATE_UTILITY_METERS): cv.boolean,
|
||||
vol.Optional(CONF_UTILITY_METER_NET_CONSUMPTION): cv.boolean,
|
||||
vol.Optional(CONF_UTILITY_METER_TARIFFS): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Optional(CONF_UTILITY_METER_TYPES): vol.All(cv.ensure_list, [vol.In(METER_TYPES)]),
|
||||
vol.Optional(CONF_UTILITY_METER_OFFSET): vol.All(cv.time_period, cv.positive_timedelta, max_28_days),
|
||||
vol.Optional(CONF_MULTIPLY_FACTOR): vol.Coerce(float),
|
||||
vol.Optional(CONF_MULTIPLY_FACTOR_STANDBY): cv.boolean,
|
||||
vol.Optional(CONF_POWER_SENSOR_NAMING): validate_name_pattern,
|
||||
vol.Optional(CONF_POWER_SENSOR_CATEGORY): vol.In(ENTITY_CATEGORIES),
|
||||
vol.Optional(CONF_ENERGY_SENSOR_ID): cv.entity_id,
|
||||
vol.Optional(CONF_ENERGY_SENSOR_NAMING): validate_name_pattern,
|
||||
vol.Optional(CONF_ENERGY_SENSOR_CATEGORY): vol.In(ENTITY_CATEGORIES),
|
||||
vol.Optional(CONF_ENERGY_INTEGRATION_METHOD): vol.In(ENERGY_INTEGRATION_METHODS),
|
||||
vol.Optional(CONF_ENERGY_FILTER_OUTLIER_ENABLED): cv.boolean,
|
||||
vol.Optional(CONF_ENERGY_FILTER_OUTLIER_MAX): cv.positive_int,
|
||||
vol.Optional(CONF_ENERGY_SENSOR_UNIT_PREFIX): vol.In([cls.value for cls in UnitPrefix]),
|
||||
vol.Optional(CONF_CREATE_GROUP): cv.string,
|
||||
vol.Optional(CONF_GROUP_ENERGY_START_AT_ZERO): cv.boolean,
|
||||
vol.Optional(CONF_GROUP_TYPE): vol.In([cls.value for cls in GroupType]),
|
||||
vol.Optional(CONF_SUBTRACT_ENTITIES): vol.All(cv.ensure_list, [cv.entity_id]),
|
||||
vol.Optional(CONF_HIDE_MEMBERS): cv.boolean,
|
||||
vol.Optional(CONF_INCLUDE): vol.Schema(
|
||||
{
|
||||
**FILTER_CONFIG.schema,
|
||||
vol.Optional(CONF_FILTER): vol.Schema(
|
||||
{
|
||||
**FILTER_CONFIG.schema,
|
||||
vol.Optional(CONF_OR): vol.All(cv.ensure_list, [FILTER_CONFIG]),
|
||||
vol.Optional(CONF_AND): vol.All(cv.ensure_list, [FILTER_CONFIG]),
|
||||
vol.Optional(CONF_NOT): vol.All(cv.ensure_list, [FILTER_CONFIG]),
|
||||
},
|
||||
),
|
||||
vol.Optional(CONF_INCLUDE_NON_POWERCALC_SENSORS, default=True): cv.boolean,
|
||||
},
|
||||
),
|
||||
vol.Optional(CONF_IGNORE_UNAVAILABLE_STATE): cv.boolean,
|
||||
vol.Optional(CONF_CALCULATION_ENABLED_CONDITION): cv.template,
|
||||
vol.Optional(CONF_SLEEP_POWER): vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_POWER): vol.Coerce(float),
|
||||
vol.Required(CONF_DELAY): cv.positive_int,
|
||||
},
|
||||
),
|
||||
vol.Optional(CONF_UNAVAILABLE_POWER): vol.Coerce(float),
|
||||
vol.Optional(CONF_COMPOSITE): COMPOSITE_SCHEMA,
|
||||
vol.Optional(CONF_VARIABLES): vol.Schema({cv.string: cv.string}),
|
||||
}
|
||||
|
||||
|
||||
def build_nested_configuration_schema(schema: dict, iteration: int = 0) -> dict:
|
||||
if iteration == MAX_GROUP_NESTING_LEVEL:
|
||||
return schema
|
||||
iteration += 1
|
||||
schema.update(
|
||||
{
|
||||
vol.Optional(CONF_ENTITIES): vol.All(
|
||||
cv.ensure_list,
|
||||
[build_nested_configuration_schema(schema.copy(), iteration)],
|
||||
),
|
||||
},
|
||||
)
|
||||
return schema
|
||||
|
||||
|
||||
SENSOR_CONFIG = build_nested_configuration_schema(SENSOR_CONFIG)
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(
|
||||
cv.has_at_least_one_key(
|
||||
CONF_ENTITY_ID,
|
||||
CONF_POWER_SENSOR_ID,
|
||||
CONF_ENTITIES,
|
||||
CONF_INCLUDE,
|
||||
CONF_DAILY_FIXED_ENERGY,
|
||||
),
|
||||
PLATFORM_SCHEMA.extend(SENSOR_CONFIG),
|
||||
)
|
||||
|
||||
ENTITY_ID_FORMAT = SENSOR_DOMAIN + ".{}"
|
||||
|
||||
|
||||
@@ -375,7 +213,7 @@ async def _async_setup_entities(
|
||||
_LOGGER.error(err)
|
||||
return
|
||||
|
||||
await attach_entities_to_source_device(config_entry, entities.new, hass, None)
|
||||
await attach_entities_to_resolved_device(config_entry, entities.new, hass, None, config)
|
||||
|
||||
entities_to_add = [entity for entity in entities.new if isinstance(entity, SensorEntity)]
|
||||
for entity in entities_to_add:
|
||||
@@ -388,13 +226,17 @@ async def _async_setup_entities(
|
||||
# See: https://github.com/bramstroker/homeassistant-powercalc/issues/1454
|
||||
# Remove entities which are disabled because of a disabled device from the list of entities to add
|
||||
# When we add nevertheless the entity_platform code will set device_id to None and abort entity addition.
|
||||
# `async_added_to_hass` hook will not be called, which powercalc uses to bind the entity to device again
|
||||
# This causes the powercalc entity to never be bound to the device again and be disabled forever.
|
||||
# `async_added_to_hass` will not be called, so BaseEntity cannot repair registry metadata.
|
||||
# This causes the powercalc entity to never be rebound and to stay disabled.
|
||||
entity_reg = er.async_get(hass)
|
||||
for entity in entities_to_add:
|
||||
existing_entry = entity_reg.async_get(entity.entity_id)
|
||||
if existing_entry and existing_entry.disabled_by == RegistryEntryDisabler.DEVICE:
|
||||
entities_to_add.remove(entity)
|
||||
entities_to_add = [
|
||||
entity
|
||||
for entity in entities_to_add
|
||||
if not (
|
||||
(existing_entry := entity_reg.async_get(entity.entity_id))
|
||||
and existing_entry.disabled_by == RegistryEntryDisabler.DEVICE
|
||||
)
|
||||
]
|
||||
|
||||
async_add_entities(entities_to_add)
|
||||
|
||||
@@ -447,6 +289,8 @@ def _register_entity_id_change_listener(
|
||||
def _resolve_entity_type(entity: Entity) -> EntityType:
|
||||
if isinstance(entity, UtilityMeterSensor):
|
||||
return EntityType.UTILITY_METER
|
||||
if isinstance(entity, CostSensor):
|
||||
return EntityType.COST_SENSOR
|
||||
if isinstance(entity, EnergySensor):
|
||||
return EntityType.ENERGY_SENSOR
|
||||
if isinstance(entity, PowerSensor):
|
||||
@@ -464,6 +308,9 @@ def save_entity_ids_on_config_entry(
|
||||
We need this in group sensor logic to differentiate between energy sensor and utility meters.
|
||||
"""
|
||||
_LOGGER.debug("Saving entity ids on config entry %s", config_entry.entry_id)
|
||||
if config_entry.data.get(CONF_SENSOR_TYPE) == SensorType.COST:
|
||||
# A standalone cost sensor entry has neither a power nor an energy sensor to track.
|
||||
return
|
||||
power_entities = [e.entity_id for e in entities.all() if isinstance(e, VirtualPowerSensor)]
|
||||
new_data = config_entry.data.copy()
|
||||
if power_entities:
|
||||
@@ -498,12 +345,24 @@ def register_entity_services() -> None:
|
||||
"async_reset",
|
||||
)
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_RESET_COST,
|
||||
{},
|
||||
"async_reset",
|
||||
)
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_CALIBRATE_UTILITY_METER,
|
||||
{vol.Required(CONF_VALUE): validate_is_number},
|
||||
"async_calibrate",
|
||||
)
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_CALIBRATE_COST,
|
||||
{vol.Required(CONF_VALUE): validate_is_number},
|
||||
"async_calibrate",
|
||||
)
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_CALIBRATE_ENERGY,
|
||||
{vol.Required(CONF_VALUE): validate_is_number},
|
||||
@@ -556,127 +415,6 @@ def register_entity_services() -> None:
|
||||
)
|
||||
|
||||
|
||||
def convert_config_entry_to_sensor_config(config_entry: ConfigEntry, hass: HomeAssistant) -> ConfigType: # noqa: C901
|
||||
"""Convert the config entry structure to the sensor config used to create the entities."""
|
||||
sensor_config = dict(config_entry.data.copy())
|
||||
sensor_type = sensor_config.get(CONF_SENSOR_TYPE)
|
||||
|
||||
def handle_sensor_type() -> None:
|
||||
"""Handle sensor type-specific configuration."""
|
||||
if sensor_type == SensorType.GROUP:
|
||||
sensor_config[CONF_CREATE_GROUP] = sensor_config.get(CONF_NAME)
|
||||
elif sensor_type == SensorType.REAL_POWER:
|
||||
sensor_config[CONF_POWER_SENSOR_ID] = sensor_config.get(CONF_ENTITY_ID)
|
||||
sensor_config[CONF_FORCE_ENERGY_SENSOR_CREATION] = True
|
||||
|
||||
def process_template(config: dict, template_key: str, target_key: str) -> None:
|
||||
"""Convert a template key in the config to a Template object."""
|
||||
if template_key in config:
|
||||
config[target_key] = Template(config[template_key], hass)
|
||||
del config[template_key]
|
||||
|
||||
def process_on_time(config: dict) -> None:
|
||||
"""Convert on_time dictionary to timedelta."""
|
||||
on_time = config.get(CONF_ON_TIME)
|
||||
config[CONF_ON_TIME] = (
|
||||
timedelta(hours=on_time["hours"], minutes=on_time["minutes"], seconds=on_time["seconds"])
|
||||
if on_time
|
||||
else timedelta(days=1)
|
||||
)
|
||||
|
||||
def process_states_power(states_power: dict | list) -> dict:
|
||||
"""Convert state power values to Template objects where necessary.
|
||||
|
||||
Handles both dict format (legacy/YAML) and list format (config flow).
|
||||
"""
|
||||
if isinstance(states_power, list):
|
||||
states_power = {item[CONF_STATE]: item[CONF_POWER] for item in states_power}
|
||||
return {
|
||||
key: Template(value, hass) if isinstance(value, str) and "{{" in value else value
|
||||
for key, value in states_power.items()
|
||||
}
|
||||
|
||||
def process_daily_fixed_energy() -> None:
|
||||
"""Process daily fixed energy configuration."""
|
||||
if CONF_DAILY_FIXED_ENERGY not in sensor_config:
|
||||
return
|
||||
|
||||
daily_fixed_config = copy.copy(sensor_config[CONF_DAILY_FIXED_ENERGY])
|
||||
process_template(daily_fixed_config, CONF_VALUE_TEMPLATE, CONF_VALUE)
|
||||
process_on_time(daily_fixed_config)
|
||||
sensor_config[CONF_DAILY_FIXED_ENERGY] = daily_fixed_config
|
||||
|
||||
def process_fixed_config() -> None:
|
||||
"""Process fixed energy configuration."""
|
||||
if CONF_FIXED not in sensor_config:
|
||||
return
|
||||
|
||||
fixed_config = copy.copy(sensor_config[CONF_FIXED])
|
||||
process_template(fixed_config, CONF_POWER_TEMPLATE, CONF_POWER)
|
||||
if CONF_STATES_POWER in fixed_config:
|
||||
fixed_config[CONF_STATES_POWER] = process_states_power(fixed_config[CONF_STATES_POWER])
|
||||
sensor_config[CONF_FIXED] = fixed_config
|
||||
|
||||
def process_linear_config() -> None:
|
||||
"""Process linear energy configuration."""
|
||||
if CONF_LINEAR not in sensor_config:
|
||||
return
|
||||
|
||||
linear_config = copy.copy(sensor_config[CONF_LINEAR])
|
||||
sensor_config[CONF_LINEAR] = linear_config
|
||||
|
||||
def process_calculation_enabled_condition() -> None:
|
||||
"""Process calculation enabled condition template."""
|
||||
if CONF_CALCULATION_ENABLED_CONDITION in sensor_config:
|
||||
sensor_config[CONF_CALCULATION_ENABLED_CONDITION] = Template(
|
||||
sensor_config[CONF_CALCULATION_ENABLED_CONDITION],
|
||||
hass,
|
||||
)
|
||||
|
||||
def process_utility_meter_offset() -> None:
|
||||
if CONF_UTILITY_METER_OFFSET in sensor_config:
|
||||
sensor_config[CONF_UTILITY_METER_OFFSET] = timedelta(days=sensor_config[CONF_UTILITY_METER_OFFSET])
|
||||
|
||||
def process_playbook_config() -> None:
|
||||
if CONF_PLAYBOOK not in sensor_config:
|
||||
return
|
||||
playbook_config = copy.copy(sensor_config[CONF_PLAYBOOK])
|
||||
playbook_config[CONF_PLAYBOOKS] = {item[CONF_ID]: item[CONF_PATH] for item in playbook_config[CONF_PLAYBOOKS]}
|
||||
sensor_config[CONF_PLAYBOOK] = playbook_config
|
||||
|
||||
handle_sensor_type()
|
||||
|
||||
process_daily_fixed_energy()
|
||||
process_fixed_config()
|
||||
process_linear_config()
|
||||
process_playbook_config()
|
||||
process_calculation_enabled_condition()
|
||||
process_utility_meter_offset()
|
||||
|
||||
return sensor_config
|
||||
|
||||
|
||||
def convert_discovery_info_to_sensor_config(
|
||||
discovery_info: DiscoveryInfoType,
|
||||
) -> ConfigType:
|
||||
"""Convert discovery info to sensor config."""
|
||||
if discovery_info[DISCOVERY_TYPE] == PowercalcDiscoveryType.DOMAIN_GROUP:
|
||||
config = discovery_info
|
||||
config[CONF_GROUP_TYPE] = GroupType.DOMAIN
|
||||
config[CONF_SENSOR_TYPE] = SensorType.GROUP
|
||||
config[CONF_ENTITY_ID] = DUMMY_ENTITY_ID
|
||||
return config
|
||||
|
||||
if discovery_info[DISCOVERY_TYPE] == PowercalcDiscoveryType.STANDBY_GROUP:
|
||||
config = discovery_info
|
||||
config[CONF_GROUP_TYPE] = GroupType.STANDBY
|
||||
config[CONF_SENSOR_TYPE] = SensorType.GROUP
|
||||
config[CONF_ENTITY_ID] = DUMMY_ENTITY_ID
|
||||
return config
|
||||
|
||||
return discovery_info
|
||||
|
||||
|
||||
async def create_sensors(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
@@ -720,16 +458,31 @@ async def setup_individual_sensors(
|
||||
context: CreationContext,
|
||||
) -> EntitiesBucket:
|
||||
"""Set up an individual sensor."""
|
||||
if CONF_COST in config:
|
||||
config[CONF_SENSOR_TYPE] = SensorType.COST
|
||||
|
||||
sensor_type = resolve_sensor_type(config)
|
||||
merged_sensor_config = get_merged_sensor_configuration(global_config, config)
|
||||
sensor_type = SensorType(str(config.get(CONF_SENSOR_TYPE, SensorType.VIRTUAL_POWER)))
|
||||
|
||||
if sensor_type == SensorType.GROUP:
|
||||
collect_sensor_analytics(hass, sensor_type, context.discovery_type, config_entry)
|
||||
return EntitiesBucket(new=await create_group_sensors(hass, merged_sensor_config, config_entry))
|
||||
|
||||
if sensor_type == SensorType.COST:
|
||||
collect_sensor_analytics(hass, sensor_type, context.discovery_type, config_entry)
|
||||
cost_sensor = create_cost_sensor_for_energy_entity(hass, merged_sensor_config)
|
||||
return EntitiesBucket(new=[cost_sensor] if cost_sensor else [])
|
||||
|
||||
return await create_individual_sensors(hass, merged_sensor_config, context, sensor_type, config_entry)
|
||||
|
||||
|
||||
def resolve_sensor_type(config: ConfigType) -> SensorType:
|
||||
"""Resolve the sensor type based on the configuration."""
|
||||
if CONF_COST in config:
|
||||
return SensorType.COST
|
||||
return SensorType(str(config.get(CONF_SENSOR_TYPE, SensorType.VIRTUAL_POWER)))
|
||||
|
||||
|
||||
def collect_sensor_analytics(
|
||||
hass: HomeAssistant,
|
||||
sensor_type: SensorType,
|
||||
@@ -918,9 +671,11 @@ async def create_individual_sensors(
|
||||
attach_energy_sensor_to_power_sensor(power_sensor, energy_sensor)
|
||||
|
||||
if energy_sensor:
|
||||
entities_to_add.extend(create_utility_meters(hass, energy_sensor, sensor_config, config_entry))
|
||||
entities_to_add.extend(
|
||||
create_energy_related_sensors(hass, sensor_config, energy_sensor, source_entity, config_entry),
|
||||
)
|
||||
|
||||
await attach_entities_to_source_device(config_entry, entities_to_add, hass, source_entity)
|
||||
await attach_entities_to_resolved_device(config_entry, entities_to_add, hass, source_entity, sensor_config)
|
||||
update_registries(hass, source_entity, entities_to_add, context)
|
||||
|
||||
unique_id = sensor_config.get(CONF_UNIQUE_ID) or source_entity.unique_id
|
||||
|
||||
Reference in New Issue
Block a user