Cache now stores only user metadata and timestamps. Price data is
managed exclusively by IntervalPool (single source of truth).
Changes:
- cache.py: Remove price_data and last_price_update fields
- core.py: Remove _cached_price_data, update references to use Pool
- core.py: Rename _data_fetcher to _price_data_manager
- AGENTS.md: Update class naming examples (DataFetcher → PriceDataManager)
This completes the Pool integration architecture where IntervalPool
handles all price data persistence and coordinator cache handles
only user account metadata.
Add user-configurable option to choose between median and arithmetic mean
as the displayed value for all 14 average price sensors, with the alternate
value exposed as attribute.
BREAKING CHANGE: Average sensor default changed from arithmetic mean to
median. Users who rely on arithmetic mean behavior may use the price_mean attribue now, or must manually reconfigure
via Settings → Devices & Services → Tibber Prices → Configure → General
Settings → "Average Sensor Display" → Select "Arithmetic Mean" to get this as sensor state.
Affected sensors (14 total):
- Daily averages: average_price_today, average_price_tomorrow
- 24h windows: trailing_price_average, leading_price_average
- Rolling hour: current_hour_average_price, next_hour_average_price
- Future forecasts: next_avg_3h, next_avg_6h, next_avg_9h, next_avg_12h
Implementation:
- All average calculators now return (mean, median) tuples
- User preference controls which value appears in sensor state
- Alternate value automatically added to attributes
- Period statistics (best_price/peak_price) extended with both values
Technical changes:
- New config option: CONF_AVERAGE_SENSOR_DISPLAY (default: "median")
- Calculator functions return tuples: (avg, median)
- Attribute builders: add_alternate_average_attribute() helper function
- Period statistics: price_avg → price_mean + price_median
- Translations: Updated all 5 languages (de, en, nb, nl, sv)
- Documentation: AGENTS.md, period-calculation.md, recorder-optimization.md
Migration path:
Users can switch back to arithmetic mean via:
Settings → Integrations → Tibber Prices → Configure
→ General Settings → "Average Sensor Display" → "Arithmetic Mean"
Impact: Median is more resistant to price spikes, providing more stable
automation triggers. Statistical analysis from coordinator still uses
arithmetic mean (e.g., trailing_avg_24h for rating calculations).
Co-developed-with: GitHub Copilot <copilot@github.com>
Implemented comprehensive entity lifecycle patterns following Home Assistant
best practices for proper state management and history tracking.
Changes:
- entity.py: Added available property to base class
- Returns False when coordinator has no data or last_update_success=False
- Prevents entities from showing stale data during errors
- Auth failures trigger reauth flow via ConfigEntryAuthFailed
- sensor/core.py: Added state restore and background task handling
- Changed inheritance: SensorEntity → RestoreSensor
- Restore native_value from SensorExtraStoredData in async_added_to_hass()
- Chart sensors restore response data from attributes
- Converted blocking service calls to background tasks using hass.async_create_task()
- Eliminates 194ms setup warning by making async_added_to_hass non-blocking
- binary_sensor/core.py: Added state restore and force_update
- Changed inheritance: BinarySensorEntity → RestoreEntity + BinarySensorEntity
- Restore is_on state in async_added_to_hass()
- Added available property override for connection sensor (always True)
- Added force_update property for connection sensor to track all state changes
- Other binary sensors use base available logic
- AGENTS.md: Documented entity lifecycle patterns in Common Pitfalls
- Added "Entity Lifecycle & State Management" section
- Documents available, state restore, and force_update patterns
- Explains why each pattern matters for proper HA integration
Impact: Entities no longer show stale data during errors, history has no gaps
after HA restart, connection state changes are properly tracked, and config
entry setup completes in <200ms (under HA threshold).
All patterns verified against HA developer documentation:
https://developers.home-assistant.io/docs/core/entity/
Update all references to reflect two separate Docusaurus instances
(user + developer) with proper file paths and navigation management.
Changes:
- AGENTS.md: Document Docusaurus structure and file organization
- CONTRIBUTING.md: Add Docusaurus workflow guidelines
- docs/developer/docs/period-calculation-theory.md: Fix cross-reference
- docs/developer/sidebars.ts: Add recorder-optimization to navigation
Documentation organized as:
- docs/user/docs/*.md (user guides, via sidebars.ts)
- docs/developer/docs/*.md (developer guides, via sidebars.ts)
- AGENTS.md (AI patterns, conventions)
Impact: AI and contributors know where to place new documentation.
Major restructuring of the scripts/ directory with consistent output
formatting, improved organization, and stricter error handling.
Breaking Changes:
- Updated development environment to Home Assistant 2025.7+
- Removed Python 3.12 compatibility (HA 2025.7+ requires Python 3.13)
- Updated all HA core requirements from 2025.7 requirement files
- Added new dependencies: python-multipart, uv (for faster package management)
- Updated GitHub Actions workflows to use Python 3.13
Changes:
- Created centralized output library (scripts/.lib/output.sh)
- Unified color codes and Unicode symbols
- Consistent formatting functions (log_header, log_success, log_error, etc.)
- Support for embedded formatting codes (${BOLD}, ${GREEN}, etc.)
- Reorganized into logical subdirectories:
- scripts/setup/ - Setup and maintenance scripts
- bootstrap: Install/update dependencies (used in CI/CD)
- setup: Full DevContainer setup (pyright, copilot, HACS)
- reset: Reset config/ directory to fresh state (NEW)
- sync-hacs: Sync HACS integrations
- scripts/release/ - Release management scripts
- prepare: Version bump and tagging
- suggest-version: Semantic version suggestion
- generate-notes: Release notes generation
- check-if-released: Check release status
- hassfest: Local integration validation
- Updated all scripts with:
- set -euo pipefail for stricter error handling
- Consistent SCRIPT_DIR pattern for reliable sourcing
- Professional output with colors and emojis
- Unified styling across all 17 scripts
- Removed redundant scripts:
- scripts/update (was just wrapper around bootstrap)
- scripts/json_schemas/ (moved to schemas/json/)
- Enhanced clean script:
- Improved artifact cleanup
- Better handling of accidental package installations
- Hints for reset and deep clean options
- New reset script features:
- Standard mode: Keep configuration.yaml
- Full mode (--full): Reset configuration.yaml from git
- Automatic re-setup after reset
- Updated documentation:
- AGENTS.md: Updated script references and workflow guidance
- docs/development/: Updated all references to new script structure
Impact: Development environment now requires Python 3.13 and Home Assistant
2025.7+. Developers get consistent, professional script output with better
error handling and logical organization. Single source of truth for styling
makes future updates trivial.
Updated attribute ordering documentation to use correct names:
- "periods" → "pricePeriods" (matches code since refactoring)
- "intervals" → "priceInfo" (flat list structure)
Impact: Documentation now matches actual code structure.
- Introduced `get_intervals_for_day_offsets` helper to streamline access to price intervals for yesterday, today, and tomorrow.
- Updated various components to replace direct access to `priceInfo` with the new helper, ensuring a flat structure for price intervals.
- Adjusted calculations and data processing methods to accommodate the new data structure.
- Enhanced documentation to reflect changes in caching strategy and data structure.
Cleaned up AGENTS.md to focus on patterns and conventions:
Removed:
- "Current State (as of Nov 2025)" section
- "Classes that need renaming" outdated list
- "Action Required" checklist
- Temporal statements about current project state
Added:
- TypedDict exemption in "When prefix can be omitted" list
- Clear rationale: documentation-only, never instantiated
Rationale:
AGENTS.md documents patterns and conventions that help AI understand
the codebase structure. Status tracking belongs in git history or
planning documents. The file should be timeless guidance, not a
snapshot of work in progress.
Impact: Documentation is now focused on "how to write code correctly"
rather than "what state is the code in now".
Set up pytest with Home Assistant support and created 6 tests for
midnight-crossing period logic (5 unit tests + 1 integration test).
Added pytest configuration, test dependencies, test runner script
(./scripts/test), and comprehensive tests for group_periods_by_day()
and midnight turnover consistency.
All tests pass in 0.12s.
Impact: Provides regression testing for midnight-crossing period bugs.
Tests validate periods remain visible across midnight turnover.
BREAKING CHANGE: Period overlap resolution now merges adjacent/overlapping periods
instead of marking them as extensions. This simplifies automation logic and provides
clearer period boundaries for users.
Previous Behavior:
- Adjacent periods created by relaxation were marked with is_extension=true
- Multiple short periods instead of one continuous period
- Complex logic needed to determine actual period length in automations
New Behavior:
- Adjacent/overlapping periods are merged into single continuous periods
- Newer period's relaxation attributes override older period's
- Simpler automation: one period = one continuous time window
Changes:
- Period Overlap Resolution (new file: period_overlap.py):
* Added merge_adjacent_periods() to combine periods and preserve attributes
* Rewrote resolve_period_overlaps() with simplified merge logic
* Removed split_period_by_overlaps() (no longer needed)
* Removed is_extension marking logic
* Removed unused parameters: min_period_length, baseline_periods
- Relaxation Strategy (relaxation.py):
* Removed all is_extension filtering from period counting
* Simplified standalone counting to just len(periods)
* Changed from period_merging import to period_overlap import
* Added MAX_FLEX_HARD_LIMIT constant (0.50)
* Improved debug logging for merged periods
- Code Quality:
* Fixed all remaining linter errors (N806, PLR2004, PLR0912)
* Extracted magic values to module-level constants:
- FLEX_SCALING_THRESHOLD = 0.20
- SCALE_FACTOR_WARNING_THRESHOLD = 0.8
- MAX_FLEX_HARD_LIMIT = 0.50
* Added appropriate noqa comments for unavoidable patterns
- Configuration (from previous work in this session):
* Removed CONF_RELAXATION_STEP_BEST, CONF_RELAXATION_STEP_PEAK
* Hard-coded 3% relaxation increment for reliability
* Optimized defaults: RELAXATION_ATTEMPTS 8→11, ENABLE_MIN_PERIODS False→True,
MIN_PERIODS undefined→2
* Removed relaxation_step UI fields from config flow
* Updated all 5 translation files
- Documentation:
* Updated period_handlers/__init__.py: period_merging → period_overlap
* No user-facing docs changes needed (already described continuous periods)
Rationale - Period Merging:
User experience was complicated by fragmented periods:
- Automations had to check multiple adjacent periods
- Binary sensors showed ON/OFF transitions within same cheap time
- No clear way to determine actual continuous period length
With merging:
- One continuous cheap time = one period
- Binary sensor clearly ON during entire period
- Attributes show merge history via merged_from dict
- Relaxation info preserved from newest/highest flex period
Rationale - Hard-Coded Relaxation Increment:
The configurable relaxation_step parameter proved problematic:
- High base flex + high step → rapid explosion (40% base + 10% step → 100% in 6 steps)
- Users don't understand the multiplicative nature
- 3% increment provides optimal balance: 11 attempts to reach 50% hard cap
Impact:
- Existing installations: Periods may appear longer (merged instead of split)
- Automations benefit from simpler logic (no is_extension checks needed)
- Custom relaxation_step values will use new 3% increment
- Users may need to adjust relaxation_attempts if they relied on high step sizes
This commit completes multiple refactoring efforts and documentation improvements:
Code Structure Changes:
- Move round_to_nearest_quarter_hour() from sensor/helpers.py to average_utils.py
- Resolve circular import between price_utils.py and sensor/helpers.py
- Split api.py into api/ package (client.py, queries.py, exceptions.py, helpers.py)
- Split coordinator.py into coordinator/ package (core.py, cache.py, listeners.py, etc.)
- Move period_utils/ to coordinator/period_handlers/ for better organization
- All lint checks passing (no PLC0415 local import warnings)
Documentation Additions:
- Add docs/development/architecture.md with Mermaid diagrams (end-to-end flow, cache coordination)
- Add docs/development/timer-architecture.md (comprehensive 3-timer system documentation)
- Add docs/development/caching-strategy.md (4-layer cache system with invalidation logic)
- Update docs/development/README.md with cross-references
- Update AGENTS.md with new module structure and patterns
Smart Boundary Tolerance:
- Implement ±2 second tolerance for quarter-hour rounding
- Prevents premature interval switching during HA restarts (14:59:30 stays at 14:45)
- Enables boundary snapping for timer jitter (14:59:58 → 15:00)
Atomic Midnight Coordination:
- Add _check_midnight_turnover_needed() for race-free midnight handling
- Coordinate Timer #1 (HA DataUpdateCoordinator) with Timer #2 (quarter-hour refresh)
- Whoever runs first performs turnover, other skips gracefully
Timer Optimization:
- Change timer scheduling from second=1 to second=0 (absolute-time scheduling)
- Document load distribution rationale (unsynchronized API polling prevents thundering herd)
- Comprehensive explanation of 3 independent timers and their coordination
Impact: Cleaner code structure with resolved circular dependencies, comprehensive
documentation of timer and caching systems, and improved reliability during
boundary conditions and midnight turnovers. All changes are developer-facing
improvements with no user-visible behavior changes.
Home Assistant's hassfest validation requires config flows to be defined
in a file named config_flow.py (not a package directory).
Changes:
- Renamed custom_components/tibber_prices/config_flow/ → config_flow_handlers/
- Created config_flow.py as bridge file re-exporting from config_flow_handlers/
- Updated all import paths across 5 files (user_flow, options_flow, subentry_flow, etc.)
- Added ./scripts/hassfest for local validation (JSON/Python syntax, required files)
- Added ./scripts/clean with three modes (--minimal, normal, --deep)
- Refactored develop/lint/lint-check to use centralized cleanup (DRY principle)
- Updated documentation in AGENTS.md and docs/development/
Technical details:
- Bridge file uses __all__ exports to maintain clean public API
- hassfest script uses ast.parse() for syntax validation (no disk artifacts)
- clean --minimal removes .egg-info only (silent, for automated scripts)
- Dual pip/uv pip compatibility for package uninstallation
Impact: Integration now passes hassfest validation. Local validation available
via ./scripts/hassfest before pushing to GitHub. Cleanup logic centralized and
DRY across all development scripts.
Split binary_sensor.py (645 lines) into binary_sensor/ package with
4 modules following the established sensor/ pattern for consistency
and maintainability.
Package structure:
- binary_sensor/__init__.py (32 lines): Platform setup
- binary_sensor/definitions.py (46 lines): ENTITY_DESCRIPTIONS, constants
- binary_sensor/attributes.py (443 lines): Attribute builder functions
- binary_sensor/core.py (282 lines): TibberPricesBinarySensor class
Changes:
- Created binary_sensor/ package with __init__.py importing from .core
- Extracted ENTITY_DESCRIPTIONS and constants to definitions.py
- Moved 13 attribute builders to attributes.py (get_price_intervals_attributes,
build_async/sync_extra_state_attributes, add_* helpers)
- Moved TibberPricesBinarySensor class to core.py with state logic and
icon handling
- Used keyword-only parameters to satisfy Ruff PLR0913 (too many args)
- Applied absolute imports (custom_components.tibber_prices.*) in modules
All 4 binary sensors tested and working:
- peak_price_period
- best_price_period
- connection
- tomorrow_data_available
Documentation updated:
- AGENTS.md: Architecture Overview, Component Structure, Common Tasks
- binary-sensor-refactoring-plan.md: Marked ✅ COMPLETED with summary
Impact: Symmetric platform structure (sensor/ ↔ binary_sensor/). Easier
to add new binary sensors following documented pattern. No user-visible
changes.
- Introduced a comprehensive refactoring guide detailing when and how to plan major refactorings, including a structured planning process and real-world examples.
- Created a README for the planning directory, outlining its purpose, document lifecycle, and best practices for creating planning documents.
- Removed the `calculate_current_rolling_5interval_avg` and `calculate_next_hour_rolling_5interval_avg` functions from `average_utils.py` to streamline the codebase.
- Introduced unified methods for retrieving interval values and rolling hour calculations in `sensor.py`, enhancing code reusability and readability.
- Organized sensor definitions into categories based on calculation methods for better maintainability.
- Updated handler methods to utilize the new unified methods, ensuring consistent data retrieval across different sensor types.
- Improved documentation and comments throughout the code to clarify the purpose and functionality of various methods.
Renamed internal sensor keys to be more explicit about their temporal scope:
- current_price → current_interval_price
- price_level → current_interval_price_level
- price_rating → current_interval_price_rating
This naming makes it clearer that these sensors represent the current
15-minute interval, distinguishing them from hourly averages and other
time-based calculations.
Updated across all components:
- Sensor entity descriptions and handlers (sensor.py)
- Time-sensitive entity keys list (coordinator.py)
- Config flow step IDs (config_flow.py)
- Translation keys in all 5 languages (de, en, nb, nl, sv)
- Custom translations (entity descriptions, usage tips)
- Price level/rating lookups (const.py, sensor.py)
- Documentation examples (AGENTS.md, README.md)
Impact: Sensor entity IDs remain unchanged due to translation_key system.
Existing automations continue to work. Only internal code references and
translation structures updated for consistency.
Extended AGENTS.md with comprehensive patterns learned from period
calculation development and documentation rewrite.
Logging Guidelines:
- Added hierarchical indentation pattern (INDENT_L0-L5)
- Defined log level strategy (INFO=compact/scannable,
DEBUG=detailed/hierarchical, WARNING=top-level)
- Added configuration context headers for complex calculations
- Documented per-day processing patterns
- Added note about logs as documentation foundation
User Documentation Quality:
- Added principles: clarity over completeness, visual examples,
use-case driven structure, practical troubleshooting, progressive
disclosure
- Added validation rules for code-documentation sync
Documentation Writing Strategy (new section):
- Live understanding vs. cold code analysis
- User feedback loop importance
- Log-driven documentation approach
- Concrete examples over abstract descriptions
- Context accumulation in long sessions
- Document the "why", not just the "what"
Impact: Future AI sessions can produce better logs (traceable logic
with visual hierarchy) and better documentation (user-focused with
concrete examples from live development).