Initil after Upgrade
This commit is contained in:
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -20,11 +20,11 @@ from custom_components.powercalc.const import (
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def remove_power_sensor_from_associated_groups(
|
||||
def remove_power_sensor_from_associated_groups(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
) -> list[ConfigEntry]:
|
||||
"""When the user remove a virtual power config entry we need to update all the groups which this sensor belongs to."""
|
||||
"""When the user removes a virtual power config entry, update all groups this sensor belongs to."""
|
||||
group_entries = get_groups_having_member(hass, config_entry)
|
||||
|
||||
for group_entry in group_entries:
|
||||
@@ -39,18 +39,18 @@ async def remove_power_sensor_from_associated_groups(
|
||||
return group_entries
|
||||
|
||||
|
||||
async def add_to_associated_groups(hass: HomeAssistant, config_entry: ConfigEntry) -> ConfigEntry | None: # type: ignore
|
||||
async def add_to_associated_groups(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
"""
|
||||
When the user has set a group on a virtual power config entry,
|
||||
we need to add this config entry to the group members sensors and update the group.
|
||||
"""
|
||||
sensor_type = config_entry.data.get(CONF_SENSOR_TYPE)
|
||||
if sensor_type not in [SensorType.VIRTUAL_POWER, SensorType.DAILY_ENERGY]:
|
||||
return None
|
||||
return
|
||||
|
||||
raw_groups = config_entry.data.get(CONF_GROUP)
|
||||
if not raw_groups:
|
||||
return None
|
||||
return
|
||||
|
||||
group_ids = raw_groups if isinstance(raw_groups, list) else [raw_groups]
|
||||
for group_entry_id in group_ids:
|
||||
@@ -130,7 +130,9 @@ async def add_to_associated_group(
|
||||
|
||||
def get_entries_having_subgroup(hass: HomeAssistant, subgroup_entry: ConfigEntry) -> list[ConfigEntry]:
|
||||
"""Get all virtual power entries which have the subgroup in their subgroups list."""
|
||||
return [entry for entry in get_group_entries(hass) if subgroup_entry.entry_id in (entry.data.get(CONF_SUB_GROUPS) or [])]
|
||||
return [
|
||||
entry for entry in get_group_entries(hass) if subgroup_entry.entry_id in (entry.data.get(CONF_SUB_GROUPS) or [])
|
||||
]
|
||||
|
||||
|
||||
def get_groups_having_member(hass: HomeAssistant, member_entry: ConfigEntry) -> list[ConfigEntry]:
|
||||
@@ -138,7 +140,8 @@ def get_groups_having_member(hass: HomeAssistant, member_entry: ConfigEntry) ->
|
||||
return [
|
||||
entry
|
||||
for entry in hass.config_entries.async_entries(DOMAIN)
|
||||
if entry.data.get(CONF_SENSOR_TYPE) == SensorType.GROUP and member_entry.entry_id in (entry.data.get(CONF_GROUP_MEMBER_SENSORS) or [])
|
||||
if entry.data.get(CONF_SENSOR_TYPE) == SensorType.GROUP
|
||||
and member_entry.entry_id in (entry.data.get(CONF_GROUP_MEMBER_SENSORS) or [])
|
||||
]
|
||||
|
||||
|
||||
@@ -154,4 +157,6 @@ def get_group_entries(hass: HomeAssistant, group_type: GroupType | None = None)
|
||||
|
||||
@callback
|
||||
def get_entries_excluding_global_config(hass: HomeAssistant) -> list[ConfigEntry]:
|
||||
return [entry for entry in hass.config_entries.async_entries(DOMAIN) if entry.unique_id != ENTRY_GLOBAL_CONFIG_UNIQUE_ID]
|
||||
return [
|
||||
entry for entry in hass.config_entries.async_entries(DOMAIN) if entry.unique_id != ENTRY_GLOBAL_CONFIG_UNIQUE_ID
|
||||
]
|
||||
|
||||
@@ -59,6 +59,8 @@ from custom_components.powercalc.analytics.analytics import collect_analytics
|
||||
from custom_components.powercalc.const import (
|
||||
ATTR_ENTITIES,
|
||||
ATTR_IS_GROUP,
|
||||
ATTR_MEMBERS,
|
||||
ATTR_STATE,
|
||||
CONF_ALL,
|
||||
CONF_AREA,
|
||||
CONF_CREATE_ENERGY_SENSOR,
|
||||
@@ -99,7 +101,14 @@ from custom_components.powercalc.const import (
|
||||
UnitPrefix,
|
||||
)
|
||||
from custom_components.powercalc.device_binding import get_device_info
|
||||
from custom_components.powercalc.group_include.filter import AreaFilter, CompositeFilter, DeviceFilter, EntityFilter, FilterOperator, FloorFilter
|
||||
from custom_components.powercalc.group_include.filter import (
|
||||
AreaFilter,
|
||||
CompositeFilter,
|
||||
DeviceFilter,
|
||||
EntityFilter,
|
||||
FilterOperator,
|
||||
FloorFilter,
|
||||
)
|
||||
from custom_components.powercalc.group_include.include import find_entities
|
||||
from custom_components.powercalc.helpers import async_cache
|
||||
from custom_components.powercalc.sensors.abstract import (
|
||||
@@ -133,7 +142,7 @@ UNIT_CONVERTERS: dict[str | None, type[BaseUnitConverter]] = {
|
||||
}
|
||||
|
||||
|
||||
async def create_group_sensors_yaml(
|
||||
def create_group_sensors_yaml(
|
||||
hass: HomeAssistant,
|
||||
sensor_config: dict[str, Any],
|
||||
entities: list[Entity],
|
||||
@@ -152,7 +161,7 @@ async def create_group_sensors_yaml(
|
||||
)
|
||||
|
||||
group_name = str(sensor_config.get(CONF_CREATE_GROUP))
|
||||
return await create_group_sensors_custom(hass, group_name, sensor_config, power_sensor_ids, energy_sensor_ids)
|
||||
return create_group_sensors_custom(hass, group_name, sensor_config, power_sensor_ids, energy_sensor_ids)
|
||||
|
||||
|
||||
async def create_group_sensors_gui(
|
||||
@@ -171,10 +180,10 @@ async def create_group_sensors_gui(
|
||||
|
||||
energy_sensor_ids = await resolve_entity_ids_recursively(hass, entry, SensorDeviceClass.ENERGY)
|
||||
|
||||
return await create_group_sensors_custom(hass, group_name, sensor_config, power_sensor_ids, energy_sensor_ids)
|
||||
return create_group_sensors_custom(hass, group_name, sensor_config, power_sensor_ids, energy_sensor_ids)
|
||||
|
||||
|
||||
async def create_group_sensors_custom(
|
||||
def create_group_sensors_custom(
|
||||
hass: HomeAssistant,
|
||||
group_name: str,
|
||||
sensor_config: dict[str, Any],
|
||||
@@ -216,7 +225,7 @@ async def create_group_sensors_custom(
|
||||
|
||||
sensor_config[CONF_UTILITY_METER_NET_CONSUMPTION] = True
|
||||
group_sensors.extend(
|
||||
await create_utility_meters(
|
||||
create_utility_meters(
|
||||
hass,
|
||||
energy_sensor,
|
||||
sensor_config,
|
||||
@@ -273,64 +282,83 @@ async def resolve_entity_ids_recursively(
|
||||
if resolved_ids is None:
|
||||
resolved_ids = set()
|
||||
|
||||
def add_member_entry_ids() -> None:
|
||||
"""Add power/energy sensors from the group member entries."""
|
||||
member_entry_ids = entry.data.get(CONF_GROUP_MEMBER_SENSORS) or []
|
||||
for member_entry_id in member_entry_ids:
|
||||
member_entry = hass.config_entries.async_get_entry(member_entry_id)
|
||||
if member_entry is None:
|
||||
continue
|
||||
|
||||
key = resolve_key_based_on_device_class(member_entry)
|
||||
if key and key in member_entry.data:
|
||||
resolved_ids.add(str(member_entry.data.get(key)))
|
||||
|
||||
def resolve_key_based_on_device_class(member_entry: ConfigEntry) -> str | None:
|
||||
"""Resolve the correct key for power/energy sensor based on device class."""
|
||||
if member_entry.data.get(CONF_SENSOR_TYPE) == SensorType.REAL_POWER:
|
||||
return CONF_ENTITY_ID if device_class == SensorDeviceClass.POWER else ENTRY_DATA_ENERGY_ENTITY
|
||||
return ENTRY_DATA_POWER_ENTITY if device_class == SensorDeviceClass.POWER else ENTRY_DATA_ENERGY_ENTITY
|
||||
|
||||
def add_specified_sensors() -> None:
|
||||
"""Add additional power/energy sensors specified by the user."""
|
||||
conf_key = CONF_GROUP_POWER_ENTITIES if device_class == SensorDeviceClass.POWER else CONF_GROUP_ENERGY_ENTITIES
|
||||
resolved_ids.update(entry.data.get(conf_key) or [])
|
||||
|
||||
async def add_include_based_sensors() -> None:
|
||||
"""Add entities from the defined areas, devices and floors."""
|
||||
if all(k not in entry.data for k in (CONF_AREA, CONF_FLOOR, CONF_GROUP_MEMBER_DEVICES)):
|
||||
return
|
||||
|
||||
result = await find_entities(
|
||||
hass,
|
||||
await build_entity_include_filter(hass, entry),
|
||||
bool(entry.data.get(CONF_INCLUDE_NON_POWERCALC_SENSORS)),
|
||||
)
|
||||
resolved_ids.update(filter_entity_list_by_class(result.resolved, device_class))
|
||||
|
||||
async def add_subgroup_entities() -> None:
|
||||
"""Recursively add entities from subgroups."""
|
||||
subgroups = entry.data.get(CONF_SUB_GROUPS)
|
||||
if not subgroups:
|
||||
return
|
||||
|
||||
for subgroup_entry_id in subgroups:
|
||||
subgroup_entry = hass.config_entries.async_get_entry(subgroup_entry_id)
|
||||
if subgroup_entry is None:
|
||||
_LOGGER.error("Subgroup config entry not found: %s", subgroup_entry_id)
|
||||
continue
|
||||
|
||||
await resolve_entity_ids_recursively(hass, subgroup_entry, device_class, resolved_ids)
|
||||
|
||||
# Process the main logic
|
||||
add_member_entry_ids()
|
||||
add_specified_sensors()
|
||||
await add_include_based_sensors()
|
||||
await add_subgroup_entities()
|
||||
_add_member_entry_ids(hass, entry, device_class, resolved_ids)
|
||||
_add_specified_sensors(entry, device_class, resolved_ids)
|
||||
await _add_include_based_sensors(hass, entry, device_class, resolved_ids)
|
||||
await _add_subgroup_entities(hass, entry, device_class, resolved_ids)
|
||||
|
||||
return resolved_ids
|
||||
|
||||
|
||||
def _add_member_entry_ids(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
device_class: SensorDeviceClass,
|
||||
resolved_ids: set[str],
|
||||
) -> None:
|
||||
"""Add power/energy sensors from the group member entries."""
|
||||
member_entry_ids = entry.data.get(CONF_GROUP_MEMBER_SENSORS) or []
|
||||
for member_entry_id in member_entry_ids:
|
||||
member_entry = hass.config_entries.async_get_entry(member_entry_id)
|
||||
if member_entry is None:
|
||||
continue
|
||||
|
||||
key = _resolve_key_based_on_device_class(member_entry, device_class)
|
||||
if key and key in member_entry.data:
|
||||
resolved_ids.add(str(member_entry.data.get(key)))
|
||||
|
||||
|
||||
def _resolve_key_based_on_device_class(member_entry: ConfigEntry, device_class: SensorDeviceClass) -> str | None:
|
||||
"""Resolve the correct key for power/energy sensor based on device class."""
|
||||
if member_entry.data.get(CONF_SENSOR_TYPE) == SensorType.REAL_POWER:
|
||||
return CONF_ENTITY_ID if device_class == SensorDeviceClass.POWER else ENTRY_DATA_ENERGY_ENTITY
|
||||
return ENTRY_DATA_POWER_ENTITY if device_class == SensorDeviceClass.POWER else ENTRY_DATA_ENERGY_ENTITY
|
||||
|
||||
|
||||
def _add_specified_sensors(entry: ConfigEntry, device_class: SensorDeviceClass, resolved_ids: set[str]) -> None:
|
||||
"""Add additional power/energy sensors specified by the user."""
|
||||
conf_key = CONF_GROUP_POWER_ENTITIES if device_class == SensorDeviceClass.POWER else CONF_GROUP_ENERGY_ENTITIES
|
||||
resolved_ids.update(entry.data.get(conf_key) or [])
|
||||
|
||||
|
||||
async def _add_include_based_sensors(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
device_class: SensorDeviceClass,
|
||||
resolved_ids: set[str],
|
||||
) -> None:
|
||||
"""Add entities from the defined areas, devices and floors."""
|
||||
if all(k not in entry.data for k in (CONF_AREA, CONF_FLOOR, CONF_GROUP_MEMBER_DEVICES)):
|
||||
return
|
||||
|
||||
result = await find_entities(
|
||||
hass,
|
||||
await build_entity_include_filter(hass, entry),
|
||||
bool(entry.data.get(CONF_INCLUDE_NON_POWERCALC_SENSORS)),
|
||||
)
|
||||
resolved_ids.update(filter_entity_list_by_class(result.resolved, device_class))
|
||||
|
||||
|
||||
async def _add_subgroup_entities(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
device_class: SensorDeviceClass,
|
||||
resolved_ids: set[str],
|
||||
) -> None:
|
||||
"""Recursively add entities from subgroups."""
|
||||
subgroups = entry.data.get(CONF_SUB_GROUPS)
|
||||
if not subgroups:
|
||||
return
|
||||
|
||||
for subgroup_entry_id in subgroups:
|
||||
subgroup_entry = hass.config_entries.async_get_entry(subgroup_entry_id)
|
||||
if subgroup_entry is None:
|
||||
_LOGGER.error("Subgroup config entry not found: %s", subgroup_entry_id)
|
||||
continue
|
||||
|
||||
await resolve_entity_ids_recursively(hass, subgroup_entry, device_class, resolved_ids)
|
||||
|
||||
|
||||
@callback
|
||||
def create_grouped_power_sensor(
|
||||
hass: HomeAssistant,
|
||||
@@ -448,11 +476,17 @@ class GroupedSensor(BaseEntity, SensorEntity):
|
||||
self._entities = entities
|
||||
self._sensor_config = sensor_config
|
||||
if self._is_energy_sensor:
|
||||
self._rounding_digits = int(sensor_config.get(CONF_ENERGY_SENSOR_PRECISION, DEFAULT_ENERGY_SENSOR_PRECISION))
|
||||
self._update_interval: int = int(sensor_config.get(CONF_GROUP_ENERGY_UPDATE_INTERVAL, DEFAULT_GROUP_ENERGY_UPDATE_INTERVAL))
|
||||
self._rounding_digits = int(
|
||||
sensor_config.get(CONF_ENERGY_SENSOR_PRECISION, DEFAULT_ENERGY_SENSOR_PRECISION),
|
||||
)
|
||||
self._update_interval: int = int(
|
||||
sensor_config.get(CONF_GROUP_ENERGY_UPDATE_INTERVAL, DEFAULT_GROUP_ENERGY_UPDATE_INTERVAL),
|
||||
)
|
||||
else:
|
||||
self._rounding_digits = int(sensor_config.get(CONF_POWER_SENSOR_PRECISION, DEFAULT_POWER_SENSOR_PRECISION))
|
||||
self._update_interval = int(sensor_config.get(CONF_GROUP_POWER_UPDATE_INTERVAL, DEFAULT_GROUP_POWER_UPDATE_INTERVAL))
|
||||
self._update_interval = int(
|
||||
sensor_config.get(CONF_GROUP_POWER_UPDATE_INTERVAL, DEFAULT_GROUP_POWER_UPDATE_INTERVAL),
|
||||
)
|
||||
self._attr_suggested_display_precision = self._rounding_digits
|
||||
if unique_id:
|
||||
self._attr_unique_id = unique_id
|
||||
@@ -523,7 +557,11 @@ class GroupedSensor(BaseEntity, SensorEntity):
|
||||
domain = self._sensor_config.get(CONF_DOMAIN)
|
||||
if domain == CONF_ALL:
|
||||
entity_registry = er.async_get(self.hass)
|
||||
entities = {entity.entity_id for entity in entity_registry.entities.values() if entity.device_class == self.device_class}
|
||||
entities = {
|
||||
entity.entity_id
|
||||
for entity in entity_registry.entities.values()
|
||||
if entity.device_class == self.device_class
|
||||
}
|
||||
else:
|
||||
entities = self.hass.data[DOMAIN].get(DATA_DOMAIN_ENTITIES).get(domain, [])
|
||||
entities = filter_entity_list_by_class(
|
||||
@@ -533,7 +571,7 @@ class GroupedSensor(BaseEntity, SensorEntity):
|
||||
excluded_entities = self._sensor_config.get(CONF_EXCLUDE_ENTITIES) or []
|
||||
self._entities = set({entity for entity in entities if entity not in excluded_entities})
|
||||
|
||||
async def on_start(self, _: Any) -> None: # noqa
|
||||
async def on_start(self, _: HomeAssistant) -> None:
|
||||
"""Initialize group sensor when HA is starting."""
|
||||
await self.init_domain_group()
|
||||
|
||||
@@ -557,7 +595,9 @@ class GroupedSensor(BaseEntity, SensorEntity):
|
||||
"""Initial update for the group sensor state."""
|
||||
all_states = [self.hass.states.get(entity_id) for entity_id in self._entities]
|
||||
states: list[State] = list(filter(None, all_states))
|
||||
available_states = [state for state in states if state and state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE]]
|
||||
available_states = [
|
||||
state for state in states if state and state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE]
|
||||
]
|
||||
if not available_states and not self._ignore_unavailable_state:
|
||||
new_state: Decimal | str = STATE_UNAVAILABLE
|
||||
else:
|
||||
@@ -652,6 +692,37 @@ class GroupedSensor(BaseEntity, SensorEntity):
|
||||
def get_group_entities(self) -> dict[str, set[str]]:
|
||||
return {ATTR_ENTITIES: self._entities}
|
||||
|
||||
def debug_group(self) -> dict[str, Any]:
|
||||
members: dict[str, dict[str, str | None]] = {}
|
||||
for entity_id in sorted(self._entities):
|
||||
members[entity_id] = self._get_member_debug_info(entity_id)
|
||||
|
||||
return {
|
||||
ATTR_STATE: str(self.state),
|
||||
ATTR_UNIT_OF_MEASUREMENT: self.native_unit_of_measurement,
|
||||
ATTR_MEMBERS: members,
|
||||
}
|
||||
|
||||
def _get_member_debug_info(self, entity_id: str) -> dict[str, str | None]:
|
||||
state = self.hass.states.get(entity_id)
|
||||
if state is None:
|
||||
return {
|
||||
ATTR_STATE: None,
|
||||
ATTR_UNIT_OF_MEASUREMENT: None,
|
||||
}
|
||||
|
||||
if state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]:
|
||||
return {
|
||||
ATTR_STATE: str(state.state),
|
||||
ATTR_UNIT_OF_MEASUREMENT: state.attributes.get(ATTR_UNIT_OF_MEASUREMENT),
|
||||
}
|
||||
|
||||
converted_value = round(self._get_state_value_in_native_unit(state), self._rounding_digits)
|
||||
return {
|
||||
ATTR_STATE: str(converted_value),
|
||||
ATTR_UNIT_OF_MEASUREMENT: self.native_unit_of_measurement,
|
||||
}
|
||||
|
||||
@abstractmethod
|
||||
def calculate_initial_state(
|
||||
self,
|
||||
@@ -681,7 +752,9 @@ class GroupedPowerSensor(GroupedSensor, PowerSensor):
|
||||
member_available_states: list[State],
|
||||
member_states: list[State],
|
||||
) -> Decimal | str:
|
||||
self._member_states = {state.entity_id: self._get_state_value_in_native_unit(state) for state in member_available_states}
|
||||
self._member_states = {
|
||||
state.entity_id: self._get_state_value_in_native_unit(state) for state in member_available_states
|
||||
}
|
||||
return self.get_summed_state()
|
||||
|
||||
def calculate_new_state(self, state: State) -> Decimal | str:
|
||||
@@ -776,8 +849,9 @@ class GroupedEnergySensor(GroupedSensor, RestoreSensor, EnergySensor):
|
||||
member_available_states: list[State],
|
||||
member_states: list[State],
|
||||
) -> Decimal:
|
||||
"""Calculate the new group energy sensor state
|
||||
For each member sensor we calculate the delta by looking at the previous known state and compare it to the current.
|
||||
"""Calculate the new group energy sensor state.
|
||||
|
||||
For each member, calculate the delta between the previous known state and the current.
|
||||
"""
|
||||
group_sum = Decimal(self._native_value_exact) if self._native_value_exact else Decimal(0)
|
||||
_LOGGER.debug("%s: Recalculate, current value: %s", self.entity_id, group_sum)
|
||||
@@ -830,10 +904,7 @@ class GroupedEnergySensor(GroupedSensor, RestoreSensor, EnergySensor):
|
||||
)
|
||||
|
||||
start_at_zero = self._sensor_config.get(CONF_GROUP_ENERGY_START_AT_ZERO, True)
|
||||
if prev_state is None and start_at_zero: # noqa: SIM108
|
||||
delta = Decimal(0)
|
||||
else:
|
||||
delta = cur_value - prev_value
|
||||
delta = Decimal(0) if prev_state is None and start_at_zero else cur_value - prev_value
|
||||
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG): # pragma: no cover
|
||||
_LOGGER.debug(
|
||||
@@ -890,7 +961,9 @@ class PreviousStateStore:
|
||||
_LOGGER.debug("Load previous energy sensor states from store")
|
||||
stored_states = await instance.store.async_load() or {}
|
||||
for group, entities in stored_states.items():
|
||||
instance.states[group] = {entity_id: State.from_dict(json_state) for (entity_id, json_state) in entities.items()}
|
||||
instance.states[group] = {
|
||||
entity_id: State.from_dict(json_state) for (entity_id, json_state) in entities.items()
|
||||
}
|
||||
except HomeAssistantError as exc: # pragma: no cover
|
||||
_LOGGER.error("Error loading previous energy sensor states", exc_info=exc)
|
||||
|
||||
@@ -943,7 +1016,7 @@ class PreviousStateStore:
|
||||
def async_setup_dump(self) -> None:
|
||||
"""Set up the listeners for persistence."""
|
||||
|
||||
async def _async_dump_states(*_: Any) -> None: # noqa: ANN401
|
||||
async def _async_dump_states(*_: object) -> None:
|
||||
await self.persist_states()
|
||||
|
||||
# Dump states periodically
|
||||
@@ -951,9 +1024,10 @@ class PreviousStateStore:
|
||||
self.hass,
|
||||
_async_dump_states,
|
||||
STATE_DUMP_INTERVAL,
|
||||
cancel_on_shutdown=True,
|
||||
)
|
||||
|
||||
async def _async_dump_states_at_stop(*_: Any) -> None: # noqa: ANN401
|
||||
async def _async_dump_states_at_stop(*_: object) -> None:
|
||||
cancel_interval()
|
||||
await self.persist_states()
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from custom_components.powercalc.const import CONF_GROUP_TYPE, GroupType
|
||||
from custom_components.powercalc.sensors.group.custom import create_group_sensors_custom
|
||||
|
||||
|
||||
async def create_domain_group_sensor(
|
||||
def create_domain_group_sensor(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
) -> list[Entity]:
|
||||
@@ -16,7 +16,7 @@ async def create_domain_group_sensor(
|
||||
if CONF_UNIQUE_ID not in config:
|
||||
config[CONF_UNIQUE_ID] = generate_unique_id(config)
|
||||
config[CONF_GROUP_TYPE] = GroupType.DOMAIN
|
||||
return await create_group_sensors_custom(
|
||||
return create_group_sensors_custom(
|
||||
hass,
|
||||
name,
|
||||
config,
|
||||
|
||||
@@ -24,12 +24,12 @@ async def create_group_sensors(
|
||||
collect_analytics(hass, config_entry).inc(DATA_GROUP_TYPES, group_type)
|
||||
|
||||
if group_type == GroupType.DOMAIN:
|
||||
return await domain_group.create_domain_group_sensor(
|
||||
return domain_group.create_domain_group_sensor(
|
||||
hass,
|
||||
sensor_config,
|
||||
)
|
||||
if group_type == GroupType.STANDBY:
|
||||
return await standby_group.create_general_standby_sensors(hass, sensor_config)
|
||||
return standby_group.create_general_standby_sensors(hass, sensor_config)
|
||||
|
||||
if group_type == GroupType.CUSTOM:
|
||||
if config_entry:
|
||||
@@ -38,14 +38,14 @@ async def create_group_sensors(
|
||||
entry=config_entry,
|
||||
sensor_config=sensor_config,
|
||||
)
|
||||
return await custom_group.create_group_sensors_yaml(
|
||||
return custom_group.create_group_sensors_yaml(
|
||||
hass=hass,
|
||||
sensor_config=sensor_config,
|
||||
entities=entities or [],
|
||||
)
|
||||
|
||||
if group_type == GroupType.SUBTRACT:
|
||||
return await subtract_group.create_subtract_group_sensors(
|
||||
return subtract_group.create_subtract_group_sensors(
|
||||
hass=hass,
|
||||
config=sensor_config,
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ from custom_components.powercalc.sensors.power import PowerSensor
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def create_general_standby_sensors(
|
||||
def create_general_standby_sensors(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
) -> list[Entity]:
|
||||
@@ -44,8 +44,8 @@ async def create_general_standby_sensors(
|
||||
power_sensor.entity_id = "sensor.all_standby_power"
|
||||
sensor_config = config.copy()
|
||||
sensor_config[CONF_NAME] = "All standby"
|
||||
source_entity = await create_source_entity(DUMMY_ENTITY_ID, hass)
|
||||
energy_sensor = await create_energy_sensor(
|
||||
source_entity = create_source_entity(DUMMY_ENTITY_ID, hass)
|
||||
energy_sensor = create_energy_sensor(
|
||||
hass,
|
||||
sensor_config,
|
||||
power_sensor,
|
||||
@@ -80,8 +80,8 @@ class StandbyPowerSensor(SensorEntity, PowerSensor):
|
||||
"""Calculate sum of all power sensors in standby, and update the state of the sensor."""
|
||||
if self.standby_sensors:
|
||||
self._attr_native_value = Decimal(
|
||||
round( # type: ignore
|
||||
sum(self.standby_sensors.values()),
|
||||
round(
|
||||
sum(self.standby_sensors.values(), Decimal(0)),
|
||||
self._rounding_digits,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ from custom_components.powercalc.sensors.utility_meter import create_utility_met
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def create_subtract_group_sensors(
|
||||
def create_subtract_group_sensors(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
) -> list[Entity]:
|
||||
@@ -58,7 +58,7 @@ async def create_subtract_group_sensors(
|
||||
)
|
||||
sensors.append(power_sensor)
|
||||
if config.get(CONF_CREATE_ENERGY_SENSORS):
|
||||
energy_sensor = await create_energy_sensor(
|
||||
energy_sensor = create_energy_sensor(
|
||||
hass,
|
||||
config,
|
||||
power_sensor,
|
||||
@@ -67,7 +67,7 @@ async def create_subtract_group_sensors(
|
||||
|
||||
config[CONF_UTILITY_METER_NET_CONSUMPTION] = True
|
||||
sensors.extend(
|
||||
await create_utility_meters(
|
||||
create_utility_meters(
|
||||
hass,
|
||||
energy_sensor,
|
||||
config,
|
||||
|
||||
@@ -2,7 +2,6 @@ from __future__ import annotations
|
||||
|
||||
from enum import StrEnum
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.sensor import SensorDeviceClass
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -53,7 +52,11 @@ async def find_auto_tracked_power_entities(hass: HomeAssistant, exclude_entities
|
||||
if exclude_entities:
|
||||
entity_filter = LambdaFilter(lambda entity: entity.entity_id not in exclude_entities)
|
||||
result = await find_entities(hass, entity_filter)
|
||||
return {entity.entity_id for entity in result.resolved if isinstance(entity, PowerSensor) and not isinstance(entity, GroupedSensor)}
|
||||
return {
|
||||
entity.entity_id
|
||||
for entity in result.resolved
|
||||
if isinstance(entity, PowerSensor) and not isinstance(entity, GroupedSensor)
|
||||
}
|
||||
|
||||
|
||||
class TrackedPowerSensorFactory:
|
||||
@@ -67,7 +70,9 @@ class TrackedPowerSensorFactory:
|
||||
"""Create tracked/untracked group sensors."""
|
||||
|
||||
unique_id = str(self.config.get(CONF_UNIQUE_ID))
|
||||
main_power_sensor = str(self.config.get(CONF_MAIN_POWER_SENSOR)) if self.config.get(CONF_MAIN_POWER_SENSOR) else None
|
||||
main_power_sensor = (
|
||||
str(self.config.get(CONF_MAIN_POWER_SENSOR)) if self.config.get(CONF_MAIN_POWER_SENSOR) else None
|
||||
)
|
||||
self.config[CONF_DISABLE_EXTENDED_ATTRIBUTES] = True # prevent adding all entities in the state attributes
|
||||
|
||||
self.tracked_entities = await self.get_tracked_power_entities()
|
||||
@@ -83,7 +88,7 @@ class TrackedPowerSensorFactory:
|
||||
energy_sensor = await self.create_energy_sensor(SensorType.TRACKED, tracked_sensor)
|
||||
entities.append(energy_sensor)
|
||||
entities.extend(
|
||||
await create_utility_meters(
|
||||
create_utility_meters(
|
||||
self.hass,
|
||||
energy_sensor,
|
||||
{CONF_UTILITY_METER_NET_CONSUMPTION: True, **self.config},
|
||||
@@ -102,7 +107,7 @@ class TrackedPowerSensorFactory:
|
||||
energy_sensor = await self.create_energy_sensor(SensorType.UNTRACKED, untracked_sensor)
|
||||
entities.append(energy_sensor)
|
||||
entities.extend(
|
||||
await create_utility_meters(
|
||||
create_utility_meters(
|
||||
self.hass,
|
||||
energy_sensor,
|
||||
{CONF_UTILITY_METER_NET_CONSUMPTION: True, **self.config},
|
||||
@@ -116,12 +121,15 @@ class TrackedPowerSensorFactory:
|
||||
Get all power entities which are part of the tracked sensor group
|
||||
"""
|
||||
if not bool(self.config.get(CONF_GROUP_TRACKED_AUTO, False)):
|
||||
return set(self.config.get(CONF_GROUP_TRACKED_POWER_ENTITIES)) # type: ignore
|
||||
tracked_entities: list[str] | None = self.config.get(CONF_GROUP_TRACKED_POWER_ENTITIES)
|
||||
if not isinstance(tracked_entities, list):
|
||||
return set()
|
||||
return set(tracked_entities)
|
||||
|
||||
# For auto mode, we also want to listen for any changes in the entity registry
|
||||
# Dynamically add/remove power sensors from the tracked group
|
||||
@callback
|
||||
def _start_entity_registry_listener(_: Any) -> None: # noqa ANN401
|
||||
def _start_entity_registry_listener(_: Event) -> None:
|
||||
self.hass.bus.async_listen(EVENT_ENTITY_REGISTRY_UPDATED, self._handle_entity_registry_updated)
|
||||
|
||||
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _start_entity_registry_listener)
|
||||
@@ -137,8 +145,9 @@ class TrackedPowerSensorFactory:
|
||||
entity_id = event.data["entity_id"]
|
||||
action = event.data["action"]
|
||||
|
||||
if action == "update" and "old_entity_id" in event.data:
|
||||
if event.data["old_entity_id"] in self.tracked_entities: # type: ignore
|
||||
old_entity_id = event.data.get("old_entity_id")
|
||||
if action == "update" and old_entity_id is not None:
|
||||
if old_entity_id in self.tracked_entities:
|
||||
return await self.reload()
|
||||
return None # pragma: no cover
|
||||
|
||||
|
||||
Reference in New Issue
Block a user