Create an issue if Opower utility is no longer supported (#150315)

This commit is contained in:
tronikos
2025-08-11 02:58:12 -07:00
committed by GitHub
parent 2a5a66f9d5
commit 23e6148d3b
4 changed files with 160 additions and 0 deletions

View File

@@ -2,9 +2,13 @@
from __future__ import annotations
from opower import select_utility
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from .const import CONF_UTILITY, DOMAIN
from .coordinator import OpowerConfigEntry, OpowerCoordinator
PLATFORMS: list[Platform] = [Platform.SENSOR]
@@ -12,6 +16,25 @@ PLATFORMS: list[Platform] = [Platform.SENSOR]
async def async_setup_entry(hass: HomeAssistant, entry: OpowerConfigEntry) -> bool:
"""Set up Opower from a config entry."""
utility_name = entry.data[CONF_UTILITY]
try:
select_utility(utility_name)
except ValueError:
ir.async_create_issue(
hass,
DOMAIN,
f"unsupported_utility_{entry.entry_id}",
is_fixable=True,
severity=ir.IssueSeverity.ERROR,
translation_key="unsupported_utility",
translation_placeholders={"utility": utility_name},
data={
"entry_id": entry.entry_id,
"utility": utility_name,
"title": entry.title,
},
)
return False
coordinator = OpowerCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh()

View File

@@ -0,0 +1,44 @@
"""Repairs for Opower."""
from __future__ import annotations
from homeassistant.components.repairs import RepairsFlow
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult
class UnsupportedUtilityFixFlow(RepairsFlow):
"""Handler for removing a configuration entry that uses an unsupported utility."""
def __init__(self, data: dict[str, str]) -> None:
"""Initialize."""
self._entry_id = data["entry_id"]
self._placeholders = data.copy()
self._placeholders.pop("entry_id")
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
"""Handle the first step of a fix flow."""
return await self.async_step_confirm()
async def async_step_confirm(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
"""Handle the confirm step of a fix flow."""
if user_input is not None:
await self.hass.config_entries.async_remove(self._entry_id)
return self.async_create_entry(title="", data={})
return self.async_show_form(
step_id="confirm", description_placeholders=self._placeholders
)
async def async_create_fix_flow(
hass: HomeAssistant, issue_id: str, data: dict[str, str] | None
) -> RepairsFlow:
"""Create flow."""
assert issue_id.startswith("unsupported_utility")
assert data
return UnsupportedUtilityFixFlow(data)

View File

@@ -70,6 +70,17 @@
"return_to_grid_migration": {
"title": "Return to grid statistics for account: {utility_account_id}",
"description": "We found negative values in your existing consumption statistics, likely because you have solar. We split those in separate return statistics for a better experience in the Energy dashboard.\n\nPlease visit the [Energy configuration page]({energy_settings}) to add the following statistics in the **Return to grid** section:\n\n{target_ids}\n\nOnce you have added them, ignore this issue."
},
"unsupported_utility": {
"title": "Unsupported utility: {utility}",
"fix_flow": {
"step": {
"confirm": {
"title": "[%key:component::opower::issues::unsupported_utility::title%]",
"description": "The utility `{utility}` used by entry `{title}` is no longer supported by the Opower integration. Select **Submit** to remove this integration entry now."
}
}
}
}
},
"entity": {

View File

@@ -0,0 +1,82 @@
"""Test the Opower repairs."""
from homeassistant.components.opower.const import DOMAIN
from homeassistant.components.recorder import Recorder
from homeassistant.components.repairs import DOMAIN as REPAIRS_DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
from tests.components.repairs import (
async_process_repairs_platforms,
process_repair_fix_flow,
start_repair_fix_flow,
)
from tests.typing import ClientSessionGenerator
async def test_unsupported_utility_fix_flow(
recorder_mock: Recorder,
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
issue_registry: ir.IssueRegistry,
) -> None:
"""Test the unsupported utility fix flow."""
assert await async_setup_component(hass, REPAIRS_DOMAIN, {REPAIRS_DOMAIN: {}})
mock_config_entry = MockConfigEntry(
domain=DOMAIN,
data={
"utility": "Unsupported Utility",
"username": "test-user",
"password": "test-password",
},
title="My Unsupported Utility",
)
mock_config_entry.add_to_hass(hass)
# Setting up the component with an unsupported utility should fail and create an issue
assert not await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
# Verify the issue was created correctly
issue_id = f"unsupported_utility_{mock_config_entry.entry_id}"
issue = issue_registry.async_get_issue(DOMAIN, issue_id)
assert issue is not None
assert issue.translation_key == "unsupported_utility"
assert issue.is_fixable is True
assert issue.data == {
"entry_id": mock_config_entry.entry_id,
"utility": "Unsupported Utility",
"title": "My Unsupported Utility",
}
await async_process_repairs_platforms(hass)
http_client = await hass_client()
# Start the repair flow
data = await start_repair_fix_flow(http_client, DOMAIN, issue_id)
flow_id = data["flow_id"]
# The flow should go directly to the confirm step
assert data["step_id"] == "confirm"
assert data["description_placeholders"] == {
"utility": "Unsupported Utility",
"title": "My Unsupported Utility",
}
# Submit the confirmation form
data = await process_repair_fix_flow(http_client, flow_id, json={})
# The flow should complete and create an empty entry, signaling success
assert data["type"] == "create_entry"
await hass.async_block_till_done()
# Check that the config entry has been removed
assert hass.config_entries.async_get_entry(mock_config_entry.entry_id) is None
# Check that the issue has been resolved
assert not issue_registry.async_get_issue(DOMAIN, issue_id)