From 1c94431efe5cc775eacf5c4e1b1353ff6991f688 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 21 Jan 2022 09:19:50 +0100 Subject: [PATCH] Handle WLED devices with CCT channels (#64581) --- homeassistant/components/wled/__init__.py | 11 ++++- homeassistant/components/wled/config_flow.py | 5 +++ homeassistant/components/wled/coordinator.py | 1 + homeassistant/components/wled/strings.json | 3 +- .../components/wled/translations/en.json | 3 +- tests/components/wled/test_config_flow.py | 41 +++++++++++++++++++ tests/components/wled/test_init.py | 18 ++++++++ 7 files changed, 79 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/wled/__init__.py b/homeassistant/components/wled/__init__.py index 5a25a461fd6..9c32f9becb8 100644 --- a/homeassistant/components/wled/__init__.py +++ b/homeassistant/components/wled/__init__.py @@ -5,7 +5,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant -from .const import DOMAIN +from .const import DOMAIN, LOGGER from .coordinator import WLEDDataUpdateCoordinator PLATFORMS = ( @@ -23,6 +23,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up WLED from a config entry.""" coordinator = WLEDDataUpdateCoordinator(hass, entry=entry) await coordinator.async_config_entry_first_refresh() + + if coordinator.data.info.leds.cct: + LOGGER.error( + "WLED device '%s' has a CCT channel, which is not supported by " + "this integration", + entry.title, + ) + return False + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator # Set up all platforms for this device/entry. diff --git a/homeassistant/components/wled/config_flow.py b/homeassistant/components/wled/config_flow.py index 5542e8d0848..99630f5781c 100644 --- a/homeassistant/components/wled/config_flow.py +++ b/homeassistant/components/wled/config_flow.py @@ -41,6 +41,8 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN): except WLEDConnectionError: errors["base"] = "cannot_connect" else: + if device.info.leds.cct: + return self.async_abort(reason="cct_unsupported") await self.async_set_unique_id(device.info.mac_address) self._abort_if_unique_id_configured( updates={CONF_HOST: user_input[CONF_HOST]} @@ -77,6 +79,9 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN): except WLEDConnectionError: return self.async_abort(reason="cannot_connect") + if self.discovered_device.info.leds.cct: + return self.async_abort(reason="cct_unsupported") + await self.async_set_unique_id(self.discovered_device.info.mac_address) self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.host}) diff --git a/homeassistant/components/wled/coordinator.py b/homeassistant/components/wled/coordinator.py index 9dbd02d65c3..a4cbaade8ba 100644 --- a/homeassistant/components/wled/coordinator.py +++ b/homeassistant/components/wled/coordinator.py @@ -113,6 +113,7 @@ class WLEDDataUpdateCoordinator(DataUpdateCoordinator[WLEDDevice]): # If the device supports a WebSocket, try activating it. if ( device.info.websocket is not None + and device.info.leds.cct is not True and not self.wled.connected and not self.unsub ): diff --git a/homeassistant/components/wled/strings.json b/homeassistant/components/wled/strings.json index 9717637fdbb..ac1eb4046dd 100644 --- a/homeassistant/components/wled/strings.json +++ b/homeassistant/components/wled/strings.json @@ -18,7 +18,8 @@ }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "cct_unsupported": "This WLED device uses CCT channels, which is not supported by this integration" } }, "options": { diff --git a/homeassistant/components/wled/translations/en.json b/homeassistant/components/wled/translations/en.json index a114d0218ca..4cc3e12bf2a 100644 --- a/homeassistant/components/wled/translations/en.json +++ b/homeassistant/components/wled/translations/en.json @@ -2,7 +2,8 @@ "config": { "abort": { "already_configured": "Device is already configured", - "cannot_connect": "Failed to connect" + "cannot_connect": "Failed to connect", + "cct_unsupported": "This WLED device uses CCT channels, which is not supported by this integration" }, "error": { "cannot_connect": "Failed to connect" diff --git a/tests/components/wled/test_config_flow.py b/tests/components/wled/test_config_flow.py index 9ca62a010ad..27023708400 100644 --- a/tests/components/wled/test_config_flow.py +++ b/tests/components/wled/test_config_flow.py @@ -139,6 +139,23 @@ async def test_user_device_exists_abort( assert result.get("reason") == "already_configured" +async def test_user_with_cct_channel_abort( + hass: HomeAssistant, + mock_wled_config_flow: MagicMock, +) -> None: + """Test we abort user flow if WLED device uses a CCT channel.""" + mock_wled_config_flow.update.return_value.info.leds.cct = True + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_USER}, + data={CONF_HOST: "192.168.1.123"}, + ) + + assert result.get("type") == RESULT_TYPE_ABORT + assert result.get("reason") == "cct_unsupported" + + async def test_zeroconf_without_mac_device_exists_abort( hass: HomeAssistant, mock_config_entry: MockConfigEntry, @@ -187,6 +204,30 @@ async def test_zeroconf_with_mac_device_exists_abort( assert result.get("reason") == "already_configured" +async def test_zeroconf_with_cct_channel_abort( + hass: HomeAssistant, + mock_wled_config_flow: MagicMock, +) -> None: + """Test we abort zeroconf flow if WLED device uses a CCT channel.""" + mock_wled_config_flow.update.return_value.info.leds.cct = True + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="192.168.1.123", + hostname="example.local.", + name="mock_name", + port=None, + properties={CONF_MAC: "aabbccddeeff"}, + type="mock_type", + ), + ) + + assert result.get("type") == RESULT_TYPE_ABORT + assert result.get("reason") == "cct_unsupported" + + async def test_options_flow( hass: HomeAssistant, mock_config_entry: MockConfigEntry ) -> None: diff --git a/tests/components/wled/test_init.py b/tests/components/wled/test_init.py index 3186cf1e7e4..72a065a3b8e 100644 --- a/tests/components/wled/test_init.py +++ b/tests/components/wled/test_init.py @@ -68,3 +68,21 @@ async def test_setting_unique_id( """Test we set unique ID if not set yet.""" assert hass.data[DOMAIN] assert init_integration.unique_id == "aabbccddeeff" + + +async def test_error_config_entry_with_cct_channel( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_wled: AsyncMock, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test the WLED fails entry setup with a CCT channel.""" + mock_wled.update.return_value.info.leds.cct = True + + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + # Ensure config entry is errored and are connected and disconnected + assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR + assert "has a CCT channel, which is not supported" in caplog.text