mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-07-28 09:36:49 +00:00
Renamed 8 sensors to clarify what they actually measure, and added 7 new
sensors for a different (and often more useful) calculation.
--- WHY THE RENAME ---
The old name "price_trend_Xh" implied the sensor shows where prices are
heading. It doesn't — it compares CURRENT price vs the FUTURE WINDOW AVERAGE.
At a price minimum, it shows "strongly_falling" (because the cheap minimum
pulls the average below your current high price), which is the opposite of
intuitive. The name "price_outlook_Xh" correctly conveys: "is now cheaper
or more expensive than the next Nh on average?"
--- NEW: price_trajectory_Xh ---
These sensors compare FIRST HALF vs SECOND HALF of the window, revealing
actual price direction within the window:
price_trajectory_2h: avg(hour 1) vs avg(hour 2)
price_trajectory_3h: avg(first 1.5h) vs avg(second 1.5h)
price_trajectory_4h: avg(first 2h) vs avg(second 2h)
price_trajectory_5h: avg(first 2.5h) vs avg(second 2.5h)
price_trajectory_6h: avg(first 3h) vs avg(second 3h)
price_trajectory_8h: avg(first 4h) vs avg(second 4h)
price_trajectory_12h: avg(first 6h) vs avg(second 6h)
The key use case: at a price minimum, price_outlook_Xh shows "strongly_falling"
but price_trajectory_Xh shows "rising" — correctly revealing the upcoming
reversal. "outlook: falling + trajectory: rising" = you're AT the minimum.
--- IMPLEMENTATION ---
sensor/calculators/trend.py:
- get_price_outlook_value() (was: get_price_trend_value())
- New: get_price_trajectory_value(*, hours: int)
- New: _calculate_first_half_average(hours, next_interval_start)
- New: get_trajectory_attributes() → first_half_avg, second_half_avg, half_diff_%
- clear_trend_cache() also resets _trajectory_attributes
sensor/definitions.py:
- 8 SensorEntityDescription entries: key/translation_key price_trend_Xh → price_outlook_Xh
- New PRICE_TRAJECTORY_SENSORS tuple (2h–5h enabled by default, 6h/8h/12h disabled)
sensor/value_getters.py:
- 8 lambda entries renamed
- 7 new trajectory lambda entries added
sensor/attributes/trend.py:
- startswith("price_trend_") → startswith("price_outlook_")
- New elif branch routing price_trajectory_* to cached trajectory_attributes
sensor/core.py:
- startswith checks updated for both prefix families
- cached_data dict extended with "trajectory_attributes"
coordinator/constants.py:
- TIME_SENSITIVE_ENTITY_KEYS: 8 renamed + 7 new trajectory keys added
config_flow_handlers/entity_check.py:
- volatility + price_trend affected-entity lists: 8 renamed + 7 new
BREAKING CHANGE: Sensors price_trend_1h, price_trend_2h, price_trend_3h,
price_trend_4h, price_trend_5h, price_trend_6h, price_trend_8h,
price_trend_12h have been removed without a deprecation period.
Migration:
Replace price_trend_Xh → price_outlook_Xh everywhere (automations,
dashboards, templates). Behavior is identical — only the entity name
changed. If you want to detect actual price direction within the window
(e.g. "are prices rising or falling right now?"), use the new
price_trajectory_Xh sensors instead.
Impact: Users must update automations and dashboards. Entity IDs change from
sensor.<home>_price_trend_Xh to sensor.<home>_price_outlook_Xh. New
price_trajectory_Xh sensors provide complementary direction information.
44 lines
2 KiB
Python
44 lines
2 KiB
Python
"""Trend attribute builders for Tibber Prices sensors."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
if TYPE_CHECKING:
|
|
from custom_components.tibber_prices.coordinator.time_service import TibberPricesTimeService
|
|
|
|
from .timing import add_period_timing_attributes
|
|
from .volatility import add_volatility_attributes
|
|
|
|
|
|
def _add_timing_or_volatility_attributes(
|
|
attributes: dict,
|
|
key: str,
|
|
cached_data: dict,
|
|
native_value: Any = None,
|
|
*,
|
|
time: TibberPricesTimeService,
|
|
) -> None:
|
|
"""Add attributes for timing or volatility sensors."""
|
|
if key.endswith("_volatility"):
|
|
add_volatility_attributes(attributes=attributes, cached_data=cached_data, time=time)
|
|
else:
|
|
add_period_timing_attributes(attributes=attributes, key=key, state_value=native_value, time=time)
|
|
|
|
|
|
def _add_cached_trend_attributes(attributes: dict, key: str, cached_data: dict) -> None:
|
|
"""Add cached trend attributes if available."""
|
|
if key.startswith("price_outlook_") and cached_data.get("trend_attributes"):
|
|
attributes.update(cached_data["trend_attributes"])
|
|
elif key.startswith("price_trajectory_") and cached_data.get("trajectory_attributes"):
|
|
attributes.update(cached_data["trajectory_attributes"])
|
|
elif key == "current_price_trend" and cached_data.get("current_trend_attributes"):
|
|
# Add cached attributes (timestamp already set by platform)
|
|
attributes.update(cached_data["current_trend_attributes"])
|
|
elif key == "next_price_trend_change" and cached_data.get("trend_change_attributes"):
|
|
# Add cached attributes (timestamp already set by platform)
|
|
# State contains the timestamp of the trend change itself
|
|
attributes.update(cached_data["trend_change_attributes"])
|
|
elif key == "trend_change_in_minutes" and cached_data.get("trend_change_attributes"):
|
|
# Duration sensor shares same cached attributes as the timestamp sensor
|
|
attributes.update(cached_data["trend_change_attributes"])
|