mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-29 21:03:40 +00:00
docs(architecture): update architecture documentation with Calculator Pattern details and sensor organization
This commit is contained in:
parent
a962289682
commit
3d1b6a64fc
1 changed files with 63 additions and 15 deletions
|
|
@ -197,17 +197,46 @@ For detailed cache behavior, see [Caching Strategy](./caching-strategy.md).
|
||||||
| **Coordinator** | `coordinator.py` | Update orchestration, cache management, absolute-time scheduling with boundary tolerance |
|
| **Coordinator** | `coordinator.py` | Update orchestration, cache management, absolute-time scheduling with boundary tolerance |
|
||||||
| **Data Transformer** | `coordinator/data_transformation.py` | Price enrichment (averages, ratings, differences) |
|
| **Data Transformer** | `coordinator/data_transformation.py` | Price enrichment (averages, ratings, differences) |
|
||||||
| **Period Calculator** | `coordinator/periods.py` | Best/peak price period calculation with relaxation |
|
| **Period Calculator** | `coordinator/periods.py` | Best/peak price period calculation with relaxation |
|
||||||
| **Sensors** | `sensor/` | 120+ entities for prices, levels, ratings, statistics |
|
| **Sensors** | `sensor/` | 80+ entities for prices, levels, ratings, statistics |
|
||||||
| **Binary Sensors** | `binary_sensor/` | Period indicators (best/peak price active) |
|
| **Binary Sensors** | `binary_sensor/` | Period indicators (best/peak price active) |
|
||||||
| **Services** | `services.py` | Custom service endpoints (get_price, ApexCharts) |
|
| **Services** | `services.py` | Custom service endpoints (get_price, ApexCharts) |
|
||||||
|
|
||||||
|
### 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
|
### Helper Utilities
|
||||||
|
|
||||||
| Utility | File | Purpose |
|
| Utility | File | Purpose |
|
||||||
|---------|------|---------|
|
|---------|------|---------|
|
||||||
| **Price Utils** | `price_utils.py` | Rating calculation, enrichment, level aggregation |
|
| **Price Utils** | `utils/price.py` | Rating calculation, enrichment, level aggregation |
|
||||||
| **Average Utils** | `average_utils.py` | Trailing/leading 24h average calculations |
|
| **Average Utils** | `utils/average.py` | Trailing/leading 24h average calculations |
|
||||||
| **Sensor Helpers** | `sensor/helpers.py` | Interval detection with smart boundary tolerance (±2s) |
|
|
||||||
| **Entity Utils** | `entity_utils/` | Shared icon/color/attribute logic |
|
| **Entity Utils** | `entity_utils/` | Shared icon/color/attribute logic |
|
||||||
| **Translations** | `const.py` | Translation loading and caching |
|
| **Translations** | `const.py` | Translation loading and caching |
|
||||||
|
|
||||||
|
|
@ -224,7 +253,7 @@ For detailed cache behavior, see [Caching Strategy](./caching-strategy.md).
|
||||||
|
|
||||||
### 2. Price Data Enrichment
|
### 2. Price Data Enrichment
|
||||||
|
|
||||||
All quarter-hourly price intervals get augmented:
|
All quarter-hourly price intervals get augmented via `utils/price.py`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Original from Tibber API
|
# Original from Tibber API
|
||||||
|
|
@ -234,7 +263,7 @@ All quarter-hourly price intervals get augmented:
|
||||||
"level": "NORMAL"
|
"level": "NORMAL"
|
||||||
}
|
}
|
||||||
|
|
||||||
# After enrichment (price_utils.py)
|
# After enrichment (utils/price.py)
|
||||||
{
|
{
|
||||||
"startsAt": "2025-11-03T14:00:00+01:00",
|
"startsAt": "2025-11-03T14:00:00+01:00",
|
||||||
"total": 0.2534,
|
"total": 0.2534,
|
||||||
|
|
@ -248,26 +277,45 @@ All quarter-hourly price intervals get augmented:
|
||||||
### 3. Quarter-Hour Precision
|
### 3. Quarter-Hour Precision
|
||||||
|
|
||||||
- **API polling**: Every 15 minutes (coordinator fetch cycle)
|
- **API polling**: Every 15 minutes (coordinator fetch cycle)
|
||||||
- **Entity updates**: On 00/15/30/45-minute boundaries via `_schedule_quarter_hour_refresh()`
|
- **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)`
|
- **Timer scheduling**: Uses `async_track_utc_time_change(minute=[0, 15, 30, 45], second=0)`
|
||||||
- HA may trigger ±few milliseconds before/after exact boundary
|
- HA may trigger ±few milliseconds before/after exact boundary
|
||||||
- Smart boundary tolerance (±2 seconds) handles scheduling jitter
|
- 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 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)
|
- 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)
|
- **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)
|
- 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
|
- **Result**: Current price sensors update without waiting for next API poll
|
||||||
|
|
||||||
### 4. Unified Sensor Handlers
|
### 4. Calculator Pattern (Sensor Platform)
|
||||||
|
|
||||||
Sensors organized by **calculation method** (post-refactoring Nov 2025):
|
Sensors organized by **calculation method** (refactored Nov 2025):
|
||||||
|
|
||||||
- **Interval-based**: `_get_interval_value(offset, type)` - current/next/previous
|
**Unified Handler Methods** (`sensor/core.py`):
|
||||||
- **Rolling hour**: `_get_rolling_hour_value(offset, type)` - 5-interval windows
|
- `_get_interval_value(offset, type)` - current/next/previous intervals
|
||||||
- **Daily stats**: `_get_daily_stat_value(day, stat_func)` - calendar day min/max/avg
|
- `_get_rolling_hour_value(offset, type)` - 5-interval rolling windows
|
||||||
- **24h windows**: `_get_24h_window_value(stat_func)` - trailing/leading statistics
|
- `_get_daily_stat_value(day, stat_func)` - calendar day min/max/avg
|
||||||
|
- `_get_24h_window_value(stat_func)` - trailing/leading statistics
|
||||||
|
|
||||||
Single implementation, minimal code duplication.
|
**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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue