---
comments: false
---

# 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`](https://github.com/jpawlowski/hass.tibber_prices/blob/main/AGENTS.md).

---

## End-to-End Data Flow

```mermaid
flowchart TB
    %% External Systems
    TIBBER[("🌐 Tibber GraphQL API<br/>api.tibber.com")]
    HA[("🏠 Home Assistant<br/>Core")]

    %% Entry Point
    SETUP["__init__.py<br/>async_setup_entry()"]

    %% Core Components
    API["api.py<br/>TibberPricesApiClient<br/><br/>GraphQL queries"]
    COORD["coordinator.py<br/>TibberPricesDataUpdateCoordinator<br/><br/>Orchestrates updates every 15min"]

    %% Caching Layers
    CACHE_API["💾 API Cache<br/>coordinator/cache.py<br/><br/>HA Storage (persistent)<br/>User: 24h | Prices: until midnight"]
    CACHE_TRANS["💾 Transformation Cache<br/>coordinator/data_transformation.py<br/><br/>Memory (enriched prices)<br/>Until config change or midnight"]
    CACHE_PERIOD["💾 Period Cache<br/>coordinator/periods.py<br/><br/>Memory (calculated periods)<br/>Hash-based invalidation"]
    CACHE_CONFIG["💾 Config Cache<br/>coordinator/*<br/><br/>Memory (parsed options)<br/>Until config change"]
    CACHE_TRANS_TEXT["💾 Translation Cache<br/>const.py<br/><br/>Memory (UI strings)<br/>Until HA restart"]

    %% Processing Components
    TRANSFORM["coordinator/data_transformation.py<br/>DataTransformer<br/><br/>Enrich prices with statistics"]
    PERIODS["coordinator/periods.py<br/>PeriodCalculator<br/><br/>Calculate best/peak periods"]
    ENRICH["price_utils.py + average_utils.py<br/><br/>Calculate trailing/leading averages<br/>rating_level, differences"]

    %% Output Components
    SENSORS["sensor/<br/>TibberPricesSensor<br/><br/>120+ price/level/rating sensors"]
    BINARY["binary_sensor/<br/>TibberPricesBinarySensor<br/><br/>Period indicators"]
    SERVICES["services/<br/><br/>Custom service endpoints<br/>(get_chartdata, ApexCharts)"]

    %% Flow Connections
    TIBBER -->|"Query user data<br/>Query prices<br/>(yesterday/today/tomorrow)"| API

    API -->|"Raw GraphQL response"| COORD

    COORD -->|"Check cache first"| CACHE_API
    CACHE_API -.->|"Cache hit:<br/>Return cached"| COORD
    CACHE_API -.->|"Cache miss:<br/>Fetch from API"| API

    COORD -->|"Raw price data"| TRANSFORM
    TRANSFORM -->|"Check cache"| CACHE_TRANS
    CACHE_TRANS -.->|"Cache hit"| TRANSFORM
    CACHE_TRANS -.->|"Cache miss"| ENRICH
    ENRICH -->|"Enriched data"| TRANSFORM

    TRANSFORM -->|"Enriched price data"| COORD

    COORD -->|"Enriched data"| PERIODS
    PERIODS -->|"Check cache"| CACHE_PERIOD
    CACHE_PERIOD -.->|"Hash match:<br/>Return cached"| PERIODS
    CACHE_PERIOD -.->|"Hash mismatch:<br/>Recalculate"| PERIODS

    PERIODS -->|"Calculated periods"| COORD

    COORD -->|"Complete data<br/>(prices + periods)"| SENSORS
    COORD -->|"Complete data"| BINARY
    COORD -->|"Data access"| SERVICES

    SENSORS -->|"Entity states"| HA
    BINARY -->|"Entity states"| HA
    SERVICES -->|"Service responses"| HA

    %% Config access
    CACHE_CONFIG -.->|"Parsed options"| TRANSFORM
    CACHE_CONFIG -.->|"Parsed options"| PERIODS
    CACHE_TRANS_TEXT -.->|"UI strings"| SENSORS
    CACHE_TRANS_TEXT -.->|"UI strings"| BINARY

    SETUP -->|"Initialize"| COORD
    SETUP -->|"Register"| SENSORS
    SETUP -->|"Register"| BINARY
    SETUP -->|"Register"| SERVICES

    %% Styling
    classDef external fill:#e1f5ff,stroke:#0288d1,stroke-width:3px
    classDef cache fill:#fff3e0,stroke:#f57c00,stroke-width:2px
    classDef processing fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
    classDef output fill:#e8f5e9,stroke:#388e3c,stroke-width:2px

    class TIBBER,HA external
    class CACHE_API,CACHE_TRANS,CACHE_PERIOD,CACHE_CONFIG,CACHE_TRANS_TEXT cache
    class TRANSFORM,PERIODS,ENRICH processing
    class SENSORS,BINARY,SERVICES output
```

### 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:

| Layer                    | Location                             | Lifetime                               | Invalidation | Memory |
| ------------------------ | ------------------------------------ | -------------------------------------- | ------------ | ------ |
| **API Cache**            | `coordinator/cache.py`               | 24h (user)<br/>Until midnight (prices) | Automatic    | 50KB   |
| **Translation Cache**    | `const.py`                           | Until HA restart                       | Never        | 5KB    |
| **Config Cache**         | `coordinator/*`                      | Until config change                    | Explicit     | 1KB    |
| **Period Cache**         | `coordinator/periods.py`             | Until data/config change               | Hash-based   | 10KB   |
| **Transformation Cache** | `coordinator/data_transformation.py` | Until midnight/config                  | Automatic    | 60KB   |

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

### Cache Coordination

```mermaid
flowchart LR
    USER[("User changes options")]
    MIDNIGHT[("Midnight turnover")]
    NEWDATA[("Tomorrow data arrives")]

    USER -->|"Explicit invalidation"| CONFIG["Config Cache<br/>❌ Clear"]
    USER -->|"Explicit invalidation"| PERIOD["Period Cache<br/>❌ Clear"]
    USER -->|"Explicit invalidation"| TRANS["Transformation Cache<br/>❌ Clear"]

    MIDNIGHT -->|"Date validation"| API["API Cache<br/>❌ Clear prices"]
    MIDNIGHT -->|"Date check"| TRANS

    NEWDATA -->|"Hash mismatch"| PERIOD

    CONFIG -.->|"Next access"| CONFIG_NEW["Reparse options"]
    PERIOD -.->|"Next access"| PERIOD_NEW["Recalculate"]
    TRANS -.->|"Next access"| TRANS_NEW["Re-enrich"]
    API -.->|"Next access"| API_NEW["Fetch from API"]

    classDef invalid fill:#ffebee,stroke:#c62828,stroke-width:2px
    classDef rebuild fill:#e8f5e9,stroke:#388e3c,stroke-width:2px

    class CONFIG,PERIOD,TRANS,API invalid
    class CONFIG_NEW,PERIOD_NEW,TRANS_NEW,API_NEW rebuild
```

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

For detailed cache behavior, see [Caching Strategy](./caching-strategy.md).

---

## Component Responsibilities

### Core Components

| Component             | File                                 | Responsibility                                                                           |
| --------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------- |
| **API Client**        | `api.py`                             | GraphQL queries to Tibber, retry logic, error handling                                   |
| **Coordinator**       | `coordinator.py`                     | Update orchestration, cache management, absolute-time scheduling with boundary tolerance |
| **Data Transformer**  | `coordinator/data_transformation.py` | Price enrichment (averages, ratings, differences)                                        |
| **Period Calculator** | `coordinator/periods.py`             | Best/peak price period calculation with relaxation                                       |
| **Sensors**           | `sensor/`                            | 80+ entities for prices, levels, ratings, statistics                                     |
| **Binary Sensors**    | `binary_sensor/`                     | Period indicators (best/peak price active)                                               |
| **Services**          | `services/`                          | 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):

| Component        | Files                     | Lines | Responsibility                                          |
| ---------------- | ------------------------- | ----- | ------------------------------------------------------- |
| **Entity Class** | `sensor/core.py`          | 909   | Entity lifecycle, coordinator, delegates to calculators |
| **Calculators**  | `sensor/calculators/`     | 1,838 | Business logic (8 specialized calculators)              |
| **Attributes**   | `sensor/attributes/`      | 1,209 | State presentation (8 specialized modules)              |
| **Routing**      | `sensor/value_getters.py` | 276   | Centralized sensor → calculator mapping                 |
| **Chart Export** | `sensor/chart_data.py`    | 144   | Service call handling, YAML parsing                     |
| **Helpers**      | `sensor/helpers.py`       | 188   | Aggregation 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

| Utility           | File               | Purpose                                           |
| ----------------- | ------------------ | ------------------------------------------------- |
| **Price Utils**   | `utils/price.py`   | Rating calculation, enrichment, level aggregation |
| **Average Utils** | `utils/average.py` | Trailing/leading 24h average calculations         |
| **Entity Utils**  | `entity_utils/`    | Shared icon/color/attribute logic                 |
| **Translations**  | `const.py`         | Translation 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`:

```python
# 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

| Optimization        | Location                 | Savings                      |
| ------------------- | ------------------------ | ---------------------------- |
| Config caching      | `coordinator/*`          | ~50% on config checks        |
| Period caching      | `coordinator/periods.py` | ~70% on period recalculation |
| Lazy logging        | Throughout               | ~15% on log-heavy operations |
| Import optimization | Module 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)

---

## Related Documentation

- **[Timer Architecture](./timer-architecture.md)** - Timer system, scheduling, coordination (3 independent timers)
- **[Caching Strategy](./caching-strategy.md)** - Detailed cache behavior, invalidation, debugging
- **[Setup Guide](./setup.md)** - Development environment setup
- **[Testing Guide](./testing.md)** - How to test changes
- **[Release Management](./release-management.md)** - Release workflow and versioning
- **[AGENTS.md](https://github.com/jpawlowski/hass.tibber_prices/blob/main/AGENTS.md)** - Complete reference for AI development
