Skip to content

Add a beta firmware update visibility setting - #92

Open
valentinocossar wants to merge 1 commit into
jfmlima:mainfrom
valentinocossar:feature/beta-firmware-updates
Open

Add a beta firmware update visibility setting#92
valentinocossar wants to merge 1 commit into
jfmlima:mainfrom
valentinocossar:feature/beta-firmware-updates

Conversation

@valentinocossar

Copy link
Copy Markdown

Summary

Beta and stable firmware releases were shown identically everywhere — dashboard table, device detail page, update dialog, and the CLI — with no way to hide beta-only updates by default. This adds a "Show beta updates" preference (off by default) that suppresses beta-only update signals across the app, while keeping the explicit manual channel picker (web dialog, device update --channel beta) fully able to select beta regardless of the setting.

Backend

  • New available_firmware_channel field on DiscoveredDevice, tracking which channel (stable/beta) an available update came from — set by both the RPC scan gateway (Gen2+) and the legacy Gen1 gateway (previously Gen1 devices never surfaced this at the scan-list level at all, nor did they ever check the beta channel there — parity fix included), and serialized through the /scan API response.

Web

  • New showBetaUpdates setting (default false) in Settings, persisted client-side.
  • Dashboard table, device detail header, and device actions card all filter beta-only signals through the same showBetaUpdates check; the update dialog's channel picker only lists Beta when the setting is on (manual override removed by design decision during review — see discussion).
  • Channel badges (beta = orange, stable = blue) shown wherever both channels are relevant.
  • Assorted UI fixes found during testing: the "Updates: Available" indicator was reading raw summary.has_updates (unfiltered) instead of the same filtered data as everything else; the update-channel select now preselects whichever channel actually explains the button's state instead of always defaulting to stable; a button/badge overflow at narrower viewport widths.

CLI

  • New --include-beta flag on scan, device list, and device status (off by default, mirroring the web default). A beta-only update now reports as no update needed in table output, and the detail view's "Updates Available" line is omitted entirely when the only release is a hidden beta — matching exactly how an already-up-to-date device displays today (no partial hint).
  • packages/cli/README.md updated with the new flag and default behavior.

⚠️ Breaking change / versioning note

This changes the default output of scan, device list, and device status — a beta-only update that was previously always visible is now hidden unless --include-beta is passed. If this project follows SemVer for its release tags (currently up to v1.8.5), this likely warrants a major version bump (v2.0.0) rather than a patch/minor, since existing scripts/automation parsing CLI output could observe a behavior change with no flag change on their end.

Test plan

  • Backend: packages/core (996 tests), packages/api (111 tests) — full suites pass, including new coverage for channel detection (RPC gateway: stable/beta/both/none; legacy Gen1 gateway: stable/beta-only/both, mirroring the RPC gateway's cases; settle-path)
  • CLI: 145 tests pass, including new coverage for the --include-beta filter (table view effective-status downgrade, detail-view filtering including the beta-only-hidden case, flag wiring/help text on all 3 commands)
  • Web: lint, format:check, type-check, build all pass (no test runner exists in this package)
  • black/ruff/mypy clean across packages/core, packages/api, packages/cli; pre-commit hooks pass
  • Manual verification against real Shelly devices (Gen1, Gen3, Gen4) for both the web UI and all three CLI commands, in both the default and --include-beta states

🤖 Generated with Claude Code

https://claude.ai/code/session_01VApHaH9KZ6BPV9SwvUAVLH

Beta and stable releases were shown identically everywhere (dashboard,
device detail, update dialog, and the CLI), with no way to hide
beta-only updates by default. Adds a "Show beta updates" preference
(off by default) that suppresses beta-only update signals across the
app, while keeping the explicit manual channel picker (web dialog,
`device update --channel beta`) able to select beta regardless of the
setting.

Backend: new available_firmware_channel field on DiscoveredDevice,
tracking which channel an available update came from, set by both the
RPC scan gateway (Gen2+) and the legacy Gen1 gateway (previously never
checked there at all), and serialized through the /scan API response.

Web: new showBetaUpdates setting (default off) in Settings; dashboard
table, device header, and device actions card all filter through it;
channel badges (beta orange, stable blue) where both channels are
relevant; the update dialog's channel select only lists Beta when the
setting is on; fixes an "Updates: Available" indicator that read raw
summary.has_updates unfiltered, a channel select that always defaulted
to stable instead of the channel that explains the button's state, and
a button/badge overflow at narrower viewport widths.

CLI: new --include-beta flag on scan, device list, and device status
(off by default). A beta-only update reports as no update needed in
table output, and the detail view's "Updates Available" line is
omitted entirely when the only release is a hidden beta, matching how
an already-up-to-date device displays today.

BREAKING CHANGE: scan, device list, and device status now hide a
beta-only update by default where they previously always showed it —
scripts/automation parsing CLI output may observe a behavior change
with no flag change on their end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VApHaH9KZ6BPV9SwvUAVLH
@valentinocossar
valentinocossar force-pushed the feature/beta-firmware-updates branch from 04a89fd to 1ef8f78 Compare August 24, 2026 14:45

@jfmlima jfmlima left a comment

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.

Hey @valentinocossar thanks also for this one, left a few comments, let me know your thoughts 👍

onValueChange={(value: UpdateChannel) =>
setUpdateChannel(value)
}
disabled={!showBetaUpdates}

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.

With the setting off this filters Beta out of the options and disables the whole select, so a device whose only release is a beta can't be updated from the web at all. That doesn't match the settings copy this PR adds ("You can still manually select the beta channel when updating a device"), the bulk actions dialog (which still offers Beta unconditionally), or the CLI (--channel beta always works). Can we keep Beta always selectable here and let the setting gate only the unsolicited signals (badges, status downgrade, indicators)? A preference that silently blocks an action feels like a trap.

available_firmware_version: str | None = Field(
None, description="Version an available update would install"
)
available_firmware_channel: str | None = Field(

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.

Core already has UpdateChannel(str, Enum), and with use_enum_values=True the serialization stays identical. What do you think about typing this UpdateChannel | None instead of raw literals? Same on the web side, "stable" | "beta" | null instead of string.

else:
self._format_legacy_device_table(devices, title)

def _effective_status(self, device: DiscoveredDevice, include_beta: bool) -> str:

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.

We can't avoid the ts copy, but for the Python side what do you think about a small method on DiscoveredDevice in core, so the rule and its tests live in one place?


return None

def _parse_update_version(

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 re-derives "has stable update" with different precedence than _parse_update_flag: a payload with has_update false but differing versions was NO_UPDATE_NEEDED before and becomes UPDATE_AVAILABLE now. Can we make one defer to the other, and cover that case in the tests?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants