Add support for changing the white strip type to flux_led (#63943)
This commit is contained in:
parent
5622db10b1
commit
1c6ca908d1
11 changed files with 304 additions and 54 deletions
|
@ -7,7 +7,7 @@ from typing import Any, Final, cast
|
||||||
|
|
||||||
from flux_led import DeviceType
|
from flux_led import DeviceType
|
||||||
from flux_led.aio import AIOWifiLedBulb
|
from flux_led.aio import AIOWifiLedBulb
|
||||||
from flux_led.const import ATTR_ID
|
from flux_led.const import ATTR_ID, WhiteChannelType
|
||||||
from flux_led.scanner import FluxLEDDiscovery
|
from flux_led.scanner import FluxLEDDiscovery
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
@ -23,6 +23,7 @@ from homeassistant.helpers.event import (
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
CONF_WHITE_CHANNEL_TYPE,
|
||||||
DISCOVER_SCAN_TIMEOUT,
|
DISCOVER_SCAN_TIMEOUT,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
FLUX_LED_DISCOVERY,
|
FLUX_LED_DISCOVERY,
|
||||||
|
@ -56,6 +57,9 @@ PLATFORMS_BY_TYPE: Final = {
|
||||||
}
|
}
|
||||||
DISCOVERY_INTERVAL: Final = timedelta(minutes=15)
|
DISCOVERY_INTERVAL: Final = timedelta(minutes=15)
|
||||||
REQUEST_REFRESH_DELAY: Final = 1.5
|
REQUEST_REFRESH_DELAY: Final = 1.5
|
||||||
|
NAME_TO_WHITE_CHANNEL_TYPE: Final = {
|
||||||
|
option.name.lower(): option for option in WhiteChannelType
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -95,6 +99,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
device: AIOWifiLedBulb = async_wifi_bulb_for_host(host, discovery=discovery)
|
device: AIOWifiLedBulb = async_wifi_bulb_for_host(host, discovery=discovery)
|
||||||
signal = SIGNAL_STATE_UPDATED.format(device.ipaddr)
|
signal = SIGNAL_STATE_UPDATED.format(device.ipaddr)
|
||||||
device.discovery = discovery
|
device.discovery = discovery
|
||||||
|
if white_channel_type := entry.data.get(CONF_WHITE_CHANNEL_TYPE):
|
||||||
|
device.white_channel_channel_type = NAME_TO_WHITE_CHANNEL_TYPE[
|
||||||
|
white_channel_type
|
||||||
|
]
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_state_changed(*_: Any) -> None:
|
def _async_state_changed(*_: Any) -> None:
|
||||||
|
|
|
@ -28,6 +28,7 @@ FLUX_COLOR_MODE_TO_HASS: Final = {
|
||||||
FLUX_COLOR_MODE_CCT: COLOR_MODE_COLOR_TEMP,
|
FLUX_COLOR_MODE_CCT: COLOR_MODE_COLOR_TEMP,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MULTI_BRIGHTNESS_COLOR_MODES: Final = {COLOR_MODE_RGBWW, COLOR_MODE_RGBW}
|
||||||
|
|
||||||
API: Final = "flux_api"
|
API: Final = "flux_api"
|
||||||
|
|
||||||
|
@ -57,6 +58,8 @@ CONF_MINOR_VERSION: Final = "minor_version"
|
||||||
CONF_REMOTE_ACCESS_ENABLED: Final = "remote_access_enabled"
|
CONF_REMOTE_ACCESS_ENABLED: Final = "remote_access_enabled"
|
||||||
CONF_REMOTE_ACCESS_HOST: Final = "remote_access_host"
|
CONF_REMOTE_ACCESS_HOST: Final = "remote_access_host"
|
||||||
CONF_REMOTE_ACCESS_PORT: Final = "remote_access_port"
|
CONF_REMOTE_ACCESS_PORT: Final = "remote_access_port"
|
||||||
|
CONF_WHITE_CHANNEL_TYPE: Final = "white_channel_type"
|
||||||
|
|
||||||
|
|
||||||
TRANSITION_GRADUAL: Final = "gradual"
|
TRANSITION_GRADUAL: Final = "gradual"
|
||||||
TRANSITION_JUMP: Final = "jump"
|
TRANSITION_JUMP: Final = "jump"
|
||||||
|
|
|
@ -7,12 +7,7 @@ from typing import Any, Final
|
||||||
|
|
||||||
from flux_led.const import MultiColorEffects
|
from flux_led.const import MultiColorEffects
|
||||||
from flux_led.protocol import MusicMode
|
from flux_led.protocol import MusicMode
|
||||||
from flux_led.utils import (
|
from flux_led.utils import rgbcw_brightness, rgbcw_to_rgbwc, rgbw_brightness
|
||||||
color_temp_to_white_levels,
|
|
||||||
rgbcw_brightness,
|
|
||||||
rgbcw_to_rgbwc,
|
|
||||||
rgbw_brightness,
|
|
||||||
)
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
@ -24,7 +19,6 @@ from homeassistant.components.light import (
|
||||||
ATTR_RGBW_COLOR,
|
ATTR_RGBW_COLOR,
|
||||||
ATTR_RGBWW_COLOR,
|
ATTR_RGBWW_COLOR,
|
||||||
ATTR_WHITE,
|
ATTR_WHITE,
|
||||||
COLOR_MODE_RGBWW,
|
|
||||||
SUPPORT_EFFECT,
|
SUPPORT_EFFECT,
|
||||||
SUPPORT_TRANSITION,
|
SUPPORT_TRANSITION,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
|
@ -50,6 +44,7 @@ from .const import (
|
||||||
CONF_TRANSITION,
|
CONF_TRANSITION,
|
||||||
DEFAULT_EFFECT_SPEED,
|
DEFAULT_EFFECT_SPEED,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
MULTI_BRIGHTNESS_COLOR_MODES,
|
||||||
TRANSITION_GRADUAL,
|
TRANSITION_GRADUAL,
|
||||||
TRANSITION_JUMP,
|
TRANSITION_JUMP,
|
||||||
TRANSITION_STROBE,
|
TRANSITION_STROBE,
|
||||||
|
@ -203,9 +198,7 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity):
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the light."""
|
"""Initialize the light."""
|
||||||
super().__init__(coordinator, unique_id, name, None)
|
super().__init__(coordinator, unique_id, name, None)
|
||||||
self._attr_min_mireds = (
|
self._attr_min_mireds = color_temperature_kelvin_to_mired(self._device.max_temp)
|
||||||
color_temperature_kelvin_to_mired(self._device.max_temp) + 1
|
|
||||||
) # for rounding
|
|
||||||
self._attr_max_mireds = color_temperature_kelvin_to_mired(self._device.min_temp)
|
self._attr_max_mireds = color_temperature_kelvin_to_mired(self._device.min_temp)
|
||||||
self._attr_supported_color_modes = _hass_color_modes(self._device)
|
self._attr_supported_color_modes = _hass_color_modes(self._device)
|
||||||
custom_effects: list[str] = []
|
custom_effects: list[str] = []
|
||||||
|
@ -306,20 +299,14 @@ class FluxLight(FluxOnOffEntity, CoordinatorEntity, LightEntity):
|
||||||
# Handle switch to CCT Color Mode
|
# Handle switch to CCT Color Mode
|
||||||
if color_temp_mired := kwargs.get(ATTR_COLOR_TEMP):
|
if color_temp_mired := kwargs.get(ATTR_COLOR_TEMP):
|
||||||
color_temp_kelvin = color_temperature_mired_to_kelvin(color_temp_mired)
|
color_temp_kelvin = color_temperature_mired_to_kelvin(color_temp_mired)
|
||||||
if self.color_mode != COLOR_MODE_RGBWW:
|
if (
|
||||||
await self._device.async_set_white_temp(color_temp_kelvin, brightness)
|
ATTR_BRIGHTNESS not in kwargs
|
||||||
return
|
and self.color_mode in MULTI_BRIGHTNESS_COLOR_MODES
|
||||||
|
):
|
||||||
# When switching to color temp from RGBWW mode,
|
# When switching to color temp from RGBWW or RGB&W mode,
|
||||||
# we do not want the overall brightness, we only
|
# we do not want the overall brightness of the RGB channels
|
||||||
# want the brightness of the white channels
|
brightness = max(self._device.rgb)
|
||||||
brightness = kwargs.get(
|
await self._device.async_set_white_temp(color_temp_kelvin, brightness)
|
||||||
ATTR_BRIGHTNESS, self._device.getWhiteTemperature()[1]
|
|
||||||
)
|
|
||||||
channels = color_temp_to_white_levels(color_temp_kelvin, brightness)
|
|
||||||
warm = channels.warm_white
|
|
||||||
cold = channels.cool_white
|
|
||||||
await self._device.async_set_levels(r=0, b=0, g=0, w=warm, w2=cold)
|
|
||||||
return
|
return
|
||||||
# Handle switch to RGB Color Mode
|
# Handle switch to RGB Color Mode
|
||||||
if rgb := kwargs.get(ATTR_RGB_COLOR):
|
if rgb := kwargs.get(ATTR_RGB_COLOR):
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"dependencies": ["network"],
|
"dependencies": ["network"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/flux_led",
|
"documentation": "https://www.home-assistant.io/integrations/flux_led",
|
||||||
"requirements": ["flux_led==0.27.45"],
|
"requirements": ["flux_led==0.28.1"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"codeowners": ["@icemanch", "@bdraco"],
|
"codeowners": ["@icemanch", "@bdraco"],
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from flux_led.aio import AIOWifiLedBulb
|
from flux_led.aio import AIOWifiLedBulb
|
||||||
from flux_led.base_device import DeviceType
|
from flux_led.base_device import DeviceType
|
||||||
|
from flux_led.const import DEFAULT_WHITE_CHANNEL_TYPE, WhiteChannelType
|
||||||
from flux_led.protocol import PowerRestoreState, RemoteConfig
|
from flux_led.protocol import PowerRestoreState, RemoteConfig
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
@ -12,9 +13,14 @@ from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity import EntityCategory
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import CONF_WHITE_CHANNEL_TYPE, DOMAIN, FLUX_COLOR_MODE_RGBW
|
||||||
from .coordinator import FluxLedUpdateCoordinator
|
from .coordinator import FluxLedUpdateCoordinator
|
||||||
from .entity import FluxBaseEntity, FluxEntity
|
from .entity import FluxBaseEntity, FluxEntity
|
||||||
|
from .util import _human_readable_option
|
||||||
|
|
||||||
|
NAME_TO_POWER_RESTORE_STATE = {
|
||||||
|
_human_readable_option(option.name): option for option in PowerRestoreState
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
|
@ -31,6 +37,7 @@ async def async_setup_entry(
|
||||||
| FluxWiringsSelect
|
| FluxWiringsSelect
|
||||||
| FluxICTypeSelect
|
| FluxICTypeSelect
|
||||||
| FluxRemoteConfigSelect
|
| FluxRemoteConfigSelect
|
||||||
|
| FluxWhiteChannelSelect
|
||||||
] = []
|
] = []
|
||||||
name = entry.data[CONF_NAME]
|
name = entry.data[CONF_NAME]
|
||||||
unique_id = entry.unique_id
|
unique_id = entry.unique_id
|
||||||
|
@ -57,20 +64,30 @@ async def async_setup_entry(
|
||||||
coordinator, unique_id, f"{name} Remote Config", "remote_config"
|
coordinator, unique_id, f"{name} Remote Config", "remote_config"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
if FLUX_COLOR_MODE_RGBW in device.color_modes:
|
||||||
|
entities.append(FluxWhiteChannelSelect(coordinator.device, entry))
|
||||||
|
|
||||||
if entities:
|
if entities:
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
def _human_readable_option(const_option: str) -> str:
|
class FluxConfigAtStartSelect(FluxBaseEntity, SelectEntity):
|
||||||
return const_option.replace("_", " ").title()
|
"""Representation of a flux config entity that only updates at start or change."""
|
||||||
|
|
||||||
|
_attr_entity_category = EntityCategory.CONFIG
|
||||||
|
|
||||||
|
|
||||||
class FluxPowerStateSelect(FluxBaseEntity, SelectEntity):
|
class FluxConfigSelect(FluxEntity, SelectEntity):
|
||||||
|
"""Representation of a flux config entity that updates."""
|
||||||
|
|
||||||
|
_attr_entity_category = EntityCategory.CONFIG
|
||||||
|
|
||||||
|
|
||||||
|
class FluxPowerStateSelect(FluxConfigAtStartSelect, SelectEntity):
|
||||||
"""Representation of a Flux power restore state option."""
|
"""Representation of a Flux power restore state option."""
|
||||||
|
|
||||||
_attr_icon = "mdi:transmission-tower-off"
|
_attr_icon = "mdi:transmission-tower-off"
|
||||||
_attr_entity_category = EntityCategory.CONFIG
|
_attr_options = list(NAME_TO_POWER_RESTORE_STATE)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -82,10 +99,6 @@ class FluxPowerStateSelect(FluxBaseEntity, SelectEntity):
|
||||||
self._attr_name = f"{entry.data[CONF_NAME]} Power Restored"
|
self._attr_name = f"{entry.data[CONF_NAME]} Power Restored"
|
||||||
if entry.unique_id:
|
if entry.unique_id:
|
||||||
self._attr_unique_id = f"{entry.unique_id}_power_restored"
|
self._attr_unique_id = f"{entry.unique_id}_power_restored"
|
||||||
self._name_to_state = {
|
|
||||||
_human_readable_option(option.name): option for option in PowerRestoreState
|
|
||||||
}
|
|
||||||
self._attr_options = list(self._name_to_state)
|
|
||||||
self._async_set_current_option_from_device()
|
self._async_set_current_option_from_device()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -98,17 +111,13 @@ class FluxPowerStateSelect(FluxBaseEntity, SelectEntity):
|
||||||
|
|
||||||
async def async_select_option(self, option: str) -> None:
|
async def async_select_option(self, option: str) -> None:
|
||||||
"""Change the power state."""
|
"""Change the power state."""
|
||||||
await self._device.async_set_power_restore(channel1=self._name_to_state[option])
|
await self._device.async_set_power_restore(
|
||||||
|
channel1=NAME_TO_POWER_RESTORE_STATE[option]
|
||||||
|
)
|
||||||
self._async_set_current_option_from_device()
|
self._async_set_current_option_from_device()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
class FluxConfigSelect(FluxEntity, SelectEntity):
|
|
||||||
"""Representation of a flux config entity that updates."""
|
|
||||||
|
|
||||||
_attr_entity_category = EntityCategory.CONFIG
|
|
||||||
|
|
||||||
|
|
||||||
class FluxICTypeSelect(FluxConfigSelect):
|
class FluxICTypeSelect(FluxConfigSelect):
|
||||||
"""Representation of Flux ic type."""
|
"""Representation of Flux ic type."""
|
||||||
|
|
||||||
|
@ -202,3 +211,39 @@ class FluxRemoteConfigSelect(FluxConfigSelect):
|
||||||
"""Change the remote config setting."""
|
"""Change the remote config setting."""
|
||||||
remote_config: RemoteConfig = self._name_to_state[option]
|
remote_config: RemoteConfig = self._name_to_state[option]
|
||||||
await self._device.async_config_remotes(remote_config)
|
await self._device.async_config_remotes(remote_config)
|
||||||
|
|
||||||
|
|
||||||
|
class FluxWhiteChannelSelect(FluxConfigAtStartSelect):
|
||||||
|
"""Representation of Flux white channel."""
|
||||||
|
|
||||||
|
_attr_options = [_human_readable_option(option.name) for option in WhiteChannelType]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
device: AIOWifiLedBulb,
|
||||||
|
entry: config_entries.ConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the white channel select."""
|
||||||
|
super().__init__(device, entry)
|
||||||
|
self._attr_name = f"{entry.data[CONF_NAME]} White Channel"
|
||||||
|
if entry.unique_id:
|
||||||
|
self._attr_unique_id = f"{entry.unique_id}_white_channel"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_option(self) -> str | None:
|
||||||
|
"""Return the current white channel type."""
|
||||||
|
return _human_readable_option(
|
||||||
|
self.entry.data.get(
|
||||||
|
CONF_WHITE_CHANNEL_TYPE, DEFAULT_WHITE_CHANNEL_TYPE.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_select_option(self, option: str) -> None:
|
||||||
|
"""Change the white channel type."""
|
||||||
|
entry = self.entry
|
||||||
|
hass = self.hass
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
entry, data={**entry.data, CONF_WHITE_CHANNEL_TYPE: option.lower()}
|
||||||
|
)
|
||||||
|
# reload since we need to reinit the device
|
||||||
|
hass.async_create_task(hass.config_entries.async_reload(entry.entry_id))
|
||||||
|
|
|
@ -23,6 +23,10 @@ def format_as_flux_mac(mac: str | None) -> str | None:
|
||||||
return None if mac is None else mac.replace(":", "").upper()
|
return None if mac is None else mac.replace(":", "").upper()
|
||||||
|
|
||||||
|
|
||||||
|
def _human_readable_option(const_option: str) -> str:
|
||||||
|
return const_option.replace("_", " ").title()
|
||||||
|
|
||||||
|
|
||||||
def _flux_color_mode_to_hass(
|
def _flux_color_mode_to_hass(
|
||||||
flux_color_mode: str | None, flux_color_modes: set[str]
|
flux_color_mode: str | None, flux_color_modes: set[str]
|
||||||
) -> str:
|
) -> str:
|
||||||
|
|
|
@ -681,7 +681,7 @@ fjaraskupan==1.0.2
|
||||||
flipr-api==1.4.1
|
flipr-api==1.4.1
|
||||||
|
|
||||||
# homeassistant.components.flux_led
|
# homeassistant.components.flux_led
|
||||||
flux_led==0.27.45
|
flux_led==0.28.1
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
fnvhash==0.1.0
|
fnvhash==0.1.0
|
||||||
|
|
|
@ -424,7 +424,7 @@ fjaraskupan==1.0.2
|
||||||
flipr-api==1.4.1
|
flipr-api==1.4.1
|
||||||
|
|
||||||
# homeassistant.components.flux_led
|
# homeassistant.components.flux_led
|
||||||
flux_led==0.27.45
|
flux_led==0.28.1
|
||||||
|
|
||||||
# homeassistant.components.homekit
|
# homeassistant.components.homekit
|
||||||
fnvhash==0.1.0
|
fnvhash==0.1.0
|
||||||
|
|
|
@ -12,6 +12,7 @@ from flux_led.aio import AIOWifiLedBulb
|
||||||
from flux_led.const import (
|
from flux_led.const import (
|
||||||
COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT,
|
COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT,
|
||||||
COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB,
|
COLOR_MODE_RGB as FLUX_COLOR_MODE_RGB,
|
||||||
|
WhiteChannelType,
|
||||||
)
|
)
|
||||||
from flux_led.models_db import MODEL_MAP
|
from flux_led.models_db import MODEL_MAP
|
||||||
from flux_led.protocol import (
|
from flux_led.protocol import (
|
||||||
|
@ -105,6 +106,7 @@ def _mocked_bulb() -> AIOWifiLedBulb:
|
||||||
bulb.async_set_brightness = AsyncMock()
|
bulb.async_set_brightness = AsyncMock()
|
||||||
bulb.async_set_device_config = AsyncMock()
|
bulb.async_set_device_config = AsyncMock()
|
||||||
bulb.async_config_remotes = AsyncMock()
|
bulb.async_config_remotes = AsyncMock()
|
||||||
|
bulb.white_channel_channel_type = WhiteChannelType.WARM
|
||||||
bulb.paired_remotes = 2
|
bulb.paired_remotes = 2
|
||||||
bulb.pixels_per_segment = 300
|
bulb.pixels_per_segment = 300
|
||||||
bulb.segments = 2
|
bulb.segments = 2
|
||||||
|
|
|
@ -12,6 +12,7 @@ from flux_led.const import (
|
||||||
COLOR_MODES_RGB_W as FLUX_COLOR_MODES_RGB_W,
|
COLOR_MODES_RGB_W as FLUX_COLOR_MODES_RGB_W,
|
||||||
MODE_MUSIC,
|
MODE_MUSIC,
|
||||||
MultiColorEffects,
|
MultiColorEffects,
|
||||||
|
WhiteChannelType,
|
||||||
)
|
)
|
||||||
from flux_led.protocol import MusicMode
|
from flux_led.protocol import MusicMode
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -25,6 +26,7 @@ from homeassistant.components.flux_led.const import (
|
||||||
CONF_EFFECT,
|
CONF_EFFECT,
|
||||||
CONF_SPEED_PCT,
|
CONF_SPEED_PCT,
|
||||||
CONF_TRANSITION,
|
CONF_TRANSITION,
|
||||||
|
CONF_WHITE_CHANNEL_TYPE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
TRANSITION_JUMP,
|
TRANSITION_JUMP,
|
||||||
)
|
)
|
||||||
|
@ -520,11 +522,15 @@ async def test_rgb_cct_light(hass: HomeAssistant) -> None:
|
||||||
bulb.async_set_brightness.reset_mock()
|
bulb.async_set_brightness.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
async def test_rgbw_light(hass: HomeAssistant) -> None:
|
async def test_rgbw_light_cold_white(hass: HomeAssistant) -> None:
|
||||||
"""Test an rgbw light."""
|
"""Test an rgbw light with a cold white channel."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE},
|
data={
|
||||||
|
CONF_HOST: IP_ADDRESS,
|
||||||
|
CONF_NAME: DEFAULT_ENTRY_TITLE,
|
||||||
|
CONF_WHITE_CHANNEL_TYPE: WhiteChannelType.COLD.name.lower(),
|
||||||
|
},
|
||||||
unique_id=MAC_ADDRESS,
|
unique_id=MAC_ADDRESS,
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
@ -622,6 +628,148 @@ async def test_rgbw_light(hass: HomeAssistant) -> None:
|
||||||
bulb.async_set_effect.reset_mock()
|
bulb.async_set_effect.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_rgbw_light_warm_white(hass: HomeAssistant) -> None:
|
||||||
|
"""Test an rgbw light with a warm white channel."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
CONF_HOST: IP_ADDRESS,
|
||||||
|
CONF_NAME: DEFAULT_ENTRY_TITLE,
|
||||||
|
CONF_WHITE_CHANNEL_TYPE: WhiteChannelType.WARM.name.lower(),
|
||||||
|
},
|
||||||
|
unique_id=MAC_ADDRESS,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
bulb = _mocked_bulb()
|
||||||
|
bulb.color_modes = {FLUX_COLOR_MODE_RGBW, FLUX_COLOR_MODE_CCT}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGBW
|
||||||
|
with _patch_discovery(), _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_BRIGHTNESS] == 128
|
||||||
|
assert attributes[ATTR_COLOR_MODE] == "rgbw"
|
||||||
|
assert attributes[ATTR_EFFECT_LIST] == bulb.effect_list
|
||||||
|
assert attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "rgbw"]
|
||||||
|
assert attributes[ATTR_RGB_COLOR] == (255, 42, 42)
|
||||||
|
|
||||||
|
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()
|
||||||
|
bulb.is_on = True
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: 100},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_brightness.assert_called_with(100)
|
||||||
|
bulb.async_set_brightness.reset_mock()
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: entity_id,
|
||||||
|
ATTR_RGBW_COLOR: (255, 255, 255, 255),
|
||||||
|
ATTR_BRIGHTNESS: 128,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_levels.assert_called_with(128, 128, 128, 128)
|
||||||
|
bulb.async_set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_RGBW_COLOR: (255, 255, 255, 255)},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_levels.assert_called_with(255, 255, 255, 255)
|
||||||
|
bulb.async_set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_RGBW_COLOR: (255, 191, 178, 0)},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_levels.assert_called_with(255, 191, 178, 0)
|
||||||
|
bulb.async_set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_white_temp.assert_called_with(6493, 255)
|
||||||
|
bulb.async_set_white_temp.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154, ATTR_BRIGHTNESS: 255},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_white_temp.assert_called_with(6493, 255)
|
||||||
|
bulb.async_set_white_temp.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 290},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_white_temp.assert_called_with(3448, 255)
|
||||||
|
bulb.async_set_white_temp.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_RGBW_COLOR: (255, 191, 178, 0)},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_levels.assert_called_with(255, 191, 178, 0)
|
||||||
|
bulb.async_set_levels.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "random"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_effect.assert_called_once()
|
||||||
|
bulb.async_set_effect.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "purple_fade", ATTR_BRIGHTNESS: 255},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
bulb.async_set_effect.assert_called_with("purple_fade", 50, 100)
|
||||||
|
bulb.async_set_effect.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
async def test_rgb_or_w_light(hass: HomeAssistant) -> None:
|
async def test_rgb_or_w_light(hass: HomeAssistant) -> None:
|
||||||
"""Test an rgb or w light."""
|
"""Test an rgb or w light."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
|
@ -811,8 +959,8 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154},
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.async_set_levels.assert_called_with(r=0, b=0, g=0, w=0, w2=127)
|
bulb.async_set_white_temp.assert_called_with(6493, 255)
|
||||||
bulb.async_set_levels.reset_mock()
|
bulb.async_set_white_temp.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -820,8 +968,8 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154, ATTR_BRIGHTNESS: 255},
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 154, ATTR_BRIGHTNESS: 255},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.async_set_levels.assert_called_with(r=0, b=0, g=0, w=0, w2=255)
|
bulb.async_set_white_temp.assert_called_with(6493, 255)
|
||||||
bulb.async_set_levels.reset_mock()
|
bulb.async_set_white_temp.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
@ -829,8 +977,8 @@ async def test_rgbcw_light(hass: HomeAssistant) -> None:
|
||||||
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 290},
|
{ATTR_ENTITY_ID: entity_id, ATTR_COLOR_TEMP: 290},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
bulb.async_set_levels.assert_called_with(r=0, b=0, g=0, w=102, w2=25)
|
bulb.async_set_white_temp.assert_called_with(3448, 255)
|
||||||
bulb.async_set_levels.reset_mock()
|
bulb.async_set_white_temp.reset_mock()
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
"""Tests for select platform."""
|
"""Tests for select platform."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from flux_led.const import (
|
||||||
|
COLOR_MODE_CCT as FLUX_COLOR_MODE_CCT,
|
||||||
|
COLOR_MODE_RGBW as FLUX_COLOR_MODE_RGBW,
|
||||||
|
WhiteChannelType,
|
||||||
|
)
|
||||||
from flux_led.protocol import PowerRestoreState, RemoteConfig
|
from flux_led.protocol import PowerRestoreState, RemoteConfig
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import flux_led
|
from homeassistant.components import flux_led
|
||||||
from homeassistant.components.flux_led.const import DOMAIN
|
from homeassistant.components.flux_led.const import CONF_WHITE_CHANNEL_TYPE, DOMAIN
|
||||||
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
|
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION, CONF_HOST, CONF_NAME
|
from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION, CONF_HOST, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
@ -191,3 +196,51 @@ async def test_select_24ghz_remote_config(hass: HomeAssistant) -> None:
|
||||||
)
|
)
|
||||||
bulb.async_config_remotes.assert_called_once_with(RemoteConfig.PAIRED_ONLY)
|
bulb.async_config_remotes.assert_called_once_with(RemoteConfig.PAIRED_ONLY)
|
||||||
bulb.async_config_remotes.reset_mock()
|
bulb.async_config_remotes.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_select_white_channel_type(hass: HomeAssistant) -> None:
|
||||||
|
"""Test selecting the white channel type."""
|
||||||
|
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.color_modes = {FLUX_COLOR_MODE_RGBW, FLUX_COLOR_MODE_CCT}
|
||||||
|
bulb.color_mode = FLUX_COLOR_MODE_RGBW
|
||||||
|
bulb.raw_state = bulb.raw_state._replace(model_num=0x06) # rgbw
|
||||||
|
with _patch_discovery(), _patch_wifibulb(device=bulb):
|
||||||
|
await async_setup_component(hass, flux_led.DOMAIN, {flux_led.DOMAIN: {}})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
operating_mode_entity_id = "select.bulb_rgbcw_ddeeff_white_channel"
|
||||||
|
state = hass.states.get(operating_mode_entity_id)
|
||||||
|
assert state.state == WhiteChannelType.WARM.name.title()
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
await hass.services.async_call(
|
||||||
|
SELECT_DOMAIN,
|
||||||
|
"select_option",
|
||||||
|
{ATTR_ENTITY_ID: operating_mode_entity_id, ATTR_OPTION: "INVALID"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.flux_led.async_setup_entry"
|
||||||
|
) as mock_setup_entry:
|
||||||
|
await hass.services.async_call(
|
||||||
|
SELECT_DOMAIN,
|
||||||
|
"select_option",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: operating_mode_entity_id,
|
||||||
|
ATTR_OPTION: WhiteChannelType.NATURAL.name.title(),
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert (
|
||||||
|
config_entry.data[CONF_WHITE_CHANNEL_TYPE]
|
||||||
|
== WhiteChannelType.NATURAL.name.lower()
|
||||||
|
)
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue