mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 05:13:40 +00:00
add friendly name
This commit is contained in:
parent
02a226819a
commit
94ef6ed4a6
4 changed files with 207 additions and 61 deletions
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
import json
|
||||
import logging
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import aiofiles
|
||||
|
||||
|
|
@ -114,6 +116,75 @@ async def async_load_translations(hass: HomeAssistant, language: str) -> dict:
|
|||
return empty_cache
|
||||
|
||||
|
||||
async def async_get_translation(
|
||||
hass: HomeAssistant,
|
||||
path: Sequence[str],
|
||||
language: str = "en",
|
||||
) -> Any:
|
||||
"""
|
||||
Get a translation value by path asynchronously.
|
||||
|
||||
Args:
|
||||
hass: HomeAssistant instance
|
||||
path: A sequence of keys defining the path to the translation value
|
||||
language: The language code (defaults to English)
|
||||
|
||||
Returns:
|
||||
The translation value if found, None otherwise
|
||||
|
||||
"""
|
||||
translations = await async_load_translations(hass, language)
|
||||
|
||||
# Navigate to the requested path
|
||||
current = translations
|
||||
for key in path:
|
||||
if not isinstance(current, dict) or key not in current:
|
||||
return None
|
||||
current = current[key]
|
||||
|
||||
return current
|
||||
|
||||
|
||||
def get_translation(
|
||||
path: Sequence[str],
|
||||
language: str = "en",
|
||||
) -> Any:
|
||||
"""
|
||||
Get a translation value by path synchronously from the cache.
|
||||
|
||||
This function only accesses the cached translations to avoid blocking I/O.
|
||||
|
||||
Args:
|
||||
path: A sequence of keys defining the path to the translation value
|
||||
language: The language code (defaults to English)
|
||||
|
||||
Returns:
|
||||
The translation value if found in cache, None otherwise
|
||||
|
||||
"""
|
||||
# Only return from cache to avoid blocking I/O
|
||||
if language not in _TRANSLATIONS_CACHE:
|
||||
# Fall back to English if the requested language is not available
|
||||
if language != "en" and "en" in _TRANSLATIONS_CACHE:
|
||||
language = "en"
|
||||
else:
|
||||
return None
|
||||
|
||||
# Navigate to the requested path
|
||||
current = _TRANSLATIONS_CACHE[language]
|
||||
for key in path:
|
||||
if not isinstance(current, dict):
|
||||
return None
|
||||
if key not in current:
|
||||
# Log the missing key for debugging
|
||||
LOGGER.debug("Translation key '%s' not found in path %s for language %s", key, path, language)
|
||||
return None
|
||||
current = current[key]
|
||||
|
||||
return current
|
||||
|
||||
|
||||
# Convenience functions for backward compatibility and common usage patterns
|
||||
async def async_get_entity_description(
|
||||
hass: HomeAssistant,
|
||||
entity_type: str,
|
||||
|
|
@ -135,26 +206,24 @@ async def async_get_entity_description(
|
|||
The requested field's value if found, None otherwise
|
||||
|
||||
"""
|
||||
translations = await async_load_translations(hass, language)
|
||||
entity_data = await async_get_translation(hass, [entity_type, entity_key], language)
|
||||
|
||||
# Check if entity exists in translations
|
||||
if entity_type in translations and entity_key in translations[entity_type]:
|
||||
# Get the entity data
|
||||
entity_data = translations[entity_type][entity_key]
|
||||
# Handle the case where entity_data is a string (for description field)
|
||||
if isinstance(entity_data, str) and field == "description":
|
||||
return entity_data
|
||||
|
||||
# If entity_data is a string, return it only for description field
|
||||
if isinstance(entity_data, str) and field == "description":
|
||||
return entity_data
|
||||
|
||||
# If entity_data is a dict, look for the requested field
|
||||
if isinstance(entity_data, dict) and field in entity_data:
|
||||
return entity_data[field]
|
||||
# Handle the case where entity_data is a dict
|
||||
if isinstance(entity_data, dict) and field in entity_data:
|
||||
return entity_data[field]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_entity_description(
|
||||
entity_type: str, entity_key: str, language: str = "en", field: str = "description"
|
||||
entity_type: str,
|
||||
entity_key: str,
|
||||
language: str = "en",
|
||||
field: str = "description",
|
||||
) -> str | None:
|
||||
"""
|
||||
Get entity description synchronously from the cache.
|
||||
|
|
@ -171,16 +240,54 @@ def get_entity_description(
|
|||
The requested field's value if found in cache, None otherwise
|
||||
|
||||
"""
|
||||
# Only return from cache to avoid blocking I/O
|
||||
if language in _TRANSLATIONS_CACHE:
|
||||
translations = _TRANSLATIONS_CACHE[language]
|
||||
if entity_type in translations and entity_key in translations[entity_type]:
|
||||
entity_data = translations[entity_type][entity_key]
|
||||
entity_data = get_translation([entity_type, entity_key], language)
|
||||
|
||||
if isinstance(entity_data, str) and field == "description":
|
||||
return entity_data
|
||||
# Handle the case where entity_data is a string (for description field)
|
||||
if isinstance(entity_data, str) and field == "description":
|
||||
return entity_data
|
||||
|
||||
if isinstance(entity_data, dict) and field in entity_data:
|
||||
return entity_data[field]
|
||||
# Handle the case where entity_data is a dict
|
||||
if isinstance(entity_data, dict) and field in entity_data:
|
||||
return entity_data[field]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def async_get_price_level_translation(
|
||||
hass: HomeAssistant,
|
||||
level: str,
|
||||
language: str = "en",
|
||||
) -> str | None:
|
||||
"""
|
||||
Get a localized translation for a price level asynchronously.
|
||||
|
||||
Args:
|
||||
hass: HomeAssistant instance
|
||||
level: The price level (e.g., VERY_CHEAP, NORMAL, etc.)
|
||||
language: The language code (defaults to English)
|
||||
|
||||
Returns:
|
||||
The localized price level if found, None otherwise
|
||||
|
||||
"""
|
||||
return await async_get_translation(hass, ["sensor", "price_level", "price_levels", level], language)
|
||||
|
||||
|
||||
def get_price_level_translation(
|
||||
level: str,
|
||||
language: str = "en",
|
||||
) -> str | None:
|
||||
"""
|
||||
Get a localized translation for a price level synchronously from the cache.
|
||||
|
||||
This function only accesses the cached translations to avoid blocking I/O.
|
||||
|
||||
Args:
|
||||
level: The price level (e.g., VERY_CHEAP, NORMAL, etc.)
|
||||
language: The language code (defaults to English)
|
||||
|
||||
Returns:
|
||||
The localized price level if found in cache, None otherwise
|
||||
|
||||
"""
|
||||
return get_translation(["sensor", "price_level", "price_levels", level], language)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,14 @@
|
|||
"price_level": {
|
||||
"description": "Aktueller Preisstandanzeige (SEHR_GÜNSTIG bis SEHR_TEUER)",
|
||||
"long_description": "Zeigt das aktuelle Preisniveau auf einer Skala von sehr günstig bis sehr teuer an",
|
||||
"usage_tips": "Verwende dies für visuelle Anzeigen oder einfache Automatisierungen ohne Schwellenwertberechnung"
|
||||
"usage_tips": "Verwende dies für visuelle Anzeigen oder einfache Automatisierungen ohne Schwellenwertberechnung",
|
||||
"price_levels": {
|
||||
"VERY_CHEAP": "Sehr Günstig",
|
||||
"CHEAP": "Günstig",
|
||||
"NORMAL": "Normal",
|
||||
"EXPENSIVE": "Teuer",
|
||||
"VERY_EXPENSIVE": "Sehr Teuer"
|
||||
}
|
||||
},
|
||||
"lowest_price_today": {
|
||||
"description": "Der niedrigste Strompreis für den aktuellen Tag",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,14 @@
|
|||
"price_level": {
|
||||
"description": "Current price level indicator (VERY_CHEAP to VERY_EXPENSIVE)",
|
||||
"long_description": "Indicates the current price level on a scale from very cheap to very expensive",
|
||||
"usage_tips": "Use this for visual indicators or simple automations without needing to calculate thresholds"
|
||||
"usage_tips": "Use this for visual indicators or simple automations without needing to calculate thresholds",
|
||||
"price_levels": {
|
||||
"VERY_CHEAP": "Very Cheap",
|
||||
"CHEAP": "Cheap",
|
||||
"NORMAL": "Normal",
|
||||
"EXPENSIVE": "Expensive",
|
||||
"VERY_EXPENSIVE": "Very Expensive"
|
||||
}
|
||||
},
|
||||
"lowest_price_today": {
|
||||
"description": "The lowest electricity price for the current day",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime
|
||||
from datetime import date, datetime, timedelta
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -17,13 +17,13 @@ from homeassistant.const import CURRENCY_EURO, EntityCategory
|
|||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
PRICE_LEVEL_CHEAP,
|
||||
PRICE_LEVEL_EXPENSIVE,
|
||||
CONF_EXTENDED_DESCRIPTIONS,
|
||||
DEFAULT_EXTENDED_DESCRIPTIONS,
|
||||
DOMAIN,
|
||||
PRICE_LEVEL_MAPPING,
|
||||
PRICE_LEVEL_NORMAL,
|
||||
PRICE_LEVEL_VERY_CHEAP,
|
||||
PRICE_LEVEL_VERY_EXPENSIVE,
|
||||
SENSOR_TYPE_PRICE_LEVEL,
|
||||
async_get_entity_description,
|
||||
get_entity_description,
|
||||
)
|
||||
from .entity import TibberPricesEntity
|
||||
|
||||
|
|
@ -314,8 +314,6 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity):
|
|||
|
||||
# Calculate the exact target datetime (not just the hour)
|
||||
# This properly handles day boundaries
|
||||
from datetime import timedelta
|
||||
|
||||
target_datetime = now.replace(microsecond=0) + timedelta(hours=hour_offset)
|
||||
target_hour = target_datetime.hour
|
||||
target_date = target_datetime.date()
|
||||
|
|
@ -468,13 +466,6 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity):
|
|||
# Get user's language preference
|
||||
language = self.hass.config.language if self.hass.config.language else "en"
|
||||
|
||||
# Import only within the method to avoid circular imports
|
||||
from .const import (
|
||||
CONF_EXTENDED_DESCRIPTIONS,
|
||||
DEFAULT_EXTENDED_DESCRIPTIONS,
|
||||
async_get_entity_description,
|
||||
)
|
||||
|
||||
# Add basic description
|
||||
description = await async_get_entity_description(self.hass, "sensor", base_key, language, "description")
|
||||
if description:
|
||||
|
|
@ -526,13 +517,6 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity):
|
|||
# Get user's language preference
|
||||
language = self.hass.config.language if self.hass.config.language else "en"
|
||||
|
||||
# Import synchronous function to get cached descriptions
|
||||
from .const import (
|
||||
CONF_EXTENDED_DESCRIPTIONS,
|
||||
DEFAULT_EXTENDED_DESCRIPTIONS,
|
||||
get_entity_description,
|
||||
)
|
||||
|
||||
# Add basic description from cache
|
||||
description = get_entity_description("sensor", base_key, language, "description")
|
||||
if description:
|
||||
|
|
@ -602,25 +586,66 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity):
|
|||
self._add_price_level_attributes(attributes, current_hour_data["level"])
|
||||
|
||||
def _add_price_level_attributes(self, attributes: dict, level: str) -> None:
|
||||
"""Add price level specific attributes."""
|
||||
if level in PRICE_LEVEL_MAPPING:
|
||||
attributes["level_value"] = PRICE_LEVEL_MAPPING[level]
|
||||
"""
|
||||
Add price level specific attributes.
|
||||
|
||||
# Add human-readable level descriptions
|
||||
level_descriptions = {
|
||||
PRICE_LEVEL_VERY_CHEAP: "Very low price compared to average",
|
||||
PRICE_LEVEL_CHEAP: "Lower than average price",
|
||||
PRICE_LEVEL_NORMAL: "Average price level",
|
||||
PRICE_LEVEL_EXPENSIVE: "Higher than average price",
|
||||
PRICE_LEVEL_VERY_EXPENSIVE: "Very high price compared to average",
|
||||
}
|
||||
if level in level_descriptions:
|
||||
attributes["description"] = level_descriptions[level]
|
||||
Args:
|
||||
attributes: Dictionary to add attributes to
|
||||
level: The price level value (e.g., VERY_CHEAP, NORMAL, etc.)
|
||||
|
||||
"""
|
||||
if level not in PRICE_LEVEL_MAPPING:
|
||||
return
|
||||
|
||||
# Add numeric value for sorting/comparison
|
||||
attributes["level_value"] = PRICE_LEVEL_MAPPING[level]
|
||||
|
||||
# Add the original English level as a reliable identifier for automations
|
||||
attributes["level_id"] = level
|
||||
|
||||
# Default to the original level value if no translation is found
|
||||
friendly_name = level
|
||||
|
||||
# Try to get localized friendly name from translations if Home Assistant is available
|
||||
if self.hass:
|
||||
# Get user's language preference (default to English)
|
||||
language = self.hass.config.language or "en"
|
||||
|
||||
# Use direct dictionary lookup for better performance and reliability
|
||||
# This matches how async_get_entity_description works
|
||||
cache_key = f"{DOMAIN}_translations_{language}"
|
||||
if cache_key in self.hass.data:
|
||||
translations = self.hass.data[cache_key]
|
||||
|
||||
# Navigate through the translation dictionary
|
||||
if (
|
||||
"sensor" in translations
|
||||
and "price_level" in translations["sensor"]
|
||||
and "price_levels" in translations["sensor"]["price_level"]
|
||||
and level in translations["sensor"]["price_level"]["price_levels"]
|
||||
):
|
||||
friendly_name = translations["sensor"]["price_level"]["price_levels"][level]
|
||||
|
||||
# If we didn't find a translation in the current language, try English
|
||||
if friendly_name == level and language != "en":
|
||||
en_cache_key = f"{DOMAIN}_translations_en"
|
||||
if en_cache_key in self.hass.data:
|
||||
en_translations = self.hass.data[en_cache_key]
|
||||
|
||||
# Try using English translation as fallback
|
||||
if (
|
||||
"sensor" in en_translations
|
||||
and "price_level" in en_translations["sensor"]
|
||||
and "price_levels" in en_translations["sensor"]["price_level"]
|
||||
and level in en_translations["sensor"]["price_level"]["price_levels"]
|
||||
):
|
||||
friendly_name = en_translations["sensor"]["price_level"]["price_levels"][level]
|
||||
|
||||
# Add the friendly name to attributes
|
||||
attributes["friendly_name"] = friendly_name
|
||||
|
||||
def _add_next_hour_attributes(self, attributes: dict) -> None:
|
||||
"""Add attributes for next hour price sensors."""
|
||||
from datetime import timedelta
|
||||
|
||||
price_info = self.coordinator.data["data"]["viewer"]["homes"][0]["currentSubscription"]["priceInfo"]
|
||||
now = dt_util.now()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue