# Ha Repairs

> Implement repair issues for Home Assistant integrations. Gold tier IQS requirement. Use when asked about repair issues, issue registry, user notifications, fixable issues, or actionable alerts.

- Skill: `l3digitalnet/ha-repairs` (Agent Skill)
- Install (CLI): `npx skillmds@latest add l3digitalnet/ha-repairs`
- Raw SKILL.md: https://api.skillmd.com/api/skills/l3digitalnet/ha-repairs/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: l3digitalnet (https://skillmd.com/u/l3digitalnet)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/l3digitalnet/ha-repairs

---


# Home Assistant Repair Issues

Repair issues provide actionable notifications to users about problems that require attention. **Gold tier IQS requirement.**

## When to Use Repairs

Use repair issues for:

- Configuration problems the user can fix
- Firmware updates available
- Deprecated features being used
- Missing optional dependencies
- Service disruptions with user action needed

Do NOT use for:

- Transient errors (use logging)
- Entity unavailability (use `available` property)
- Internal errors (use exceptions)

## Basic Implementation

```python
from homeassistant.helpers import issue_registry as ir

# Create an issue
ir.async_create_issue(
    hass,
    DOMAIN,
    "firmware_update_available",
    is_fixable=False,
    severity=ir.IssueSeverity.WARNING,
    translation_key="firmware_update_available",
    translation_placeholders={
        "current_version": "1.0.0",
        "new_version": "2.0.0",
    },
)
```

## Severity Levels

```python
from homeassistant.helpers.issue_registry import IssueSeverity

IssueSeverity.CRITICAL  # Reserved for HA-internal use — do NOT use in custom integrations
IssueSeverity.ERROR     # Something is broken
IssueSeverity.WARNING   # Should be addressed soon
```

Custom integrations should use `ERROR` or `WARNING`; `CRITICAL` is reserved for Home Assistant core.

## Issue Types

### Non-Fixable Issue

User must take action outside Home Assistant:

```python
ir.async_create_issue(
    hass,
    DOMAIN,
    "device_firmware_outdated",
    is_fixable=False,
    severity=ir.IssueSeverity.WARNING,
    translation_key="device_firmware_outdated",
    translation_placeholders={
        "device": device_name,
        "version": current_version,
    },
    learn_more_url="https://example.com/firmware-update",
)
```

### Fixable Issue (with Repair Flow)

User can fix directly in Home Assistant:

```python
# In __init__.py or where issue is detected
ir.async_create_issue(
    hass,
    DOMAIN,
    f"reauth_required_{entry.entry_id}",
    is_fixable=True,
    severity=ir.IssueSeverity.ERROR,
    translation_key="reauth_required",
    data={"entry_id": entry.entry_id},
)
```

```python
# repairs.py
from homeassistant import data_entry_flow
from homeassistant.components.repairs import RepairsFlow

class ReauthRequiredRepairFlow(RepairsFlow):
    """Handler for reauth repair flow."""

    async def async_step_init(
        self, user_input: dict[str, Any] | None = None
    ) -> data_entry_flow.FlowResult:
        """Handle the first step."""
        return await self.async_step_confirm()

    async def async_step_confirm(
        self, user_input: dict[str, Any] | None = None
    ) -> data_entry_flow.FlowResult:
        """Handle the confirm step."""
        if user_input is not None:
            entry_id = self.data["entry_id"]
            entry = self.hass.config_entries.async_get_entry(entry_id)
            if entry:
                self.hass.async_create_task(
                    self.hass.config_entries.flow.async_init(
                        DOMAIN,
                        context={"source": "reauth", "entry_id": entry_id},
                    )
                )
            return self.async_create_entry(data={})

        return self.async_show_form(step_id="confirm")


async def async_create_fix_flow(
    hass: HomeAssistant,
    issue_id: str,
    data: dict[str, Any] | None,
) -> RepairsFlow:
    """Create flow."""
    if issue_id.startswith("reauth_required_"):
        return ReauthRequiredRepairFlow()
    raise ValueError(f"Unknown issue: {issue_id}")
```

## strings.json for Repairs

```json
{
	"issues": {
		"firmware_update_available": {
			"title": "Firmware update available",
			"description": "Device {device} has firmware {current_version}. Version {new_version} is available with important fixes."
		},
		"reauth_required": {
			"title": "Re-authentication required",
			"fix_flow": {
				"step": {
					"confirm": {
						"title": "Re-authenticate",
						"description": "Your credentials have expired. Click submit to re-authenticate."
					}
				}
			}
		}
	}
}
```

## Removing Issues

```python
# Remove when issue is resolved
ir.async_delete_issue(hass, DOMAIN, "firmware_update_available")

# Remove every issue this integration may have created for an entry (e.g., on unload).
# async_delete_issue has no "delete all for entry" variant, so delete each known
# per-entry id; a no-op if the issue does not exist.
for prefix in ("firmware_", "auth_", "reauth_required_"):
    ir.async_delete_issue(hass, DOMAIN, f"{prefix}{entry.entry_id}")
```

## Complete Example

```python
# coordinator.py
class MyCoordinator(DataUpdateCoordinator):
    async def _async_update_data(self):
        try:
            data = await self.client.async_get_data()

            # Check for conditions that need user attention
            if data.get("firmware_update_available"):
                ir.async_create_issue(
                    self.hass,
                    DOMAIN,
                    f"firmware_{self.config_entry.entry_id}",
                    is_fixable=False,
                    severity=ir.IssueSeverity.WARNING,
                    translation_key="firmware_update",
                    translation_placeholders={
                        "current": data["firmware_version"],
                        "available": data["available_version"],
                    },
                )
            else:
                # Clear issue if no longer applicable
                ir.async_delete_issue(
                    self.hass,
                    DOMAIN,
                    f"firmware_{self.config_entry.entry_id}",
                )

            return data

        except AuthenticationError:
            ir.async_create_issue(
                self.hass,
                DOMAIN,
                f"auth_{self.config_entry.entry_id}",
                is_fixable=True,
                severity=ir.IssueSeverity.ERROR,
                translation_key="authentication_failed",
            )
            raise ConfigEntryAuthFailed
```

## Related Skills

- Config flow → `ha-config-flow`
- Coordinator → `ha-coordinator`
- Quality review → `ha-quality-review`

