mirror of
https://github.com/home-assistant/core.git
synced 2026-10-06 22:38:02 -04:00
Enhance connection error exception handling for light entities of lunatone (#184294)
This commit is contained in:
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user