mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 05:13:40 +00:00
330 lines
10 KiB
Markdown
330 lines
10 KiB
Markdown
# 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`
|