Update wled to 0.19.2 (#122101)

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Franck Nijhof 2024-07-18 10:09:12 +02:00 committed by GitHub
parent b06d3fe3b7
commit 41d75e159b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 1694 additions and 1095 deletions

View File

@ -5,9 +5,12 @@ from __future__ import annotations
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.hass_dict import HassKey
from .const import LOGGER from .const import DOMAIN
from .coordinator import WLEDDataUpdateCoordinator from .coordinator import WLEDDataUpdateCoordinator, WLEDReleasesDataUpdateCoordinator
PLATFORMS = ( PLATFORMS = (
Platform.BUTTON, Platform.BUTTON,
@ -21,23 +24,26 @@ PLATFORMS = (
type WLEDConfigEntry = ConfigEntry[WLEDDataUpdateCoordinator] type WLEDConfigEntry = ConfigEntry[WLEDDataUpdateCoordinator]
WLED_KEY: HassKey[WLEDReleasesDataUpdateCoordinator] = HassKey(DOMAIN)
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the WLED integration.
We set up a single coordinator for fetching WLED releases, which
is used across all WLED devices (and config entries) to avoid
fetching the same data multiple times for each.
"""
hass.data[WLED_KEY] = WLEDReleasesDataUpdateCoordinator(hass)
await hass.data[WLED_KEY].async_request_refresh()
return True
async def async_setup_entry(hass: HomeAssistant, entry: WLEDConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: WLEDConfigEntry) -> bool:
"""Set up WLED from a config entry.""" """Set up WLED from a config entry."""
coordinator = WLEDDataUpdateCoordinator(hass, entry=entry) entry.runtime_data = WLEDDataUpdateCoordinator(hass, entry=entry)
await coordinator.async_config_entry_first_refresh() await entry.runtime_data.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
entry.runtime_data = coordinator
# Set up all platforms for this device/entry. # Set up all platforms for this device/entry.
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -46,8 +46,6 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN):
except WLEDConnectionError: except WLEDConnectionError:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
else: else:
if device.info.leds.cct:
return self.async_abort(reason="cct_unsupported")
await self.async_set_unique_id(device.info.mac_address) await self.async_set_unique_id(device.info.mac_address)
self._abort_if_unique_id_configured( self._abort_if_unique_id_configured(
updates={CONF_HOST: user_input[CONF_HOST]} updates={CONF_HOST: user_input[CONF_HOST]}
@ -84,9 +82,6 @@ class WLEDFlowHandler(ConfigFlow, domain=DOMAIN):
except WLEDConnectionError: except WLEDConnectionError:
return self.async_abort(reason="cannot_connect") 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) await self.async_set_unique_id(self.discovered_device.info.mac_address)
self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.host}) self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.host})

View File

@ -3,11 +3,16 @@
from datetime import timedelta from datetime import timedelta
import logging import logging
from wled import LightCapability
from homeassistant.components.light import ColorMode
# Integration domain # Integration domain
DOMAIN = "wled" DOMAIN = "wled"
LOGGER = logging.getLogger(__package__) LOGGER = logging.getLogger(__package__)
SCAN_INTERVAL = timedelta(seconds=10) SCAN_INTERVAL = timedelta(seconds=10)
RELEASES_SCAN_INTERVAL = timedelta(hours=3)
# Options # Options
CONF_KEEP_MAIN_LIGHT = "keep_master_light" CONF_KEEP_MAIN_LIGHT = "keep_master_light"
@ -24,3 +29,72 @@ ATTR_SOFTWARE_VERSION = "sw_version"
ATTR_SPEED = "speed" ATTR_SPEED = "speed"
ATTR_TARGET_BRIGHTNESS = "target_brightness" ATTR_TARGET_BRIGHTNESS = "target_brightness"
ATTR_UDP_PORT = "udp_port" ATTR_UDP_PORT = "udp_port"
LIGHT_CAPABILITIES_COLOR_MODE_MAPPING: dict[LightCapability, list[ColorMode]] = {
LightCapability.NONE: [
ColorMode.ONOFF,
],
LightCapability.RGB_COLOR: [
ColorMode.RGB,
],
LightCapability.WHITE_CHANNEL: [
ColorMode.BRIGHTNESS,
],
LightCapability.RGB_COLOR | LightCapability.WHITE_CHANNEL: [
ColorMode.RGBW,
],
LightCapability.COLOR_TEMPERATURE: [
ColorMode.COLOR_TEMP,
],
LightCapability.RGB_COLOR | LightCapability.COLOR_TEMPERATURE: [
ColorMode.RGBWW,
],
LightCapability.WHITE_CHANNEL | LightCapability.COLOR_TEMPERATURE: [
ColorMode.COLOR_TEMP,
],
LightCapability.RGB_COLOR
| LightCapability.WHITE_CHANNEL
| LightCapability.COLOR_TEMPERATURE: [
ColorMode.RGB,
ColorMode.COLOR_TEMP,
],
LightCapability.MANUAL_WHITE: [
ColorMode.BRIGHTNESS,
],
LightCapability.RGB_COLOR | LightCapability.MANUAL_WHITE: [
ColorMode.RGBW,
],
LightCapability.WHITE_CHANNEL | LightCapability.MANUAL_WHITE: [
ColorMode.BRIGHTNESS,
],
LightCapability.RGB_COLOR
| LightCapability.WHITE_CHANNEL
| LightCapability.MANUAL_WHITE: [
ColorMode.RGBW,
ColorMode.WHITE,
],
LightCapability.COLOR_TEMPERATURE | LightCapability.MANUAL_WHITE: [
ColorMode.COLOR_TEMP,
ColorMode.WHITE,
],
LightCapability.RGB_COLOR
| LightCapability.COLOR_TEMPERATURE
| LightCapability.MANUAL_WHITE: [
ColorMode.RGBW,
ColorMode.COLOR_TEMP,
],
LightCapability.WHITE_CHANNEL
| LightCapability.COLOR_TEMPERATURE
| LightCapability.MANUAL_WHITE: [
ColorMode.COLOR_TEMP,
ColorMode.WHITE,
],
LightCapability.RGB_COLOR
| LightCapability.WHITE_CHANNEL
| LightCapability.COLOR_TEMPERATURE
| LightCapability.MANUAL_WHITE: [
ColorMode.RGBW,
ColorMode.COLOR_TEMP,
],
}

View File

@ -2,7 +2,14 @@
from __future__ import annotations from __future__ import annotations
from wled import WLED, Device as WLEDDevice, WLEDConnectionClosedError, WLEDError from wled import (
WLED,
Device as WLEDDevice,
Releases,
WLEDConnectionClosedError,
WLEDError,
WLEDReleases,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP
@ -15,6 +22,7 @@ from .const import (
DEFAULT_KEEP_MAIN_LIGHT, DEFAULT_KEEP_MAIN_LIGHT,
DOMAIN, DOMAIN,
LOGGER, LOGGER,
RELEASES_SCAN_INTERVAL,
SCAN_INTERVAL, SCAN_INTERVAL,
) )
@ -101,17 +109,37 @@ class WLEDDataUpdateCoordinator(DataUpdateCoordinator[WLEDDevice]):
async def _async_update_data(self) -> WLEDDevice: async def _async_update_data(self) -> WLEDDevice:
"""Fetch data from WLED.""" """Fetch data from WLED."""
try: try:
device = await self.wled.update(full_update=not self.last_update_success) device = await self.wled.update()
except WLEDError as error: except WLEDError as error:
raise UpdateFailed(f"Invalid response from API: {error}") from error raise UpdateFailed(f"Invalid response from API: {error}") from error
# If the device supports a WebSocket, try activating it. # If the device supports a WebSocket, try activating it.
if ( if (
device.info.websocket is not None device.info.websocket is not None
and device.info.leds.cct is not True
and not self.wled.connected and not self.wled.connected
and not self.unsub and not self.unsub
): ):
self._use_websocket() self._use_websocket()
return device return device
class WLEDReleasesDataUpdateCoordinator(DataUpdateCoordinator[Releases]):
"""Class to manage fetching WLED releases."""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize global WLED releases updater."""
self.wled = WLEDReleases(session=async_get_clientsession(hass))
super().__init__(
hass,
LOGGER,
name=DOMAIN,
update_interval=RELEASES_SCAN_INTERVAL,
)
async def _async_update_data(self) -> Releases:
"""Fetch release data from WLED."""
try:
return await self.wled.releases()
except WLEDError as error:
raise UpdateFailed(f"Invalid response from GitHub API: {error}") from error

View File

@ -17,31 +17,23 @@ async def async_get_config_entry_diagnostics(
coordinator = entry.runtime_data coordinator = entry.runtime_data
data: dict[str, Any] = { data: dict[str, Any] = {
"info": async_redact_data(coordinator.data.info.__dict__, "wifi"), "info": async_redact_data(coordinator.data.info.to_dict(), "wifi"),
"state": coordinator.data.state.__dict__, "state": coordinator.data.state.to_dict(),
"effects": { "effects": {
effect.effect_id: effect.name for effect in coordinator.data.effects effect.effect_id: effect.name
for effect in coordinator.data.effects.values()
}, },
"palettes": { "palettes": {
palette.palette_id: palette.name for palette in coordinator.data.palettes palette.palette_id: palette.name
for palette in coordinator.data.palettes.values()
}, },
"playlists": { "playlists": {
playlist.playlist_id: { playlist.playlist_id: playlist.name
"name": playlist.name, for playlist in coordinator.data.playlists.values()
"repeat": playlist.repeat,
"shuffle": playlist.shuffle,
"end": playlist.end.preset_id if playlist.end else None,
}
for playlist in coordinator.data.playlists
}, },
"presets": { "presets": {
preset.preset_id: { preset.preset_id: preset.name
"name": preset.name, for preset in coordinator.data.presets.values()
"quick_label": preset.quick_label,
"on": preset.on,
"transition": preset.transition,
}
for preset in coordinator.data.presets
}, },
} }
return data return data

View File

@ -19,7 +19,12 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import WLEDConfigEntry from . import WLEDConfigEntry
from .const import ATTR_COLOR_PRIMARY, ATTR_ON, ATTR_SEGMENT_ID from .const import (
ATTR_COLOR_PRIMARY,
ATTR_ON,
ATTR_SEGMENT_ID,
LIGHT_CAPABILITIES_COLOR_MODE_MAPPING,
)
from .coordinator import WLEDDataUpdateCoordinator from .coordinator import WLEDDataUpdateCoordinator
from .entity import WLEDEntity from .entity import WLEDEntity
from .helpers import wled_exception_handler from .helpers import wled_exception_handler
@ -112,8 +117,6 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
) -> None: ) -> None:
"""Initialize WLED segment light.""" """Initialize WLED segment light."""
super().__init__(coordinator=coordinator) super().__init__(coordinator=coordinator)
self._rgbw = coordinator.data.info.leds.rgbw
self._wv = coordinator.data.info.leds.wv
self._segment = segment self._segment = segment
# Segment 0 uses a simpler name, which is more natural for when using # Segment 0 uses a simpler name, which is more natural for when using
@ -127,18 +130,24 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
f"{self.coordinator.data.info.mac_address}_{self._segment}" f"{self.coordinator.data.info.mac_address}_{self._segment}"
) )
self._attr_color_mode = ColorMode.RGB if (
self._attr_supported_color_modes = {ColorMode.RGB} coordinator.data.info.leds.segment_light_capabilities is not None
if self._rgbw and self._wv: and (
self._attr_color_mode = ColorMode.RGBW color_modes := LIGHT_CAPABILITIES_COLOR_MODE_MAPPING.get(
self._attr_supported_color_modes = {ColorMode.RGBW} coordinator.data.info.leds.segment_light_capabilities[segment]
)
)
is not None
):
self._attr_color_mode = color_modes[0]
self._attr_supported_color_modes = set(color_modes)
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Return True if entity is available.""" """Return True if entity is available."""
try: try:
self.coordinator.data.state.segments[self._segment] self.coordinator.data.state.segments[self._segment]
except IndexError: except KeyError:
return False return False
return super().available return super().available
@ -146,20 +155,23 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
@property @property
def rgb_color(self) -> tuple[int, int, int] | None: def rgb_color(self) -> tuple[int, int, int] | None:
"""Return the color value.""" """Return the color value."""
return self.coordinator.data.state.segments[self._segment].color_primary[:3] if not (color := self.coordinator.data.state.segments[self._segment].color):
return None
return color.primary[:3]
@property @property
def rgbw_color(self) -> tuple[int, int, int, int] | None: def rgbw_color(self) -> tuple[int, int, int, int] | None:
"""Return the color value.""" """Return the color value."""
return cast( if not (color := self.coordinator.data.state.segments[self._segment].color):
tuple[int, int, int, int], return None
self.coordinator.data.state.segments[self._segment].color_primary, return cast(tuple[int, int, int, int], color.primary)
)
@property @property
def effect(self) -> str | None: def effect(self) -> str | None:
"""Return the current effect of the light.""" """Return the current effect of the light."""
return self.coordinator.data.state.segments[self._segment].effect.name return self.coordinator.data.effects[
int(self.coordinator.data.state.segments[self._segment].effect_id)
].name
@property @property
def brightness(self) -> int | None: def brightness(self) -> int | None:
@ -178,7 +190,7 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
@property @property
def effect_list(self) -> list[str]: def effect_list(self) -> list[str]:
"""Return the list of supported effects.""" """Return the list of supported effects."""
return [effect.name for effect in self.coordinator.data.effects] return [effect.name for effect in self.coordinator.data.effects.values()]
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
@ -258,7 +270,11 @@ def async_update_segments(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Update segments.""" """Update segments."""
segment_ids = {light.segment_id for light in coordinator.data.state.segments} segment_ids = {
light.segment_id
for light in coordinator.data.state.segments.values()
if light.segment_id is not None
}
new_entities: list[WLEDMainLight | WLEDSegmentLight] = [] new_entities: list[WLEDMainLight | WLEDSegmentLight] = []
# More than 1 segment now? No main? Add main controls # More than 1 segment now? No main? Add main controls

View File

@ -7,6 +7,6 @@
"integration_type": "device", "integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": ["wled==0.18.0"], "requirements": ["wled==0.19.2"],
"zeroconf": ["_wled._tcp.local."] "zeroconf": ["_wled._tcp.local."]
} }

View File

@ -44,7 +44,7 @@ async def async_setup_entry(
class WLEDNumberEntityDescription(NumberEntityDescription): class WLEDNumberEntityDescription(NumberEntityDescription):
"""Class describing WLED number entities.""" """Class describing WLED number entities."""
value_fn: Callable[[Segment], float | None] value_fn: Callable[[Segment], int | None]
NUMBERS = [ NUMBERS = [
@ -64,7 +64,7 @@ NUMBERS = [
native_step=1, native_step=1,
native_min_value=0, native_min_value=0,
native_max_value=255, native_max_value=255,
value_fn=lambda segment: segment.intensity, value_fn=lambda segment: int(segment.intensity),
), ),
] ]
@ -100,7 +100,7 @@ class WLEDNumber(WLEDEntity, NumberEntity):
"""Return True if entity is available.""" """Return True if entity is available."""
try: try:
self.coordinator.data.state.segments[self._segment] self.coordinator.data.state.segments[self._segment]
except IndexError: except KeyError:
return False return False
return super().available return super().available
@ -133,7 +133,11 @@ def async_update_segments(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Update segments.""" """Update segments."""
segment_ids = {segment.segment_id for segment in coordinator.data.state.segments} segment_ids = {
segment.segment_id
for segment in coordinator.data.state.segments.values()
if segment.segment_id is not None
}
new_entities: list[WLEDNumber] = [] new_entities: list[WLEDNumber] = []

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from functools import partial from functools import partial
from wled import Live, Playlist, Preset from wled import LiveDataOverride
from homeassistant.components.select import SelectEntity from homeassistant.components.select import SelectEntity
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
@ -56,17 +56,17 @@ class WLEDLiveOverrideSelect(WLEDEntity, SelectEntity):
super().__init__(coordinator=coordinator) super().__init__(coordinator=coordinator)
self._attr_unique_id = f"{coordinator.data.info.mac_address}_live_override" self._attr_unique_id = f"{coordinator.data.info.mac_address}_live_override"
self._attr_options = [str(live.value) for live in Live] self._attr_options = [str(live.value) for live in LiveDataOverride]
@property @property
def current_option(self) -> str: def current_option(self) -> str:
"""Return the current selected live override.""" """Return the current selected live override."""
return str(self.coordinator.data.state.lor.value) return str(self.coordinator.data.state.live_data_override.value)
@wled_exception_handler @wled_exception_handler
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
"""Set WLED state to the selected live override state.""" """Set WLED state to the selected live override state."""
await self.coordinator.wled.live(live=Live(int(option))) await self.coordinator.wled.live(live=LiveDataOverride(int(option)))
class WLEDPresetSelect(WLEDEntity, SelectEntity): class WLEDPresetSelect(WLEDEntity, SelectEntity):
@ -79,7 +79,9 @@ class WLEDPresetSelect(WLEDEntity, SelectEntity):
super().__init__(coordinator=coordinator) super().__init__(coordinator=coordinator)
self._attr_unique_id = f"{coordinator.data.info.mac_address}_preset" self._attr_unique_id = f"{coordinator.data.info.mac_address}_preset"
self._attr_options = [preset.name for preset in self.coordinator.data.presets] self._attr_options = [
preset.name for preset in self.coordinator.data.presets.values()
]
@property @property
def available(self) -> bool: def available(self) -> bool:
@ -89,9 +91,13 @@ class WLEDPresetSelect(WLEDEntity, SelectEntity):
@property @property
def current_option(self) -> str | None: def current_option(self) -> str | None:
"""Return the current selected preset.""" """Return the current selected preset."""
if not isinstance(self.coordinator.data.state.preset, Preset): if not self.coordinator.data.state.preset_id:
return None
if preset := self.coordinator.data.presets.get(
self.coordinator.data.state.preset_id
):
return preset.name
return None return None
return self.coordinator.data.state.preset.name
@wled_exception_handler @wled_exception_handler
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
@ -110,7 +116,7 @@ class WLEDPlaylistSelect(WLEDEntity, SelectEntity):
self._attr_unique_id = f"{coordinator.data.info.mac_address}_playlist" self._attr_unique_id = f"{coordinator.data.info.mac_address}_playlist"
self._attr_options = [ self._attr_options = [
playlist.name for playlist in self.coordinator.data.playlists playlist.name for playlist in self.coordinator.data.playlists.values()
] ]
@property @property
@ -121,9 +127,13 @@ class WLEDPlaylistSelect(WLEDEntity, SelectEntity):
@property @property
def current_option(self) -> str | None: def current_option(self) -> str | None:
"""Return the currently selected playlist.""" """Return the currently selected playlist."""
if not isinstance(self.coordinator.data.state.playlist, Playlist): if not self.coordinator.data.state.playlist_id:
return None
if playlist := self.coordinator.data.playlists.get(
self.coordinator.data.state.playlist_id
):
return playlist.name
return None return None
return self.coordinator.data.state.playlist.name
@wled_exception_handler @wled_exception_handler
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
@ -150,7 +160,7 @@ class WLEDPaletteSelect(WLEDEntity, SelectEntity):
self._attr_unique_id = f"{coordinator.data.info.mac_address}_palette_{segment}" self._attr_unique_id = f"{coordinator.data.info.mac_address}_palette_{segment}"
self._attr_options = [ self._attr_options = [
palette.name for palette in self.coordinator.data.palettes palette.name for palette in self.coordinator.data.palettes.values()
] ]
self._segment = segment self._segment = segment
@ -159,7 +169,7 @@ class WLEDPaletteSelect(WLEDEntity, SelectEntity):
"""Return True if entity is available.""" """Return True if entity is available."""
try: try:
self.coordinator.data.state.segments[self._segment] self.coordinator.data.state.segments[self._segment]
except IndexError: except KeyError:
return False return False
return super().available return super().available
@ -167,7 +177,9 @@ class WLEDPaletteSelect(WLEDEntity, SelectEntity):
@property @property
def current_option(self) -> str | None: def current_option(self) -> str | None:
"""Return the current selected color palette.""" """Return the current selected color palette."""
return self.coordinator.data.state.segments[self._segment].palette.name return self.coordinator.data.palettes[
int(self.coordinator.data.state.segments[self._segment].palette_id)
].name
@wled_exception_handler @wled_exception_handler
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
@ -182,7 +194,11 @@ def async_update_segments(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Update segments.""" """Update segments."""
segment_ids = {segment.segment_id for segment in coordinator.data.state.segments} segment_ids = {
segment.segment_id
for segment in coordinator.data.state.segments.values()
if segment.segment_id is not None
}
new_entities: list[WLEDPaletteSelect] = [] new_entities: list[WLEDPaletteSelect] = []

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime
from wled import Device as WLEDDevice from wled import Device as WLEDDevice
@ -71,7 +71,7 @@ SENSORS: tuple[WLEDSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.TIMESTAMP, device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
value_fn=lambda device: (utcnow() - timedelta(seconds=device.info.uptime)), value_fn=lambda device: (utcnow() - device.info.uptime),
), ),
WLEDSensorEntityDescription( WLEDSensorEntityDescription(
key="free_heap", key="free_heap",

View File

@ -21,8 +21,7 @@
}, },
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "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": { "options": {

View File

@ -11,7 +11,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import WLEDConfigEntry from . import WLEDConfigEntry
from .const import ATTR_DURATION, ATTR_FADE, ATTR_TARGET_BRIGHTNESS, ATTR_UDP_PORT from .const import ATTR_DURATION, ATTR_TARGET_BRIGHTNESS, ATTR_UDP_PORT
from .coordinator import WLEDDataUpdateCoordinator from .coordinator import WLEDDataUpdateCoordinator
from .entity import WLEDEntity from .entity import WLEDEntity
from .helpers import wled_exception_handler from .helpers import wled_exception_handler
@ -62,7 +62,6 @@ class WLEDNightlightSwitch(WLEDEntity, SwitchEntity):
state = self.coordinator.data.state state = self.coordinator.data.state
return { return {
ATTR_DURATION: state.nightlight.duration, ATTR_DURATION: state.nightlight.duration,
ATTR_FADE: state.nightlight.fade,
ATTR_TARGET_BRIGHTNESS: state.nightlight.target_brightness, ATTR_TARGET_BRIGHTNESS: state.nightlight.target_brightness,
} }
@ -171,7 +170,7 @@ class WLEDReverseSwitch(WLEDEntity, SwitchEntity):
"""Return True if entity is available.""" """Return True if entity is available."""
try: try:
self.coordinator.data.state.segments[self._segment] self.coordinator.data.state.segments[self._segment]
except IndexError: except KeyError:
return False return False
return super().available return super().available
@ -199,7 +198,11 @@ def async_update_segments(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Update segments.""" """Update segments."""
segment_ids = {segment.segment_id for segment in coordinator.data.state.segments} segment_ids = {
segment.segment_id
for segment in coordinator.data.state.segments.values()
if segment.segment_id is not None
}
new_entities: list[WLEDReverseSwitch] = [] new_entities: list[WLEDReverseSwitch] = []

View File

@ -12,8 +12,8 @@ from homeassistant.components.update import (
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import WLEDConfigEntry from . import WLED_KEY, WLEDConfigEntry
from .coordinator import WLEDDataUpdateCoordinator from .coordinator import WLEDDataUpdateCoordinator, WLEDReleasesDataUpdateCoordinator
from .entity import WLEDEntity from .entity import WLEDEntity
from .helpers import wled_exception_handler from .helpers import wled_exception_handler
@ -24,7 +24,7 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up WLED update based on a config entry.""" """Set up WLED update based on a config entry."""
async_add_entities([WLEDUpdateEntity(entry.runtime_data)]) async_add_entities([WLEDUpdateEntity(entry.runtime_data, hass.data[WLED_KEY])])
class WLEDUpdateEntity(WLEDEntity, UpdateEntity): class WLEDUpdateEntity(WLEDEntity, UpdateEntity):
@ -36,11 +36,33 @@ class WLEDUpdateEntity(WLEDEntity, UpdateEntity):
) )
_attr_title = "WLED" _attr_title = "WLED"
def __init__(self, coordinator: WLEDDataUpdateCoordinator) -> None: def __init__(
self,
coordinator: WLEDDataUpdateCoordinator,
releases_coordinator: WLEDReleasesDataUpdateCoordinator,
) -> None:
"""Initialize the update entity.""" """Initialize the update entity."""
super().__init__(coordinator=coordinator) super().__init__(coordinator=coordinator)
self.releases_coordinator = releases_coordinator
self._attr_unique_id = coordinator.data.info.mac_address self._attr_unique_id = coordinator.data.info.mac_address
async def async_added_to_hass(self) -> None:
"""When entity is added to hass.
Register extra update listener for the releases coordinator.
"""
await super().async_added_to_hass()
self.async_on_remove(
self.releases_coordinator.async_add_listener(
self._handle_coordinator_update
)
)
@property
def available(self) -> bool:
"""Return if entity is available."""
return super().available and self.releases_coordinator.last_update_success
@property @property
def installed_version(self) -> str | None: def installed_version(self) -> str | None:
"""Version currently installed and in use.""" """Version currently installed and in use."""
@ -54,17 +76,17 @@ class WLEDUpdateEntity(WLEDEntity, UpdateEntity):
# If we already run a pre-release, we consider being on the beta channel. # If we already run a pre-release, we consider being on the beta channel.
# Offer beta version upgrade, unless stable is newer # Offer beta version upgrade, unless stable is newer
if ( if (
(beta := self.coordinator.data.info.version_latest_beta) is not None (beta := self.releases_coordinator.data.beta) is not None
and (current := self.coordinator.data.info.version) is not None and (current := self.coordinator.data.info.version) is not None
and (current.alpha or current.beta or current.release_candidate) and (current.alpha or current.beta or current.release_candidate)
and ( and (
(stable := self.coordinator.data.info.version_latest_stable) is None (stable := self.releases_coordinator.data.stable) is None
or (stable is not None and stable < beta) or (stable is not None and stable < beta and current > stable)
) )
): ):
return str(beta) return str(beta)
if (stable := self.coordinator.data.info.version_latest_stable) is not None: if (stable := self.releases_coordinator.data.stable) is not None:
return str(stable) return str(stable)
return None return None

View File

@ -2896,7 +2896,7 @@ wiffi==1.1.2
wirelesstagpy==0.8.1 wirelesstagpy==0.8.1
# homeassistant.components.wled # homeassistant.components.wled
wled==0.18.0 wled==0.19.2
# homeassistant.components.wolflink # homeassistant.components.wolflink
wolf-comm==0.0.9 wolf-comm==0.0.9

View File

@ -2270,7 +2270,7 @@ whois==0.9.27
wiffi==1.1.2 wiffi==1.1.2
# homeassistant.components.wled # homeassistant.components.wled
wled==0.18.0 wled==0.19.2
# homeassistant.components.wolflink # homeassistant.components.wolflink
wolf-comm==0.0.9 wolf-comm==0.0.9

View File

@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from wled import Device as WLEDDevice from wled import Device as WLEDDevice, Releases
from homeassistant.components.wled.const import DOMAIN from homeassistant.components.wled.const import DOMAIN
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
@ -51,7 +51,24 @@ def device_fixture() -> str:
@pytest.fixture @pytest.fixture
def mock_wled(device_fixture: str) -> Generator[MagicMock]: def mock_wled_releases() -> Generator[MagicMock]:
"""Return a mocked WLEDReleases client."""
with patch(
"homeassistant.components.wled.coordinator.WLEDReleases", autospec=True
) as wled_releases_mock:
wled_releases = wled_releases_mock.return_value
wled_releases.releases.return_value = Releases(
beta="1.0.0b5",
stable="0.99.0",
)
yield wled_releases
@pytest.fixture
def mock_wled(
device_fixture: str, mock_wled_releases: MagicMock
) -> Generator[MagicMock]:
"""Return a mocked WLED client.""" """Return a mocked WLED client."""
with ( with (
patch( patch(
@ -60,11 +77,12 @@ def mock_wled(device_fixture: str) -> Generator[MagicMock]:
patch("homeassistant.components.wled.config_flow.WLED", new=wled_mock), patch("homeassistant.components.wled.config_flow.WLED", new=wled_mock),
): ):
wled = wled_mock.return_value wled = wled_mock.return_value
wled.update.return_value = WLEDDevice( wled.update.return_value = WLEDDevice.from_dict(
load_json_object_fixture(f"{device_fixture}.json", DOMAIN) load_json_object_fixture(f"{device_fixture}.json", DOMAIN)
) )
wled.connected = False wled.connected = False
wled.host = "127.0.0.1" wled.host = "127.0.0.1"
yield wled yield wled

View File

@ -1,28 +1,41 @@
{ {
"state": { "state": {
"on": true, "on": true,
"bri": 127, "bri": 128,
"transition": 7, "transition": 7,
"ps": -1, "ps": -1,
"pl": -1, "pl": -1,
"nl": { "nl": {
"on": false, "on": false,
"dur": 60, "dur": 60,
"fade": true, "mode": 1,
"tbri": 0 "tbri": 0,
"rem": -1
}, },
"udpn": { "udpn": {
"send": false, "send": false,
"recv": true "recv": true,
"sgrp": 1,
"rgrp": 1
}, },
"lor": 0,
"mainseg": 1,
"seg": [ "seg": [
{ {
"id": 0, "id": 0,
"start": 0, "start": 0,
"stop": 19, "stop": 15,
"len": 20, "len": 15,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"col": [ "col": [
[255, 159, 0], [127, 172, 255],
[0, 0, 0], [0, 0, 0],
[0, 0, 0] [0, 0, 0]
], ],
@ -30,62 +43,106 @@
"sx": 32, "sx": 32,
"ix": 128, "ix": 128,
"pal": 0, "pal": 0,
"sel": true, "c1": 128,
"c2": 128,
"c3": 16,
"sel": false,
"rev": false, "rev": false,
"cln": -1 "mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
}, },
{ {
"id": 1, "id": 1,
"start": 20, "start": 15,
"stop": 30, "stop": 30,
"len": 10, "len": 15,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"col": [ "col": [
[0, 255, 123], [255, 170, 0],
[0, 0, 0], [0, 0, 0],
[0, 0, 0] [0, 0, 0]
], ],
"fx": 1, "fx": 3,
"sx": 16, "sx": 16,
"ix": 64, "ix": 64,
"pal": 1, "pal": 1,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true, "sel": true,
"rev": true, "rev": true,
"cln": -1 "mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
} }
] ]
}, },
"info": { "info": {
"ver": "0.8.5", "ver": "0.14.4",
"version_latest_stable": "0.12.0", "vid": 2405180,
"version_latest_beta": "0.13.0b1",
"vid": 1909122,
"leds": { "leds": {
"count": 30, "count": 30,
"rgbw": false, "pwr": 515,
"pin": [2], "fps": 5,
"pwr": 470,
"maxpwr": 850, "maxpwr": 850,
"maxseg": 10 "maxseg": 32,
"seglc": [1, 1],
"lc": 1,
"rgbw": false,
"wv": 0,
"cct": 0
}, },
"str": false,
"name": "WLED RGB Light", "name": "WLED RGB Light",
"udpport": 21324, "udpport": 21324,
"live": false, "live": false,
"fxcount": 81, "liveseg": -1,
"palcount": 50, "lm": "",
"lip": "",
"ws": -1,
"fxcount": 187,
"palcount": 71,
"cpalcount": 0,
"maps": [
{
"id": 0
}
],
"wifi": { "wifi": {
"bssid": "AA:AA:AA:AA:AA:BB", "bssid": "AA:AA:AA:AA:AA:BB",
"rssi": -62, "rssi": -43,
"signal": 76, "signal": 100,
"channel": 11 "channel": 11
}, },
"arch": "esp8266", "fs": {
"core": "2_4_2", "u": 12,
"freeheap": 14600, "t": 983,
"uptime": 32, "pmt": 1718827787
"opt": 119, },
"ndc": 1,
"arch": "esp32",
"core": "v3.3.6-16-gcc5440f6a2",
"lwip": 0,
"freeheap": 198384,
"uptime": 966,
"time": "2024-6-19, 20:10:38",
"opt": 79,
"brand": "WLED", "brand": "WLED",
"product": "DIY light", "product": "FOSS",
"btype": "bin",
"mac": "aabbccddeeff", "mac": "aabbccddeeff",
"ip": "127.0.0.1" "ip": "127.0.0.1"
}, },
@ -101,21 +158,21 @@
"Colorloop", "Colorloop",
"Rainbow", "Rainbow",
"Scan", "Scan",
"Dual Scan", "Scan Dual",
"Fade", "Fade",
"Chase", "Theater",
"Chase Rainbow", "Theater Rainbow",
"Running", "Running",
"Saw", "Saw",
"Twinkle", "Twinkle",
"Dissolve", "Dissolve",
"Dissolve Rnd", "Dissolve Rnd",
"Sparkle", "Sparkle",
"Dark Sparkle", "Sparkle Dark",
"Sparkle+", "Sparkle+",
"Strobe", "Strobe",
"Strobe Rainbow", "Strobe Rainbow",
"Mega Strobe", "Strobe Mega",
"Blink Rainbow", "Blink Rainbow",
"Android", "Android",
"Chase", "Chase",
@ -127,30 +184,30 @@
"Colorful", "Colorful",
"Traffic Light", "Traffic Light",
"Sweep Random", "Sweep Random",
"Running 2", "Chase 2",
"Red & Blue", "Aurora",
"Stream", "Stream",
"Scanner", "Scanner",
"Lighthouse", "Lighthouse",
"Fireworks", "Fireworks",
"Rain", "Rain",
"Merry Christmas", "Tetrix",
"Fire Flicker", "Fire Flicker",
"Gradient", "Gradient",
"Loading", "Loading",
"In Out", "Rolling Balls",
"In In", "Fairy",
"Out Out", "Two Dots",
"Out In", "Fairytwinkle",
"Circus", "Running Dual",
"Halloween", "RSVD",
"Tri Chase", "Chase 3",
"Tri Wipe", "Tri Wipe",
"Tri Fade", "Tri Fade",
"Lightning", "Lightning",
"ICU", "ICU",
"Multi Comet", "Multi Comet",
"Dual Scanner", "Scanner Dual",
"Stream 2", "Stream 2",
"Oscillate", "Oscillate",
"Pride 2015", "Pride 2015",
@ -158,27 +215,133 @@
"Palette", "Palette",
"Fire 2012", "Fire 2012",
"Colorwaves", "Colorwaves",
"BPM", "Bpm",
"Fill Noise", "Fill Noise",
"Noise 1", "Noise 1",
"Noise 2", "Noise 2",
"Noise 3", "Noise 3",
"Noise 4", "Noise 4",
"Colortwinkle", "Colortwinkles",
"Lake", "Lake",
"Meteor", "Meteor",
"Smooth Meteor", "Meteor Smooth",
"Railway", "Railway",
"Ripple", "Ripple",
"Twinklefox" "Twinklefox",
"Twinklecat",
"Halloween Eyes",
"Solid Pattern",
"Solid Pattern Tri",
"Spots",
"Spots Fade",
"Glitter",
"Candle",
"Fireworks Starburst",
"Fireworks 1D",
"Bouncing Balls",
"Sinelon",
"Sinelon Dual",
"Sinelon Rainbow",
"Popcorn",
"Drip",
"Plasma",
"Percent",
"Ripple Rainbow",
"Heartbeat",
"Pacifica",
"Candle Multi",
"Solid Glitter",
"Sunrise",
"Phased",
"Twinkleup",
"Noise Pal",
"Sine",
"Phased Noise",
"Flow",
"Chunchun",
"Dancing Shadows",
"Washing Machine",
"RSVD",
"Blends",
"TV Simulator",
"Dynamic Smooth",
"Spaceships",
"Crazy Bees",
"Ghost Rider",
"Blobs",
"Scrolling Text",
"Drift Rose",
"Distortion Waves",
"Soap",
"Octopus",
"Waving Cell",
"Pixels",
"Pixelwave",
"Juggles",
"Matripix",
"Gravimeter",
"Plasmoid",
"Puddles",
"Midnoise",
"Noisemeter",
"Freqwave",
"Freqmatrix",
"GEQ",
"Waterfall",
"Freqpixels",
"RSVD",
"Noisefire",
"Puddlepeak",
"Noisemove",
"Noise2D",
"Perlin Move",
"Ripple Peak",
"Firenoise",
"Squared Swirl",
"RSVD",
"DNA",
"Matrix",
"Metaballs",
"Freqmap",
"Gravcenter",
"Gravcentric",
"Gravfreq",
"DJ Light",
"Funky Plank",
"RSVD",
"Pulser",
"Blurz",
"Drift",
"Waverly",
"Sun Radiation",
"Colored Bursts",
"Julia",
"RSVD",
"RSVD",
"RSVD",
"Game Of Life",
"Tartan",
"Polar Lights",
"Swirl",
"Lissajous",
"Frizzles",
"Plasma Ball",
"Flow Stripe",
"Hiphotic",
"Sindots",
"DNA Spiral",
"Black Hole",
"Wavesins",
"Rocktaves",
"Akemi"
], ],
"palettes": [ "palettes": [
"Default", "Default",
"Random Cycle", "* Random Cycle",
"Primary Color", "* Color 1",
"Based on Primary", "* Colors 1&2",
"Set Colors", "* Color Gradient",
"Based on Set", "* Colors Only",
"Party", "Party",
"Cloud", "Cloud",
"Lava", "Lava",
@ -195,11 +358,11 @@
"Splash", "Splash",
"Pastel", "Pastel",
"Sunset 2", "Sunset 2",
"Beech", "Beach",
"Vintage", "Vintage",
"Departure", "Departure",
"Landscape", "Landscape",
"Beach", "Beech",
"Sherbet", "Sherbet",
"Hult", "Hult",
"Hult 64", "Hult 64",
@ -222,6 +385,27 @@
"April Night", "April Night",
"Orangery", "Orangery",
"C9", "C9",
"Sakura" "Sakura",
"Aurora",
"Atlantica",
"C9 2",
"C9 New",
"Temperature",
"Aurora 2",
"Retro Clown",
"Candy",
"Toxy Reaf",
"Fairy Reaf",
"Semi Blue",
"Pink Candy",
"Red Reaf",
"Aqua Flash",
"Yelblu Hot",
"Lite Light",
"Red Flash",
"Blink Red",
"Red Shift",
"Red Tide",
"Candy2"
] ]
} }

View File

@ -1,227 +0,0 @@
{
"state": {
"on": true,
"bri": 127,
"transition": 7,
"ps": -1,
"pl": -1,
"nl": {
"on": false,
"dur": 60,
"fade": true,
"tbri": 0
},
"udpn": {
"send": false,
"recv": true
},
"seg": [
{
"id": 0,
"start": 0,
"stop": 19,
"len": 20,
"col": [
[255, 159, 0],
[0, 0, 0],
[0, 0, 0]
],
"fx": 0,
"sx": 32,
"ix": 128,
"pal": 0,
"sel": true,
"rev": false,
"cln": -1
},
{
"id": 1,
"start": 20,
"stop": 30,
"len": 10,
"col": [
[0, 255, 123],
[0, 0, 0],
[0, 0, 0]
],
"fx": 1,
"sx": 16,
"ix": 64,
"pal": 1,
"sel": true,
"rev": true,
"cln": -1
}
]
},
"info": {
"ver": null,
"version_latest_stable": null,
"version_latest_beta": null,
"vid": 1909122,
"leds": {
"count": 30,
"rgbw": false,
"pin": [2],
"pwr": 470,
"maxpwr": 850,
"maxseg": 10
},
"name": "WLED RGB Light",
"udpport": 21324,
"live": false,
"fxcount": 81,
"palcount": 50,
"wifi": {
"bssid": "AA:AA:AA:AA:AA:BB",
"rssi": -62,
"signal": 76,
"channel": 11
},
"arch": "esp8266",
"core": "2_4_2",
"freeheap": 14600,
"uptime": 32,
"opt": 119,
"brand": "WLED",
"product": "DIY light",
"btype": "bin",
"mac": "aabbccddeeff",
"ip": "127.0.0.1"
},
"effects": [
"Solid",
"Blink",
"Breathe",
"Wipe",
"Wipe Random",
"Random Colors",
"Sweep",
"Dynamic",
"Colorloop",
"Rainbow",
"Scan",
"Dual Scan",
"Fade",
"Chase",
"Chase Rainbow",
"Running",
"Saw",
"Twinkle",
"Dissolve",
"Dissolve Rnd",
"Sparkle",
"Dark Sparkle",
"Sparkle+",
"Strobe",
"Strobe Rainbow",
"Mega Strobe",
"Blink Rainbow",
"Android",
"Chase",
"Chase Random",
"Chase Rainbow",
"Chase Flash",
"Chase Flash Rnd",
"Rainbow Runner",
"Colorful",
"Traffic Light",
"Sweep Random",
"Running 2",
"Red & Blue",
"Stream",
"Scanner",
"Lighthouse",
"Fireworks",
"Rain",
"Merry Christmas",
"Fire Flicker",
"Gradient",
"Loading",
"In Out",
"In In",
"Out Out",
"Out In",
"Circus",
"Halloween",
"Tri Chase",
"Tri Wipe",
"Tri Fade",
"Lightning",
"ICU",
"Multi Comet",
"Dual Scanner",
"Stream 2",
"Oscillate",
"Pride 2015",
"Juggle",
"Palette",
"Fire 2012",
"Colorwaves",
"BPM",
"Fill Noise",
"Noise 1",
"Noise 2",
"Noise 3",
"Noise 4",
"Colortwinkle",
"Lake",
"Meteor",
"Smooth Meteor",
"Railway",
"Ripple",
"Twinklefox"
],
"palettes": [
"Default",
"Random Cycle",
"Primary Color",
"Based on Primary",
"Set Colors",
"Based on Set",
"Party",
"Cloud",
"Lava",
"Ocean",
"Forest",
"Rainbow",
"Rainbow Bands",
"Sunset",
"Rivendell",
"Breeze",
"Red & Blue",
"Yellowout",
"Analogous",
"Splash",
"Pastel",
"Sunset 2",
"Beech",
"Vintage",
"Departure",
"Landscape",
"Beach",
"Sherbet",
"Hult",
"Hult 64",
"Drywet",
"Jul",
"Grintage",
"Rewhi",
"Tertiary",
"Fire",
"Icefire",
"Cyane",
"Light Pink",
"Autumn",
"Magenta",
"Magred",
"Yelmag",
"Yelblu",
"Orange & Teal",
"Tiamat",
"April Night",
"Orangery",
"C9",
"Sakura"
]
}

View File

@ -1,28 +1,41 @@
{ {
"state": { "state": {
"on": true, "on": true,
"bri": 127, "bri": 128,
"transition": 7, "transition": 7,
"ps": -1, "ps": -1,
"pl": -1, "pl": -1,
"nl": { "nl": {
"on": false, "on": false,
"dur": 60, "dur": 60,
"fade": true, "mode": 1,
"tbri": 0 "tbri": 0,
"rem": -1
}, },
"udpn": { "udpn": {
"send": false, "send": false,
"recv": true "recv": true,
"sgrp": 1,
"rgrp": 1
}, },
"lor": 0,
"mainseg": 0,
"seg": [ "seg": [
{ {
"id": 0, "id": 0,
"start": 0, "start": 0,
"stop": 30, "stop": 30,
"len": 20, "len": 30,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"col": [ "col": [
[255, 159, 0], [127, 172, 255],
[0, 0, 0], [0, 0, 0],
[0, 0, 0] [0, 0, 0]
], ],
@ -30,44 +43,72 @@
"sx": 32, "sx": 32,
"ix": 128, "ix": 128,
"pal": 0, "pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true, "sel": true,
"rev": false, "rev": false,
"cln": -1 "mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
} }
] ]
}, },
"info": { "info": {
"ver": "0.8.6b1", "ver": "1.0.0b4",
"version_latest_stable": "0.8.5", "vid": 2405180,
"version_latest_beta": "0.8.6b2",
"vid": 1909122,
"leds": { "leds": {
"count": 30, "count": 30,
"rgbw": false, "pwr": 536,
"pin": [2], "fps": 5,
"pwr": 470,
"maxpwr": 850, "maxpwr": 850,
"maxseg": 10 "maxseg": 32,
"seglc": [1],
"lc": 1,
"rgbw": false,
"wv": 0,
"cct": 0
}, },
"str": false,
"name": "WLED RGB Light", "name": "WLED RGB Light",
"udpport": 21324, "udpport": 21324,
"live": false, "live": false,
"fxcount": 81, "liveseg": -1,
"palcount": 50, "lm": "",
"lip": "",
"ws": -1,
"fxcount": 187,
"palcount": 71,
"cpalcount": 0,
"maps": [
{
"id": 0
}
],
"wifi": { "wifi": {
"bssid": "AA:AA:AA:AA:AA:BB", "bssid": "AA:AA:AA:AA:AA:BB",
"rssi": -62, "rssi": -44,
"signal": 76, "signal": 100,
"channel": 11 "channel": 11
}, },
"arch": "esp8266", "fs": {
"core": "2_4_2", "u": 12,
"freeheap": 14600, "t": 983,
"uptime": 32, "pmt": 0
"opt": 119, },
"ndc": 1,
"arch": "esp32",
"core": "v3.3.6-16-gcc5440f6a2",
"lwip": 0,
"freeheap": 196960,
"uptime": 461,
"time": "1970-1-1, 00:07:41",
"opt": 79,
"brand": "WLED", "brand": "WLED",
"product": "DIY light", "product": "FOSS",
"btype": "bin",
"mac": "aabbccddeeff", "mac": "aabbccddeeff",
"ip": "127.0.0.1" "ip": "127.0.0.1"
}, },
@ -83,21 +124,21 @@
"Colorloop", "Colorloop",
"Rainbow", "Rainbow",
"Scan", "Scan",
"Dual Scan", "Scan Dual",
"Fade", "Fade",
"Chase", "Theater",
"Chase Rainbow", "Theater Rainbow",
"Running", "Running",
"Saw", "Saw",
"Twinkle", "Twinkle",
"Dissolve", "Dissolve",
"Dissolve Rnd", "Dissolve Rnd",
"Sparkle", "Sparkle",
"Dark Sparkle", "Sparkle Dark",
"Sparkle+", "Sparkle+",
"Strobe", "Strobe",
"Strobe Rainbow", "Strobe Rainbow",
"Mega Strobe", "Strobe Mega",
"Blink Rainbow", "Blink Rainbow",
"Android", "Android",
"Chase", "Chase",
@ -109,30 +150,30 @@
"Colorful", "Colorful",
"Traffic Light", "Traffic Light",
"Sweep Random", "Sweep Random",
"Running 2", "Chase 2",
"Red & Blue", "Aurora",
"Stream", "Stream",
"Scanner", "Scanner",
"Lighthouse", "Lighthouse",
"Fireworks", "Fireworks",
"Rain", "Rain",
"Merry Christmas", "Tetrix",
"Fire Flicker", "Fire Flicker",
"Gradient", "Gradient",
"Loading", "Loading",
"In Out", "Rolling Balls",
"In In", "Fairy",
"Out Out", "Two Dots",
"Out In", "Fairytwinkle",
"Circus", "Running Dual",
"Halloween", "RSVD",
"Tri Chase", "Chase 3",
"Tri Wipe", "Tri Wipe",
"Tri Fade", "Tri Fade",
"Lightning", "Lightning",
"ICU", "ICU",
"Multi Comet", "Multi Comet",
"Dual Scanner", "Scanner Dual",
"Stream 2", "Stream 2",
"Oscillate", "Oscillate",
"Pride 2015", "Pride 2015",
@ -140,27 +181,133 @@
"Palette", "Palette",
"Fire 2012", "Fire 2012",
"Colorwaves", "Colorwaves",
"BPM", "Bpm",
"Fill Noise", "Fill Noise",
"Noise 1", "Noise 1",
"Noise 2", "Noise 2",
"Noise 3", "Noise 3",
"Noise 4", "Noise 4",
"Colortwinkle", "Colortwinkles",
"Lake", "Lake",
"Meteor", "Meteor",
"Smooth Meteor", "Meteor Smooth",
"Railway", "Railway",
"Ripple", "Ripple",
"Twinklefox" "Twinklefox",
"Twinklecat",
"Halloween Eyes",
"Solid Pattern",
"Solid Pattern Tri",
"Spots",
"Spots Fade",
"Glitter",
"Candle",
"Fireworks Starburst",
"Fireworks 1D",
"Bouncing Balls",
"Sinelon",
"Sinelon Dual",
"Sinelon Rainbow",
"Popcorn",
"Drip",
"Plasma",
"Percent",
"Ripple Rainbow",
"Heartbeat",
"Pacifica",
"Candle Multi",
"Solid Glitter",
"Sunrise",
"Phased",
"Twinkleup",
"Noise Pal",
"Sine",
"Phased Noise",
"Flow",
"Chunchun",
"Dancing Shadows",
"Washing Machine",
"RSVD",
"Blends",
"TV Simulator",
"Dynamic Smooth",
"Spaceships",
"Crazy Bees",
"Ghost Rider",
"Blobs",
"Scrolling Text",
"Drift Rose",
"Distortion Waves",
"Soap",
"Octopus",
"Waving Cell",
"Pixels",
"Pixelwave",
"Juggles",
"Matripix",
"Gravimeter",
"Plasmoid",
"Puddles",
"Midnoise",
"Noisemeter",
"Freqwave",
"Freqmatrix",
"GEQ",
"Waterfall",
"Freqpixels",
"RSVD",
"Noisefire",
"Puddlepeak",
"Noisemove",
"Noise2D",
"Perlin Move",
"Ripple Peak",
"Firenoise",
"Squared Swirl",
"RSVD",
"DNA",
"Matrix",
"Metaballs",
"Freqmap",
"Gravcenter",
"Gravcentric",
"Gravfreq",
"DJ Light",
"Funky Plank",
"RSVD",
"Pulser",
"Blurz",
"Drift",
"Waverly",
"Sun Radiation",
"Colored Bursts",
"Julia",
"RSVD",
"RSVD",
"RSVD",
"Game Of Life",
"Tartan",
"Polar Lights",
"Swirl",
"Lissajous",
"Frizzles",
"Plasma Ball",
"Flow Stripe",
"Hiphotic",
"Sindots",
"DNA Spiral",
"Black Hole",
"Wavesins",
"Rocktaves",
"Akemi"
], ],
"palettes": [ "palettes": [
"Default", "Default",
"Random Cycle", "* Random Cycle",
"Primary Color", "* Color 1",
"Based on Primary", "* Colors 1&2",
"Set Colors", "* Color Gradient",
"Based on Set", "* Colors Only",
"Party", "Party",
"Cloud", "Cloud",
"Lava", "Lava",
@ -177,11 +324,11 @@
"Splash", "Splash",
"Pastel", "Pastel",
"Sunset 2", "Sunset 2",
"Beech", "Beach",
"Vintage", "Vintage",
"Departure", "Departure",
"Landscape", "Landscape",
"Beach", "Beech",
"Sherbet", "Sherbet",
"Hult", "Hult",
"Hult 64", "Hult 64",
@ -204,6 +351,27 @@
"April Night", "April Night",
"Orangery", "Orangery",
"C9", "C9",
"Sakura" "Sakura",
"Aurora",
"Atlantica",
"C9 2",
"C9 New",
"Temperature",
"Aurora 2",
"Retro Clown",
"Candy",
"Toxy Reaf",
"Fairy Reaf",
"Semi Blue",
"Pink Candy",
"Red Reaf",
"Aqua Flash",
"Yelblu Hot",
"Lite Light",
"Red Flash",
"Blink Red",
"Red Shift",
"Red Tide",
"Candy2"
] ]
} }

View File

@ -1,26 +1,22 @@
{ {
"state": { "state": {
"on": true, "on": true,
"bri": 255, "bri": 128,
"transition": 7, "transition": 7,
"ps": -1, "ps": -1,
"pl": -1, "pl": -1,
"ccnf": {
"min": 1,
"max": 5,
"time": 12
},
"nl": { "nl": {
"on": false, "on": false,
"dur": 60, "dur": 60,
"fade": true,
"mode": 1, "mode": 1,
"tbri": 0, "tbri": 0,
"rem": -1 "rem": -1
}, },
"udpn": { "udpn": {
"send": false, "send": false,
"recv": true "recv": true,
"sgrp": 1,
"rgrp": 1
}, },
"lor": 0, "lor": 0,
"mainseg": 0, "mainseg": 0,
@ -28,70 +24,89 @@
{ {
"id": 0, "id": 0,
"start": 0, "start": 0,
"stop": 13, "stop": 30,
"len": 13, "len": 30,
"grp": 1, "grp": 1,
"spc": 0, "spc": 0,
"of": 0,
"on": true, "on": true,
"frz": false,
"bri": 255, "bri": 255,
"cct": 127,
"set": 0,
"col": [ "col": [
[255, 181, 218], [127, 172, 255],
[0, 0, 0], [0, 0, 0],
[0, 0, 0] [0, 0, 0]
], ],
"fx": 0, "fx": 0,
"sx": 43, "sx": 128,
"ix": 128, "ix": 128,
"pal": 2, "pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true, "sel": true,
"rev": false, "rev": false,
"mi": false "mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
} }
] ]
}, },
"info": { "info": {
"ver": "0.12.0-b2", "ver": "0.99.0",
"version_latest_stable": "0.11.0", "vid": 2405180,
"version_latest_beta": "0.12.0-b2",
"vid": 2103220,
"leds": { "leds": {
"count": 13, "count": 30,
"pwr": 536,
"fps": 5,
"maxpwr": 850,
"maxseg": 32,
"seglc": [1],
"lc": 1,
"rgbw": false, "rgbw": false,
"wv": false, "wv": 0,
"pin": [2], "cct": 0
"pwr": 266,
"fps": 2,
"maxpwr": 1000,
"maxseg": 12,
"seglock": false
}, },
"str": false, "str": false,
"name": "WLED WebSocket", "name": "WLED WebSocket",
"udpport": 21324, "udpport": 21324,
"live": false, "live": false,
"liveseg": -1,
"lm": "", "lm": "",
"lip": "", "lip": "",
"ws": 0, "ws": 0,
"fxcount": 118, "fxcount": 187,
"palcount": 56, "palcount": 71,
"cpalcount": 0,
"maps": [
{
"id": 0
}
],
"wifi": { "wifi": {
"bssid": "AA:AA:AA:AA:AA:BB", "bssid": "AA:AA:AA:AA:AA:BB",
"rssi": -68, "rssi": -44,
"signal": 64, "signal": 100,
"channel": 6 "channel": 11
}, },
"fs": { "fs": {
"u": 40, "u": 12,
"t": 1024, "t": 983,
"pmt": 1623156685 "pmt": 0
}, },
"ndc": 1, "ndc": 1,
"arch": "esp8266", "arch": "esp32",
"core": "2_7_4_7", "core": "v3.3.6-16-gcc5440f6a2",
"lwip": 1, "lwip": 0,
"freeheap": 22752, "freeheap": 196960,
"uptime": 258411, "uptime": 461,
"opt": 127, "time": "1970-1-1, 00:07:41",
"opt": 79,
"brand": "WLED", "brand": "WLED",
"product": "FOSS", "product": "FOSS",
"mac": "aabbccddeeff", "mac": "aabbccddeeff",
@ -135,7 +150,7 @@
"Colorful", "Colorful",
"Traffic Light", "Traffic Light",
"Sweep Random", "Sweep Random",
"Running 2", "Chase 2",
"Aurora", "Aurora",
"Stream", "Stream",
"Scanner", "Scanner",
@ -146,13 +161,13 @@
"Fire Flicker", "Fire Flicker",
"Gradient", "Gradient",
"Loading", "Loading",
"Police", "Rolling Balls",
"Police All", "Fairy",
"Two Dots", "Two Dots",
"Two Areas", "Fairytwinkle",
"Circus", "Running Dual",
"Halloween", "RSVD",
"Tri Chase", "Chase 3",
"Tri Wipe", "Tri Wipe",
"Tri Fade", "Tri Fade",
"Lightning", "Lightning",
@ -212,10 +227,79 @@
"Chunchun", "Chunchun",
"Dancing Shadows", "Dancing Shadows",
"Washing Machine", "Washing Machine",
"Candy Cane", "RSVD",
"Blends", "Blends",
"TV Simulator", "TV Simulator",
"Dynamic Smooth" "Dynamic Smooth",
"Spaceships",
"Crazy Bees",
"Ghost Rider",
"Blobs",
"Scrolling Text",
"Drift Rose",
"Distortion Waves",
"Soap",
"Octopus",
"Waving Cell",
"Pixels",
"Pixelwave",
"Juggles",
"Matripix",
"Gravimeter",
"Plasmoid",
"Puddles",
"Midnoise",
"Noisemeter",
"Freqwave",
"Freqmatrix",
"GEQ",
"Waterfall",
"Freqpixels",
"RSVD",
"Noisefire",
"Puddlepeak",
"Noisemove",
"Noise2D",
"Perlin Move",
"Ripple Peak",
"Firenoise",
"Squared Swirl",
"RSVD",
"DNA",
"Matrix",
"Metaballs",
"Freqmap",
"Gravcenter",
"Gravcentric",
"Gravfreq",
"DJ Light",
"Funky Plank",
"RSVD",
"Pulser",
"Blurz",
"Drift",
"Waverly",
"Sun Radiation",
"Colored Bursts",
"Julia",
"RSVD",
"RSVD",
"RSVD",
"Game Of Life",
"Tartan",
"Polar Lights",
"Swirl",
"Lissajous",
"Frizzles",
"Plasma Ball",
"Flow Stripe",
"Hiphotic",
"Sindots",
"DNA Spiral",
"Black Hole",
"Wavesins",
"Rocktaves",
"Akemi"
], ],
"palettes": [ "palettes": [
"Default", "Default",
@ -240,11 +324,11 @@
"Splash", "Splash",
"Pastel", "Pastel",
"Sunset 2", "Sunset 2",
"Beech", "Beach",
"Vintage", "Vintage",
"Departure", "Departure",
"Landscape", "Landscape",
"Beach", "Beech",
"Sherbet", "Sherbet",
"Hult", "Hult",
"Hult 64", "Hult 64",
@ -273,6 +357,21 @@
"C9 2", "C9 2",
"C9 New", "C9 New",
"Temperature", "Temperature",
"Aurora 2" "Aurora 2",
"Retro Clown",
"Candy",
"Toxy Reaf",
"Fairy Reaf",
"Semi Blue",
"Pink Candy",
"Red Reaf",
"Aqua Flash",
"Yelblu Hot",
"Lite Light",
"Red Flash",
"Blink Red",
"Red Shift",
"Red Tide",
"Candy2"
] ]
} }

View File

@ -1,74 +1,115 @@
{ {
"state": { "state": {
"on": true, "on": true,
"bri": 140, "bri": 128,
"transition": 7, "transition": 7,
"ps": 1, "ps": -1,
"pl": 3, "pl": -1,
"nl": { "nl": {
"on": false, "on": false,
"dur": 60, "dur": 60,
"fade": true, "mode": 1,
"tbri": 0 "tbri": 0,
"rem": -1
}, },
"udpn": { "udpn": {
"send": false, "send": false,
"recv": true "recv": true,
"sgrp": 1,
"rgrp": 1
}, },
"lor": 0,
"mainseg": 0,
"seg": [ "seg": [
{ {
"id": 0, "id": 0,
"start": 0, "start": 0,
"stop": 13, "stop": 30,
"len": 13, "len": 30,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"col": [ "col": [
[255, 0, 0, 139], [255, 0, 0, 139],
[0, 0, 0, 0], [0, 0, 0, 0],
[0, 0, 0, 0] [0, 0, 0, 0]
], ],
"fx": 9, "fx": 0,
"sx": 165, "sx": 128,
"ix": 128, "ix": 128,
"pal": 0, "pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true, "sel": true,
"rev": false, "rev": false,
"cln": -1 "mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
} }
] ]
}, },
"info": { "info": {
"ver": "0.8.6b4", "ver": "0.99.0b1",
"version_latest_stable": "0.8.6", "vid": 2405180,
"version_latest_beta": "0.8.6b5",
"vid": 1910255,
"leds": { "leds": {
"count": 13, "count": 30,
"rgbw": true, "pwr": 536,
"pin": [2], "fps": 5,
"pwr": 208,
"maxpwr": 850, "maxpwr": 850,
"maxseg": 10 "maxseg": 32,
"seglc": [3],
"lc": 3,
"rgbw": true,
"wv": 0,
"cct": 0
}, },
"str": false,
"name": "WLED RGBW Light", "name": "WLED RGBW Light",
"udpport": 21324, "udpport": 21324,
"live": false, "live": false,
"fxcount": 83, "liveseg": -1,
"palcount": 50, "lm": "",
"lip": "",
"ws": -1,
"fxcount": 187,
"palcount": 71,
"cpalcount": 0,
"maps": [
{
"id": 0
}
],
"wifi": { "wifi": {
"bssid": "AA:AA:AA:AA:AA:BB", "bssid": "AA:AA:AA:AA:AA:BB",
"rssi": -62, "rssi": -44,
"signal": 76, "signal": 100,
"channel": 11 "channel": 11
}, },
"arch": "esp8266", "fs": {
"core": "2_5_2", "u": 12,
"freeheap": 20136, "t": 983,
"uptime": 5591, "pmt": 0
"opt": 119, },
"ndc": 1,
"arch": "esp32",
"core": "v3.3.6-16-gcc5440f6a2",
"lwip": 0,
"freeheap": 196960,
"uptime": 461,
"time": "1970-1-1, 00:07:41",
"opt": 79,
"brand": "WLED", "brand": "WLED",
"product": "DIY light", "product": "FOSS",
"btype": "bin", "mac": "aabbccddeeff",
"mac": "aabbccddee11",
"ip": "127.0.0.1" "ip": "127.0.0.1"
}, },
"effects": [ "effects": [
@ -83,21 +124,21 @@
"Colorloop", "Colorloop",
"Rainbow", "Rainbow",
"Scan", "Scan",
"Dual Scan", "Scan Dual",
"Fade", "Fade",
"Chase", "Theater",
"Chase Rainbow", "Theater Rainbow",
"Running", "Running",
"Saw", "Saw",
"Twinkle", "Twinkle",
"Dissolve", "Dissolve",
"Dissolve Rnd", "Dissolve Rnd",
"Sparkle", "Sparkle",
"Dark Sparkle", "Sparkle Dark",
"Sparkle+", "Sparkle+",
"Strobe", "Strobe",
"Strobe Rainbow", "Strobe Rainbow",
"Mega Strobe", "Strobe Mega",
"Blink Rainbow", "Blink Rainbow",
"Android", "Android",
"Chase", "Chase",
@ -109,30 +150,30 @@
"Colorful", "Colorful",
"Traffic Light", "Traffic Light",
"Sweep Random", "Sweep Random",
"Running 2", "Chase 2",
"Red & Blue", "Aurora",
"Stream", "Stream",
"Scanner", "Scanner",
"Lighthouse", "Lighthouse",
"Fireworks", "Fireworks",
"Rain", "Rain",
"Merry Christmas", "Tetrix",
"Fire Flicker", "Fire Flicker",
"Gradient", "Gradient",
"Loading", "Loading",
"In Out", "Rolling Balls",
"In In", "Fairy",
"Out Out", "Two Dots",
"Out In", "Fairytwinkle",
"Circus", "Running Dual",
"Halloween", "RSVD",
"Tri Chase", "Chase 3",
"Tri Wipe", "Tri Wipe",
"Tri Fade", "Tri Fade",
"Lightning", "Lightning",
"ICU", "ICU",
"Multi Comet", "Multi Comet",
"Dual Scanner", "Scanner Dual",
"Stream 2", "Stream 2",
"Oscillate", "Oscillate",
"Pride 2015", "Pride 2015",
@ -140,7 +181,7 @@
"Palette", "Palette",
"Fire 2012", "Fire 2012",
"Colorwaves", "Colorwaves",
"BPM", "Bpm",
"Fill Noise", "Fill Noise",
"Noise 1", "Noise 1",
"Noise 2", "Noise 2",
@ -149,20 +190,124 @@
"Colortwinkles", "Colortwinkles",
"Lake", "Lake",
"Meteor", "Meteor",
"Smooth Meteor", "Meteor Smooth",
"Railway", "Railway",
"Ripple", "Ripple",
"Twinklefox", "Twinklefox",
"Twinklecat", "Twinklecat",
"Halloween Eyes" "Halloween Eyes",
"Solid Pattern",
"Solid Pattern Tri",
"Spots",
"Spots Fade",
"Glitter",
"Candle",
"Fireworks Starburst",
"Fireworks 1D",
"Bouncing Balls",
"Sinelon",
"Sinelon Dual",
"Sinelon Rainbow",
"Popcorn",
"Drip",
"Plasma",
"Percent",
"Ripple Rainbow",
"Heartbeat",
"Pacifica",
"Candle Multi",
"Solid Glitter",
"Sunrise",
"Phased",
"Twinkleup",
"Noise Pal",
"Sine",
"Phased Noise",
"Flow",
"Chunchun",
"Dancing Shadows",
"Washing Machine",
"RSVD",
"Blends",
"TV Simulator",
"Dynamic Smooth",
"Spaceships",
"Crazy Bees",
"Ghost Rider",
"Blobs",
"Scrolling Text",
"Drift Rose",
"Distortion Waves",
"Soap",
"Octopus",
"Waving Cell",
"Pixels",
"Pixelwave",
"Juggles",
"Matripix",
"Gravimeter",
"Plasmoid",
"Puddles",
"Midnoise",
"Noisemeter",
"Freqwave",
"Freqmatrix",
"GEQ",
"Waterfall",
"Freqpixels",
"RSVD",
"Noisefire",
"Puddlepeak",
"Noisemove",
"Noise2D",
"Perlin Move",
"Ripple Peak",
"Firenoise",
"Squared Swirl",
"RSVD",
"DNA",
"Matrix",
"Metaballs",
"Freqmap",
"Gravcenter",
"Gravcentric",
"Gravfreq",
"DJ Light",
"Funky Plank",
"RSVD",
"Pulser",
"Blurz",
"Drift",
"Waverly",
"Sun Radiation",
"Colored Bursts",
"Julia",
"RSVD",
"RSVD",
"RSVD",
"Game Of Life",
"Tartan",
"Polar Lights",
"Swirl",
"Lissajous",
"Frizzles",
"Plasma Ball",
"Flow Stripe",
"Hiphotic",
"Sindots",
"DNA Spiral",
"Black Hole",
"Wavesins",
"Rocktaves",
"Akemi"
], ],
"palettes": [ "palettes": [
"Default", "Default",
"Random Cycle", "* Random Cycle",
"Primary Color", "* Color 1",
"Based on Primary", "* Colors 1&2",
"Set Colors", "* Color Gradient",
"Based on Set", "* Colors Only",
"Party", "Party",
"Cloud", "Cloud",
"Lava", "Lava",
@ -179,11 +324,11 @@
"Splash", "Splash",
"Pastel", "Pastel",
"Sunset 2", "Sunset 2",
"Beech", "Beach",
"Vintage", "Vintage",
"Departure", "Departure",
"Landscape", "Landscape",
"Beach", "Beech",
"Sherbet", "Sherbet",
"Hult", "Hult",
"Hult 64", "Hult 64",
@ -206,36 +351,82 @@
"April Night", "April Night",
"Orangery", "Orangery",
"C9", "C9",
"Sakura" "Sakura",
"Aurora",
"Atlantica",
"C9 2",
"C9 New",
"Temperature",
"Aurora 2",
"Retro Clown",
"Candy",
"Toxy Reaf",
"Fairy Reaf",
"Semi Blue",
"Pink Candy",
"Red Reaf",
"Aqua Flash",
"Yelblu Hot",
"Lite Light",
"Red Flash",
"Blink Red",
"Red Shift",
"Red Tide",
"Candy2"
], ],
"presets": { "presets": {
"0": {}, "0": {},
"1": { "1": {
"on": false, "on": true,
"bri": 255, "bri": 128,
"transition": 7, "transition": 7,
"mainseg": 0, "mainseg": 0,
"seg": [ "seg": [
{ {
"id": 0, "id": 0,
"start": 0, "start": 0,
"stop": 13, "stop": 131,
"grp": 1, "grp": 1,
"spc": 0, "spc": 0,
"of": 0,
"on": true, "on": true,
"frz": false,
"bri": 255, "bri": 255,
"cct": 127,
"set": 0,
"n": "",
"col": [ "col": [
[97, 144, 255], [40, 255, 3],
[0, 0, 0], [0, 0, 0],
[0, 0, 0] [0, 0, 0]
], ],
"fx": 9, "fx": 0,
"sx": 183, "sx": 128,
"ix": 255, "ix": 128,
"pal": 1, "pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true, "sel": true,
"rev": false, "rev": false,
"mi": false "mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
},
{
"stop": 0
},
{
"stop": 0
},
{
"stop": 0
},
{
"stop": 0
}, },
{ {
"stop": 0 "stop": 0
@ -274,31 +465,56 @@
"n": "Preset 1" "n": "Preset 1"
}, },
"2": { "2": {
"on": false, "on": true,
"bri": 255, "bri": 128,
"transition": 7, "transition": 7,
"mainseg": 0, "mainseg": 0,
"seg": [ "seg": [
{ {
"id": 0, "id": 0,
"start": 0, "start": 0,
"stop": 13, "stop": 131,
"grp": 1, "grp": 1,
"spc": 0, "spc": 0,
"of": 0,
"on": true, "on": true,
"frz": false,
"bri": 255, "bri": 255,
"cct": 127,
"set": 0,
"n": "",
"col": [ "col": [
[97, 144, 255], [51, 88, 255],
[0, 0, 0], [0, 0, 0],
[0, 0, 0] [0, 0, 0]
], ],
"fx": 9, "fx": 0,
"sx": 183, "sx": 128,
"ix": 255, "ix": 128,
"pal": 1, "pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true, "sel": true,
"rev": false, "rev": false,
"mi": false "mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
},
{
"stop": 0
},
{
"stop": 0
},
{
"stop": 0
},
{
"stop": 0
}, },
{ {
"stop": 0 "stop": 0
@ -339,23 +555,25 @@
"3": { "3": {
"playlist": { "playlist": {
"ps": [1, 2], "ps": [1, 2],
"dur": [30, 30], "dur": [300, 300],
"transition": [7, 7], "transition": [7, 7],
"repeat": 0, "repeat": 0,
"r": false, "end": 0,
"end": 0 "r": 0
}, },
"on": true,
"n": "Playlist 1" "n": "Playlist 1"
}, },
"4": { "4": {
"playlist": { "playlist": {
"ps": [1, 2], "ps": [2, 0],
"dur": [30, 30], "dur": [300, 300],
"transition": [7, 7], "transition": [7, 7],
"repeat": 0, "repeat": 0,
"r": false, "end": 0,
"end": 0 "r": 0
}, },
"on": true,
"n": "Playlist 2" "n": "Playlist 2"
} }
} }

View File

@ -59,7 +59,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -71,14 +71,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.5', 'sw_version': '0.14.4',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---

View File

@ -5,22 +5,109 @@
'0': 'Solid', '0': 'Solid',
'1': 'Blink', '1': 'Blink',
'10': 'Scan', '10': 'Scan',
'11': 'Dual Scan', '100': 'Heartbeat',
'101': 'Pacifica',
'102': 'Candle Multi',
'103': 'Solid Glitter',
'104': 'Sunrise',
'105': 'Phased',
'106': 'Twinkleup',
'107': 'Noise Pal',
'108': 'Sine',
'109': 'Phased Noise',
'11': 'Scan Dual',
'110': 'Flow',
'111': 'Chunchun',
'112': 'Dancing Shadows',
'113': 'Washing Machine',
'114': 'RSVD',
'115': 'Blends',
'116': 'TV Simulator',
'117': 'Dynamic Smooth',
'118': 'Spaceships',
'119': 'Crazy Bees',
'12': 'Fade', '12': 'Fade',
'13': 'Chase', '120': 'Ghost Rider',
'14': 'Chase Rainbow', '121': 'Blobs',
'122': 'Scrolling Text',
'123': 'Drift Rose',
'124': 'Distortion Waves',
'125': 'Soap',
'126': 'Octopus',
'127': 'Waving Cell',
'128': 'Pixels',
'129': 'Pixelwave',
'13': 'Theater',
'130': 'Juggles',
'131': 'Matripix',
'132': 'Gravimeter',
'133': 'Plasmoid',
'134': 'Puddles',
'135': 'Midnoise',
'136': 'Noisemeter',
'137': 'Freqwave',
'138': 'Freqmatrix',
'139': 'GEQ',
'14': 'Theater Rainbow',
'140': 'Waterfall',
'141': 'Freqpixels',
'142': 'RSVD',
'143': 'Noisefire',
'144': 'Puddlepeak',
'145': 'Noisemove',
'146': 'Noise2D',
'147': 'Perlin Move',
'148': 'Ripple Peak',
'149': 'Firenoise',
'15': 'Running', '15': 'Running',
'150': 'Squared Swirl',
'151': 'RSVD',
'152': 'DNA',
'153': 'Matrix',
'154': 'Metaballs',
'155': 'Freqmap',
'156': 'Gravcenter',
'157': 'Gravcentric',
'158': 'Gravfreq',
'159': 'DJ Light',
'16': 'Saw', '16': 'Saw',
'160': 'Funky Plank',
'161': 'RSVD',
'162': 'Pulser',
'163': 'Blurz',
'164': 'Drift',
'165': 'Waverly',
'166': 'Sun Radiation',
'167': 'Colored Bursts',
'168': 'Julia',
'169': 'RSVD',
'17': 'Twinkle', '17': 'Twinkle',
'170': 'RSVD',
'171': 'RSVD',
'172': 'Game Of Life',
'173': 'Tartan',
'174': 'Polar Lights',
'175': 'Swirl',
'176': 'Lissajous',
'177': 'Frizzles',
'178': 'Plasma Ball',
'179': 'Flow Stripe',
'18': 'Dissolve', '18': 'Dissolve',
'180': 'Hiphotic',
'181': 'Sindots',
'182': 'DNA Spiral',
'183': 'Black Hole',
'184': 'Wavesins',
'185': 'Rocktaves',
'186': 'Akemi',
'19': 'Dissolve Rnd', '19': 'Dissolve Rnd',
'2': 'Breathe', '2': 'Breathe',
'20': 'Sparkle', '20': 'Sparkle',
'21': 'Dark Sparkle', '21': 'Sparkle Dark',
'22': 'Sparkle+', '22': 'Sparkle+',
'23': 'Strobe', '23': 'Strobe',
'24': 'Strobe Rainbow', '24': 'Strobe Rainbow',
'25': 'Mega Strobe', '25': 'Strobe Mega',
'26': 'Blink Rainbow', '26': 'Blink Rainbow',
'27': 'Android', '27': 'Android',
'28': 'Chase', '28': 'Chase',
@ -33,33 +120,33 @@
'34': 'Colorful', '34': 'Colorful',
'35': 'Traffic Light', '35': 'Traffic Light',
'36': 'Sweep Random', '36': 'Sweep Random',
'37': 'Running 2', '37': 'Chase 2',
'38': 'Red & Blue', '38': 'Aurora',
'39': 'Stream', '39': 'Stream',
'4': 'Wipe Random', '4': 'Wipe Random',
'40': 'Scanner', '40': 'Scanner',
'41': 'Lighthouse', '41': 'Lighthouse',
'42': 'Fireworks', '42': 'Fireworks',
'43': 'Rain', '43': 'Rain',
'44': 'Merry Christmas', '44': 'Tetrix',
'45': 'Fire Flicker', '45': 'Fire Flicker',
'46': 'Gradient', '46': 'Gradient',
'47': 'Loading', '47': 'Loading',
'48': 'In Out', '48': 'Rolling Balls',
'49': 'In In', '49': 'Fairy',
'5': 'Random Colors', '5': 'Random Colors',
'50': 'Out Out', '50': 'Two Dots',
'51': 'Out In', '51': 'Fairytwinkle',
'52': 'Circus', '52': 'Running Dual',
'53': 'Halloween', '53': 'RSVD',
'54': 'Tri Chase', '54': 'Chase 3',
'55': 'Tri Wipe', '55': 'Tri Wipe',
'56': 'Tri Fade', '56': 'Tri Fade',
'57': 'Lightning', '57': 'Lightning',
'58': 'ICU', '58': 'ICU',
'59': 'Multi Comet', '59': 'Multi Comet',
'6': 'Sweep', '6': 'Sweep',
'60': 'Dual Scanner', '60': 'Scanner Dual',
'61': 'Stream 2', '61': 'Stream 2',
'62': 'Oscillate', '62': 'Oscillate',
'63': 'Pride 2015', '63': 'Pride 2015',
@ -67,55 +154,82 @@
'65': 'Palette', '65': 'Palette',
'66': 'Fire 2012', '66': 'Fire 2012',
'67': 'Colorwaves', '67': 'Colorwaves',
'68': 'BPM', '68': 'Bpm',
'69': 'Fill Noise', '69': 'Fill Noise',
'7': 'Dynamic', '7': 'Dynamic',
'70': 'Noise 1', '70': 'Noise 1',
'71': 'Noise 2', '71': 'Noise 2',
'72': 'Noise 3', '72': 'Noise 3',
'73': 'Noise 4', '73': 'Noise 4',
'74': 'Colortwinkle', '74': 'Colortwinkles',
'75': 'Lake', '75': 'Lake',
'76': 'Meteor', '76': 'Meteor',
'77': 'Smooth Meteor', '77': 'Meteor Smooth',
'78': 'Railway', '78': 'Railway',
'79': 'Ripple', '79': 'Ripple',
'8': 'Colorloop', '8': 'Colorloop',
'80': 'Twinklefox', '80': 'Twinklefox',
'81': 'Twinklecat',
'82': 'Halloween Eyes',
'83': 'Solid Pattern',
'84': 'Solid Pattern Tri',
'85': 'Spots',
'86': 'Spots Fade',
'87': 'Glitter',
'88': 'Candle',
'89': 'Fireworks Starburst',
'9': 'Rainbow', '9': 'Rainbow',
'90': 'Fireworks 1D',
'91': 'Bouncing Balls',
'92': 'Sinelon',
'93': 'Sinelon Dual',
'94': 'Sinelon Rainbow',
'95': 'Popcorn',
'96': 'Drip',
'97': 'Plasma',
'98': 'Percent',
'99': 'Ripple Rainbow',
}), }),
'info': dict({ 'info': dict({
'architecture': 'esp8266', 'arch': 'esp32',
'arduino_core_version': '2.4.2',
'brand': 'WLED', 'brand': 'WLED',
'build_type': 'bin', 'core': 'v3.3.6-16-gcc5440f6a2',
'effect_count': 81, 'freeheap': 198384,
'filesystem': None, 'fs': dict({
'free_heap': 14600, 'pmt': 1718827787.0,
't': 983,
'u': 12,
}),
'fxcount': 187,
'ip': '127.0.0.1', 'ip': '127.0.0.1',
'leds': dict({ 'leds': dict({
'__type': "<class 'wled.models.Leds'>", 'count': 30,
'repr': 'Leds(cct=False, count=30, fps=None, light_capabilities=None, max_power=850, max_segments=10, power=470, rgbw=False, wv=True, segment_light_capabilities=None)', 'fps': 5,
'light_capabilities': 1,
'max_power': 850,
'max_segments': 32,
'power': 515,
'segment_light_capabilities': list([
1,
1,
]),
}), }),
'lip': '',
'live': False, 'live': False,
'live_ip': 'Unknown', 'lm': '',
'live_mode': 'Unknown', 'mac': 'aabbccddeeff',
'mac_address': 'aabbccddeeff',
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'pallet_count': 50, 'palcount': 71,
'product': 'DIY light', 'product': 'FOSS',
'udp_port': 21324, 'udpport': 21324,
'uptime': 32, 'uptime': 966,
'version': '0.8.5', 'ver': '0.14.4',
'version_id': 1909122, 'vid': 2405180,
'version_latest_beta': '0.13.0b1',
'version_latest_stable': '0.12.0',
'websocket': None,
'wifi': '**REDACTED**', 'wifi': '**REDACTED**',
}), }),
'palettes': dict({ 'palettes': dict({
'0': 'Default', '0': 'Default',
'1': 'Random Cycle', '1': '* Random Cycle',
'10': 'Forest', '10': 'Forest',
'11': 'Rainbow', '11': 'Rainbow',
'12': 'Rainbow Bands', '12': 'Rainbow Bands',
@ -126,18 +240,18 @@
'17': 'Yellowout', '17': 'Yellowout',
'18': 'Analogous', '18': 'Analogous',
'19': 'Splash', '19': 'Splash',
'2': 'Primary Color', '2': '* Color 1',
'20': 'Pastel', '20': 'Pastel',
'21': 'Sunset 2', '21': 'Sunset 2',
'22': 'Beech', '22': 'Beach',
'23': 'Vintage', '23': 'Vintage',
'24': 'Departure', '24': 'Departure',
'25': 'Landscape', '25': 'Landscape',
'26': 'Beach', '26': 'Beech',
'27': 'Sherbet', '27': 'Sherbet',
'28': 'Hult', '28': 'Hult',
'29': 'Hult 64', '29': 'Hult 64',
'3': 'Based on Primary', '3': '* Colors 1&2',
'30': 'Drywet', '30': 'Drywet',
'31': 'Jul', '31': 'Jul',
'32': 'Grintage', '32': 'Grintage',
@ -148,7 +262,7 @@
'37': 'Cyane', '37': 'Cyane',
'38': 'Light Pink', '38': 'Light Pink',
'39': 'Autumn', '39': 'Autumn',
'4': 'Set Colors', '4': '* Color Gradient',
'40': 'Magenta', '40': 'Magenta',
'41': 'Magred', '41': 'Magred',
'42': 'Yelmag', '42': 'Yelmag',
@ -159,9 +273,30 @@
'47': 'Orangery', '47': 'Orangery',
'48': 'C9', '48': 'C9',
'49': 'Sakura', '49': 'Sakura',
'5': 'Based on Set', '5': '* Colors Only',
'50': 'Aurora',
'51': 'Atlantica',
'52': 'C9 2',
'53': 'C9 New',
'54': 'Temperature',
'55': 'Aurora 2',
'56': 'Retro Clown',
'57': 'Candy',
'58': 'Toxy Reaf',
'59': 'Fairy Reaf',
'6': 'Party', '6': 'Party',
'60': 'Semi Blue',
'61': 'Pink Candy',
'62': 'Red Reaf',
'63': 'Aqua Flash',
'64': 'Yelblu Hot',
'65': 'Lite Light',
'66': 'Red Flash',
'67': 'Blink Red',
'68': 'Red Shift',
'69': 'Red Tide',
'7': 'Cloud', '7': 'Cloud',
'70': 'Candy2',
'8': 'Lava', '8': 'Lava',
'9': 'Ocean', '9': 'Ocean',
}), }),
@ -170,30 +305,88 @@
'presets': dict({ 'presets': dict({
}), }),
'state': dict({ 'state': dict({
'brightness': 127, 'bri': 128,
'lor': 0, 'lor': 0,
'nightlight': dict({ 'nl': dict({
'__type': "<class 'wled.models.Nightlight'>", 'dur': 60,
'repr': 'Nightlight(duration=60, fade=True, on=False, mode=<NightlightMode.FADE: 1>, target_brightness=0)', 'mode': 1,
'on': False,
'tbri': 0,
}), }),
'on': True, 'on': True,
'playlist': -1, 'seg': dict({
'preset': -1, '0': dict({
'segments': list([ 'bri': 255,
dict({ 'cln': -1,
'__type': "<class 'wled.models.Segment'>", 'col': list([
'repr': "Segment(brightness=127, clones=-1, color_primary=(255, 159, 0), color_secondary=(0, 0, 0), color_tertiary=(0, 0, 0), effect=Effect(effect_id=0, name='Solid'), intensity=128, length=20, on=True, palette=Palette(name='Default', palette_id=0), reverse=False, segment_id=0, selected=True, speed=32, start=0, stop=19)", list([
}), 127,
dict({ 172,
'__type': "<class 'wled.models.Segment'>", 255,
'repr': "Segment(brightness=127, clones=-1, color_primary=(0, 255, 123), color_secondary=(0, 0, 0), color_tertiary=(0, 0, 0), effect=Effect(effect_id=1, name='Blink'), intensity=64, length=10, on=True, palette=Palette(name='Random Cycle', palette_id=1), reverse=True, segment_id=1, selected=True, speed=16, start=20, stop=30)",
}),
]), ]),
'sync': dict({ list([
'__type': "<class 'wled.models.Sync'>", 0,
'repr': 'Sync(receive=True, send=False)', 0,
0,
]),
list([
0,
0,
0,
]),
]),
'fx': 0,
'id': 0,
'ix': 128,
'len': 15,
'on': True,
'pal': 0,
'rev': False,
'sel': False,
'start': 0,
'stop': 15,
'sx': 32,
}),
'1': dict({
'bri': 255,
'cln': -1,
'col': list([
list([
255,
170,
0,
]),
list([
0,
0,
0,
]),
list([
0,
0,
0,
]),
]),
'fx': 3,
'id': 1,
'ix': 64,
'len': 15,
'on': True,
'pal': 1,
'rev': True,
'sel': True,
'start': 15,
'stop': 30,
'sx': 16,
}),
}), }),
'transition': 7, 'transition': 7,
'udpn': dict({
'recv': True,
'rgrp': 1,
'send': False,
'sgrp': 1,
}),
}), }),
}) })
# --- # ---

View File

@ -67,7 +67,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -79,14 +79,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.5', 'sw_version': '0.14.4',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---
@ -158,7 +158,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -170,183 +170,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.5', 'sw_version': '0.14.4',
'via_device_id': None,
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_intensity-42-intensity]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Segment 1 Intensity',
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'context': <ANY>,
'entity_id': 'number.wled_rgb_light_segment_1_intensity',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '64',
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_intensity-42-intensity].1
EntityRegistryEntrySnapshot({
'_display_repr': <UndefinedType._singleton: 0>,
'_partial_repr': <UndefinedType._singleton: 0>,
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.wled_rgb_light_segment_1_intensity',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Segment 1 Intensity',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_intensity_1',
'unit_of_measurement': None,
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_intensity-42-intensity].2
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'configuration_url': 'http://127.0.0.1',
'connections': set({
tuple(
'mac',
'aa:bb:cc:dd:ee:ff',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'id': <ANY>,
'identifiers': set({
tuple(
'wled',
'aabbccddeeff',
),
}),
'is_new': False,
'manufacturer': 'WLED',
'model': 'DIY light',
'name': 'WLED RGB Light',
'name_by_user': None,
'suggested_area': None,
'sw_version': '0.8.5',
'via_device_id': None,
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_speed-42-speed]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Segment 1 Speed',
'icon': 'mdi:speedometer',
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'context': <ANY>,
'entity_id': 'number.wled_rgb_light_segment_1_speed',
'last_changed': <ANY>,
'last_updated': <ANY>,
'state': '16',
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_speed-42-speed].1
EntityRegistryEntrySnapshot({
'_display_repr': <UndefinedType._singleton: 0>,
'_partial_repr': <UndefinedType._singleton: 0>,
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'max': 255,
'min': 0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 1,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.wled_rgb_light_segment_1_speed',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': 'mdi:speedometer',
'original_name': 'Segment 1 Speed',
'platform': 'wled',
'supported_features': 0,
'translation_key': None,
'unique_id': 'aabbccddeeff_speed_1',
'unit_of_measurement': None,
})
# ---
# name: test_speed_state[number.wled_rgb_light_segment_1_speed-42-speed].2
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'configuration_url': 'http://127.0.0.1',
'connections': set({
tuple(
'mac',
'aa:bb:cc:dd:ee:ff',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'id': <ANY>,
'identifiers': set({
tuple(
'wled',
'aabbccddeeff',
),
}),
'is_new': False,
'manufacturer': 'WLED',
'model': 'DIY light',
'name': 'WLED RGB Light',
'name_by_user': None,
'suggested_area': None,
'sw_version': '0.8.5',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---

View File

@ -69,7 +69,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -81,14 +81,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.5', 'sw_version': '0.14.4',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---
@ -97,56 +97,77 @@
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Segment 1 color palette', 'friendly_name': 'WLED RGB Light Segment 1 color palette',
'options': list([ 'options': list([
'Analogous',
'April Night',
'Autumn',
'Based on Primary',
'Based on Set',
'Beach',
'Beech',
'Breeze',
'C9',
'Cloud',
'Cyane',
'Default', 'Default',
'Departure', '* Random Cycle',
'Drywet', '* Color 1',
'Fire', '* Colors 1&2',
'Forest', '* Color Gradient',
'Grintage', '* Colors Only',
'Hult',
'Hult 64',
'Icefire',
'Jul',
'Landscape',
'Lava',
'Light Pink',
'Magenta',
'Magred',
'Ocean',
'Orange & Teal',
'Orangery',
'Party', 'Party',
'Pastel', 'Cloud',
'Primary Color', 'Lava',
'Ocean',
'Forest',
'Rainbow', 'Rainbow',
'Rainbow Bands', 'Rainbow Bands',
'Random Cycle',
'Red & Blue',
'Rewhi',
'Rivendell',
'Sakura',
'Set Colors',
'Sherbet',
'Splash',
'Sunset', 'Sunset',
'Sunset 2', 'Rivendell',
'Tertiary', 'Breeze',
'Tiamat', 'Red & Blue',
'Vintage',
'Yelblu',
'Yellowout', 'Yellowout',
'Analogous',
'Splash',
'Pastel',
'Sunset 2',
'Beach',
'Vintage',
'Departure',
'Landscape',
'Beech',
'Sherbet',
'Hult',
'Hult 64',
'Drywet',
'Jul',
'Grintage',
'Rewhi',
'Tertiary',
'Fire',
'Icefire',
'Cyane',
'Light Pink',
'Autumn',
'Magenta',
'Magred',
'Yelmag', 'Yelmag',
'Yelblu',
'Orange & Teal',
'Tiamat',
'April Night',
'Orangery',
'C9',
'Sakura',
'Aurora',
'Atlantica',
'C9 2',
'C9 New',
'Temperature',
'Aurora 2',
'Retro Clown',
'Candy',
'Toxy Reaf',
'Fairy Reaf',
'Semi Blue',
'Pink Candy',
'Red Reaf',
'Aqua Flash',
'Yelblu Hot',
'Lite Light',
'Red Flash',
'Blink Red',
'Red Shift',
'Red Tide',
'Candy2',
]), ]),
}), }),
'context': <ANY>, 'context': <ANY>,
@ -154,7 +175,7 @@
'last_changed': <ANY>, 'last_changed': <ANY>,
'last_reported': <ANY>, 'last_reported': <ANY>,
'last_updated': <ANY>, 'last_updated': <ANY>,
'state': 'Random Cycle', 'state': '* Random Cycle',
}) })
# --- # ---
# name: test_color_palette_state[rgb-select.wled_rgb_light_segment_1_color_palette-Icefire-segment-called_with0].1 # name: test_color_palette_state[rgb-select.wled_rgb_light_segment_1_color_palette-Icefire-segment-called_with0].1
@ -164,56 +185,77 @@
'area_id': None, 'area_id': None,
'capabilities': dict({ 'capabilities': dict({
'options': list([ 'options': list([
'Analogous',
'April Night',
'Autumn',
'Based on Primary',
'Based on Set',
'Beach',
'Beech',
'Breeze',
'C9',
'Cloud',
'Cyane',
'Default', 'Default',
'Departure', '* Random Cycle',
'Drywet', '* Color 1',
'Fire', '* Colors 1&2',
'Forest', '* Color Gradient',
'Grintage', '* Colors Only',
'Hult',
'Hult 64',
'Icefire',
'Jul',
'Landscape',
'Lava',
'Light Pink',
'Magenta',
'Magred',
'Ocean',
'Orange & Teal',
'Orangery',
'Party', 'Party',
'Pastel', 'Cloud',
'Primary Color', 'Lava',
'Ocean',
'Forest',
'Rainbow', 'Rainbow',
'Rainbow Bands', 'Rainbow Bands',
'Random Cycle',
'Red & Blue',
'Rewhi',
'Rivendell',
'Sakura',
'Set Colors',
'Sherbet',
'Splash',
'Sunset', 'Sunset',
'Sunset 2', 'Rivendell',
'Tertiary', 'Breeze',
'Tiamat', 'Red & Blue',
'Vintage',
'Yelblu',
'Yellowout', 'Yellowout',
'Analogous',
'Splash',
'Pastel',
'Sunset 2',
'Beach',
'Vintage',
'Departure',
'Landscape',
'Beech',
'Sherbet',
'Hult',
'Hult 64',
'Drywet',
'Jul',
'Grintage',
'Rewhi',
'Tertiary',
'Fire',
'Icefire',
'Cyane',
'Light Pink',
'Autumn',
'Magenta',
'Magred',
'Yelmag', 'Yelmag',
'Yelblu',
'Orange & Teal',
'Tiamat',
'April Night',
'Orangery',
'C9',
'Sakura',
'Aurora',
'Atlantica',
'C9 2',
'C9 New',
'Temperature',
'Aurora 2',
'Retro Clown',
'Candy',
'Toxy Reaf',
'Fairy Reaf',
'Semi Blue',
'Pink Candy',
'Red Reaf',
'Aqua Flash',
'Yelblu Hot',
'Lite Light',
'Red Flash',
'Blink Red',
'Red Shift',
'Red Tide',
'Candy2',
]), ]),
}), }),
'config_entry_id': <ANY>, 'config_entry_id': <ANY>,
@ -256,7 +298,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -268,14 +310,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.5', 'sw_version': '0.14.4',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---
@ -293,7 +335,7 @@
'last_changed': <ANY>, 'last_changed': <ANY>,
'last_reported': <ANY>, 'last_reported': <ANY>,
'last_updated': <ANY>, 'last_updated': <ANY>,
'state': 'Playlist 1', 'state': 'unknown',
}) })
# --- # ---
# name: test_color_palette_state[rgbw-select.wled_rgbw_light_playlist-Playlist 2-playlist-called_with2].1 # name: test_color_palette_state[rgbw-select.wled_rgbw_light_playlist-Playlist 2-playlist-called_with2].1
@ -330,7 +372,7 @@
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': 0, 'supported_features': 0,
'translation_key': 'playlist', 'translation_key': 'playlist',
'unique_id': 'aabbccddee11_playlist', 'unique_id': 'aabbccddeeff_playlist',
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
@ -342,31 +384,31 @@
'connections': set({ 'connections': set({
tuple( tuple(
'mac', 'mac',
'aa:bb:cc:dd:ee:11', 'aa:bb:cc:dd:ee:ff',
), ),
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
'wled', 'wled',
'aabbccddee11', 'aabbccddeeff',
), ),
}), }),
'is_new': False, 'is_new': False,
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGBW Light', 'name': 'WLED RGBW Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.6b4', 'sw_version': '0.99.0b1',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---
@ -384,7 +426,7 @@
'last_changed': <ANY>, 'last_changed': <ANY>,
'last_reported': <ANY>, 'last_reported': <ANY>,
'last_updated': <ANY>, 'last_updated': <ANY>,
'state': 'Preset 1', 'state': 'unknown',
}) })
# --- # ---
# name: test_color_palette_state[rgbw-select.wled_rgbw_light_preset-Preset 2-preset-called_with3].1 # name: test_color_palette_state[rgbw-select.wled_rgbw_light_preset-Preset 2-preset-called_with3].1
@ -421,7 +463,7 @@
'previous_unique_id': None, 'previous_unique_id': None,
'supported_features': 0, 'supported_features': 0,
'translation_key': 'preset', 'translation_key': 'preset',
'unique_id': 'aabbccddee11_preset', 'unique_id': 'aabbccddeeff_preset',
'unit_of_measurement': None, 'unit_of_measurement': None,
}) })
# --- # ---
@ -433,31 +475,31 @@
'connections': set({ 'connections': set({
tuple( tuple(
'mac', 'mac',
'aa:bb:cc:dd:ee:11', 'aa:bb:cc:dd:ee:ff',
), ),
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
'wled', 'wled',
'aabbccddee11', 'aabbccddeeff',
), ),
}), }),
'is_new': False, 'is_new': False,
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGBW Light', 'name': 'WLED RGBW Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.6b4', 'sw_version': '0.99.0b1',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---

View File

@ -3,7 +3,6 @@
StateSnapshot({ StateSnapshot({
'attributes': ReadOnlyDict({ 'attributes': ReadOnlyDict({
'duration': 60, 'duration': 60,
'fade': True,
'friendly_name': 'WLED RGB Light Nightlight', 'friendly_name': 'WLED RGB Light Nightlight',
'target_brightness': 0, 'target_brightness': 0,
}), }),
@ -61,7 +60,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -73,14 +72,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.5', 'sw_version': '0.14.4',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---
@ -143,7 +142,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -155,14 +154,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.5', 'sw_version': '0.14.4',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---
@ -226,7 +225,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -238,14 +237,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.5', 'sw_version': '0.14.4',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---
@ -309,7 +308,7 @@
}), }),
'disabled_by': None, 'disabled_by': None,
'entry_type': None, 'entry_type': None,
'hw_version': 'esp8266', 'hw_version': 'esp32',
'id': <ANY>, 'id': <ANY>,
'identifiers': set({ 'identifiers': set({
tuple( tuple(
@ -321,14 +320,14 @@
'labels': set({ 'labels': set({
}), }),
'manufacturer': 'WLED', 'manufacturer': 'WLED',
'model': 'DIY light', 'model': 'FOSS',
'model_id': None, 'model_id': None,
'name': 'WLED RGB Light', 'name': 'WLED RGB Light',
'name_by_user': None, 'name_by_user': None,
'primary_config_entry': <ANY>, 'primary_config_entry': <ANY>,
'serial_number': None, 'serial_number': None,
'suggested_area': None, 'suggested_area': None,
'sw_version': '0.8.5', 'sw_version': '0.14.4',
'via_device_id': None, 'via_device_id': None,
}) })
# --- # ---

View File

@ -33,9 +33,7 @@ async def test_full_user_flow_implementation(hass: HomeAssistant) -> None:
assert result.get("title") == "WLED RGB Light" assert result.get("title") == "WLED RGB Light"
assert result.get("type") is FlowResultType.CREATE_ENTRY assert result.get("type") is FlowResultType.CREATE_ENTRY
assert "data" in result
assert result["data"][CONF_HOST] == "192.168.1.123" assert result["data"][CONF_HOST] == "192.168.1.123"
assert "result" in result
assert result["result"].unique_id == "aabbccddeeff" assert result["result"].unique_id == "aabbccddeeff"
@ -167,23 +165,6 @@ async def test_user_device_exists_abort(
assert result.get("reason") == "already_configured" assert result.get("reason") == "already_configured"
async def test_user_with_cct_channel_abort(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test we abort user flow if WLED device uses a CCT channel."""
mock_wled.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") is FlowResultType.ABORT
assert result.get("reason") == "cct_unsupported"
@pytest.mark.usefixtures("mock_wled") @pytest.mark.usefixtures("mock_wled")
async def test_zeroconf_without_mac_device_exists_abort( async def test_zeroconf_without_mac_device_exists_abort(
hass: HomeAssistant, hass: HomeAssistant,
@ -234,31 +215,6 @@ async def test_zeroconf_with_mac_device_exists_abort(
assert result.get("reason") == "already_configured" assert result.get("reason") == "already_configured"
async def test_zeroconf_with_cct_channel_abort(
hass: HomeAssistant,
mock_wled: MagicMock,
) -> None:
"""Test we abort zeroconf flow if WLED device uses a CCT channel."""
mock_wled.update.return_value.info.leds.cct = True
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_ZEROCONF},
data=zeroconf.ZeroconfServiceInfo(
ip_address=ip_address("192.168.1.123"),
ip_addresses=[ip_address("192.168.1.123")],
hostname="example.local.",
name="mock_name",
port=None,
properties={CONF_MAC: "aabbccddeeff"},
type="mock_type",
),
)
assert result.get("type") is FlowResultType.ABORT
assert result.get("reason") == "cct_unsupported"
async def test_options_flow( async def test_options_flow(
hass: HomeAssistant, mock_config_entry: MockConfigEntry hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None: ) -> None:

View File

@ -7,7 +7,6 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest import pytest
from wled import WLEDConnectionError from wled import WLEDConnectionError
from homeassistant.components.wled.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -44,7 +43,6 @@ async def test_load_unload_config_entry(
# Ensure everything is cleaned up nicely and are disconnected # Ensure everything is cleaned up nicely and are disconnected
assert mock_wled.disconnect.call_count == 1 assert mock_wled.disconnect.call_count == 1
assert not hass.data.get(DOMAIN)
@patch( @patch(
@ -69,21 +67,3 @@ async def test_setting_unique_id(
"""Test we set unique ID if not set yet.""" """Test we set unique ID if not set yet."""
assert init_integration.runtime_data assert init_integration.runtime_data
assert init_integration.unique_id == "aabbccddeeff" 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

View File

@ -1,6 +1,5 @@
"""Tests for the WLED light platform.""" """Tests for the WLED light platform."""
import json
from unittest.mock import MagicMock from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
@ -9,14 +8,21 @@ from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_EFFECT, ATTR_EFFECT,
ATTR_HS_COLOR, ATTR_HS_COLOR,
ATTR_RGB_COLOR, ATTR_RGB_COLOR,
ATTR_RGBW_COLOR, ATTR_RGBW_COLOR,
ATTR_SUPPORTED_COLOR_MODES,
ATTR_TRANSITION, ATTR_TRANSITION,
DOMAIN as LIGHT_DOMAIN, DOMAIN as LIGHT_DOMAIN,
ColorMode,
)
from homeassistant.components.wled.const import (
CONF_KEEP_MAIN_LIGHT,
DOMAIN,
SCAN_INTERVAL,
) )
from homeassistant.components.wled.const import CONF_KEEP_MAIN_LIGHT, SCAN_INTERVAL
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_ICON, ATTR_ICON,
@ -30,7 +36,11 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture from tests.common import (
MockConfigEntry,
async_fire_time_changed,
load_json_object_fixture,
)
pytestmark = pytest.mark.usefixtures("init_integration") pytestmark = pytest.mark.usefixtures("init_integration")
@ -41,9 +51,9 @@ async def test_rgb_light_state(
"""Test the creation and values of the WLED lights.""" """Test the creation and values of the WLED lights."""
# First segment of the strip # First segment of the strip
assert (state := hass.states.get("light.wled_rgb_light")) assert (state := hass.states.get("light.wled_rgb_light"))
assert state.attributes.get(ATTR_BRIGHTNESS) == 127 assert state.attributes.get(ATTR_BRIGHTNESS) == 255
assert state.attributes.get(ATTR_EFFECT) == "Solid" assert state.attributes.get(ATTR_EFFECT) == "Solid"
assert state.attributes.get(ATTR_HS_COLOR) == (37.412, 100.0) assert state.attributes.get(ATTR_HS_COLOR) == (218.906, 50.196)
assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_ICON) is None
assert state.state == STATE_ON assert state.state == STATE_ON
@ -52,9 +62,9 @@ async def test_rgb_light_state(
# Second segment of the strip # Second segment of the strip
assert (state := hass.states.get("light.wled_rgb_light_segment_1")) assert (state := hass.states.get("light.wled_rgb_light_segment_1"))
assert state.attributes.get(ATTR_BRIGHTNESS) == 127 assert state.attributes.get(ATTR_BRIGHTNESS) == 255
assert state.attributes.get(ATTR_EFFECT) == "Blink" assert state.attributes.get(ATTR_EFFECT) == "Wipe"
assert state.attributes.get(ATTR_HS_COLOR) == (148.941, 100.0) assert state.attributes.get(ATTR_HS_COLOR) == (40.0, 100.0)
assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_ICON) is None
assert state.state == STATE_ON assert state.state == STATE_ON
@ -63,7 +73,7 @@ async def test_rgb_light_state(
# Test main control of the lightstrip # Test main control of the lightstrip
assert (state := hass.states.get("light.wled_rgb_light_main")) assert (state := hass.states.get("light.wled_rgb_light_main"))
assert state.attributes.get(ATTR_BRIGHTNESS) == 127 assert state.attributes.get(ATTR_BRIGHTNESS) == 128
assert state.state == STATE_ON assert state.state == STATE_ON
assert (entry := entity_registry.async_get("light.wled_rgb_light_main")) assert (entry := entity_registry.async_get("light.wled_rgb_light_main"))
@ -188,8 +198,8 @@ async def test_dynamically_handle_segments(
assert not hass.states.get("light.wled_rgb_light_segment_1") assert not hass.states.get("light.wled_rgb_light_segment_1")
return_value = mock_wled.update.return_value return_value = mock_wled.update.return_value
mock_wled.update.return_value = WLEDDevice( mock_wled.update.return_value = WLEDDevice.from_dict(
json.loads(load_fixture("wled/rgb.json")) load_json_object_fixture("rgb.json", DOMAIN)
) )
freezer.tick(SCAN_INTERVAL) freezer.tick(SCAN_INTERVAL)
@ -327,6 +337,8 @@ async def test_rgbw_light(hass: HomeAssistant, mock_wled: MagicMock) -> None:
"""Test RGBW support for WLED.""" """Test RGBW support for WLED."""
assert (state := hass.states.get("light.wled_rgbw_light")) assert (state := hass.states.get("light.wled_rgbw_light"))
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes.get(ATTR_SUPPORTED_COLOR_MODES) == [ColorMode.RGBW]
assert state.attributes.get(ATTR_COLOR_MODE) == ColorMode.RGBW
assert state.attributes.get(ATTR_RGBW_COLOR) == (255, 0, 0, 139) assert state.attributes.get(ATTR_RGBW_COLOR) == (255, 0, 0, 139)
await hass.services.async_call( await hass.services.async_call(

View File

@ -1,6 +1,5 @@
"""Tests for the WLED number platform.""" """Tests for the WLED number platform."""
import json
from unittest.mock import MagicMock from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
@ -13,13 +12,13 @@ from homeassistant.components.number import (
DOMAIN as NUMBER_DOMAIN, DOMAIN as NUMBER_DOMAIN,
SERVICE_SET_VALUE, SERVICE_SET_VALUE,
) )
from homeassistant.components.wled.const import SCAN_INTERVAL from homeassistant.components.wled.const import DOMAIN, SCAN_INTERVAL
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from tests.common import async_fire_time_changed, load_fixture from tests.common import async_fire_time_changed, load_json_object_fixture
pytestmark = pytest.mark.usefixtures("init_integration") pytestmark = pytest.mark.usefixtures("init_integration")
@ -128,8 +127,8 @@ async def test_speed_dynamically_handle_segments(
# Test adding a segment dynamically... # Test adding a segment dynamically...
return_value = mock_wled.update.return_value return_value = mock_wled.update.return_value
mock_wled.update.return_value = WLEDDevice( mock_wled.update.return_value = WLEDDevice.from_dict(
json.loads(load_fixture("wled/rgb.json")) load_json_object_fixture("rgb.json", DOMAIN)
) )
freezer.tick(SCAN_INTERVAL) freezer.tick(SCAN_INTERVAL)

View File

@ -1,6 +1,5 @@
"""Tests for the WLED select platform.""" """Tests for the WLED select platform."""
import json
from unittest.mock import MagicMock from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
@ -9,18 +8,13 @@ from syrupy.assertion import SnapshotAssertion
from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError
from homeassistant.components.select import ATTR_OPTION, DOMAIN as SELECT_DOMAIN from homeassistant.components.select import ATTR_OPTION, DOMAIN as SELECT_DOMAIN
from homeassistant.components.wled.const import SCAN_INTERVAL from homeassistant.components.wled.const import DOMAIN, SCAN_INTERVAL
from homeassistant.const import ( from homeassistant.const import ATTR_ENTITY_ID, SERVICE_SELECT_OPTION, STATE_UNAVAILABLE
ATTR_ENTITY_ID,
SERVICE_SELECT_OPTION,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from tests.common import async_fire_time_changed, load_fixture from tests.common import async_fire_time_changed, load_json_object_fixture
pytestmark = pytest.mark.usefixtures("init_integration") pytestmark = pytest.mark.usefixtures("init_integration")
@ -135,8 +129,8 @@ async def test_color_palette_dynamically_handle_segments(
assert not hass.states.get("select.wled_rgb_light_segment_1_color_palette") assert not hass.states.get("select.wled_rgb_light_segment_1_color_palette")
return_value = mock_wled.update.return_value return_value = mock_wled.update.return_value
mock_wled.update.return_value = WLEDDevice( mock_wled.update.return_value = WLEDDevice.from_dict(
json.loads(load_fixture("wled/rgb.json")) load_json_object_fixture("rgb.json", DOMAIN)
) )
freezer.tick(SCAN_INTERVAL) freezer.tick(SCAN_INTERVAL)
@ -148,7 +142,7 @@ async def test_color_palette_dynamically_handle_segments(
assert ( assert (
segment1 := hass.states.get("select.wled_rgb_light_segment_1_color_palette") segment1 := hass.states.get("select.wled_rgb_light_segment_1_color_palette")
) )
assert segment1.state == "Random Cycle" assert segment1.state == "* Random Cycle"
# Test adding if segment shows up again, including the master entity # Test adding if segment shows up again, including the master entity
mock_wled.update.return_value = return_value mock_wled.update.return_value = return_value
@ -174,39 +168,3 @@ async def test_playlist_unavailable_without_playlists(hass: HomeAssistant) -> No
"""Test WLED playlist entity is unavailable when playlists are not available.""" """Test WLED playlist entity is unavailable when playlists are not available."""
assert (state := hass.states.get("select.wled_rgb_light_playlist")) assert (state := hass.states.get("select.wled_rgb_light_playlist"))
assert state.state == STATE_UNAVAILABLE assert state.state == STATE_UNAVAILABLE
@pytest.mark.parametrize("device_fixture", ["rgbw"])
async def test_old_style_preset_active(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
mock_wled: MagicMock,
) -> None:
"""Test unknown preset returned (when old style/unknown) preset is active."""
# Set device preset state to a random number
mock_wled.update.return_value.state.preset = 99
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert (state := hass.states.get("select.wled_rgbw_light_preset"))
assert state.state == STATE_UNKNOWN
@pytest.mark.parametrize("device_fixture", ["rgbw"])
async def test_old_style_playlist_active(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
mock_wled: MagicMock,
) -> None:
"""Test when old style playlist cycle is active."""
# Set device playlist to 0, which meant "cycle" previously.
mock_wled.update.return_value.state.playlist = 0
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert (state := hass.states.get("select.wled_rgbw_light_playlist"))
assert state.state == STATE_UNKNOWN

View File

@ -44,7 +44,7 @@ async def test_sensors(
== UnitOfElectricCurrent.MILLIAMPERE == UnitOfElectricCurrent.MILLIAMPERE
) )
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.CURRENT assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.CURRENT
assert state.state == "470" assert state.state == "515"
assert ( assert (
entry := entity_registry.async_get("sensor.wled_rgb_light_estimated_current") entry := entity_registry.async_get("sensor.wled_rgb_light_estimated_current")
@ -55,7 +55,7 @@ async def test_sensors(
assert (state := hass.states.get("sensor.wled_rgb_light_uptime")) assert (state := hass.states.get("sensor.wled_rgb_light_uptime"))
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TIMESTAMP assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TIMESTAMP
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None
assert state.state == "2019-11-11T09:10:00+00:00" assert state.state == "2019-11-11T08:54:26+00:00"
assert (entry := entity_registry.async_get("sensor.wled_rgb_light_uptime")) assert (entry := entity_registry.async_get("sensor.wled_rgb_light_uptime"))
assert entry.unique_id == "aabbccddeeff_uptime" assert entry.unique_id == "aabbccddeeff_uptime"
@ -64,7 +64,7 @@ async def test_sensors(
assert (state := hass.states.get("sensor.wled_rgb_light_free_memory")) assert (state := hass.states.get("sensor.wled_rgb_light_free_memory"))
assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_ICON) is None
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfInformation.BYTES assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfInformation.BYTES
assert state.state == "14600" assert state.state == "198384"
assert entry.entity_category is EntityCategory.DIAGNOSTIC assert entry.entity_category is EntityCategory.DIAGNOSTIC
assert (entry := entity_registry.async_get("sensor.wled_rgb_light_free_memory")) assert (entry := entity_registry.async_get("sensor.wled_rgb_light_free_memory"))
@ -74,7 +74,7 @@ async def test_sensors(
assert (state := hass.states.get("sensor.wled_rgb_light_wi_fi_signal")) assert (state := hass.states.get("sensor.wled_rgb_light_wi_fi_signal"))
assert state.attributes.get(ATTR_ICON) is None assert state.attributes.get(ATTR_ICON) is None
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
assert state.state == "76" assert state.state == "100"
assert entry.entity_category is EntityCategory.DIAGNOSTIC assert entry.entity_category is EntityCategory.DIAGNOSTIC
assert (entry := entity_registry.async_get("sensor.wled_rgb_light_wi_fi_signal")) assert (entry := entity_registry.async_get("sensor.wled_rgb_light_wi_fi_signal"))
@ -87,7 +87,7 @@ async def test_sensors(
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
== SIGNAL_STRENGTH_DECIBELS_MILLIWATT == SIGNAL_STRENGTH_DECIBELS_MILLIWATT
) )
assert state.state == "-62" assert state.state == "-43"
assert (entry := entity_registry.async_get("sensor.wled_rgb_light_wi_fi_rssi")) assert (entry := entity_registry.async_get("sensor.wled_rgb_light_wi_fi_rssi"))
assert entry.unique_id == "aabbccddeeff_wifi_rssi" assert entry.unique_id == "aabbccddeeff_wifi_rssi"

View File

@ -1,6 +1,5 @@
"""Tests for the WLED switch platform.""" """Tests for the WLED switch platform."""
import json
from unittest.mock import MagicMock from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
@ -9,7 +8,7 @@ from syrupy.assertion import SnapshotAssertion
from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.components.wled.const import SCAN_INTERVAL from homeassistant.components.wled.const import DOMAIN, SCAN_INTERVAL
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
@ -22,7 +21,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from tests.common import async_fire_time_changed, load_fixture from tests.common import async_fire_time_changed, load_json_object_fixture
pytestmark = pytest.mark.usefixtures("init_integration") pytestmark = pytest.mark.usefixtures("init_integration")
@ -144,8 +143,8 @@ async def test_switch_dynamically_handle_segments(
# Test adding a segment dynamically... # Test adding a segment dynamically...
return_value = mock_wled.update.return_value return_value = mock_wled.update.return_value
mock_wled.update.return_value = WLEDDevice( mock_wled.update.return_value = WLEDDevice.from_dict(
json.loads(load_fixture("wled/rgb.json")) load_json_object_fixture("rgb.json", DOMAIN)
) )
freezer.tick(SCAN_INTERVAL) freezer.tick(SCAN_INTERVAL)

View File

@ -2,8 +2,9 @@
from unittest.mock import MagicMock from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from wled import WLEDError from wled import Releases, WLEDError
from homeassistant.components.update import ( from homeassistant.components.update import (
ATTR_INSTALLED_VERSION, ATTR_INSTALLED_VERSION,
@ -16,6 +17,7 @@ from homeassistant.components.update import (
UpdateDeviceClass, UpdateDeviceClass,
UpdateEntityFeature, UpdateEntityFeature,
) )
from homeassistant.components.wled.const import RELEASES_SCAN_INTERVAL
from homeassistant.const import ( from homeassistant.const import (
ATTR_DEVICE_CLASS, ATTR_DEVICE_CLASS,
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
@ -31,6 +33,8 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from tests.common import async_fire_time_changed
pytestmark = pytest.mark.usefixtures("init_integration") pytestmark = pytest.mark.usefixtures("init_integration")
@ -45,12 +49,12 @@ async def test_update_available(
state.attributes[ATTR_ENTITY_PICTURE] state.attributes[ATTR_ENTITY_PICTURE]
== "https://brands.home-assistant.io/_/wled/icon.png" == "https://brands.home-assistant.io/_/wled/icon.png"
) )
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.8.5" assert state.attributes[ATTR_INSTALLED_VERSION] == "0.14.4"
assert state.attributes[ATTR_LATEST_VERSION] == "0.12.0" assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"
assert state.attributes[ATTR_RELEASE_SUMMARY] is None assert state.attributes[ATTR_RELEASE_SUMMARY] is None
assert ( assert (
state.attributes[ATTR_RELEASE_URL] state.attributes[ATTR_RELEASE_URL]
== "https://github.com/Aircoookie/WLED/releases/tag/v0.12.0" == "https://github.com/Aircoookie/WLED/releases/tag/v0.99.0"
) )
assert ( assert (
state.attributes[ATTR_SUPPORTED_FEATURES] state.attributes[ATTR_SUPPORTED_FEATURES]
@ -64,15 +68,26 @@ async def test_update_available(
assert entry.entity_category is EntityCategory.CONFIG assert entry.entity_category is EntityCategory.CONFIG
@pytest.mark.parametrize("device_fixture", ["rgb_no_update"])
async def test_update_information_available( async def test_update_information_available(
hass: HomeAssistant, entity_registry: er.EntityRegistry hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
entity_registry: er.EntityRegistry,
mock_wled_releases: MagicMock,
) -> None: ) -> None:
"""Test having no update information available at all.""" """Test having no update information available at all."""
mock_wled_releases.releases.return_value = Releases(
beta=None,
stable=None,
)
freezer.tick(RELEASES_SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert (state := hass.states.get("update.wled_rgb_light_firmware")) assert (state := hass.states.get("update.wled_rgb_light_firmware"))
assert state.attributes.get(ATTR_DEVICE_CLASS) == UpdateDeviceClass.FIRMWARE assert state.attributes.get(ATTR_DEVICE_CLASS) == UpdateDeviceClass.FIRMWARE
assert state.state == STATE_UNKNOWN assert state.state == STATE_UNKNOWN
assert state.attributes[ATTR_INSTALLED_VERSION] is None assert state.attributes[ATTR_INSTALLED_VERSION] == "0.14.4"
assert state.attributes[ATTR_LATEST_VERSION] is None assert state.attributes[ATTR_LATEST_VERSION] is None
assert state.attributes[ATTR_RELEASE_SUMMARY] is None assert state.attributes[ATTR_RELEASE_SUMMARY] is None
assert state.attributes[ATTR_RELEASE_URL] is None assert state.attributes[ATTR_RELEASE_URL] is None
@ -98,12 +113,12 @@ async def test_no_update_available(
assert (state := hass.states.get("update.wled_websocket_firmware")) assert (state := hass.states.get("update.wled_websocket_firmware"))
assert state.state == STATE_OFF assert state.state == STATE_OFF
assert state.attributes.get(ATTR_DEVICE_CLASS) == UpdateDeviceClass.FIRMWARE assert state.attributes.get(ATTR_DEVICE_CLASS) == UpdateDeviceClass.FIRMWARE
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.12.0-b2" assert state.attributes[ATTR_INSTALLED_VERSION] == "0.99.0"
assert state.attributes[ATTR_LATEST_VERSION] == "0.12.0-b2" assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"
assert state.attributes[ATTR_RELEASE_SUMMARY] is None assert state.attributes[ATTR_RELEASE_SUMMARY] is None
assert ( assert (
state.attributes[ATTR_RELEASE_URL] state.attributes[ATTR_RELEASE_URL]
== "https://github.com/Aircoookie/WLED/releases/tag/v0.12.0-b2" == "https://github.com/Aircoookie/WLED/releases/tag/v0.99.0"
) )
assert ( assert (
state.attributes[ATTR_SUPPORTED_FEATURES] state.attributes[ATTR_SUPPORTED_FEATURES]
@ -151,8 +166,8 @@ async def test_update_stay_stable(
""" """
assert (state := hass.states.get("update.wled_rgb_light_firmware")) assert (state := hass.states.get("update.wled_rgb_light_firmware"))
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.8.5" assert state.attributes[ATTR_INSTALLED_VERSION] == "0.14.4"
assert state.attributes[ATTR_LATEST_VERSION] == "0.12.0" assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"
await hass.services.async_call( await hass.services.async_call(
UPDATE_DOMAIN, UPDATE_DOMAIN,
@ -161,7 +176,7 @@ async def test_update_stay_stable(
blocking=True, blocking=True,
) )
assert mock_wled.upgrade.call_count == 1 assert mock_wled.upgrade.call_count == 1
mock_wled.upgrade.assert_called_with(version="0.12.0") mock_wled.upgrade.assert_called_with(version="0.99.0")
@pytest.mark.parametrize("device_fixture", ["rgbw"]) @pytest.mark.parametrize("device_fixture", ["rgbw"])
@ -177,8 +192,8 @@ async def test_update_beta_to_stable(
""" """
assert (state := hass.states.get("update.wled_rgbw_light_firmware")) assert (state := hass.states.get("update.wled_rgbw_light_firmware"))
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.8.6b4" assert state.attributes[ATTR_INSTALLED_VERSION] == "0.99.0b1"
assert state.attributes[ATTR_LATEST_VERSION] == "0.8.6" assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"
await hass.services.async_call( await hass.services.async_call(
UPDATE_DOMAIN, UPDATE_DOMAIN,
@ -187,7 +202,7 @@ async def test_update_beta_to_stable(
blocking=True, blocking=True,
) )
assert mock_wled.upgrade.call_count == 1 assert mock_wled.upgrade.call_count == 1
mock_wled.upgrade.assert_called_with(version="0.8.6") mock_wled.upgrade.assert_called_with(version="0.99.0")
@pytest.mark.parametrize("device_fixture", ["rgb_single_segment"]) @pytest.mark.parametrize("device_fixture", ["rgb_single_segment"])
@ -202,8 +217,8 @@ async def test_update_stay_beta(
""" """
assert (state := hass.states.get("update.wled_rgb_light_firmware")) assert (state := hass.states.get("update.wled_rgb_light_firmware"))
assert state.state == STATE_ON assert state.state == STATE_ON
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.8.6b1" assert state.attributes[ATTR_INSTALLED_VERSION] == "1.0.0b4"
assert state.attributes[ATTR_LATEST_VERSION] == "0.8.6b2" assert state.attributes[ATTR_LATEST_VERSION] == "1.0.0b5"
await hass.services.async_call( await hass.services.async_call(
UPDATE_DOMAIN, UPDATE_DOMAIN,
@ -212,4 +227,4 @@ async def test_update_stay_beta(
blocking=True, blocking=True,
) )
assert mock_wled.upgrade.call_count == 1 assert mock_wled.upgrade.call_count == 1
mock_wled.upgrade.assert_called_with(version="0.8.6b2") mock_wled.upgrade.assert_called_with(version="1.0.0b5")