Enhance connection error exception handling for light entities of lunatone (#184294)

This commit is contained in:
MoonDevLT
2026-10-05 14:36:17 +02:00
committed by GitHub
parent 88fa492896
commit c884337f85
4 changed files with 161 additions and 33 deletions
+64 -30
View File
@@ -2,6 +2,7 @@
from typing import Any, override
import aiohttp
from lunatone_rest_api_client import DALIBroadcast
from lunatone_rest_api_client.models import LineStatus
@@ -15,6 +16,7 @@ from homeassistant.components.light import (
brightness_supported,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -191,38 +193,54 @@ class LunatoneLight(
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Instruct the light to turn on."""
if brightness_supported(self.supported_color_modes):
if ATTR_COLOR_TEMP_KELVIN in kwargs:
await self._device.fade_to_color_temperature(
kwargs[ATTR_COLOR_TEMP_KELVIN]
)
if ATTR_RGB_COLOR in kwargs:
await self._device.fade_to_rgbw_color(
tuple(color / 255 for color in kwargs[ATTR_RGB_COLOR])
)
if ATTR_RGBW_COLOR in kwargs:
rgbw_color = tuple(color / 255 for color in kwargs[ATTR_RGBW_COLOR])
await self._device.fade_to_rgbw_color(rgbw_color[:-1], rgbw_color[-1])
if ATTR_BRIGHTNESS in kwargs or not self.is_on:
await self._device.fade_to_brightness(
brightness_to_value(
self.BRIGHTNESS_SCALE,
kwargs.get(ATTR_BRIGHTNESS, self._last_brightness),
try:
if brightness_supported(self.supported_color_modes):
if ATTR_COLOR_TEMP_KELVIN in kwargs:
await self._device.fade_to_color_temperature(
kwargs[ATTR_COLOR_TEMP_KELVIN]
)
)
else:
await self._device.switch_on()
if ATTR_RGB_COLOR in kwargs:
await self._device.fade_to_rgbw_color(
tuple(color / 255 for color in kwargs[ATTR_RGB_COLOR])
)
if ATTR_RGBW_COLOR in kwargs:
rgbw_color = tuple(color / 255 for color in kwargs[ATTR_RGBW_COLOR])
await self._device.fade_to_rgbw_color(
rgbw_color[:-1], rgbw_color[-1]
)
if ATTR_BRIGHTNESS in kwargs or not self.is_on:
await self._device.fade_to_brightness(
brightness_to_value(
self.BRIGHTNESS_SCALE,
kwargs.get(ATTR_BRIGHTNESS, self._last_brightness),
)
)
else:
await self._device.switch_on()
except aiohttp.ClientConnectionError as ex:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_turn_on_light",
translation_placeholders={"entity": self.entity_id},
) from ex
await self.coordinator.async_refresh()
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Instruct the light to turn off."""
if brightness_supported(self.supported_color_modes):
if self.brightness:
self._last_brightness = self.brightness
await self._device.fade_to_brightness(0)
else:
await self._device.switch_off()
try:
if brightness_supported(self.supported_color_modes):
if self.brightness:
self._last_brightness = self.brightness
await self._device.fade_to_brightness(0)
else:
await self._device.switch_off()
except aiohttp.ClientConnectionError as ex:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_turn_off_light",
translation_placeholders={"entity": self.entity_id},
) from ex
await self.coordinator.async_refresh()
@@ -294,13 +312,29 @@ class LunatoneLineBroadcastLight(
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Instruct the line to turn on."""
await self._broadcast.fade_to_brightness(
brightness_to_value(self.BRIGHTNESS_SCALE, kwargs.get(ATTR_BRIGHTNESS, 255))
)
try:
await self._broadcast.fade_to_brightness(
brightness_to_value(
self.BRIGHTNESS_SCALE, kwargs.get(ATTR_BRIGHTNESS, 255)
)
)
except aiohttp.ClientConnectionError as ex:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_turn_on_light",
translation_placeholders={"entity": self.entity_id},
) from ex
await self.coordinator.async_refresh()
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Instruct the line to turn off."""
await self._broadcast.fade_to_brightness(0)
try:
await self._broadcast.fade_to_brightness(0)
except aiohttp.ClientConnectionError as ex:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_turn_off_light",
translation_placeholders={"entity": self.entity_id},
) from ex
await self.coordinator.async_refresh()
@@ -38,9 +38,7 @@ rules:
unique-config-entry: done
# Silver
action-exceptions:
status: exempt
comment: no actions
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters:
status: exempt
@@ -54,6 +54,12 @@
}
},
"exceptions": {
"cannot_turn_off_light": {
"message": "Unable to connect to the device and turn off {entity}"
},
"cannot_turn_on_light": {
"message": "Unable to connect to the device and turn on {entity}"
},
"missing_device_info": {
"message": "Unable to read device information. Please verify the device's network connection."
}
+90
View File
@@ -3,6 +3,7 @@
import copy
from unittest.mock import AsyncMock
import aiohttp
from freezegun.api import FrozenDateTimeFactory
from lunatone_rest_api_client.models import LineStatus
import pytest
@@ -24,6 +25,7 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import setup_integration
@@ -96,6 +98,48 @@ async def test_turn_on_off(
assert state.state == STATE_OFF
async def test_turn_on_off_with_connection_error(
hass: HomeAssistant,
mock_lunatone_info: AsyncMock,
mock_lunatone_devices: AsyncMock,
mock_lunatone_sensors: AsyncMock,
mock_lunatone_scan: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test a connection error is raised while turning on and off the light."""
device_id = 1
entity_id = f"light.device_{device_id}"
await setup_integration(hass, mock_config_entry)
device = mock_config_entry.runtime_data.coordinator_devices.data[0][device_id]
device.switch_on.side_effect = aiohttp.ClientConnectionError()
with pytest.raises(
HomeAssistantError,
match=f"Unable to connect to the device and turn on {entity_id}",
):
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 128},
blocking=True,
)
device.switch_off.side_effect = aiohttp.ClientConnectionError()
with pytest.raises(
HomeAssistantError,
match=f"Unable to connect to the device and turn off {entity_id}",
):
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)
async def test_turn_on_off_with_brightness(
hass: HomeAssistant,
mock_lunatone_info: AsyncMock,
@@ -221,6 +265,52 @@ async def test_turn_on_off_broadcast(
assert state.state == "off"
async def test_turn_on_off_broadcast_with_connection_error(
hass: HomeAssistant,
mock_lunatone_info: AsyncMock,
mock_lunatone_devices: AsyncMock,
mock_lunatone_sensors: AsyncMock,
mock_lunatone_scan: AsyncMock,
mock_lunatone_dali_broadcast: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test a connection error is raised while turning on and off the broadcast light."""
line_id = mock_lunatone_dali_broadcast.line
entity_id = f"light.dali_line_{line_id}"
await setup_integration(hass, mock_config_entry)
mock_lunatone_dali_broadcast.fade_to_brightness.side_effect = (
aiohttp.ClientConnectionError()
)
with pytest.raises(
HomeAssistantError,
match=f"Unable to connect to the device and turn on {entity_id}",
):
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 128},
blocking=True,
)
mock_lunatone_dali_broadcast.fade_to_brightness.side_effect = (
aiohttp.ClientConnectionError()
)
with pytest.raises(
HomeAssistantError,
match=f"Unable to connect to the device and turn off {entity_id}",
):
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: entity_id},
blocking=True,
)
async def test_line_broadcast_available_status(
hass: HomeAssistant,
mock_lunatone_info: AsyncMock,