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
@@ -9,6 +9,7 @@ from typing import Any
from homeassistant.const import CONF_ATTRIBUTE, CONF_CONDITION, CONF_ENTITY_ID, STATE_OFF
from homeassistant.core import HomeAssistant, State
from homeassistant.exceptions import ConditionError
from homeassistant.helpers.condition import ConditionCheckerType
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import TrackTemplate
@@ -152,15 +153,13 @@ class CompositeStrategy(PowerCalculationStrategyInterface):
for sub_strategy in self.strategies:
strategy = sub_strategy.strategy
if sub_strategy.condition and not sub_strategy.condition(self.hass, {"state": entity_state}):
if sub_strategy.condition and not self._condition_matches(sub_strategy.condition, entity_state):
continue
if isinstance(strategy, PlaybookStrategy):
await self.activate_playbook(strategy)
if (
entity_state.state == STATE_OFF and strategy.can_calculate_standby()
) or entity_state.state != STATE_OFF:
if entity_state.state != STATE_OFF or strategy.can_calculate_standby():
value = await strategy.calculate(entity_state)
if value is not None:
if self.mode == CompositeMode.STOP_AT_FIRST:
@@ -169,6 +168,13 @@ class CompositeStrategy(PowerCalculationStrategyInterface):
return total if self.mode == CompositeMode.SUM_ALL else None
def _condition_matches(self, condition: ConditionCheckerType, entity_state: State) -> bool:
try:
return condition(self.hass, {"state": entity_state})
except ConditionError:
_LOGGER.debug("Skipping composite sub-strategy because condition evaluation failed", exc_info=True)
return False
async def stop_active_playbooks(self) -> None:
"""Stop any active playbooks from sub strategies."""
for playbook in self.playbook_strategies:
@@ -211,7 +211,8 @@ class PowerCalculatorStrategyFactory:
"""Create instance of multi switch strategy."""
multi_switch_config: ConfigType = {}
if power_profile and power_profile.multi_switch_config:
multi_switch_config = power_profile.multi_switch_config
# Copy to avoid mutating the (potentially cached) profile config with the user's config below.
multi_switch_config = dict(power_profile.multi_switch_config)
multi_switch_config.update(config.get(CONF_MULTI_SWITCH, {}))
if not multi_switch_config:
@@ -12,7 +12,7 @@ import voluptuous as vol
from custom_components.powercalc.common import SourceEntity
from custom_components.powercalc.const import CONF_POWER, CONF_STATES_POWER
from custom_components.powercalc.errors import StrategyConfigurationError
from custom_components.powercalc.helpers import evaluate_power
from custom_components.powercalc.unit import evaluate_to_decimal
from .strategy_interface import PowerCalculationStrategyInterface
@@ -46,21 +46,21 @@ class FixedStrategy(PowerCalculationStrategyInterface):
if self._per_state_power is not None:
# Lookup by state
if entity_state.state in self._per_state_power:
return evaluate_power(
return evaluate_to_decimal(
self._per_state_power.get(entity_state.state) or 0,
)
# Lookup by state attribute (attribute|value)
for state_key, power in self._per_state_power.items():
if "|" in state_key:
attribute, value = state_key.split("|", 2)
attribute, value = state_key.split("|", 1)
if str(entity_state.attributes.get(attribute)) == value:
return evaluate_power(power)
return evaluate_to_decimal(power)
if self._power is None:
return None
return evaluate_power(self._power)
return evaluate_to_decimal(self._power)
async def validate_config(self) -> None:
"""Validate correct setup of the strategy."""
@@ -119,7 +119,7 @@ class LinearStrategy(PowerCalculationStrategyInterface):
def is_enabled(self, entity_state: State) -> bool:
"""Return if this strategy is enabled based on entity state."""
return not (self._source_entity.domain == media_player.DOMAIN and entity_state.state is not STATE_PLAYING)
return not (self._source_entity.domain == media_player.DOMAIN and entity_state.state != STATE_PLAYING)
def get_min_calibrate(self, value: int) -> tuple[int, float]:
"""Get closest lower value from calibration table."""
@@ -176,7 +176,7 @@ class LinearStrategy(PowerCalculationStrategyInterface):
return self.get_value_from_attribute(entity_state)
value_entity = self.get_initialized_value_entity()
if value_entity.entity_id is not self._source_entity.entity_id:
if value_entity.entity_id != self._source_entity.entity_id:
# If the value entity is different from the source entity, we need to fetch the state of the value entity
entity_state = self._hass.states.get(value_entity.entity_id)
if not entity_state:
+2 -2
View File
@@ -188,7 +188,7 @@ class LutRegistry:
_LOGGER.debug("Loading LUT data file: %s", path)
return open(path)
raise LutFileNotFoundError("Data file not found: %s")
raise LutFileNotFoundError(f"Data file not found: {path}")
class LutStrategy(PowerCalculationStrategyInterface):
@@ -312,7 +312,7 @@ class LutStrategy(PowerCalculationStrategyInterface):
)
light_setting.hue = int(hs[0] / 360 * 65535)
light_setting.saturation = int(hs[1] / 100 * 255)
except (KeyError, TypeError, ValueError):
except KeyError, TypeError, ValueError:
_LOGGER.error(
"%s: Could not calculate power. no hue/sat set. "
"Please check the attributes of your light in the developer tools.",
+16 -8
View File
@@ -4,7 +4,6 @@ from decimal import Decimal
import logging
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import entity_registry
from homeassistant.helpers.event import TrackTemplate
@@ -16,9 +15,11 @@ from custom_components.powercalc.const import (
CONF_POWER_FACTOR,
CONF_VOLTAGE,
OFF_STATES,
UNAVAILABLE_STATES,
)
from custom_components.powercalc.errors import StrategyConfigurationError
from custom_components.powercalc.helpers import evaluate_power, get_related_entity_by_device_class
from custom_components.powercalc.helpers import get_related_entity_by_device_class
from custom_components.powercalc.unit import evaluate_to_decimal
from .strategy_interface import PowerCalculationStrategyInterface
@@ -53,14 +54,21 @@ class WledStrategy(PowerCalculationStrategyInterface):
if entity_state.entity_id == self._light_entity.entity_id
else self._hass.states.get(self._light_entity.entity_id)
)
if light_state is None:
return None
if light_state.state in OFF_STATES and self._standby_power:
return self._standby_power
if entity_state.entity_id != self._estimated_current_entity:
entity_state = self._hass.states.get(self._estimated_current_entity)
current_state = (
entity_state
if entity_state.entity_id == self._estimated_current_entity
else self._hass.states.get(self._estimated_current_entity)
)
if current_state is None:
return None
if entity_state.state in [STATE_UNAVAILABLE, STATE_UNKNOWN]:
if current_state.state in UNAVAILABLE_STATES:
_LOGGER.warning(
"%s: Estimated current entity %s is not available",
self._light_entity.entity_id,
@@ -71,12 +79,12 @@ class WledStrategy(PowerCalculationStrategyInterface):
_LOGGER.debug(
"%s: Estimated current %s (voltage=%d, power_factor=%.2f)",
self._light_entity.entity_id,
entity_state.state,
current_state.state,
self._voltage,
self._power_factor,
)
power = float(entity_state.state) / 1000 * self._voltage * self._power_factor
return evaluate_power(power)
power = float(current_state.state) / 1000 * self._voltage * self._power_factor
return evaluate_to_decimal(power)
async def find_estimated_current_entity(self) -> str:
entity_reg = entity_registry.async_get(self._hass)