# Repairs System The Tibber Prices integration includes a proactive repair notification system that alerts users to important issues requiring attention. This system leverages Home Assistant's built-in `issue_registry` to create user-facing notifications in the UI. ## Overview The repairs system is implemented in `coordinator/repairs.py` via the `TibberPricesRepairManager` class, which is instantiated in the coordinator and integrated into the update cycle. **Design Principles:** - **Proactive**: Detect issues before they become critical - **User-friendly**: Clear explanations with actionable guidance - **Auto-clearing**: Repairs automatically disappear when conditions resolve - **Non-blocking**: Integration continues to work even with active repairs ## Implemented Repair Types ### 1. Tomorrow Data Missing **Issue ID:** `tomorrow_data_missing_{entry_id}` **When triggered:** - Current time is after 18:00 (configurable via `TOMORROW_DATA_WARNING_HOUR`) - Tomorrow's electricity price data is still not available **When cleared:** - Tomorrow's data becomes available - Automatically checks on every successful API update **User impact:** Users cannot plan ahead for tomorrow's electricity usage optimization. Automations relying on tomorrow's prices will not work. **Implementation:** ```python # In coordinator update cycle has_tomorrow_data = self._data_fetcher.has_tomorrow_data(result["priceInfo"]) await self._repair_manager.check_tomorrow_data_availability( has_tomorrow_data=has_tomorrow_data, current_time=current_time, ) ``` **Translation placeholders:** - `home_name`: Name of the affected home - `warning_hour`: Hour after which warning appears (default: 18) ### 2. Rate Limit Exceeded **Issue ID:** `rate_limit_exceeded_{entry_id}` **When triggered:** - Integration encounters 3 or more consecutive rate limit errors (HTTP 429) - Threshold configurable via `RATE_LIMIT_WARNING_THRESHOLD` **When cleared:** - Successful API call completes (no rate limit error) - Error counter resets to 0 **User impact:** API requests are being throttled, causing stale data. Updates may be delayed until rate limit expires. **Implementation:** ```python # In error handler is_rate_limit = ( "429" in error_str or "rate limit" in error_str or "too many requests" in error_str ) if is_rate_limit: await self._repair_manager.track_rate_limit_error() # On successful update await self._repair_manager.clear_rate_limit_tracking() ``` **Translation placeholders:** - `home_name`: Name of the affected home - `error_count`: Number of consecutive rate limit errors ### 3. Home Not Found **Issue ID:** `home_not_found_{entry_id}` **When triggered:** - Home configured in this integration is no longer present in Tibber account - Detected during user data refresh (daily check) **When cleared:** - Home reappears in Tibber account (unlikely - manual cleanup expected) - Integration entry is removed (shutdown cleanup) **User impact:** Integration cannot fetch data for a non-existent home. User must remove the config entry and re-add if needed. **Implementation:** ```python # After user data update home_exists = self._data_fetcher._check_home_exists(home_id) if not home_exists: await self._repair_manager.create_home_not_found_repair() else: await self._repair_manager.clear_home_not_found_repair() ``` **Translation placeholders:** - `home_name`: Name of the missing home - `entry_id`: Config entry ID for reference ## Configuration Constants Defined in `coordinator/constants.py`: ```python TOMORROW_DATA_WARNING_HOUR = 18 # Hour after which to warn about missing tomorrow data RATE_LIMIT_WARNING_THRESHOLD = 3 # Number of consecutive errors before creating repair ``` ## Architecture ### Class Structure ```python class TibberPricesRepairManager: """Manages repair issues for a single Tibber home.""" def __init__( self, hass: HomeAssistant, entry_id: str, home_name: str, ) -> None: """Initialize repair manager.""" self._hass = hass self._entry_id = entry_id self._home_name = home_name # State tracking self._tomorrow_data_repair_active = False self._rate_limit_error_count = 0 self._rate_limit_repair_active = False self._home_not_found_repair_active = False ``` ### State Tracking Each repair type maintains internal state to avoid redundant operations: - **`_tomorrow_data_repair_active`**: Boolean flag, prevents creating duplicate repairs - **`_rate_limit_error_count`**: Integer counter, tracks consecutive errors - **`_rate_limit_repair_active`**: Boolean flag, tracks repair status - **`_home_not_found_repair_active`**: Boolean flag, one-time repair (manual cleanup) ### Lifecycle Integration **Coordinator Initialization:** ```python self._repair_manager = TibberPricesRepairManager( hass=hass, entry_id=self.config_entry.entry_id, home_name=self._home_name, ) ``` **Update Cycle Integration:** ```python # Success path - check conditions if result and "priceInfo" in result: has_tomorrow_data = self._data_fetcher.has_tomorrow_data(result["priceInfo"]) await self._repair_manager.check_tomorrow_data_availability( has_tomorrow_data=has_tomorrow_data, current_time=current_time, ) await self._repair_manager.clear_rate_limit_tracking() # Error path - track rate limits if is_rate_limit: await self._repair_manager.track_rate_limit_error() ``` **Shutdown Cleanup:** ```python async def async_shutdown(self) -> None: """Shut down coordinator and clean up.""" await self._repair_manager.clear_all_repairs() # ... other cleanup ... ``` ## Translation System Repairs use Home Assistant's standard translation system. Translations are defined in: - `/translations/en.json` - `/translations/de.json` - `/translations/nb.json` - `/translations/nl.json` - `/translations/sv.json` **Structure:** ```json { "issues": { "tomorrow_data_missing": { "title": "Tomorrow's price data missing for {home_name}", "description": "Detailed explanation with multiple paragraphs...\n\nPossible causes:\n- Cause 1\n- Cause 2" } } } ``` ## Home Assistant Integration Repairs appear in: - **Settings → System → Repairs** (main repairs panel) - **Notifications** (bell icon in UI shows repair count) Repair properties: - **`is_fixable=False`**: No automated fix available (user action required) - **`severity=IssueSeverity.WARNING`**: Yellow warning level (not critical) - **`translation_key`**: References `issues.{key}` in translation files ## Testing Repairs ### Tomorrow Data Missing 1. Wait until after 18:00 local time 2. Ensure integration has no tomorrow price data 3. Repair should appear in UI 4. When tomorrow data arrives (next API fetch), repair clears **Manual trigger:** ```python # Temporarily set warning hour to current hour for testing TOMORROW_DATA_WARNING_HOUR = datetime.now().hour ``` ### Rate Limit Exceeded 1. Simulate 3+ consecutive rate limit errors 2. Repair should appear after 3rd error 3. Successful API call clears the repair **Manual test:** - Reduce API polling interval to trigger rate limiting - Or temporarily return HTTP 429 in API client ### Home Not Found 1. Remove home from Tibber account via app/web 2. Wait for user data refresh (daily check) 3. Repair appears indicating home is missing 4. Remove integration entry to clear repair ## Adding New Repair Types To add a new repair type: 1. **Add constants** (if needed) in `coordinator/constants.py` 2. **Add state tracking** in `TibberPricesRepairManager.__init__` 3. **Implement check method** with create/clear logic 4. **Add translations** to all 5 language files 5. **Integrate into coordinator** update cycle or error handlers 6. **Add cleanup** to `clear_all_repairs()` method 7. **Document** in this file **Example template:** ```python async def check_new_condition(self, *, param: bool) -> None: """Check new condition and create/clear repair.""" should_warn = param # Your condition logic if should_warn and not self._new_repair_active: await self._create_new_repair() elif not should_warn and self._new_repair_active: await self._clear_new_repair() async def _create_new_repair(self) -> None: """Create new repair issue.""" _LOGGER.warning("New issue detected - creating repair") ir.async_create_issue( self._hass, DOMAIN, f"new_issue_{self._entry_id}", is_fixable=False, severity=ir.IssueSeverity.WARNING, translation_key="new_issue", translation_placeholders={ "home_name": self._home_name, }, ) self._new_repair_active = True async def _clear_new_repair(self) -> None: """Clear new repair issue.""" _LOGGER.debug("New issue resolved - clearing repair") ir.async_delete_issue( self._hass, DOMAIN, f"new_issue_{self._entry_id}", ) self._new_repair_active = False ``` ## Best Practices 1. **Always use state tracking** - Prevents duplicate repair creation 2. **Auto-clear when resolved** - Improves user experience 3. **Clear on shutdown** - Prevents orphaned repairs 4. **Use descriptive issue IDs** - Include entry_id for multi-home setups 5. **Provide actionable guidance** - Tell users what they can do 6. **Use appropriate severity** - WARNING for most cases, ERROR only for critical 7. **Test all language translations** - Ensure placeholders work correctly 8. **Document expected behavior** - What triggers, what clears, what user should do ## Future Enhancements Potential additions to the repairs system: - **Stale data warning**: Alert when cache is >24 hours old with no API updates - **Missing permissions**: Detect insufficient API token scopes - **Config migration needed**: Notify users of breaking changes requiring reconfiguration - **Extreme price alert**: Warn when prices exceed historical thresholds (optional, user-configurable) ## References - Home Assistant Repairs Documentation: https://developers.home-assistant.io/docs/core/platform/repairs - Issue Registry API: `homeassistant.helpers.issue_registry` - Integration Constants: `custom_components/tibber_prices/const.py` - Repair Manager Implementation: `custom_components/tibber_prices/coordinator/repairs.py`