Compare commits

..

1 Commits

Author SHA1 Message Date
Klaas Schoute
642864959a Update translatable exceptions for Powerfox integration (#164322) 2026-02-28 01:57:02 +00:00
6 changed files with 71 additions and 23 deletions

View File

@@ -4,13 +4,19 @@ from __future__ import annotations
import asyncio
from powerfox import DeviceType, Powerfox, PowerfoxConnectionError
from powerfox import (
DeviceType,
Powerfox,
PowerfoxAuthenticationError,
PowerfoxConnectionError,
)
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
from .coordinator import (
PowerfoxConfigEntry,
PowerfoxDataUpdateCoordinator,
@@ -30,9 +36,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: PowerfoxConfigEntry) ->
try:
devices = await client.all_devices()
except PowerfoxAuthenticationError as err:
await client.close()
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="auth_failed",
) from err
except PowerfoxConnectionError as err:
await client.close()
raise ConfigEntryNotReady from err
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="connection_error",
) from err
coordinators: list[
PowerfoxDataUpdateCoordinator | PowerfoxReportDataUpdateCoordinator

View File

@@ -59,18 +59,24 @@ class PowerfoxBaseCoordinator[T](DataUpdateCoordinator[T]):
except PowerfoxAuthenticationError as err:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
translation_placeholders={"error": str(err)},
translation_key="auth_failed",
) from err
except (
PowerfoxConnectionError,
PowerfoxNoDataError,
PowerfoxPrivacyError,
) as err:
except PowerfoxConnectionError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="update_failed",
translation_placeholders={"error": str(err)},
translation_key="connection_error",
) from err
except PowerfoxNoDataError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="no_data_error",
translation_placeholders={"device_name": self.device.name},
) from err
except PowerfoxPrivacyError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="privacy_error",
translation_placeholders={"device_name": self.device.name},
) from err
async def _async_fetch_data(self) -> T:

View File

@@ -116,11 +116,17 @@
}
},
"exceptions": {
"invalid_auth": {
"message": "Error while authenticating with the Powerfox service: {error}"
"auth_failed": {
"message": "Authentication with the Powerfox service failed. Please re-authenticate your account."
},
"update_failed": {
"message": "Error while updating the Powerfox service: {error}"
"connection_error": {
"message": "Could not connect to the Powerfox service. Please check your network connection."
},
"no_data_error": {
"message": "No data available for device \"{device_name}\". The device may not have reported data yet."
},
"privacy_error": {
"message": "Data for device \"{device_name}\" is restricted due to privacy settings in the Powerfox app."
}
}
}

View File

@@ -288,8 +288,6 @@ class SongpalEntity(MediaPlayerEntity):
self._volume_min = volume.minVolume
self._volume = volume.volume
self._volume_control = volume
if self._volume_max:
self._attr_volume_step = 1 / self._volume_max
self._attr_is_volume_muted = self._volume_control.is_muted
status = await self._dev.get_power()
@@ -383,6 +381,14 @@ class SongpalEntity(MediaPlayerEntity):
_LOGGER.debug("Setting volume to %s", volume)
return await self._volume_control.set_volume(volume)
async def async_volume_up(self) -> None:
"""Set volume up."""
return await self._volume_control.set_volume(self._volume + 1)
async def async_volume_down(self) -> None:
"""Set volume down."""
return await self._volume_control.set_volume(self._volume - 1)
async def async_turn_on(self) -> None:
"""Turn the device on."""
try:

View File

@@ -5,6 +5,7 @@ from __future__ import annotations
from unittest.mock import AsyncMock
from powerfox import PowerfoxAuthenticationError, PowerfoxConnectionError
import pytest
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
@@ -45,16 +46,20 @@ async def test_config_entry_not_ready(
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
async def test_setup_entry_exception(
@pytest.mark.parametrize("method", ["all_devices", "device"])
async def test_config_entry_auth_failed(
hass: HomeAssistant,
mock_powerfox_client: AsyncMock,
mock_config_entry: MockConfigEntry,
method: str,
) -> None:
"""Test ConfigEntryNotReady when API raises an exception during entry setup."""
"""Test ConfigEntryAuthFailed when authentication fails."""
getattr(mock_powerfox_client, method).side_effect = PowerfoxAuthenticationError
mock_config_entry.add_to_hass(hass)
mock_powerfox_client.device.side_effect = PowerfoxAuthenticationError
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
flows = hass.config_entries.flow.async_progress()

View File

@@ -6,7 +6,12 @@ from datetime import timedelta
from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory
from powerfox import DeviceReport, PowerfoxConnectionError
from powerfox import (
DeviceReport,
PowerfoxConnectionError,
PowerfoxNoDataError,
PowerfoxPrivacyError,
)
import pytest
from syrupy.assertion import SnapshotAssertion
@@ -35,11 +40,16 @@ async def test_all_sensors(
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
@pytest.mark.parametrize(
"exception",
[PowerfoxConnectionError, PowerfoxNoDataError, PowerfoxPrivacyError],
)
async def test_update_failed(
hass: HomeAssistant,
mock_powerfox_client: AsyncMock,
mock_config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
exception: Exception,
) -> None:
"""Test entities become unavailable after failed update."""
await setup_integration(hass, mock_config_entry)
@@ -47,7 +57,7 @@ async def test_update_failed(
assert hass.states.get("sensor.poweropti_energy_usage").state is not None
mock_powerfox_client.device.side_effect = PowerfoxConnectionError
mock_powerfox_client.device.side_effect = exception
freezer.tick(timedelta(minutes=5))
async_fire_time_changed(hass)
await hass.async_block_till_done()