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.const import Platform
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 .coordinator import WLEDDataUpdateCoordinator
from .const import DOMAIN
from .coordinator import WLEDDataUpdateCoordinator, WLEDReleasesDataUpdateCoordinator
PLATFORMS = (
Platform.BUTTON,
@ -21,23 +24,26 @@ PLATFORMS = (
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:
"""Set up WLED from a config entry."""
coordinator = WLEDDataUpdateCoordinator(hass, entry=entry)
await coordinator.async_config_entry_first_refresh()
if coordinator.data.info.leds.cct:
LOGGER.error(
(
"WLED device '%s' has a CCT channel, which is not supported by "
"this integration"
),
entry.title,
)
return False
entry.runtime_data = coordinator
entry.runtime_data = WLEDDataUpdateCoordinator(hass, entry=entry)
await entry.runtime_data.async_config_entry_first_refresh()
# Set up all platforms for this device/entry.
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

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

View File

@ -3,11 +3,16 @@
from datetime import timedelta
import logging
from wled import LightCapability
from homeassistant.components.light import ColorMode
# Integration domain
DOMAIN = "wled"
LOGGER = logging.getLogger(__package__)
SCAN_INTERVAL = timedelta(seconds=10)
RELEASES_SCAN_INTERVAL = timedelta(hours=3)
# Options
CONF_KEEP_MAIN_LIGHT = "keep_master_light"
@ -24,3 +29,72 @@ ATTR_SOFTWARE_VERSION = "sw_version"
ATTR_SPEED = "speed"
ATTR_TARGET_BRIGHTNESS = "target_brightness"
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 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.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP
@ -15,6 +22,7 @@ from .const import (
DEFAULT_KEEP_MAIN_LIGHT,
DOMAIN,
LOGGER,
RELEASES_SCAN_INTERVAL,
SCAN_INTERVAL,
)
@ -101,17 +109,37 @@ class WLEDDataUpdateCoordinator(DataUpdateCoordinator[WLEDDevice]):
async def _async_update_data(self) -> WLEDDevice:
"""Fetch data from WLED."""
try:
device = await self.wled.update(full_update=not self.last_update_success)
device = await self.wled.update()
except WLEDError as error:
raise UpdateFailed(f"Invalid response from API: {error}") from error
# If the device supports a WebSocket, try activating it.
if (
device.info.websocket is not None
and device.info.leds.cct is not True
and not self.wled.connected
and not self.unsub
):
self._use_websocket()
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
data: dict[str, Any] = {
"info": async_redact_data(coordinator.data.info.__dict__, "wifi"),
"state": coordinator.data.state.__dict__,
"info": async_redact_data(coordinator.data.info.to_dict(), "wifi"),
"state": coordinator.data.state.to_dict(),
"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": {
palette.palette_id: palette.name for palette in coordinator.data.palettes
palette.palette_id: palette.name
for palette in coordinator.data.palettes.values()
},
"playlists": {
playlist.playlist_id: {
"name": playlist.name,
"repeat": playlist.repeat,
"shuffle": playlist.shuffle,
"end": playlist.end.preset_id if playlist.end else None,
}
for playlist in coordinator.data.playlists
playlist.playlist_id: playlist.name
for playlist in coordinator.data.playlists.values()
},
"presets": {
preset.preset_id: {
"name": preset.name,
"quick_label": preset.quick_label,
"on": preset.on,
"transition": preset.transition,
}
for preset in coordinator.data.presets
preset.preset_id: preset.name
for preset in coordinator.data.presets.values()
},
}
return data

View File

@ -19,7 +19,12 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
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 .entity import WLEDEntity
from .helpers import wled_exception_handler
@ -112,8 +117,6 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
) -> None:
"""Initialize WLED segment light."""
super().__init__(coordinator=coordinator)
self._rgbw = coordinator.data.info.leds.rgbw
self._wv = coordinator.data.info.leds.wv
self._segment = segment
# 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}"
)
self._attr_color_mode = ColorMode.RGB
self._attr_supported_color_modes = {ColorMode.RGB}
if self._rgbw and self._wv:
self._attr_color_mode = ColorMode.RGBW
self._attr_supported_color_modes = {ColorMode.RGBW}
if (
coordinator.data.info.leds.segment_light_capabilities is not None
and (
color_modes := LIGHT_CAPABILITIES_COLOR_MODE_MAPPING.get(
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
def available(self) -> bool:
"""Return True if entity is available."""
try:
self.coordinator.data.state.segments[self._segment]
except IndexError:
except KeyError:
return False
return super().available
@ -146,20 +155,23 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
@property
def rgb_color(self) -> tuple[int, int, int] | None:
"""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
def rgbw_color(self) -> tuple[int, int, int, int] | None:
"""Return the color value."""
return cast(
tuple[int, int, int, int],
self.coordinator.data.state.segments[self._segment].color_primary,
)
if not (color := self.coordinator.data.state.segments[self._segment].color):
return None
return cast(tuple[int, int, int, int], color.primary)
@property
def effect(self) -> str | None:
"""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
def brightness(self) -> int | None:
@ -178,7 +190,7 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
@property
def effect_list(self) -> list[str]:
"""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
def is_on(self) -> bool:
@ -258,7 +270,11 @@ def async_update_segments(
async_add_entities: AddEntitiesCallback,
) -> None:
"""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] = []
# More than 1 segment now? No main? Add main controls

View File

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

View File

@ -44,7 +44,7 @@ async def async_setup_entry(
class WLEDNumberEntityDescription(NumberEntityDescription):
"""Class describing WLED number entities."""
value_fn: Callable[[Segment], float | None]
value_fn: Callable[[Segment], int | None]
NUMBERS = [
@ -64,7 +64,7 @@ NUMBERS = [
native_step=1,
native_min_value=0,
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."""
try:
self.coordinator.data.state.segments[self._segment]
except IndexError:
except KeyError:
return False
return super().available
@ -133,7 +133,11 @@ def async_update_segments(
async_add_entities: AddEntitiesCallback,
) -> None:
"""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] = []

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from functools import partial
from wled import Live, Playlist, Preset
from wled import LiveDataOverride
from homeassistant.components.select import SelectEntity
from homeassistant.const import EntityCategory
@ -56,17 +56,17 @@ class WLEDLiveOverrideSelect(WLEDEntity, SelectEntity):
super().__init__(coordinator=coordinator)
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
def current_option(self) -> str:
"""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
async def async_select_option(self, option: str) -> None:
"""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):
@ -79,7 +79,9 @@ class WLEDPresetSelect(WLEDEntity, SelectEntity):
super().__init__(coordinator=coordinator)
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
def available(self) -> bool:
@ -89,9 +91,13 @@ class WLEDPresetSelect(WLEDEntity, SelectEntity):
@property
def current_option(self) -> str | None:
"""Return the current selected preset."""
if not isinstance(self.coordinator.data.state.preset, Preset):
if not self.coordinator.data.state.preset_id:
return None
return self.coordinator.data.state.preset.name
if preset := self.coordinator.data.presets.get(
self.coordinator.data.state.preset_id
):
return preset.name
return None
@wled_exception_handler
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_options = [
playlist.name for playlist in self.coordinator.data.playlists
playlist.name for playlist in self.coordinator.data.playlists.values()
]
@property
@ -121,9 +127,13 @@ class WLEDPlaylistSelect(WLEDEntity, SelectEntity):
@property
def current_option(self) -> str | None:
"""Return the currently selected playlist."""
if not isinstance(self.coordinator.data.state.playlist, Playlist):
if not self.coordinator.data.state.playlist_id:
return None
return self.coordinator.data.state.playlist.name
if playlist := self.coordinator.data.playlists.get(
self.coordinator.data.state.playlist_id
):
return playlist.name
return None
@wled_exception_handler
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_options = [
palette.name for palette in self.coordinator.data.palettes
palette.name for palette in self.coordinator.data.palettes.values()
]
self._segment = segment
@ -159,7 +169,7 @@ class WLEDPaletteSelect(WLEDEntity, SelectEntity):
"""Return True if entity is available."""
try:
self.coordinator.data.state.segments[self._segment]
except IndexError:
except KeyError:
return False
return super().available
@ -167,7 +177,9 @@ class WLEDPaletteSelect(WLEDEntity, SelectEntity):
@property
def current_option(self) -> str | None:
"""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
async def async_select_option(self, option: str) -> None:
@ -182,7 +194,11 @@ def async_update_segments(
async_add_entities: AddEntitiesCallback,
) -> None:
"""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] = []

View File

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

View File

@ -21,8 +21,7 @@
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"cct_unsupported": "This WLED device uses CCT channels, which is not supported by this integration"
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
}
},
"options": {

View File

@ -11,7 +11,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
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 .entity import WLEDEntity
from .helpers import wled_exception_handler
@ -62,7 +62,6 @@ class WLEDNightlightSwitch(WLEDEntity, SwitchEntity):
state = self.coordinator.data.state
return {
ATTR_DURATION: state.nightlight.duration,
ATTR_FADE: state.nightlight.fade,
ATTR_TARGET_BRIGHTNESS: state.nightlight.target_brightness,
}
@ -171,7 +170,7 @@ class WLEDReverseSwitch(WLEDEntity, SwitchEntity):
"""Return True if entity is available."""
try:
self.coordinator.data.state.segments[self._segment]
except IndexError:
except KeyError:
return False
return super().available
@ -199,7 +198,11 @@ def async_update_segments(
async_add_entities: AddEntitiesCallback,
) -> None:
"""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] = []

View File

@ -12,8 +12,8 @@ from homeassistant.components.update import (
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import WLEDConfigEntry
from .coordinator import WLEDDataUpdateCoordinator
from . import WLED_KEY, WLEDConfigEntry
from .coordinator import WLEDDataUpdateCoordinator, WLEDReleasesDataUpdateCoordinator
from .entity import WLEDEntity
from .helpers import wled_exception_handler
@ -24,7 +24,7 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""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):
@ -36,11 +36,33 @@ class WLEDUpdateEntity(WLEDEntity, UpdateEntity):
)
_attr_title = "WLED"
def __init__(self, coordinator: WLEDDataUpdateCoordinator) -> None:
def __init__(
self,
coordinator: WLEDDataUpdateCoordinator,
releases_coordinator: WLEDReleasesDataUpdateCoordinator,
) -> None:
"""Initialize the update entity."""
super().__init__(coordinator=coordinator)
self.releases_coordinator = releases_coordinator
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
def installed_version(self) -> str | None:
"""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.
# Offer beta version upgrade, unless stable is newer
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.alpha or current.beta or current.release_candidate)
and (
(stable := self.coordinator.data.info.version_latest_stable) is None
or (stable is not None and stable < beta)
(stable := self.releases_coordinator.data.stable) is None
or (stable is not None and stable < beta and current > stable)
)
):
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 None

View File

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

View File

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

View File

@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from wled import Device as WLEDDevice
from wled import Device as WLEDDevice, Releases
from homeassistant.components.wled.const import DOMAIN
from homeassistant.const import CONF_HOST
@ -51,7 +51,24 @@ def device_fixture() -> str:
@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."""
with (
patch(
@ -60,11 +77,12 @@ def mock_wled(device_fixture: str) -> Generator[MagicMock]:
patch("homeassistant.components.wled.config_flow.WLED", new=wled_mock),
):
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)
)
wled.connected = False
wled.host = "127.0.0.1"
yield wled

View File

@ -1,28 +1,41 @@
{
"state": {
"on": true,
"bri": 127,
"bri": 128,
"transition": 7,
"ps": -1,
"pl": -1,
"nl": {
"on": false,
"dur": 60,
"fade": true,
"tbri": 0
"mode": 1,
"tbri": 0,
"rem": -1
},
"udpn": {
"send": false,
"recv": true
"recv": true,
"sgrp": 1,
"rgrp": 1
},
"lor": 0,
"mainseg": 1,
"seg": [
{
"id": 0,
"start": 0,
"stop": 19,
"len": 20,
"stop": 15,
"len": 15,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"col": [
[255, 159, 0],
[127, 172, 255],
[0, 0, 0],
[0, 0, 0]
],
@ -30,62 +43,106 @@
"sx": 32,
"ix": 128,
"pal": 0,
"sel": true,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": false,
"rev": false,
"cln": -1
"mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
},
{
"id": 1,
"start": 20,
"start": 15,
"stop": 30,
"len": 10,
"len": 15,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"col": [
[0, 255, 123],
[255, 170, 0],
[0, 0, 0],
[0, 0, 0]
],
"fx": 1,
"fx": 3,
"sx": 16,
"ix": 64,
"pal": 1,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true,
"rev": true,
"cln": -1
"mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
}
]
},
"info": {
"ver": "0.8.5",
"version_latest_stable": "0.12.0",
"version_latest_beta": "0.13.0b1",
"vid": 1909122,
"ver": "0.14.4",
"vid": 2405180,
"leds": {
"count": 30,
"rgbw": false,
"pin": [2],
"pwr": 470,
"pwr": 515,
"fps": 5,
"maxpwr": 850,
"maxseg": 10
"maxseg": 32,
"seglc": [1, 1],
"lc": 1,
"rgbw": false,
"wv": 0,
"cct": 0
},
"str": false,
"name": "WLED RGB Light",
"udpport": 21324,
"live": false,
"fxcount": 81,
"palcount": 50,
"liveseg": -1,
"lm": "",
"lip": "",
"ws": -1,
"fxcount": 187,
"palcount": 71,
"cpalcount": 0,
"maps": [
{
"id": 0
}
],
"wifi": {
"bssid": "AA:AA:AA:AA:AA:BB",
"rssi": -62,
"signal": 76,
"rssi": -43,
"signal": 100,
"channel": 11
},
"arch": "esp8266",
"core": "2_4_2",
"freeheap": 14600,
"uptime": 32,
"opt": 119,
"fs": {
"u": 12,
"t": 983,
"pmt": 1718827787
},
"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",
"product": "DIY light",
"btype": "bin",
"product": "FOSS",
"mac": "aabbccddeeff",
"ip": "127.0.0.1"
},
@ -101,21 +158,21 @@
"Colorloop",
"Rainbow",
"Scan",
"Dual Scan",
"Scan Dual",
"Fade",
"Chase",
"Chase Rainbow",
"Theater",
"Theater Rainbow",
"Running",
"Saw",
"Twinkle",
"Dissolve",
"Dissolve Rnd",
"Sparkle",
"Dark Sparkle",
"Sparkle Dark",
"Sparkle+",
"Strobe",
"Strobe Rainbow",
"Mega Strobe",
"Strobe Mega",
"Blink Rainbow",
"Android",
"Chase",
@ -127,30 +184,30 @@
"Colorful",
"Traffic Light",
"Sweep Random",
"Running 2",
"Red & Blue",
"Chase 2",
"Aurora",
"Stream",
"Scanner",
"Lighthouse",
"Fireworks",
"Rain",
"Merry Christmas",
"Tetrix",
"Fire Flicker",
"Gradient",
"Loading",
"In Out",
"In In",
"Out Out",
"Out In",
"Circus",
"Halloween",
"Tri Chase",
"Rolling Balls",
"Fairy",
"Two Dots",
"Fairytwinkle",
"Running Dual",
"RSVD",
"Chase 3",
"Tri Wipe",
"Tri Fade",
"Lightning",
"ICU",
"Multi Comet",
"Dual Scanner",
"Scanner Dual",
"Stream 2",
"Oscillate",
"Pride 2015",
@ -158,27 +215,133 @@
"Palette",
"Fire 2012",
"Colorwaves",
"BPM",
"Bpm",
"Fill Noise",
"Noise 1",
"Noise 2",
"Noise 3",
"Noise 4",
"Colortwinkle",
"Colortwinkles",
"Lake",
"Meteor",
"Smooth Meteor",
"Meteor Smooth",
"Railway",
"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": [
"Default",
"Random Cycle",
"Primary Color",
"Based on Primary",
"Set Colors",
"Based on Set",
"* Random Cycle",
"* Color 1",
"* Colors 1&2",
"* Color Gradient",
"* Colors Only",
"Party",
"Cloud",
"Lava",
@ -195,11 +358,11 @@
"Splash",
"Pastel",
"Sunset 2",
"Beech",
"Beach",
"Vintage",
"Departure",
"Landscape",
"Beach",
"Beech",
"Sherbet",
"Hult",
"Hult 64",
@ -222,6 +385,27 @@
"April Night",
"Orangery",
"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": {
"on": true,
"bri": 127,
"bri": 128,
"transition": 7,
"ps": -1,
"pl": -1,
"nl": {
"on": false,
"dur": 60,
"fade": true,
"tbri": 0
"mode": 1,
"tbri": 0,
"rem": -1
},
"udpn": {
"send": false,
"recv": true
"recv": true,
"sgrp": 1,
"rgrp": 1
},
"lor": 0,
"mainseg": 0,
"seg": [
{
"id": 0,
"start": 0,
"stop": 30,
"len": 20,
"len": 30,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"col": [
[255, 159, 0],
[127, 172, 255],
[0, 0, 0],
[0, 0, 0]
],
@ -30,44 +43,72 @@
"sx": 32,
"ix": 128,
"pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true,
"rev": false,
"cln": -1
"mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
}
]
},
"info": {
"ver": "0.8.6b1",
"version_latest_stable": "0.8.5",
"version_latest_beta": "0.8.6b2",
"vid": 1909122,
"ver": "1.0.0b4",
"vid": 2405180,
"leds": {
"count": 30,
"rgbw": false,
"pin": [2],
"pwr": 470,
"pwr": 536,
"fps": 5,
"maxpwr": 850,
"maxseg": 10
"maxseg": 32,
"seglc": [1],
"lc": 1,
"rgbw": false,
"wv": 0,
"cct": 0
},
"str": false,
"name": "WLED RGB Light",
"udpport": 21324,
"live": false,
"fxcount": 81,
"palcount": 50,
"liveseg": -1,
"lm": "",
"lip": "",
"ws": -1,
"fxcount": 187,
"palcount": 71,
"cpalcount": 0,
"maps": [
{
"id": 0
}
],
"wifi": {
"bssid": "AA:AA:AA:AA:AA:BB",
"rssi": -62,
"signal": 76,
"rssi": -44,
"signal": 100,
"channel": 11
},
"arch": "esp8266",
"core": "2_4_2",
"freeheap": 14600,
"uptime": 32,
"opt": 119,
"fs": {
"u": 12,
"t": 983,
"pmt": 0
},
"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",
"product": "DIY light",
"btype": "bin",
"product": "FOSS",
"mac": "aabbccddeeff",
"ip": "127.0.0.1"
},
@ -83,21 +124,21 @@
"Colorloop",
"Rainbow",
"Scan",
"Dual Scan",
"Scan Dual",
"Fade",
"Chase",
"Chase Rainbow",
"Theater",
"Theater Rainbow",
"Running",
"Saw",
"Twinkle",
"Dissolve",
"Dissolve Rnd",
"Sparkle",
"Dark Sparkle",
"Sparkle Dark",
"Sparkle+",
"Strobe",
"Strobe Rainbow",
"Mega Strobe",
"Strobe Mega",
"Blink Rainbow",
"Android",
"Chase",
@ -109,30 +150,30 @@
"Colorful",
"Traffic Light",
"Sweep Random",
"Running 2",
"Red & Blue",
"Chase 2",
"Aurora",
"Stream",
"Scanner",
"Lighthouse",
"Fireworks",
"Rain",
"Merry Christmas",
"Tetrix",
"Fire Flicker",
"Gradient",
"Loading",
"In Out",
"In In",
"Out Out",
"Out In",
"Circus",
"Halloween",
"Tri Chase",
"Rolling Balls",
"Fairy",
"Two Dots",
"Fairytwinkle",
"Running Dual",
"RSVD",
"Chase 3",
"Tri Wipe",
"Tri Fade",
"Lightning",
"ICU",
"Multi Comet",
"Dual Scanner",
"Scanner Dual",
"Stream 2",
"Oscillate",
"Pride 2015",
@ -140,27 +181,133 @@
"Palette",
"Fire 2012",
"Colorwaves",
"BPM",
"Bpm",
"Fill Noise",
"Noise 1",
"Noise 2",
"Noise 3",
"Noise 4",
"Colortwinkle",
"Colortwinkles",
"Lake",
"Meteor",
"Smooth Meteor",
"Meteor Smooth",
"Railway",
"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": [
"Default",
"Random Cycle",
"Primary Color",
"Based on Primary",
"Set Colors",
"Based on Set",
"* Random Cycle",
"* Color 1",
"* Colors 1&2",
"* Color Gradient",
"* Colors Only",
"Party",
"Cloud",
"Lava",
@ -177,11 +324,11 @@
"Splash",
"Pastel",
"Sunset 2",
"Beech",
"Beach",
"Vintage",
"Departure",
"Landscape",
"Beach",
"Beech",
"Sherbet",
"Hult",
"Hult 64",
@ -204,6 +351,27 @@
"April Night",
"Orangery",
"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": {
"on": true,
"bri": 255,
"bri": 128,
"transition": 7,
"ps": -1,
"pl": -1,
"ccnf": {
"min": 1,
"max": 5,
"time": 12
},
"nl": {
"on": false,
"dur": 60,
"fade": true,
"mode": 1,
"tbri": 0,
"rem": -1
},
"udpn": {
"send": false,
"recv": true
"recv": true,
"sgrp": 1,
"rgrp": 1
},
"lor": 0,
"mainseg": 0,
@ -28,70 +24,89 @@
{
"id": 0,
"start": 0,
"stop": 13,
"len": 13,
"stop": 30,
"len": 30,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"col": [
[255, 181, 218],
[127, 172, 255],
[0, 0, 0],
[0, 0, 0]
],
"fx": 0,
"sx": 43,
"sx": 128,
"ix": 128,
"pal": 2,
"pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true,
"rev": false,
"mi": false
"mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
}
]
},
"info": {
"ver": "0.12.0-b2",
"version_latest_stable": "0.11.0",
"version_latest_beta": "0.12.0-b2",
"vid": 2103220,
"ver": "0.99.0",
"vid": 2405180,
"leds": {
"count": 13,
"count": 30,
"pwr": 536,
"fps": 5,
"maxpwr": 850,
"maxseg": 32,
"seglc": [1],
"lc": 1,
"rgbw": false,
"wv": false,
"pin": [2],
"pwr": 266,
"fps": 2,
"maxpwr": 1000,
"maxseg": 12,
"seglock": false
"wv": 0,
"cct": 0
},
"str": false,
"name": "WLED WebSocket",
"udpport": 21324,
"live": false,
"liveseg": -1,
"lm": "",
"lip": "",
"ws": 0,
"fxcount": 118,
"palcount": 56,
"fxcount": 187,
"palcount": 71,
"cpalcount": 0,
"maps": [
{
"id": 0
}
],
"wifi": {
"bssid": "AA:AA:AA:AA:AA:BB",
"rssi": -68,
"signal": 64,
"channel": 6
"rssi": -44,
"signal": 100,
"channel": 11
},
"fs": {
"u": 40,
"t": 1024,
"pmt": 1623156685
"u": 12,
"t": 983,
"pmt": 0
},
"ndc": 1,
"arch": "esp8266",
"core": "2_7_4_7",
"lwip": 1,
"freeheap": 22752,
"uptime": 258411,
"opt": 127,
"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",
"product": "FOSS",
"mac": "aabbccddeeff",
@ -135,7 +150,7 @@
"Colorful",
"Traffic Light",
"Sweep Random",
"Running 2",
"Chase 2",
"Aurora",
"Stream",
"Scanner",
@ -146,13 +161,13 @@
"Fire Flicker",
"Gradient",
"Loading",
"Police",
"Police All",
"Rolling Balls",
"Fairy",
"Two Dots",
"Two Areas",
"Circus",
"Halloween",
"Tri Chase",
"Fairytwinkle",
"Running Dual",
"RSVD",
"Chase 3",
"Tri Wipe",
"Tri Fade",
"Lightning",
@ -212,10 +227,79 @@
"Chunchun",
"Dancing Shadows",
"Washing Machine",
"Candy Cane",
"RSVD",
"Blends",
"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": [
"Default",
@ -240,11 +324,11 @@
"Splash",
"Pastel",
"Sunset 2",
"Beech",
"Beach",
"Vintage",
"Departure",
"Landscape",
"Beach",
"Beech",
"Sherbet",
"Hult",
"Hult 64",
@ -273,6 +357,21 @@
"C9 2",
"C9 New",
"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": {
"on": true,
"bri": 140,
"bri": 128,
"transition": 7,
"ps": 1,
"pl": 3,
"ps": -1,
"pl": -1,
"nl": {
"on": false,
"dur": 60,
"fade": true,
"tbri": 0
"mode": 1,
"tbri": 0,
"rem": -1
},
"udpn": {
"send": false,
"recv": true
"recv": true,
"sgrp": 1,
"rgrp": 1
},
"lor": 0,
"mainseg": 0,
"seg": [
{
"id": 0,
"start": 0,
"stop": 13,
"len": 13,
"stop": 30,
"len": 30,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"col": [
[255, 0, 0, 139],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
"fx": 9,
"sx": 165,
"fx": 0,
"sx": 128,
"ix": 128,
"pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true,
"rev": false,
"cln": -1
"mi": false,
"o1": false,
"o2": false,
"o3": false,
"si": 0,
"m12": 0
}
]
},
"info": {
"ver": "0.8.6b4",
"version_latest_stable": "0.8.6",
"version_latest_beta": "0.8.6b5",
"vid": 1910255,
"ver": "0.99.0b1",
"vid": 2405180,
"leds": {
"count": 13,
"rgbw": true,
"pin": [2],
"pwr": 208,
"count": 30,
"pwr": 536,
"fps": 5,
"maxpwr": 850,
"maxseg": 10
"maxseg": 32,
"seglc": [3],
"lc": 3,
"rgbw": true,
"wv": 0,
"cct": 0
},
"str": false,
"name": "WLED RGBW Light",
"udpport": 21324,
"live": false,
"fxcount": 83,
"palcount": 50,
"liveseg": -1,
"lm": "",
"lip": "",
"ws": -1,
"fxcount": 187,
"palcount": 71,
"cpalcount": 0,
"maps": [
{
"id": 0
}
],
"wifi": {
"bssid": "AA:AA:AA:AA:AA:BB",
"rssi": -62,
"signal": 76,
"rssi": -44,
"signal": 100,
"channel": 11
},
"arch": "esp8266",
"core": "2_5_2",
"freeheap": 20136,
"uptime": 5591,
"opt": 119,
"fs": {
"u": 12,
"t": 983,
"pmt": 0
},
"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",
"product": "DIY light",
"btype": "bin",
"mac": "aabbccddee11",
"product": "FOSS",
"mac": "aabbccddeeff",
"ip": "127.0.0.1"
},
"effects": [
@ -83,21 +124,21 @@
"Colorloop",
"Rainbow",
"Scan",
"Dual Scan",
"Scan Dual",
"Fade",
"Chase",
"Chase Rainbow",
"Theater",
"Theater Rainbow",
"Running",
"Saw",
"Twinkle",
"Dissolve",
"Dissolve Rnd",
"Sparkle",
"Dark Sparkle",
"Sparkle Dark",
"Sparkle+",
"Strobe",
"Strobe Rainbow",
"Mega Strobe",
"Strobe Mega",
"Blink Rainbow",
"Android",
"Chase",
@ -109,30 +150,30 @@
"Colorful",
"Traffic Light",
"Sweep Random",
"Running 2",
"Red & Blue",
"Chase 2",
"Aurora",
"Stream",
"Scanner",
"Lighthouse",
"Fireworks",
"Rain",
"Merry Christmas",
"Tetrix",
"Fire Flicker",
"Gradient",
"Loading",
"In Out",
"In In",
"Out Out",
"Out In",
"Circus",
"Halloween",
"Tri Chase",
"Rolling Balls",
"Fairy",
"Two Dots",
"Fairytwinkle",
"Running Dual",
"RSVD",
"Chase 3",
"Tri Wipe",
"Tri Fade",
"Lightning",
"ICU",
"Multi Comet",
"Dual Scanner",
"Scanner Dual",
"Stream 2",
"Oscillate",
"Pride 2015",
@ -140,7 +181,7 @@
"Palette",
"Fire 2012",
"Colorwaves",
"BPM",
"Bpm",
"Fill Noise",
"Noise 1",
"Noise 2",
@ -149,20 +190,124 @@
"Colortwinkles",
"Lake",
"Meteor",
"Smooth Meteor",
"Meteor Smooth",
"Railway",
"Ripple",
"Twinklefox",
"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": [
"Default",
"Random Cycle",
"Primary Color",
"Based on Primary",
"Set Colors",
"Based on Set",
"* Random Cycle",
"* Color 1",
"* Colors 1&2",
"* Color Gradient",
"* Colors Only",
"Party",
"Cloud",
"Lava",
@ -179,11 +324,11 @@
"Splash",
"Pastel",
"Sunset 2",
"Beech",
"Beach",
"Vintage",
"Departure",
"Landscape",
"Beach",
"Beech",
"Sherbet",
"Hult",
"Hult 64",
@ -206,36 +351,82 @@
"April Night",
"Orangery",
"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": {
"0": {},
"1": {
"on": false,
"bri": 255,
"on": true,
"bri": 128,
"transition": 7,
"mainseg": 0,
"seg": [
{
"id": 0,
"start": 0,
"stop": 13,
"stop": 131,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"n": "",
"col": [
[97, 144, 255],
[40, 255, 3],
[0, 0, 0],
[0, 0, 0]
],
"fx": 9,
"sx": 183,
"ix": 255,
"pal": 1,
"fx": 0,
"sx": 128,
"ix": 128,
"pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true,
"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
@ -274,31 +465,56 @@
"n": "Preset 1"
},
"2": {
"on": false,
"bri": 255,
"on": true,
"bri": 128,
"transition": 7,
"mainseg": 0,
"seg": [
{
"id": 0,
"start": 0,
"stop": 13,
"stop": 131,
"grp": 1,
"spc": 0,
"of": 0,
"on": true,
"frz": false,
"bri": 255,
"cct": 127,
"set": 0,
"n": "",
"col": [
[97, 144, 255],
[51, 88, 255],
[0, 0, 0],
[0, 0, 0]
],
"fx": 9,
"sx": 183,
"ix": 255,
"pal": 1,
"fx": 0,
"sx": 128,
"ix": 128,
"pal": 0,
"c1": 128,
"c2": 128,
"c3": 16,
"sel": true,
"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
@ -339,23 +555,25 @@
"3": {
"playlist": {
"ps": [1, 2],
"dur": [30, 30],
"dur": [300, 300],
"transition": [7, 7],
"repeat": 0,
"r": false,
"end": 0
"end": 0,
"r": 0
},
"on": true,
"n": "Playlist 1"
},
"4": {
"playlist": {
"ps": [1, 2],
"dur": [30, 30],
"ps": [2, 0],
"dur": [300, 300],
"transition": [7, 7],
"repeat": 0,
"r": false,
"end": 0
"end": 0,
"r": 0
},
"on": true,
"n": "Playlist 2"
}
}

View File

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

View File

@ -5,22 +5,109 @@
'0': 'Solid',
'1': 'Blink',
'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',
'13': 'Chase',
'14': 'Chase Rainbow',
'120': 'Ghost Rider',
'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',
'150': 'Squared Swirl',
'151': 'RSVD',
'152': 'DNA',
'153': 'Matrix',
'154': 'Metaballs',
'155': 'Freqmap',
'156': 'Gravcenter',
'157': 'Gravcentric',
'158': 'Gravfreq',
'159': 'DJ Light',
'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',
'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',
'180': 'Hiphotic',
'181': 'Sindots',
'182': 'DNA Spiral',
'183': 'Black Hole',
'184': 'Wavesins',
'185': 'Rocktaves',
'186': 'Akemi',
'19': 'Dissolve Rnd',
'2': 'Breathe',
'20': 'Sparkle',
'21': 'Dark Sparkle',
'21': 'Sparkle Dark',
'22': 'Sparkle+',
'23': 'Strobe',
'24': 'Strobe Rainbow',
'25': 'Mega Strobe',
'25': 'Strobe Mega',
'26': 'Blink Rainbow',
'27': 'Android',
'28': 'Chase',
@ -33,33 +120,33 @@
'34': 'Colorful',
'35': 'Traffic Light',
'36': 'Sweep Random',
'37': 'Running 2',
'38': 'Red & Blue',
'37': 'Chase 2',
'38': 'Aurora',
'39': 'Stream',
'4': 'Wipe Random',
'40': 'Scanner',
'41': 'Lighthouse',
'42': 'Fireworks',
'43': 'Rain',
'44': 'Merry Christmas',
'44': 'Tetrix',
'45': 'Fire Flicker',
'46': 'Gradient',
'47': 'Loading',
'48': 'In Out',
'49': 'In In',
'48': 'Rolling Balls',
'49': 'Fairy',
'5': 'Random Colors',
'50': 'Out Out',
'51': 'Out In',
'52': 'Circus',
'53': 'Halloween',
'54': 'Tri Chase',
'50': 'Two Dots',
'51': 'Fairytwinkle',
'52': 'Running Dual',
'53': 'RSVD',
'54': 'Chase 3',
'55': 'Tri Wipe',
'56': 'Tri Fade',
'57': 'Lightning',
'58': 'ICU',
'59': 'Multi Comet',
'6': 'Sweep',
'60': 'Dual Scanner',
'60': 'Scanner Dual',
'61': 'Stream 2',
'62': 'Oscillate',
'63': 'Pride 2015',
@ -67,55 +154,82 @@
'65': 'Palette',
'66': 'Fire 2012',
'67': 'Colorwaves',
'68': 'BPM',
'68': 'Bpm',
'69': 'Fill Noise',
'7': 'Dynamic',
'70': 'Noise 1',
'71': 'Noise 2',
'72': 'Noise 3',
'73': 'Noise 4',
'74': 'Colortwinkle',
'74': 'Colortwinkles',
'75': 'Lake',
'76': 'Meteor',
'77': 'Smooth Meteor',
'77': 'Meteor Smooth',
'78': 'Railway',
'79': 'Ripple',
'8': 'Colorloop',
'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',
'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({
'architecture': 'esp8266',
'arduino_core_version': '2.4.2',
'arch': 'esp32',
'brand': 'WLED',
'build_type': 'bin',
'effect_count': 81,
'filesystem': None,
'free_heap': 14600,
'core': 'v3.3.6-16-gcc5440f6a2',
'freeheap': 198384,
'fs': dict({
'pmt': 1718827787.0,
't': 983,
'u': 12,
}),
'fxcount': 187,
'ip': '127.0.0.1',
'leds': dict({
'__type': "<class 'wled.models.Leds'>",
'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)',
'count': 30,
'fps': 5,
'light_capabilities': 1,
'max_power': 850,
'max_segments': 32,
'power': 515,
'segment_light_capabilities': list([
1,
1,
]),
}),
'lip': '',
'live': False,
'live_ip': 'Unknown',
'live_mode': 'Unknown',
'mac_address': 'aabbccddeeff',
'lm': '',
'mac': 'aabbccddeeff',
'name': 'WLED RGB Light',
'pallet_count': 50,
'product': 'DIY light',
'udp_port': 21324,
'uptime': 32,
'version': '0.8.5',
'version_id': 1909122,
'version_latest_beta': '0.13.0b1',
'version_latest_stable': '0.12.0',
'websocket': None,
'palcount': 71,
'product': 'FOSS',
'udpport': 21324,
'uptime': 966,
'ver': '0.14.4',
'vid': 2405180,
'wifi': '**REDACTED**',
}),
'palettes': dict({
'0': 'Default',
'1': 'Random Cycle',
'1': '* Random Cycle',
'10': 'Forest',
'11': 'Rainbow',
'12': 'Rainbow Bands',
@ -126,18 +240,18 @@
'17': 'Yellowout',
'18': 'Analogous',
'19': 'Splash',
'2': 'Primary Color',
'2': '* Color 1',
'20': 'Pastel',
'21': 'Sunset 2',
'22': 'Beech',
'22': 'Beach',
'23': 'Vintage',
'24': 'Departure',
'25': 'Landscape',
'26': 'Beach',
'26': 'Beech',
'27': 'Sherbet',
'28': 'Hult',
'29': 'Hult 64',
'3': 'Based on Primary',
'3': '* Colors 1&2',
'30': 'Drywet',
'31': 'Jul',
'32': 'Grintage',
@ -148,7 +262,7 @@
'37': 'Cyane',
'38': 'Light Pink',
'39': 'Autumn',
'4': 'Set Colors',
'4': '* Color Gradient',
'40': 'Magenta',
'41': 'Magred',
'42': 'Yelmag',
@ -159,9 +273,30 @@
'47': 'Orangery',
'48': 'C9',
'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',
'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',
'70': 'Candy2',
'8': 'Lava',
'9': 'Ocean',
}),
@ -170,30 +305,88 @@
'presets': dict({
}),
'state': dict({
'brightness': 127,
'bri': 128,
'lor': 0,
'nightlight': dict({
'__type': "<class 'wled.models.Nightlight'>",
'repr': 'Nightlight(duration=60, fade=True, on=False, mode=<NightlightMode.FADE: 1>, target_brightness=0)',
'nl': dict({
'dur': 60,
'mode': 1,
'on': False,
'tbri': 0,
}),
'on': True,
'playlist': -1,
'preset': -1,
'segments': list([
dict({
'__type': "<class 'wled.models.Segment'>",
'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)",
'seg': dict({
'0': dict({
'bri': 255,
'cln': -1,
'col': list([
list([
127,
172,
255,
]),
list([
0,
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,
}),
dict({
'__type': "<class 'wled.models.Segment'>",
'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)",
'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,
}),
]),
'sync': dict({
'__type': "<class 'wled.models.Sync'>",
'repr': 'Sync(receive=True, send=False)',
}),
'transition': 7,
'udpn': dict({
'recv': True,
'rgrp': 1,
'send': False,
'sgrp': 1,
}),
}),
})
# ---

View File

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

View File

@ -69,7 +69,7 @@
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'hw_version': 'esp32',
'id': <ANY>,
'identifiers': set({
tuple(
@ -81,14 +81,14 @@
'labels': set({
}),
'manufacturer': 'WLED',
'model': 'DIY light',
'model': 'FOSS',
'model_id': None,
'name': 'WLED RGB Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '0.8.5',
'sw_version': '0.14.4',
'via_device_id': None,
})
# ---
@ -97,56 +97,77 @@
'attributes': ReadOnlyDict({
'friendly_name': 'WLED RGB Light Segment 1 color palette',
'options': list([
'Analogous',
'April Night',
'Autumn',
'Based on Primary',
'Based on Set',
'Beach',
'Beech',
'Breeze',
'C9',
'Cloud',
'Cyane',
'Default',
'Departure',
'Drywet',
'Fire',
'Forest',
'Grintage',
'Hult',
'Hult 64',
'Icefire',
'Jul',
'Landscape',
'Lava',
'Light Pink',
'Magenta',
'Magred',
'Ocean',
'Orange & Teal',
'Orangery',
'* Random Cycle',
'* Color 1',
'* Colors 1&2',
'* Color Gradient',
'* Colors Only',
'Party',
'Pastel',
'Primary Color',
'Cloud',
'Lava',
'Ocean',
'Forest',
'Rainbow',
'Rainbow Bands',
'Random Cycle',
'Red & Blue',
'Rewhi',
'Rivendell',
'Sakura',
'Set Colors',
'Sherbet',
'Splash',
'Sunset',
'Sunset 2',
'Tertiary',
'Tiamat',
'Vintage',
'Yelblu',
'Rivendell',
'Breeze',
'Red & Blue',
'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',
'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>,
@ -154,7 +175,7 @@
'last_changed': <ANY>,
'last_reported': <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
@ -164,56 +185,77 @@
'area_id': None,
'capabilities': dict({
'options': list([
'Analogous',
'April Night',
'Autumn',
'Based on Primary',
'Based on Set',
'Beach',
'Beech',
'Breeze',
'C9',
'Cloud',
'Cyane',
'Default',
'Departure',
'Drywet',
'Fire',
'Forest',
'Grintage',
'Hult',
'Hult 64',
'Icefire',
'Jul',
'Landscape',
'Lava',
'Light Pink',
'Magenta',
'Magred',
'Ocean',
'Orange & Teal',
'Orangery',
'* Random Cycle',
'* Color 1',
'* Colors 1&2',
'* Color Gradient',
'* Colors Only',
'Party',
'Pastel',
'Primary Color',
'Cloud',
'Lava',
'Ocean',
'Forest',
'Rainbow',
'Rainbow Bands',
'Random Cycle',
'Red & Blue',
'Rewhi',
'Rivendell',
'Sakura',
'Set Colors',
'Sherbet',
'Splash',
'Sunset',
'Sunset 2',
'Tertiary',
'Tiamat',
'Vintage',
'Yelblu',
'Rivendell',
'Breeze',
'Red & Blue',
'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',
'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>,
@ -256,7 +298,7 @@
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'hw_version': 'esp32',
'id': <ANY>,
'identifiers': set({
tuple(
@ -268,14 +310,14 @@
'labels': set({
}),
'manufacturer': 'WLED',
'model': 'DIY light',
'model': 'FOSS',
'model_id': None,
'name': 'WLED RGB Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '0.8.5',
'sw_version': '0.14.4',
'via_device_id': None,
})
# ---
@ -293,7 +335,7 @@
'last_changed': <ANY>,
'last_reported': <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
@ -330,7 +372,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'playlist',
'unique_id': 'aabbccddee11_playlist',
'unique_id': 'aabbccddeeff_playlist',
'unit_of_measurement': None,
})
# ---
@ -342,31 +384,31 @@
'connections': set({
tuple(
'mac',
'aa:bb:cc:dd:ee:11',
'aa:bb:cc:dd:ee:ff',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'hw_version': 'esp32',
'id': <ANY>,
'identifiers': set({
tuple(
'wled',
'aabbccddee11',
'aabbccddeeff',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WLED',
'model': 'DIY light',
'model': 'FOSS',
'model_id': None,
'name': 'WLED RGBW Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '0.8.6b4',
'sw_version': '0.99.0b1',
'via_device_id': None,
})
# ---
@ -384,7 +426,7 @@
'last_changed': <ANY>,
'last_reported': <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
@ -421,7 +463,7 @@
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'preset',
'unique_id': 'aabbccddee11_preset',
'unique_id': 'aabbccddeeff_preset',
'unit_of_measurement': None,
})
# ---
@ -433,31 +475,31 @@
'connections': set({
tuple(
'mac',
'aa:bb:cc:dd:ee:11',
'aa:bb:cc:dd:ee:ff',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'hw_version': 'esp32',
'id': <ANY>,
'identifiers': set({
tuple(
'wled',
'aabbccddee11',
'aabbccddeeff',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'WLED',
'model': 'DIY light',
'model': 'FOSS',
'model_id': None,
'name': 'WLED RGBW Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '0.8.6b4',
'sw_version': '0.99.0b1',
'via_device_id': None,
})
# ---

View File

@ -3,7 +3,6 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'duration': 60,
'fade': True,
'friendly_name': 'WLED RGB Light Nightlight',
'target_brightness': 0,
}),
@ -61,7 +60,7 @@
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'hw_version': 'esp32',
'id': <ANY>,
'identifiers': set({
tuple(
@ -73,14 +72,14 @@
'labels': set({
}),
'manufacturer': 'WLED',
'model': 'DIY light',
'model': 'FOSS',
'model_id': None,
'name': 'WLED RGB Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '0.8.5',
'sw_version': '0.14.4',
'via_device_id': None,
})
# ---
@ -143,7 +142,7 @@
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'hw_version': 'esp32',
'id': <ANY>,
'identifiers': set({
tuple(
@ -155,14 +154,14 @@
'labels': set({
}),
'manufacturer': 'WLED',
'model': 'DIY light',
'model': 'FOSS',
'model_id': None,
'name': 'WLED RGB Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '0.8.5',
'sw_version': '0.14.4',
'via_device_id': None,
})
# ---
@ -226,7 +225,7 @@
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'hw_version': 'esp32',
'id': <ANY>,
'identifiers': set({
tuple(
@ -238,14 +237,14 @@
'labels': set({
}),
'manufacturer': 'WLED',
'model': 'DIY light',
'model': 'FOSS',
'model_id': None,
'name': 'WLED RGB Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '0.8.5',
'sw_version': '0.14.4',
'via_device_id': None,
})
# ---
@ -309,7 +308,7 @@
}),
'disabled_by': None,
'entry_type': None,
'hw_version': 'esp8266',
'hw_version': 'esp32',
'id': <ANY>,
'identifiers': set({
tuple(
@ -321,14 +320,14 @@
'labels': set({
}),
'manufacturer': 'WLED',
'model': 'DIY light',
'model': 'FOSS',
'model_id': None,
'name': 'WLED RGB Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '0.8.5',
'sw_version': '0.14.4',
'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("type") is FlowResultType.CREATE_ENTRY
assert "data" in result
assert result["data"][CONF_HOST] == "192.168.1.123"
assert "result" in result
assert result["result"].unique_id == "aabbccddeeff"
@ -167,23 +165,6 @@ async def test_user_device_exists_abort(
assert result.get("reason") == "already_configured"
async def test_user_with_cct_channel_abort(
hass: HomeAssistant,
mock_wled: 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")
async def test_zeroconf_without_mac_device_exists_abort(
hass: HomeAssistant,
@ -234,31 +215,6 @@ async def test_zeroconf_with_mac_device_exists_abort(
assert result.get("reason") == "already_configured"
async def test_zeroconf_with_cct_channel_abort(
hass: HomeAssistant,
mock_wled: 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(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:

View File

@ -7,7 +7,6 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from wled import WLEDConnectionError
from homeassistant.components.wled.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
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
assert mock_wled.disconnect.call_count == 1
assert not hass.data.get(DOMAIN)
@patch(
@ -69,21 +67,3 @@ async def test_setting_unique_id(
"""Test we set unique ID if not set yet."""
assert init_integration.runtime_data
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."""
import json
from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory
@ -9,14 +8,21 @@ from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_MODE,
ATTR_EFFECT,
ATTR_HS_COLOR,
ATTR_RGB_COLOR,
ATTR_RGBW_COLOR,
ATTR_SUPPORTED_COLOR_MODES,
ATTR_TRANSITION,
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 (
ATTR_ENTITY_ID,
ATTR_ICON,
@ -30,7 +36,11 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
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")
@ -41,9 +51,9 @@ async def test_rgb_light_state(
"""Test the creation and values of the WLED lights."""
# First segment of the strip
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_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.state == STATE_ON
@ -52,9 +62,9 @@ async def test_rgb_light_state(
# Second segment of the strip
assert (state := hass.states.get("light.wled_rgb_light_segment_1"))
assert state.attributes.get(ATTR_BRIGHTNESS) == 127
assert state.attributes.get(ATTR_EFFECT) == "Blink"
assert state.attributes.get(ATTR_HS_COLOR) == (148.941, 100.0)
assert state.attributes.get(ATTR_BRIGHTNESS) == 255
assert state.attributes.get(ATTR_EFFECT) == "Wipe"
assert state.attributes.get(ATTR_HS_COLOR) == (40.0, 100.0)
assert state.attributes.get(ATTR_ICON) is None
assert state.state == STATE_ON
@ -63,7 +73,7 @@ async def test_rgb_light_state(
# Test main control of the lightstrip
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 (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")
return_value = mock_wled.update.return_value
mock_wled.update.return_value = WLEDDevice(
json.loads(load_fixture("wled/rgb.json"))
mock_wled.update.return_value = WLEDDevice.from_dict(
load_json_object_fixture("rgb.json", DOMAIN)
)
freezer.tick(SCAN_INTERVAL)
@ -327,6 +337,8 @@ async def test_rgbw_light(hass: HomeAssistant, mock_wled: MagicMock) -> None:
"""Test RGBW support for WLED."""
assert (state := hass.states.get("light.wled_rgbw_light"))
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)
await hass.services.async_call(

View File

@ -1,6 +1,5 @@
"""Tests for the WLED number platform."""
import json
from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory
@ -13,13 +12,13 @@ from homeassistant.components.number import (
DOMAIN as NUMBER_DOMAIN,
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.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
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")
@ -128,8 +127,8 @@ async def test_speed_dynamically_handle_segments(
# Test adding a segment dynamically...
return_value = mock_wled.update.return_value
mock_wled.update.return_value = WLEDDevice(
json.loads(load_fixture("wled/rgb.json"))
mock_wled.update.return_value = WLEDDevice.from_dict(
load_json_object_fixture("rgb.json", DOMAIN)
)
freezer.tick(SCAN_INTERVAL)

View File

@ -1,6 +1,5 @@
"""Tests for the WLED select platform."""
import json
from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory
@ -9,18 +8,13 @@ from syrupy.assertion import SnapshotAssertion
from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError
from homeassistant.components.select import ATTR_OPTION, DOMAIN as SELECT_DOMAIN
from homeassistant.components.wled.const import SCAN_INTERVAL
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_SELECT_OPTION,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.components.wled.const import DOMAIN, SCAN_INTERVAL
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_SELECT_OPTION, STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
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")
@ -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")
return_value = mock_wled.update.return_value
mock_wled.update.return_value = WLEDDevice(
json.loads(load_fixture("wled/rgb.json"))
mock_wled.update.return_value = WLEDDevice.from_dict(
load_json_object_fixture("rgb.json", DOMAIN)
)
freezer.tick(SCAN_INTERVAL)
@ -148,7 +142,7 @@ async def test_color_palette_dynamically_handle_segments(
assert (
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
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."""
assert (state := hass.states.get("select.wled_rgb_light_playlist"))
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
)
assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.CURRENT
assert state.state == "470"
assert state.state == "515"
assert (
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.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TIMESTAMP
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.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.attributes.get(ATTR_ICON) is None
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_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.attributes.get(ATTR_ICON) is None
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_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)
== 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.unique_id == "aabbccddeeff_wifi_rssi"

View File

@ -1,6 +1,5 @@
"""Tests for the WLED switch platform."""
import json
from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory
@ -9,7 +8,7 @@ from syrupy.assertion import SnapshotAssertion
from wled import Device as WLEDDevice, WLEDConnectionError, WLEDError
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 (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
@ -22,7 +21,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
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")
@ -144,8 +143,8 @@ async def test_switch_dynamically_handle_segments(
# Test adding a segment dynamically...
return_value = mock_wled.update.return_value
mock_wled.update.return_value = WLEDDevice(
json.loads(load_fixture("wled/rgb.json"))
mock_wled.update.return_value = WLEDDevice.from_dict(
load_json_object_fixture("rgb.json", DOMAIN)
)
freezer.tick(SCAN_INTERVAL)

View File

@ -2,8 +2,9 @@
from unittest.mock import MagicMock
from freezegun.api import FrozenDateTimeFactory
import pytest
from wled import WLEDError
from wled import Releases, WLEDError
from homeassistant.components.update import (
ATTR_INSTALLED_VERSION,
@ -16,6 +17,7 @@ from homeassistant.components.update import (
UpdateDeviceClass,
UpdateEntityFeature,
)
from homeassistant.components.wled.const import RELEASES_SCAN_INTERVAL
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_ENTITY_ID,
@ -31,6 +33,8 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import async_fire_time_changed
pytestmark = pytest.mark.usefixtures("init_integration")
@ -45,12 +49,12 @@ async def test_update_available(
state.attributes[ATTR_ENTITY_PICTURE]
== "https://brands.home-assistant.io/_/wled/icon.png"
)
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.8.5"
assert state.attributes[ATTR_LATEST_VERSION] == "0.12.0"
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.14.4"
assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"
assert state.attributes[ATTR_RELEASE_SUMMARY] is None
assert (
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 (
state.attributes[ATTR_SUPPORTED_FEATURES]
@ -64,15 +68,26 @@ async def test_update_available(
assert entry.entity_category is EntityCategory.CONFIG
@pytest.mark.parametrize("device_fixture", ["rgb_no_update"])
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:
"""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.attributes.get(ATTR_DEVICE_CLASS) == UpdateDeviceClass.FIRMWARE
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_RELEASE_SUMMARY] 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.state == STATE_OFF
assert state.attributes.get(ATTR_DEVICE_CLASS) == UpdateDeviceClass.FIRMWARE
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.12.0-b2"
assert state.attributes[ATTR_LATEST_VERSION] == "0.12.0-b2"
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.99.0"
assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"
assert state.attributes[ATTR_RELEASE_SUMMARY] is None
assert (
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 (
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.state == STATE_ON
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.8.5"
assert state.attributes[ATTR_LATEST_VERSION] == "0.12.0"
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.14.4"
assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"
await hass.services.async_call(
UPDATE_DOMAIN,
@ -161,7 +176,7 @@ async def test_update_stay_stable(
blocking=True,
)
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"])
@ -177,8 +192,8 @@ async def test_update_beta_to_stable(
"""
assert (state := hass.states.get("update.wled_rgbw_light_firmware"))
assert state.state == STATE_ON
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.8.6b4"
assert state.attributes[ATTR_LATEST_VERSION] == "0.8.6"
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.99.0b1"
assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"
await hass.services.async_call(
UPDATE_DOMAIN,
@ -187,7 +202,7 @@ async def test_update_beta_to_stable(
blocking=True,
)
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"])
@ -202,8 +217,8 @@ async def test_update_stay_beta(
"""
assert (state := hass.states.get("update.wled_rgb_light_firmware"))
assert state.state == STATE_ON
assert state.attributes[ATTR_INSTALLED_VERSION] == "0.8.6b1"
assert state.attributes[ATTR_LATEST_VERSION] == "0.8.6b2"
assert state.attributes[ATTR_INSTALLED_VERSION] == "1.0.0b4"
assert state.attributes[ATTR_LATEST_VERSION] == "1.0.0b5"
await hass.services.async_call(
UPDATE_DOMAIN,
@ -212,4 +227,4 @@ async def test_update_stay_beta(
blocking=True,
)
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")