Skip to main content
Version: v0.24.0

Architecture

This document provides a visual overview of the integration's architecture, focusing on end-to-end data flow and caching layers.

For detailed implementation patterns, see AGENTS.md.


End-to-End Data Flow

Flow Description

  1. Setup (__init__.py)

    • Integration loads, creates coordinator instance
    • Registers entity platforms (sensor, binary_sensor)
    • Sets up custom services
  2. Data Fetch (every 15 minutes)

    • Coordinator triggers update via api.py
    • API client checks persistent cache first (coordinator/cache.py)
    • If cache valid → return cached data
    • If cache stale → query Tibber GraphQL API
    • Store fresh data in persistent cache (survives HA restart)
  3. Price Enrichment

    • Coordinator passes raw prices to DataTransformer
    • Transformer checks transformation cache (memory)
    • If cache valid → return enriched data
    • If cache invalid → enrich via price_utils.py + average_utils.py
      • Calculate 24h trailing/leading averages
      • Calculate price differences (% from average)
      • Assign rating levels (LOW/NORMAL/HIGH)
    • Store enriched data in transformation cache
  4. Period Calculation

    • Coordinator passes enriched data to PeriodCalculator
    • Calculator computes hash from prices + config
    • If hash matches cache → return cached periods
    • If hash differs → recalculate best/peak price periods
    • Store periods with new hash
  5. Entity Updates

    • Coordinator provides complete data (prices + periods)
    • Sensors read values via unified handlers
    • Binary sensors evaluate period states
    • Entities update on quarter-hour boundaries (00/15/30/45)
  6. Service Calls

    • Custom services access coordinator data directly
    • Return formatted responses (JSON, ApexCharts format)

Caching Architecture

Overview

The integration uses 5 independent caching layers for optimal performance:

LayerLocationLifetimeInvalidationMemory
API Cachecoordinator/cache.py24h (user)
Until midnight (prices)
Automatic50KB
Translation Cacheconst.pyUntil HA restartNever5KB
Config Cachecoordinator/*Until config changeExplicit1KB
Period Cachecoordinator/periods.pyUntil data/config changeHash-based10KB
Transformation Cachecoordinator/data_transformation.pyUntil midnight/configAutomatic60KB

Total cache overhead: ~126KB per coordinator instance (main entry + subentries)

Cache Coordination

Key insight: No cascading invalidations - each cache is independent and rebuilds on-demand.

For detailed cache behavior, see Caching Strategy.


Component Responsibilities

Core Components

ComponentFileResponsibility
API Clientapi.pyGraphQL queries to Tibber, retry logic, error handling
Coordinatorcoordinator.pyUpdate orchestration, cache management, absolute-time scheduling with boundary tolerance
Data Transformercoordinator/data_transformation.pyPrice enrichment (averages, ratings, differences)
Period Calculatorcoordinator/periods.pyBest/peak price period calculation with relaxation
Sensorssensor/80+ entities for prices, levels, ratings, statistics
Binary Sensorsbinary_sensor/Period indicators (best/peak price active)
Servicesservices/Custom service endpoints (get_chartdata, get_apexcharts_yaml, refresh_user_data)

Sensor Architecture (Calculator Pattern)

The sensor platform uses Calculator Pattern for clean separation of concerns (refactored Nov 2025):

ComponentFilesLinesResponsibility
Entity Classsensor/core.py909Entity lifecycle, coordinator, delegates to calculators
Calculatorssensor/calculators/1,838Business logic (8 specialized calculators)
Attributessensor/attributes/1,209State presentation (8 specialized modules)
Routingsensor/value_getters.py276Centralized sensor → calculator mapping
Chart Exportsensor/chart_data.py144Service call handling, YAML parsing
Helperssensor/helpers.py188Aggregation functions, utilities

Calculator Package (sensor/calculators/):

  • base.py - Abstract BaseCalculator with coordinator access
  • interval.py - Single interval calculations (current/next/previous)
  • rolling_hour.py - 5-interval rolling windows
  • daily_stat.py - Calendar day min/max/avg statistics
  • window_24h.py - Trailing/leading 24h windows
  • volatility.py - Price volatility analysis
  • trend.py - Complex trend analysis with caching
  • timing.py - Best/peak price period timing
  • metadata.py - Home/metering metadata

Benefits:

  • 58% reduction in core.py (2,170 → 909 lines)
  • Clear separation: Calculators (logic) vs Attributes (presentation)
  • Independent testability for each calculator
  • Easy to add sensors: Choose calculation pattern, add to routing

Helper Utilities

UtilityFilePurpose
Price Utilsutils/price.pyRating calculation, enrichment, level aggregation
Average Utilsutils/average.pyTrailing/leading 24h average calculations
Entity Utilsentity_utils/Shared icon/color/attribute logic
Translationsconst.pyTranslation loading and caching

Key Patterns

1. Dual Translation System

  • Standard translations (/translations/*.json): HA-compliant schema for entity names
  • Custom translations (/custom_translations/*.json): Extended descriptions, usage tips
  • Both loaded at integration setup, cached in memory
  • Access via get_translation() helper function

2. Price Data Enrichment

All quarter-hourly price intervals get augmented via utils/price.py:

# Original from Tibber API
{
"startsAt": "2025-11-03T14:00:00+01:00",
"total": 0.2534,
"level": "NORMAL"
}

# After enrichment (utils/price.py)
{
"startsAt": "2025-11-03T14:00:00+01:00",
"total": 0.2534,
"level": "NORMAL",
"trailing_avg_24h": 0.2312, # ← Added: 24h trailing average
"difference": 9.6, # ← Added: % diff from trailing avg
"rating_level": "NORMAL" # ← Added: LOW/NORMAL/HIGH based on thresholds
}

3. Quarter-Hour Precision

  • API polling: Every 15 minutes (coordinator fetch cycle)
  • Entity updates: On 00/15/30/45-minute boundaries via coordinator/listeners.py
  • Timer scheduling: Uses async_track_utc_time_change(minute=[0, 15, 30, 45], second=0)
    • HA may trigger ±few milliseconds before/after exact boundary
    • Smart boundary tolerance (±2 seconds) handles scheduling jitter in sensor/helpers.py
    • If HA schedules at 14:59:58 → rounds to 15:00:00 (shows new interval data)
    • If HA restarts at 14:59:30 → stays at 14:45:00 (shows current interval data)
  • Absolute time tracking: Timer plans for all future boundaries (not relative delays)
    • Prevents double-updates (if triggered at 14:59:58, next trigger is 15:15:00, not 15:00:00)
  • Result: Current price sensors update without waiting for next API poll

4. Calculator Pattern (Sensor Platform)

Sensors organized by calculation method (refactored Nov 2025):

Unified Handler Methods (sensor/core.py):

  • _get_interval_value(offset, type) - current/next/previous intervals
  • _get_rolling_hour_value(offset, type) - 5-interval rolling windows
  • _get_daily_stat_value(day, stat_func) - calendar day min/max/avg
  • _get_24h_window_value(stat_func) - trailing/leading statistics

Routing (sensor/value_getters.py):

  • Single source of truth mapping 80+ entity keys to calculator methods
  • Organized by calculation type (Interval, Rolling Hour, Daily Stats, etc.)

Calculators (sensor/calculators/):

  • Each calculator inherits from BaseCalculator with coordinator access
  • Focused responsibility: IntervalCalculator, TrendCalculator, etc.
  • Complex logic isolated (e.g., TrendCalculator has internal caching)

Attributes (sensor/attributes/):

  • Separate from business logic, handles state presentation
  • Builds extra_state_attributes dicts for entity classes
  • Unified builders: build_sensor_attributes(), build_extra_state_attributes()

Benefits:

  • Minimal code duplication across 80+ sensors
  • Clear separation of concerns (calculation vs presentation)
  • Easy to extend: Add sensor → choose pattern → add to routing
  • Independent testability for each component

Performance Characteristics

API Call Reduction

  • Without caching: 96 API calls/day (every 15 min)
  • With caching: ~1-2 API calls/day (only when cache expires)
  • Reduction: ~98%

CPU Optimization

OptimizationLocationSavings
Config cachingcoordinator/*~50% on config checks
Period cachingcoordinator/periods.py~70% on period recalculation
Lazy loggingThroughout~15% on log-heavy operations
Import optimizationModule structure~20% faster loading

Memory Usage

  • Per coordinator instance: ~126KB cache overhead
  • Typical setup: 1 main + 2 subentries = ~378KB total
  • Redundancy eliminated: 14% reduction (10KB saved per coordinator)