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 77 additions and 22 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

@@ -55,7 +55,6 @@ class Ws66iZone(CoordinatorEntity[Ws66iDataUpdateCoordinator], MediaPlayerEntity
| MediaPlayerEntityFeature.TURN_OFF
| MediaPlayerEntityFeature.SELECT_SOURCE
)
_attr_volume_step = 1 / MAX_VOL
def __init__(
self,
@@ -148,6 +147,20 @@ class Ws66iZone(CoordinatorEntity[Ws66iDataUpdateCoordinator], MediaPlayerEntity
await self.hass.async_add_executor_job(self._set_volume, int(volume * MAX_VOL))
self._async_update_attrs_write_ha_state()
async def async_volume_up(self) -> None:
"""Volume up the media player."""
await self.hass.async_add_executor_job(
self._set_volume, min(self._status.volume + 1, MAX_VOL)
)
self._async_update_attrs_write_ha_state()
async def async_volume_down(self) -> None:
"""Volume down media player."""
await self.hass.async_add_executor_job(
self._set_volume, max(self._status.volume - 1, 0)
)
self._async_update_attrs_write_ha_state()
def _set_volume(self, volume: int) -> None:
"""Set the volume of the media player."""
# Can't set a new volume level when this zone is muted.

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()