updated apps

This commit is contained in:
2026-07-14 23:57:03 -04:00
parent 6cc7212cef
commit 010e828e9c
797 changed files with 45153 additions and 4246 deletions
@@ -3,7 +3,7 @@ from __future__ import annotations
from abc import abstractmethod
from collections.abc import Callable
from datetime import datetime, timedelta
from decimal import Decimal, DecimalException
from decimal import Decimal
import logging
import time
from typing import Any
@@ -19,7 +19,6 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_UNIT_OF_MEASUREMENT,
CONF_DEVICE,
CONF_DOMAIN,
CONF_ENTITY_ID,
CONF_NAME,
@@ -49,11 +48,6 @@ from homeassistant.helpers.event import (
from homeassistant.helpers.json import JSONEncoder
from homeassistant.helpers.singleton import singleton
from homeassistant.helpers.storage import Store
from homeassistant.util.unit_conversion import (
BaseUnitConverter,
EnergyConverter,
PowerConverter,
)
from custom_components.powercalc.analytics.analytics import collect_analytics
from custom_components.powercalc.const import (
@@ -96,11 +90,11 @@ from custom_components.powercalc.const import (
ENTRY_DATA_ENERGY_ENTITY,
ENTRY_DATA_POWER_ENTITY,
SERVICE_RESET_ENERGY,
UNAVAILABLE_STATES,
GroupType,
SensorType,
UnitPrefix,
)
from custom_components.powercalc.device_binding import get_device_info
from custom_components.powercalc.group_include.filter import (
AreaFilter,
CompositeFilter,
@@ -119,8 +113,13 @@ from custom_components.powercalc.sensors.abstract import (
generate_power_sensor_name,
)
from custom_components.powercalc.sensors.energy import EnergySensor, VirtualEnergySensor
from custom_components.powercalc.sensors.energy_related import create_energy_related_sensors
from custom_components.powercalc.sensors.power import PowerSensor
from custom_components.powercalc.sensors.utility_meter import create_utility_meters
from custom_components.powercalc.unit import (
ENERGY_UNIT_PREFIX_MAPPING,
convert_to_decimal,
parse_decimal,
)
ENTITY_ID_FORMAT = SENSOR_DOMAIN + ".{}"
@@ -130,17 +129,6 @@ STORAGE_VERSION = 2
# How long between periodically saving the current states to disk
STATE_DUMP_INTERVAL = timedelta(minutes=10)
ENERGY_UNIT_PREFIX_MAPPING = {
UnitPrefix.KILO: UnitOfEnergy.KILO_WATT_HOUR,
UnitPrefix.MEGA: UnitOfEnergy.MEGA_WATT_HOUR,
UnitPrefix.NONE: UnitOfEnergy.WATT_HOUR,
}
UNIT_CONVERTERS: dict[str | None, type[BaseUnitConverter]] = {
**dict.fromkeys(EnergyConverter.VALID_UNITS, EnergyConverter),
**dict.fromkeys(PowerConverter.VALID_UNITS, PowerConverter),
}
def create_group_sensors_yaml(
hass: HomeAssistant,
@@ -224,13 +212,7 @@ def create_group_sensors_custom(
group_sensors.append(energy_sensor)
sensor_config[CONF_UTILITY_METER_NET_CONSUMPTION] = True
group_sensors.extend(
create_utility_meters(
hass,
energy_sensor,
sensor_config,
),
)
group_sensors.extend(create_energy_related_sensors(hass, sensor_config, energy_sensor))
collect_analytics(hass, None).add(DATA_GROUP_SIZES, len(power_sensor_ids) + len(energy_sensor_ids))
@@ -388,7 +370,6 @@ def create_grouped_power_sensor(
sensor_config=sensor_config,
group_type=group_type,
entity_id=entity_id,
device_id=sensor_config.get(CONF_DEVICE),
)
@@ -428,7 +409,6 @@ def create_grouped_energy_sensor(
name=name,
unique_id=energy_unique_id,
sensor_config=sensor_config,
device_info=get_device_info(hass, sensor_config, None),
unit_prefix=sensor_config.get(CONF_ENERGY_SENSOR_UNIT_PREFIX, UnitPrefix.NONE),
)
@@ -440,7 +420,6 @@ def create_grouped_energy_sensor(
sensor_config=sensor_config,
group_type=group_type,
entity_id=entity_id,
device_id=sensor_config.get(CONF_DEVICE),
)
@@ -465,10 +444,8 @@ class GroupedSensor(BaseEntity, SensorEntity):
sensor_config: dict[str, Any],
group_type: GroupType,
unique_id: str | None = None,
device_id: str | None = None,
) -> None:
self.entity_id = entity_id
self.source_device_id = device_id
self._attr_name = name
# Remove own entity from entities, when it happens to be there. To prevent recursion
@@ -569,7 +546,7 @@ class GroupedSensor(BaseEntity, SensorEntity):
SensorDeviceClass.ENERGY if self._is_energy_sensor else SensorDeviceClass.POWER,
)
excluded_entities = self._sensor_config.get(CONF_EXCLUDE_ENTITIES) or []
self._entities = set({entity for entity in entities if entity not in excluded_entities})
self._entities = {entity for entity in entities if entity not in excluded_entities}
async def on_start(self, _: HomeAssistant) -> None:
"""Initialize group sensor when HA is starting."""
@@ -595,9 +572,7 @@ 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 UNAVAILABLE_STATES]
if not available_states and not self._ignore_unavailable_state:
new_state: Decimal | str = STATE_UNAVAILABLE
else:
@@ -663,21 +638,19 @@ class GroupedSensor(BaseEntity, SensorEntity):
def _get_state_value_in_native_unit(self, state: State) -> Decimal:
"""Convert value of member entity state to match the unit of measurement of the group sensor."""
value: str | float = state.state
unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
if unit and self._attr_native_unit_of_measurement != unit:
converter = UNIT_CONVERTERS[unit]
value = converter.convert(float(value), unit, self._attr_native_unit_of_measurement)
try:
return Decimal(value)
except DecimalException as err:
value = convert_to_decimal(state.state, unit, self._attr_native_unit_of_measurement)
else:
value = parse_decimal(state.state)
if value is None:
_LOGGER.warning(
"Error converting state value %s to Decimal for %s: %s",
value,
"Error converting state value %s to Decimal for %s",
state.state,
state.entity_id,
err,
)
return Decimal(0)
return value
def _set_native_value(self, value: Decimal, write_state: bool = True) -> None:
self._native_value_exact = value
@@ -711,7 +684,7 @@ class GroupedSensor(BaseEntity, SensorEntity):
ATTR_UNIT_OF_MEASUREMENT: None,
}
if state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]:
if state.state in UNAVAILABLE_STATES:
return {
ATTR_STATE: str(state.state),
ATTR_UNIT_OF_MEASUREMENT: state.attributes.get(ATTR_UNIT_OF_MEASUREMENT),
@@ -758,7 +731,7 @@ class GroupedPowerSensor(GroupedSensor, PowerSensor):
return self.get_summed_state()
def calculate_new_state(self, state: State) -> Decimal | str:
if state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]:
if state.state in UNAVAILABLE_STATES:
if state.entity_id in self._member_states:
del self._member_states[state.entity_id]
else:
@@ -788,7 +761,6 @@ class GroupedEnergySensor(GroupedSensor, RestoreSensor, EnergySensor):
sensor_config: dict[str, Any],
group_type: GroupType,
unique_id: str | None = None,
device_id: str | None = None,
) -> None:
super().__init__(
hass,
@@ -798,7 +770,6 @@ class GroupedEnergySensor(GroupedSensor, RestoreSensor, EnergySensor):
sensor_config,
group_type,
unique_id,
device_id,
)
self._attr_native_unit_of_measurement = ENERGY_UNIT_PREFIX_MAPPING.get(
@@ -853,7 +824,7 @@ class GroupedEnergySensor(GroupedSensor, RestoreSensor, EnergySensor):
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)
group_sum = self._native_value_exact
_LOGGER.debug("%s: Recalculate, current value: %s", self.entity_id, group_sum)
for state in member_available_states:
group_sum += self.calculate_delta(state)
@@ -866,8 +837,8 @@ class GroupedEnergySensor(GroupedSensor, RestoreSensor, EnergySensor):
return group_sum
def calculate_new_state(self, state: State) -> Decimal | str:
group_sum = Decimal(self._native_value_exact) if self._native_value_exact else Decimal(0)
if state.state in [STATE_UNKNOWN, STATE_UNAVAILABLE]:
group_sum = self._native_value_exact
if state.state in UNAVAILABLE_STATES:
if group_sum == 0:
return STATE_UNAVAILABLE
_LOGGER.debug(
@@ -931,22 +902,23 @@ class GroupedEnergySensor(GroupedSensor, RestoreSensor, EnergySensor):
return
last_sensor_state = await self.async_get_last_sensor_data()
try:
if last_sensor_state and last_sensor_state.native_value:
self._set_native_value(Decimal(last_sensor_state.native_value)) # type: ignore
elif last_state:
self._set_native_value(Decimal(last_state.state))
_LOGGER.debug(
"%s: Restoring state: %s",
self.entity_id,
self._attr_native_value,
)
except DecimalException as err:
_LOGGER.warning(
"%s: Could not restore last state: %s",
self.entity_id,
err,
)
if last_sensor_state and last_sensor_state.native_value:
restored = parse_decimal(last_sensor_state.native_value)
elif last_state:
restored = parse_decimal(last_state.state)
else:
restored = None
if restored is None:
_LOGGER.warning("%s: Could not restore last state", self.entity_id)
return
self._set_native_value(restored)
_LOGGER.debug(
"%s: Restoring state: %s",
self.entity_id,
self._attr_native_value,
)
class PreviousStateStore: