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
6 changes: 6 additions & 0 deletions src/wled/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from mashumaro.types import SerializableType, SerializationStrategy

from .const import (
DEFAULT_REPO,
MIN_REQUIRED_VERSION,
LightCapability,
LiveDataOverride,
Expand Down Expand Up @@ -489,6 +490,11 @@ class Info(BaseModel): # pylint: disable=too-many-instance-attributes
product: str = "DIY Light"
"""The product name. Always FOSS for standard installations."""

repo: str = field(default=DEFAULT_REPO, metadata=field_options(alias="repo"))
"""GitHub repository in 'org/repo' format reported by the device firmware.
Used in preference to the default repo during upgrade operations.
"""

Comment thread
frenck marked this conversation as resolved.
release: str | None = None
"""The release name of the firmware build.

Expand Down
6 changes: 6 additions & 0 deletions src/wled/wled.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,12 @@ async def upgrade( # noqa: PLR0912
msg = "Device already running the requested version"
raise WLEDUpgradeError(msg)

# Prefer the repo reported by the device itself only when the caller
# did not override the default repository. Older firmware that does not
# include the field will already have fallen back to DEFAULT_REPO.
if repo == DEFAULT_REPO:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has a side effect, if the default repo was actually meant to be set, it wouldn't work. We probably need to use a sentinel to make this more robust.

repo = self._device.info.repo

# Determine if this is an Ethernet board
ethernet = ""
if (
Expand Down
10 changes: 5 additions & 5 deletions tests/__snapshots__/test_models.ambr

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions tests/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Constants for tests."""

from wled.const import DEFAULT_REPO

__all__ = ["DEFAULT_REPO"]
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not make much sense. Instead just import directly

13 changes: 13 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from wled.utils import get_awesome_version

from .conftest import full_device_data, load_fixture_json
from .const import DEFAULT_REPO

# =========================================================================
# Helper functions
Expand Down Expand Up @@ -315,6 +316,18 @@ def test_info_version_deserialized() -> None:
assert str(info.version) == "0.14.0"


def test_info_repo_defaults_to_default_repo() -> None:
"""Test repo defaults to DEFAULT_REPO when not present in response."""
info = Info.from_dict(_base_info())
assert info.repo == DEFAULT_REPO
Comment thread
coderabbitai[bot] marked this conversation as resolved.


def test_info_repo_uses_device_value_when_present() -> None:
"""Test repo is taken from the device response when present."""
info = Info.from_dict(_base_info(repo="MoonModules/WLED"))
assert info.repo == "MoonModules/WLED"


# =========================================================================
# State model
# =========================================================================
Expand Down
36 changes: 36 additions & 0 deletions tests/test_wled.py
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,42 @@ async def test_upgrade_success() -> None:
await wled.upgrade(version="0.15.0")


async def test_upgrade_uses_device_repo() -> None:
"""Test upgrade downloads firmware from the repo reported by the device."""
with aioresponses() as mocked:
async with aiohttp.ClientSession() as session:
wled_data = load_fixture_json("wled")
wled_data["info"]["arch"] = "esp32"
wled_data["info"]["ver"] = "0.14.0"
wled_data["info"]["repo"] = "MoonModules/WLED"
mocked.get(
"http://example.com/json",
status=200,
body=json.dumps(wled_data),
content_type="application/json",
)
mocked.get(
"http://example.com/presets.json",
status=200,
body=json.dumps(load_fixture_json("presets")),
content_type="application/json",
)
wled = WLED("example.com", session=session)
await wled.update()
Comment thread
frenck marked this conversation as resolved.
mocked.get(
"https://github.com/MoonModules/WLED/releases/download/v0.15.0/WLED_0.15.0_ESP32.bin",
status=200,
body=b"fake firmware",
)
mocked.post(
"http://example.com/update",
status=200,
body="OK",
content_type="text/plain",
)
await wled.upgrade(version="0.15.0")


async def test_upgrade_ethernet_board() -> None:
"""Test upgrade with Ethernet board (empty bssid)."""
with aioresponses() as mocked:
Expand Down
Loading