From da2fb17d9469a4b3914f94a11270aed899b5e225 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 1 Dec 2021 23:55:06 -1000 Subject: [PATCH] Update flux_led for upstream strict typing (#60800) - Bump library to 0.25.10 - Changelog: https://github.com/Danielhiversen/flux_led/compare/0.25.2...0.25.10 - This is a squashed version of #60554 since that one keeps failing to restore the python env on 3.9 --- homeassistant/components/flux_led/__init__.py | 32 ++++---- .../components/flux_led/config_flow.py | 78 +++++++++++++------ homeassistant/components/flux_led/entity.py | 4 +- homeassistant/components/flux_led/light.py | 21 ++--- .../components/flux_led/manifest.json | 2 +- homeassistant/components/flux_led/util.py | 6 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/flux_led/__init__.py | 48 +++++++----- tests/components/flux_led/test_config_flow.py | 5 +- tests/components/flux_led/test_init.py | 3 +- tests/components/flux_led/test_light.py | 66 ++++++++++++---- tests/components/flux_led/test_number.py | 14 ++-- tests/components/flux_led/test_switch.py | 2 +- 14 files changed, 181 insertions(+), 104 deletions(-) diff --git a/homeassistant/components/flux_led/__init__.py b/homeassistant/components/flux_led/__init__.py index b37a3640db3..7ea86af2e9d 100644 --- a/homeassistant/components/flux_led/__init__.py +++ b/homeassistant/components/flux_led/__init__.py @@ -4,12 +4,13 @@ from __future__ import annotations import asyncio from datetime import timedelta import logging -from typing import Any, Final, cast +from typing import Any, Final from flux_led import DeviceType from flux_led.aio import AIOWifiLedBulb from flux_led.aioscanner import AIOBulbScanner from flux_led.const import ATTR_ID, ATTR_IPADDR, ATTR_MODEL, ATTR_MODEL_DESCRIPTION +from flux_led.scanner import FluxLEDDiscovery from homeassistant import config_entries from homeassistant.config_entries import ConfigEntry @@ -50,33 +51,36 @@ def async_wifi_bulb_for_host(host: str) -> AIOWifiLedBulb: @callback -def async_name_from_discovery(device: dict[str, Any]) -> str: +def async_name_from_discovery(device: FluxLEDDiscovery) -> str: """Convert a flux_led discovery to a human readable name.""" - if (mac := device.get(ATTR_ID)) is None: - return cast(str, device[ATTR_IPADDR]) - short_mac = mac[-6:] - if device.get(ATTR_MODEL_DESCRIPTION): + mac_address = device[ATTR_ID] + if mac_address is None: + return device[ATTR_IPADDR] + short_mac = mac_address[-6:] + if device[ATTR_MODEL_DESCRIPTION]: return f"{device[ATTR_MODEL_DESCRIPTION]} {short_mac}" return f"{device[ATTR_MODEL]} {short_mac}" @callback def async_update_entry_from_discovery( - hass: HomeAssistant, entry: config_entries.ConfigEntry, device: dict[str, Any] + hass: HomeAssistant, entry: config_entries.ConfigEntry, device: FluxLEDDiscovery ) -> None: """Update a config entry from a flux_led discovery.""" name = async_name_from_discovery(device) + mac_address = device[ATTR_ID] + assert mac_address is not None hass.config_entries.async_update_entry( entry, data={**entry.data, CONF_NAME: name}, title=name, - unique_id=dr.format_mac(device[ATTR_ID]), + unique_id=dr.format_mac(mac_address), ) async def async_discover_devices( hass: HomeAssistant, timeout: int, address: str | None = None -) -> list[dict[str, str]]: +) -> list[FluxLEDDiscovery]: """Discover flux led devices.""" domain_data = hass.data.setdefault(DOMAIN, {}) if FLUX_LED_DISCOVERY_LOCK not in domain_data: @@ -84,9 +88,7 @@ async def async_discover_devices( async with domain_data[FLUX_LED_DISCOVERY_LOCK]: scanner = AIOBulbScanner() try: - discovered: list[dict[str, str]] = await scanner.async_scan( - timeout=timeout, address=address - ) + discovered = await scanner.async_scan(timeout=timeout, address=address) except OSError as ex: _LOGGER.debug("Scanning failed with error: %s", ex) return [] @@ -96,7 +98,7 @@ async def async_discover_devices( async def async_discover_device( hass: HomeAssistant, host: str -) -> dict[str, str] | None: +) -> FluxLEDDiscovery | None: """Direct discovery at a single ip instead of broadcast.""" # If we are missing the unique_id we should be able to fetch it # from the device by doing a directed discovery at the host only @@ -109,7 +111,7 @@ async def async_discover_device( @callback def async_trigger_discovery( hass: HomeAssistant, - discovered_devices: list[dict[str, Any]], + discovered_devices: list[FluxLEDDiscovery], ) -> None: """Trigger config flows for discovered devices.""" for device in discovered_devices: @@ -117,7 +119,7 @@ def async_trigger_discovery( hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_DISCOVERY}, - data=device, + data={**device}, ) ) diff --git a/homeassistant/components/flux_led/config_flow.py b/homeassistant/components/flux_led/config_flow.py index 7a161c56826..ab39e5b8ace 100644 --- a/homeassistant/components/flux_led/config_flow.py +++ b/homeassistant/components/flux_led/config_flow.py @@ -2,9 +2,10 @@ from __future__ import annotations import logging -from typing import Any, Final +from typing import Any, Final, cast from flux_led.const import ATTR_ID, ATTR_IPADDR, ATTR_MODEL, ATTR_MODEL_DESCRIPTION +from flux_led.scanner import FluxLEDDiscovery import voluptuous as vol from homeassistant import config_entries @@ -48,8 +49,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): def __init__(self) -> None: """Initialize the config flow.""" - self._discovered_devices: dict[str, dict[str, Any]] = {} - self._discovered_device: dict[str, Any] = {} + self._discovered_devices: dict[str, FluxLEDDiscovery] = {} + self._discovered_device: FluxLEDDiscovery | None = None @staticmethod @callback @@ -84,24 +85,32 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult: """Handle discovery via dhcp.""" - self._discovered_device = { - ATTR_IPADDR: discovery_info.ip, - ATTR_MODEL: discovery_info.hostname, - ATTR_ID: discovery_info.macaddress.replace(":", ""), - } + self._discovered_device = FluxLEDDiscovery( + ipaddr=discovery_info.ip, + model=discovery_info.hostname, + id=discovery_info.macaddress.replace(":", ""), + model_num=None, + version_num=None, + firmware_date=None, + model_info=None, + model_description=None, + ) return await self._async_handle_discovery() async def async_step_discovery( self, discovery_info: DiscoveryInfoType ) -> FlowResult: """Handle discovery.""" - self._discovered_device = discovery_info + self._discovered_device = cast(FluxLEDDiscovery, discovery_info) return await self._async_handle_discovery() async def _async_handle_discovery(self) -> FlowResult: """Handle any discovery.""" device = self._discovered_device - mac = dr.format_mac(device[ATTR_ID]) + assert device is not None + mac_address = device[ATTR_ID] + assert mac_address is not None + mac = dr.format_mac(mac_address) host = device[ATTR_IPADDR] await self.async_set_unique_id(mac) self._abort_if_unique_id_configured(updates={CONF_HOST: host}) @@ -113,13 +122,15 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): for progress in self._async_in_progress(): if progress.get("context", {}).get(CONF_HOST) == host: return self.async_abort(reason="already_in_progress") - if not device.get(ATTR_MODEL_DESCRIPTION): + if not device[ATTR_MODEL_DESCRIPTION]: try: - device = await self._async_try_connect(host) + device = await self._async_try_connect( + host, device[ATTR_ID], device[ATTR_MODEL] + ) except FLUX_LED_EXCEPTIONS: return self.async_abort(reason="cannot_connect") else: - if device.get(ATTR_MODEL_DESCRIPTION): + if device[ATTR_MODEL_DESCRIPTION]: self._discovered_device = device return await self.async_step_discovery_confirm() @@ -127,14 +138,17 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self, user_input: dict[str, Any] | None = None ) -> FlowResult: """Confirm discovery.""" + assert self._discovered_device is not None + device = self._discovered_device + mac_address = device[ATTR_ID] + assert mac_address is not None if user_input is not None: return self._async_create_entry_from_device(self._discovered_device) self._set_confirm_only() - device = self._discovered_device placeholders = { - "model": device.get(ATTR_MODEL_DESCRIPTION, device[ATTR_MODEL]), - "id": device[ATTR_ID][-6:], + "model": device[ATTR_MODEL_DESCRIPTION] or device[ATTR_MODEL], + "id": mac_address[-6:], "ipaddr": device[ATTR_IPADDR], } self.context["title_placeholders"] = placeholders @@ -143,7 +157,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) @callback - def _async_create_entry_from_device(self, device: dict[str, Any]) -> FlowResult: + def _async_create_entry_from_device(self, device: FluxLEDDiscovery) -> FlowResult: """Create a config entry from a device.""" self._async_abort_entries_match({CONF_HOST: device[ATTR_IPADDR]}) name = async_name_from_discovery(device) @@ -164,13 +178,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if not (host := user_input[CONF_HOST]): return await self.async_step_pick_device() try: - device = await self._async_try_connect(host) + device = await self._async_try_connect(host, None, None) except FLUX_LED_EXCEPTIONS: errors["base"] = "cannot_connect" else: - if device[ATTR_ID]: + mac_address = device[ATTR_ID] + if mac_address is not None: await self.async_set_unique_id( - dr.format_mac(device[ATTR_ID]), raise_on_progress=False + dr.format_mac(mac_address), raise_on_progress=False ) self._abort_if_unique_id_configured(updates={CONF_HOST: host}) return self._async_create_entry_from_device(device) @@ -198,9 +213,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): discovered_devices = await async_discover_devices( self.hass, DISCOVER_SCAN_TIMEOUT ) - self._discovered_devices = { - dr.format_mac(device[ATTR_ID]): device for device in discovered_devices - } + self._discovered_devices = {} + for device in discovered_devices: + mac_address = device[ATTR_ID] + assert mac_address is not None + self._discovered_devices[dr.format_mac(mac_address)] = device devices_name = { mac: f"{async_name_from_discovery(device)} ({device[ATTR_IPADDR]})" for mac, device in self._discovered_devices.items() @@ -215,7 +232,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): data_schema=vol.Schema({vol.Required(CONF_DEVICE): vol.In(devices_name)}), ) - async def _async_try_connect(self, host: str) -> dict[str, Any]: + async def _async_try_connect( + self, host: str, mac_address: str | None, model: str | None + ) -> FluxLEDDiscovery: """Try to connect.""" self._async_abort_entries_match({CONF_HOST: host}) if device := await async_discover_device(self.hass, host): @@ -225,7 +244,16 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): await bulb.async_setup(lambda: None) finally: await bulb.async_stop() - return {ATTR_ID: None, ATTR_MODEL: None, ATTR_IPADDR: host} + return FluxLEDDiscovery( + ipaddr=host, + model=model, + id=mac_address, + model_num=bulb.model_num, + version_num=bulb.version_num, + firmware_date=None, + model_info=None, + model_description=bulb.model_data.description, + ) class OptionsFlow(config_entries.OptionsFlow): diff --git a/homeassistant/components/flux_led/entity.py b/homeassistant/components/flux_led/entity.py index bab9a24bfe5..f4425f3b2a2 100644 --- a/homeassistant/components/flux_led/entity.py +++ b/homeassistant/components/flux_led/entity.py @@ -2,7 +2,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import Any, cast +from typing import Any from flux_led.aiodevice import AIOWifiLedBulb @@ -72,7 +72,7 @@ class FluxOnOffEntity(FluxEntity): @property def is_on(self) -> bool: """Return true if device is on.""" - return cast(bool, self._device.is_on) + return self._device.is_on async def async_turn_on(self, **kwargs: Any) -> None: """Turn the specified device on.""" diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index cf43a90bf22..fafa6a0b22e 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -3,7 +3,7 @@ from __future__ import annotations import ast import logging -from typing import Any, Final, cast +from typing import Any, Final from flux_led.const import ATTR_ID, ATTR_IPADDR from flux_led.utils import ( @@ -244,7 +244,7 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity): @property def brightness(self) -> int: """Return the brightness of this light between 0..255.""" - return cast(int, self._device.brightness) + return self._device.brightness @property def color_temp(self) -> int: @@ -254,20 +254,17 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity): @property def rgb_color(self) -> tuple[int, int, int]: """Return the rgb color value.""" - rgb: tuple[int, int, int] = self._device.rgb_unscaled - return rgb + return self._device.rgb_unscaled @property def rgbw_color(self) -> tuple[int, int, int, int]: """Return the rgbw color value.""" - rgbw: tuple[int, int, int, int] = self._device.rgbw - return rgbw + return self._device.rgbw @property def rgbww_color(self) -> tuple[int, int, int, int, int]: """Return the rgbww aka rgbcw color value.""" - rgbcw: tuple[int, int, int, int, int] = self._device.rgbcw - return rgbcw + return self._device.rgbcw @property def color_mode(self) -> str: @@ -279,10 +276,7 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity): @property def effect(self) -> str | None: """Return the current effect.""" - effect = self._device.effect - if effect is None: - return None - return cast(str, effect) + return self._device.effect async def _async_turn_on(self, **kwargs: Any) -> None: """Turn the specified or all lights on.""" @@ -353,7 +347,8 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity): return # Handle switch to RGB Color Mode if rgb := kwargs.get(ATTR_RGB_COLOR): - await self._device.async_set_levels(*rgb, brightness=brightness) + red, green, blue = rgb + await self._device.async_set_levels(red, green, blue, brightness=brightness) return # Handle switch to RGBW Color Mode if rgbw := kwargs.get(ATTR_RGBW_COLOR): diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index fcd66521153..91de8cb5b7d 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.25.2"], + "requirements": ["flux_led==0.25.10"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/homeassistant/components/flux_led/util.py b/homeassistant/components/flux_led/util.py index 8a089c9ebb6..774ae1aaa53 100644 --- a/homeassistant/components/flux_led/util.py +++ b/homeassistant/components/flux_led/util.py @@ -18,8 +18,12 @@ def _hass_color_modes(device: AIOWifiLedBulb) -> set[str]: return {_flux_color_mode_to_hass(mode, color_modes) for mode in color_modes} -def _flux_color_mode_to_hass(flux_color_mode: str, flux_color_modes: set[str]) -> str: +def _flux_color_mode_to_hass( + flux_color_mode: str | None, flux_color_modes: set[str] +) -> str: """Map the flux color mode to Home Assistant color mode.""" + if flux_color_mode is None: + return COLOR_MODE_ONOFF if flux_color_mode == FLUX_COLOR_MODE_DIM: if len(flux_color_modes) > 1: return COLOR_MODE_WHITE diff --git a/requirements_all.txt b/requirements_all.txt index 3c4fcfc431b..8e5bfdb2d43 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -658,7 +658,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.25.2 +flux_led==0.25.10 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4b63f53d759..4e41729ebd5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -399,7 +399,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.25.2 +flux_led==0.25.10 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/tests/components/flux_led/__init__.py b/tests/components/flux_led/__init__.py index 47aeb6be491..5b39c5656f6 100644 --- a/tests/components/flux_led/__init__.py +++ b/tests/components/flux_led/__init__.py @@ -2,20 +2,19 @@ from __future__ import annotations import asyncio +import datetime from typing import Callable from unittest.mock import AsyncMock, MagicMock, patch from flux_led import DeviceType from flux_led.aio import AIOWifiLedBulb from flux_led.const import ( - ATTR_ID, - ATTR_IPADDR, - ATTR_MODEL, - ATTR_MODEL_DESCRIPTION, COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT, COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB, ) +from flux_led.models_db import MODEL_MAP from flux_led.protocol import LEDENETRawState +from flux_led.scanner import FluxLEDDiscovery from homeassistant.components import dhcp from homeassistant.core import HomeAssistant @@ -23,14 +22,14 @@ from homeassistant.core import HomeAssistant MODULE = "homeassistant.components.flux_led" MODULE_CONFIG_FLOW = "homeassistant.components.flux_led.config_flow" IP_ADDRESS = "127.0.0.1" +MODEL_NUM_HEX = "0x35" MODEL = "AZ120444" -MODEL_DESCRIPTION = "RGBW Controller" +MODEL_DESCRIPTION = "Bulb RGBCW" MAC_ADDRESS = "aa:bb:cc:dd:ee:ff" FLUX_MAC_ADDRESS = "aabbccddeeff" SHORT_MAC_ADDRESS = "ddeeff" DEFAULT_ENTRY_TITLE = f"{MODEL_DESCRIPTION} {SHORT_MAC_ADDRESS}" -DEFAULT_ENTRY_TITLE_PARTIAL = f"{MODEL} {SHORT_MAC_ADDRESS}" DHCP_DISCOVERY = dhcp.DhcpServiceInfo( @@ -38,17 +37,26 @@ DHCP_DISCOVERY = dhcp.DhcpServiceInfo( ip=IP_ADDRESS, macaddress=MAC_ADDRESS, ) -FLUX_DISCOVERY_PARTIAL = { - ATTR_IPADDR: IP_ADDRESS, - ATTR_MODEL: MODEL, - ATTR_ID: FLUX_MAC_ADDRESS, -} -FLUX_DISCOVERY = { - ATTR_IPADDR: IP_ADDRESS, - ATTR_MODEL: MODEL, - ATTR_ID: FLUX_MAC_ADDRESS, - ATTR_MODEL_DESCRIPTION: MODEL_DESCRIPTION, -} +FLUX_DISCOVERY_PARTIAL = FluxLEDDiscovery( + ipaddr=IP_ADDRESS, + model=MODEL, + id=FLUX_MAC_ADDRESS, + model_num=None, + version_num=None, + firmware_date=None, + model_info=None, + model_description=None, +) +FLUX_DISCOVERY = FluxLEDDiscovery( + ipaddr=IP_ADDRESS, + model=MODEL, + id=FLUX_MAC_ADDRESS, + model_num=0x25, + version_num=0x04, + firmware_date=datetime.date(2021, 5, 5), + model_info=MODEL, + model_description=MODEL_DESCRIPTION, +) def _mocked_bulb() -> AIOWifiLedBulb: @@ -85,9 +93,10 @@ def _mocked_bulb() -> AIOWifiLedBulb: bulb.getWhiteTemperature = MagicMock(return_value=(2700, 128)) bulb.brightness = 128 bulb.model_num = 0x35 + bulb.model_data = MODEL_MAP[0x35] bulb.effect = None bulb.speed = 50 - bulb.model = "Smart Bulb (0x35)" + bulb.model = "Bulb RGBCW (0x35)" bulb.version_num = 8 bulb.speed_adjust_off = True bulb.rgbwcapable = True @@ -112,7 +121,8 @@ def _mocked_switch() -> AIOWifiLedBulb: switch.async_turn_off = AsyncMock() switch.async_turn_on = AsyncMock() switch.model_num = 0x97 - switch.model = "Smart Switch (0x97)" + switch.model_data = MODEL_MAP[0x97] + switch.model = "Switch (0x97)" switch.version_num = 0x97 switch.raw_state = LEDENETRawState( 0, 0x97, 0, 0x61, 0x97, 50, 255, 0, 0, 50, 8, 0, 0, 0 diff --git a/tests/components/flux_led/test_config_flow.py b/tests/components/flux_led/test_config_flow.py index 06b47dd2788..af84d3561f7 100644 --- a/tests/components/flux_led/test_config_flow.py +++ b/tests/components/flux_led/test_config_flow.py @@ -29,7 +29,6 @@ from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_FORM from . import ( DEFAULT_ENTRY_TITLE, - DEFAULT_ENTRY_TITLE_PARTIAL, DHCP_DISCOVERY, FLUX_DISCOVERY, IP_ADDRESS, @@ -428,7 +427,7 @@ async def test_discovered_by_dhcp_no_udp_response(hass): assert result2["type"] == "create_entry" assert result2["data"] == { CONF_HOST: IP_ADDRESS, - CONF_NAME: DEFAULT_ENTRY_TITLE_PARTIAL, + CONF_NAME: DEFAULT_ENTRY_TITLE, } assert mock_async_setup.called assert mock_async_setup_entry.called @@ -509,4 +508,4 @@ async def test_options(hass: HomeAssistant): assert result2["type"] == "create_entry" assert result2["data"] == user_input assert result2["data"] == config_entry.options - assert hass.states.get("light.rgbw_controller_ddeeff") is not None + assert hass.states.get("light.bulb_rgbcw_ddeeff") is not None diff --git a/tests/components/flux_led/test_init.py b/tests/components/flux_led/test_init.py index abb671da9c2..23a238fa812 100644 --- a/tests/components/flux_led/test_init.py +++ b/tests/components/flux_led/test_init.py @@ -15,7 +15,6 @@ from homeassistant.util.dt import utcnow from . import ( DEFAULT_ENTRY_TITLE, - DEFAULT_ENTRY_TITLE_PARTIAL, FLUX_DISCOVERY, FLUX_DISCOVERY_PARTIAL, IP_ADDRESS, @@ -75,7 +74,7 @@ async def test_config_entry_retry(hass: HomeAssistant) -> None: "discovery,title", [ (FLUX_DISCOVERY, DEFAULT_ENTRY_TITLE), - (FLUX_DISCOVERY_PARTIAL, DEFAULT_ENTRY_TITLE_PARTIAL), + (FLUX_DISCOVERY_PARTIAL, "AZ120444 ddeeff"), ], ) async def test_config_entry_fills_unique_id_with_directed_discovery( diff --git a/tests/components/flux_led/test_light.py b/tests/components/flux_led/test_light.py index e66492d2f43..4f401197173 100644 --- a/tests/components/flux_led/test_light.py +++ b/tests/components/flux_led/test_light.py @@ -84,7 +84,7 @@ async def test_light_unique_id(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" entity_registry = er.async_get(hass) assert entity_registry.async_get(entity_id).unique_id == MAC_ADDRESS state = hass.states.get(entity_id) @@ -104,7 +104,7 @@ async def test_light_goes_unavailable_and_recovers(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" entity_registry = er.async_get(hass) assert entity_registry.async_get(entity_id).unique_id == MAC_ADDRESS state = hass.states.get(entity_id) @@ -136,7 +136,7 @@ async def test_light_no_unique_id(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" entity_registry = er.async_get(hass) assert entity_registry.async_get(entity_id) is None state = hass.states.get(entity_id) @@ -194,7 +194,7 @@ async def test_rgb_light(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON @@ -300,7 +300,7 @@ async def test_rgb_cct_light(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON @@ -419,7 +419,7 @@ async def test_rgbw_light(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON @@ -521,7 +521,7 @@ async def test_rgb_or_w_light(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON @@ -632,7 +632,7 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON @@ -772,7 +772,7 @@ async def test_white_light(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON @@ -806,6 +806,46 @@ async def test_white_light(hass: HomeAssistant) -> None: bulb.async_set_brightness.reset_mock() +async def test_no_color_modes(hass: HomeAssistant) -> None: + """Test a light that has no color modes defined in the database.""" + config_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE}, + unique_id=MAC_ADDRESS, + ) + config_entry.add_to_hass(hass) + bulb = _mocked_bulb() + bulb.mode = "ww" + bulb.protocol = None + bulb.color_modes = set() + bulb.color_mode = None + with _patch_discovery(device=bulb), _patch_wifibulb(device=bulb): + await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) + await hass.async_block_till_done() + + entity_id = "light.bulb_rgbcw_ddeeff" + + state = hass.states.get(entity_id) + assert state.state == STATE_ON + attributes = state.attributes + assert attributes[ATTR_COLOR_MODE] == "onoff" + assert ATTR_EFFECT_LIST in attributes # single channel now supports effects + + await hass.services.async_call( + LIGHT_DOMAIN, "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + bulb.async_turn_off.assert_called_once() + await async_mock_device_turn_off(hass, bulb) + + assert hass.states.get(entity_id).state == STATE_OFF + + await hass.services.async_call( + LIGHT_DOMAIN, "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + bulb.async_turn_on.assert_called_once() + bulb.async_turn_on.reset_mock() + + async def test_rgb_light_custom_effects(hass: HomeAssistant) -> None: """Test an rgb light with a custom effect.""" config_entry = MockConfigEntry( @@ -827,7 +867,7 @@ async def test_rgb_light_custom_effects(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON @@ -909,7 +949,7 @@ async def test_rgb_light_custom_effects_invalid_colors( await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON @@ -938,7 +978,7 @@ async def test_rgb_light_custom_effect_via_service( await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON @@ -1083,7 +1123,7 @@ async def test_addressable_light(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "light.rgbw_controller_ddeeff" + entity_id = "light.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON diff --git a/tests/components/flux_led/test_number.py b/tests/components/flux_led/test_number.py index c6167ebcedc..11df6daae4a 100644 --- a/tests/components/flux_led/test_number.py +++ b/tests/components/flux_led/test_number.py @@ -45,7 +45,7 @@ async def test_number_unique_id(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "number.rgbw_controller_ddeeff_effect_speed" + entity_id = "number.bulb_rgbcw_ddeeff_effect_speed" entity_registry = er.async_get(hass) assert entity_registry.async_get(entity_id).unique_id == MAC_ADDRESS @@ -70,8 +70,8 @@ async def test_rgb_light_effect_speed(hass: HomeAssistant) -> None: await async_mock_device_turn_on(hass, bulb) - light_entity_id = "light.rgbw_controller_ddeeff" - number_entity_id = "number.rgbw_controller_ddeeff_effect_speed" + light_entity_id = "light.bulb_rgbcw_ddeeff" + number_entity_id = "number.bulb_rgbcw_ddeeff_effect_speed" with pytest.raises(HomeAssistantError): await hass.services.async_call( NUMBER_DOMAIN, @@ -135,8 +135,8 @@ async def test_original_addressable_light_effect_speed(hass: HomeAssistant) -> N await async_mock_device_turn_on(hass, bulb) - light_entity_id = "light.rgbw_controller_ddeeff" - number_entity_id = "number.rgbw_controller_ddeeff_effect_speed" + light_entity_id = "light.bulb_rgbcw_ddeeff" + number_entity_id = "number.bulb_rgbcw_ddeeff_effect_speed" state = hass.states.get(light_entity_id) assert state.state == STATE_ON @@ -192,8 +192,8 @@ async def test_addressable_light_effect_speed(hass: HomeAssistant) -> None: await async_mock_device_turn_on(hass, bulb) - light_entity_id = "light.rgbw_controller_ddeeff" - number_entity_id = "number.rgbw_controller_ddeeff_effect_speed" + light_entity_id = "light.bulb_rgbcw_ddeeff" + number_entity_id = "number.bulb_rgbcw_ddeeff_effect_speed" state = hass.states.get(light_entity_id) assert state.state == STATE_ON diff --git a/tests/components/flux_led/test_switch.py b/tests/components/flux_led/test_switch.py index b3f27c28b5e..852e1efd49e 100644 --- a/tests/components/flux_led/test_switch.py +++ b/tests/components/flux_led/test_switch.py @@ -39,7 +39,7 @@ async def test_switch_on_off(hass: HomeAssistant) -> None: await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}}) await hass.async_block_till_done() - entity_id = "switch.rgbw_controller_ddeeff" + entity_id = "switch.bulb_rgbcw_ddeeff" state = hass.states.get(entity_id) assert state.state == STATE_ON