Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/wled/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
WLEDUpgradeError,
)
from .models import (
AudioReactive,
Comment thread
mcaulifn marked this conversation as resolved.
Color,
Device,
Effect,
Expand All @@ -39,6 +40,7 @@

__all__ = [
"WLED",
"AudioReactive",
Comment thread
mcaulifn marked this conversation as resolved.
"Color",
"Device",
"Effect",
Expand Down
21 changes: 21 additions & 0 deletions src/wled/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ class Nightlight(BaseModel):
"""Target brightness of nightlight feature."""


@dataclass(kw_only=True)
class AudioReactive(BaseModel):
"""Object holding the AudioReactive usermod state in WLED.

The AudioReactive usermod reports its state in the `AudioReactive`
key of the state object. Note that the state is reported in the `on`
field, while changing the state is done using the `enabled` field.
"""

on: bool = field(default=False)
"""AudioReactive currently enabled."""


@dataclass(kw_only=True)
class UDPSync(BaseModel):
"""Object holding UDP sync state in WLED.
Expand Down Expand Up @@ -571,6 +584,14 @@ def __post_deserialize__(cls, obj: Info) -> Info:
class State(BaseModel):
"""Object holding the state of WLED."""

audio_reactive: AudioReactive | None = field(
default=None, metadata=field_options(alias="AudioReactive")
)
"""AudioReactive usermod state.

`None` if the AudioReactive usermod is not installed on the device.
"""

brightness: int = field(default=1, metadata=field_options(alias="bri"))
"""Brightness of the light.

Expand Down
17 changes: 17 additions & 0 deletions src/wled/wled.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,23 @@ async def nightlight(
nightlight = {k: v for k, v in nightlight.items() if v is not None}
await self.request("/json/state", method="POST", data={"nl": nightlight})

async def audio_reactive(self, *, on: bool) -> None:
"""Control the AudioReactive usermod of a WLED device.

Args:
----
on: A boolean, true to enable the AudioReactive usermod,
false otherwise.

"""
Comment thread
mcaulifn marked this conversation as resolved.
# The AudioReactive usermod reports its state in the `on` field,
# but accepts state changes in the `enabled` field.
await self.request(
"/json/state",
method="POST",
data={"AudioReactive": {"enabled": on}},
)

async def upgrade( # noqa: PLR0912
self,
*,
Expand Down
14 changes: 14 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,20 @@ def test_state_preset_id_positive() -> None:
assert state.preset_id == 3


def test_state_audio_reactive_absent() -> None:
"""Test audio_reactive is None when the usermod is not installed."""
state = State.from_dict(_base_state())
assert state.audio_reactive is None


@pytest.mark.parametrize("on", [True, False])
def test_state_audio_reactive_present(on: bool) -> None:
"""Test audio_reactive state is deserialized when the usermod is present."""
state = State.from_dict(_base_state(AudioReactive={"on": on}))
assert state.audio_reactive is not None
assert state.audio_reactive.on is on


# =========================================================================
# Preset model
# =========================================================================
Expand Down
19 changes: 19 additions & 0 deletions tests/test_wled.py
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,25 @@ async def test_nightlight(
)


@pytest.mark.parametrize("on", [True, False])
async def test_audio_reactive(responses: aioresponses, wled: WLED, on: bool) -> None:
"""Test setting AudioReactive usermod state."""
responses.post(
"http://example.com/json/state",
status=200,
body="{}",
content_type="application/json",
)

await wled.audio_reactive(on=on)

assert_post_payload(
responses,
"http://example.com/json/state",
{"AudioReactive": {"enabled": on}, "v": True},
)


# =========================================================================
# Section 14: WLED client - reset, close, context manager, connected
# =========================================================================
Expand Down