mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 13:23:41 +00:00
API Client:
- Changed async_get_price_info() to accept home_ids parameter
- Implemented _get_price_info_for_specific_homes() using GraphQL aliases
(home0: home(id: "abc") { ... }) for efficient multi-home queries
- Extended async_get_viewer_details() with comprehensive home metadata
(owner, address, meteringPointData, subscription, features)
- Removed deprecated async_get_data() method (combined query no longer needed)
- Updated _is_data_empty() to handle aliased response structure
Coordinator:
- Added _get_configured_home_ids() to collect all active config entries
- Modified _fetch_all_homes_data() to only query configured homes
- Added refresh_user_data() forcing user data refresh (bypasses cache)
- Improved get_user_profile() with detailed user info (name, login, accountType)
- Fixed get_user_homes() to extract from viewer object
Binary Sensors:
- Added has_ventilation_system sensor (home metadata)
- Added realtime_consumption_enabled sensor (features check)
- Refactored state getter mapping to dictionary pattern
Diagnostic Sensors (12 new):
- Home metadata: home_type, home_size, main_fuse_size, number_of_residents,
primary_heating_source
- Metering point: grid_company, grid_area_code, price_area_code,
consumption_ean, production_ean, energy_tax_type, vat_type,
estimated_annual_consumption
- Subscription: subscription_status
- Added available property override to hide diagnostic sensors with no data
Config Flow:
- Fixed subentry flow to exclude parent home_id from available homes
- Added debug logging for home title generation
Entity:
- Made attribution translatable (get_translation("attribution"))
- Removed hardcoded user name suffix from subentry device names
Impact: Enables multi-home setups with dedicated subentries. Each home gets
its own set of sensors and only configured homes are queried (reduces API
load). New diagnostic sensors provide comprehensive home metadata from Tibber
API. Users can track ventilation systems, heating types, metering point info,
and subscription status.
122 lines
4.7 KiB
Python
122 lines
4.7 KiB
Python
"""TibberPricesEntity class."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|
|
|
from .const import ATTRIBUTION, DOMAIN, get_home_type_translation, get_translation
|
|
from .coordinator import TibberPricesDataUpdateCoordinator
|
|
|
|
|
|
class TibberPricesEntity(CoordinatorEntity[TibberPricesDataUpdateCoordinator]):
|
|
"""TibberPricesEntity class."""
|
|
|
|
_attr_has_entity_name = True
|
|
|
|
def __init__(self, coordinator: TibberPricesDataUpdateCoordinator) -> None:
|
|
"""Initialize."""
|
|
super().__init__(coordinator)
|
|
|
|
# Get device information
|
|
home_name, home_id, home_type = self._get_device_info()
|
|
|
|
# Get configured language
|
|
language = coordinator.hass.config.language or "en"
|
|
|
|
# Get translated home type and attribution
|
|
translated_model = get_home_type_translation(home_type, language) if home_type else "Unknown"
|
|
# Get translated attribution, fallback to constant if translation not found
|
|
self._attr_attribution = get_translation(["attribution"], language) or ATTRIBUTION
|
|
|
|
self._attr_device_info = DeviceInfo(
|
|
entry_type=DeviceEntryType.SERVICE,
|
|
identifiers={
|
|
(
|
|
DOMAIN,
|
|
coordinator.config_entry.unique_id or coordinator.config_entry.entry_id,
|
|
)
|
|
},
|
|
name=home_name,
|
|
manufacturer="Tibber",
|
|
model=translated_model,
|
|
serial_number=home_id if home_id else None,
|
|
configuration_url="https://developer.tibber.com/explorer",
|
|
)
|
|
|
|
def _get_device_info(self) -> tuple[str, str | None, str | None]:
|
|
"""Get device name, ID and type."""
|
|
user_profile = self.coordinator.get_user_profile()
|
|
is_subentry = bool(self.coordinator.config_entry.data.get("home_id"))
|
|
home_id = self.coordinator.config_entry.unique_id
|
|
home_type = None
|
|
|
|
if is_subentry:
|
|
home_name, home_id, home_type = self._get_subentry_device_info()
|
|
elif user_profile:
|
|
home_name = self._get_main_entry_device_info(user_profile)
|
|
else:
|
|
home_name, home_type = self._get_fallback_device_info()
|
|
|
|
return home_name, home_id, home_type
|
|
|
|
def _get_subentry_device_info(self) -> tuple[str, str | None, str | None]:
|
|
"""Get device info for subentry."""
|
|
home_data = self.coordinator.config_entry.data.get("home_data", {})
|
|
home_id = self.coordinator.config_entry.data.get("home_id")
|
|
|
|
# Get home details
|
|
address = home_data.get("address", {})
|
|
address1 = address.get("address1", "")
|
|
city = address.get("city", "")
|
|
app_nickname = home_data.get("appNickname", "")
|
|
home_type = home_data.get("type", "")
|
|
|
|
# Compose home name
|
|
if app_nickname and app_nickname.strip():
|
|
# If appNickname is set, use it as-is (don't add city)
|
|
home_name = app_nickname.strip()
|
|
elif address1:
|
|
# If no appNickname, use address and optionally add city
|
|
home_name = address1
|
|
if city:
|
|
home_name = f"{home_name}, {city}"
|
|
else:
|
|
# Fallback to home ID
|
|
home_name = f"Tibber Home {home_id}"
|
|
|
|
return home_name, home_id, home_type
|
|
|
|
def _get_main_entry_device_info(self, user_profile: dict) -> str:
|
|
"""Get device info for main entry."""
|
|
user_name = user_profile.get("name", "Tibber User")
|
|
user_email = user_profile.get("email", "")
|
|
home_name = f"Tibber - {user_name}"
|
|
if user_email:
|
|
home_name = f"{home_name} ({user_email})"
|
|
return home_name
|
|
|
|
def _get_fallback_device_info(self) -> tuple[str, str | None]:
|
|
"""Get fallback device info if user data not available yet."""
|
|
if not self.coordinator.data:
|
|
return "Tibber Home", None
|
|
|
|
try:
|
|
address1 = str(self.coordinator.data.get("address", {}).get("address1", ""))
|
|
city = str(self.coordinator.data.get("address", {}).get("city", ""))
|
|
app_nickname = str(self.coordinator.data.get("appNickname", ""))
|
|
home_type = str(self.coordinator.data.get("type", ""))
|
|
|
|
# Compose a nice name
|
|
if app_nickname and app_nickname.strip():
|
|
home_name = f"Tibber {app_nickname.strip()}"
|
|
elif address1:
|
|
home_name = f"Tibber {address1}"
|
|
if city:
|
|
home_name = f"{home_name}, {city}"
|
|
else:
|
|
home_name = "Tibber Home"
|
|
except (KeyError, IndexError, TypeError):
|
|
return "Tibber Home", None
|
|
else:
|
|
return home_name, home_type
|