From 8f367a3f28d537fdd2c0ec366e92ad9da3a1542e Mon Sep 17 00:00:00 2001 From: Manu Date: Thu, 18 Jun 2026 17:28:20 +0200 Subject: [PATCH] Raise repair issue for under-voltage events in Raspberry Pi power supply checker (#174127) --- .../components/rpi_power/binary_sensor.py | 10 ++++ homeassistant/components/rpi_power/repairs.py | 34 +++++++++++++ .../components/rpi_power/strings.json | 13 +++++ .../rpi_power/test_binary_sensor.py | 21 ++++++-- tests/components/rpi_power/test_repairs.py | 48 +++++++++++++++++++ 5 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/rpi_power/repairs.py create mode 100644 tests/components/rpi_power/test_repairs.py diff --git a/homeassistant/components/rpi_power/binary_sensor.py b/homeassistant/components/rpi_power/binary_sensor.py index 923f1250a16..c17b0850442 100644 --- a/homeassistant/components/rpi_power/binary_sensor.py +++ b/homeassistant/components/rpi_power/binary_sensor.py @@ -15,6 +15,7 @@ from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback +from homeassistant.helpers.issue_registry import IssueSeverity, create_issue from . import RpiPowerConfigEntry from .const import DOMAIN @@ -63,6 +64,15 @@ class RaspberryChargerBinarySensor(BinarySensorEntity): if self._attr_is_on != value: if value: _LOGGER.warning(DESCRIPTION_UNDER_VOLTAGE) + create_issue( + self.hass, + DOMAIN, + "under_voltage_detected", + is_fixable=True, + is_persistent=True, + severity=IssueSeverity.CRITICAL, + translation_key="under_voltage_detected", + ) else: _LOGGER.debug(DESCRIPTION_NORMALIZED) self._attr_is_on = value diff --git a/homeassistant/components/rpi_power/repairs.py b/homeassistant/components/rpi_power/repairs.py new file mode 100644 index 00000000000..635a3649123 --- /dev/null +++ b/homeassistant/components/rpi_power/repairs.py @@ -0,0 +1,34 @@ +"""Repairs for Raspberry Pi Power Supply Checker.""" + +from homeassistant.components.repairs import RepairsFlow, RepairsFlowResult +from homeassistant.core import HomeAssistant + + +async def async_create_fix_flow( + hass: HomeAssistant, + issue_id: str, + data: dict[str, str | int | float | None] | None, +) -> RepairsFlow: + """Create flow.""" + + return UnderVoltageRepairFlow() + + +class UnderVoltageRepairFlow(RepairsFlow): + """Handler for issue fixing flow.""" + + async def async_step_init( + self, user_input: dict[str, str] | None = None + ) -> RepairsFlowResult: + """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 + ) -> RepairsFlowResult: + """Handle the confirm step of a fix flow.""" + if user_input is not None: + return self.async_create_entry(data={}) + + return self.async_show_form(step_id="confirm") diff --git a/homeassistant/components/rpi_power/strings.json b/homeassistant/components/rpi_power/strings.json index f781f6d7184..4fb98325a73 100644 --- a/homeassistant/components/rpi_power/strings.json +++ b/homeassistant/components/rpi_power/strings.json @@ -21,5 +21,18 @@ "message": "Under-voltage monitoring is not supported on this device." } }, + "issues": { + "under_voltage_detected": { + "fix_flow": { + "step": { + "confirm": { + "description": "Your device has reported a low-voltage condition. The Raspberry Pi monitors its power input and records an under-voltage event when the supply voltage drops below approximately 4.63V. This can lead to instability, unexpected reboots, poor performance, storage corruption, or unreliable operation of Home Assistant.\n\nCommon causes include:\n\n- A power supply that cannot provide sufficient current.\n- A low-quality or damaged USB power cable causing voltage drop.\n- High-demand USB devices connected to the system.\n- Loose power connections.\n\nTo resolve this issue:\n\n- Switch to a higher quality power supply and cable.\n- Disconnect unnecessary USB devices and check whether the warning stops.\n\nAfter correcting the power issue, monitor the system to ensure no additional under-voltage events are reported.", + "title": "[%key:component::rpi_power::issues::under_voltage_detected::title%]" + } + } + }, + "title": "Under-voltage detected" + } + }, "title": "Raspberry Pi Power Supply Checker" } diff --git a/tests/components/rpi_power/test_binary_sensor.py b/tests/components/rpi_power/test_binary_sensor.py index 4153df53a01..a9b6fa63c45 100644 --- a/tests/components/rpi_power/test_binary_sensor.py +++ b/tests/components/rpi_power/test_binary_sensor.py @@ -16,7 +16,7 @@ from homeassistant.components.rpi_power.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import entity_registry as er, issue_registry as ir from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util @@ -44,16 +44,26 @@ async def _async_setup_component(hass: HomeAssistant, detected: bool) -> MagicMo return mocked_under_voltage -async def test_new(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) -> None: +async def test_new( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + issue_registry: ir.IssueRegistry, +) -> None: """Test new entry.""" await _async_setup_component(hass, False) state = hass.states.get(ENTITY_ID) assert state.state == STATE_OFF assert not any(x.levelno == logging.WARNING for x in caplog.records) + assert not issue_registry.async_get_issue( + domain=DOMAIN, + issue_id="under_voltage_detected", + ) async def test_new_detected( - hass: HomeAssistant, caplog: pytest.LogCaptureFixture + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + issue_registry: ir.IssueRegistry, ) -> None: """Test new entry with under voltage detected.""" mocked_under_voltage = await _async_setup_component(hass, True) @@ -81,6 +91,11 @@ async def test_new_detected( DESCRIPTION_NORMALIZED, ) in caplog.record_tuples + assert issue_registry.async_get_issue( + domain=DOMAIN, + issue_id="under_voltage_detected", + ) + async def test_setup( hass: HomeAssistant, diff --git a/tests/components/rpi_power/test_repairs.py b/tests/components/rpi_power/test_repairs.py new file mode 100644 index 00000000000..85bf4a759bb --- /dev/null +++ b/tests/components/rpi_power/test_repairs.py @@ -0,0 +1,48 @@ +"""Tests for rpi_power repairs.""" + +from homeassistant.components.rpi_power.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType +from homeassistant.helpers import issue_registry as ir +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry, patch +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_repair_flow( + hass: HomeAssistant, + issue_registry: ir.IssueRegistry, + hass_client: ClientSessionGenerator, +) -> None: + """Test repair flow.""" + + config_entry = MockConfigEntry(domain=DOMAIN) + config_entry.add_to_hass(hass) + with patch("homeassistant.components.rpi_power.new_under_voltage", get=True): + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert issue_registry.async_get_issue( + domain=DOMAIN, + issue_id="under_voltage_detected", + ) + + assert await async_setup_component(hass, "repairs", {}) + await async_process_repairs_platforms(hass) + client = await hass_client() + + result = await start_repair_fix_flow(client, DOMAIN, "under_voltage_detected") + + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "confirm" + + result = await process_repair_fix_flow(client, result["flow_id"], {}) + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert issue_registry.async_get_issue(DOMAIN, "under_voltage_detected") is None