Conversation
…ings + with More menu - HomeLayout: one-time migration force-hides the Groups card for users upgrading from 1.6.0, matching new-install behavior - HomeDevicesCard: only render Online/Offline/Untracked stats when count > 0 - SettingsView: toolbar + becomes a More (ellipsis) menu — Add Bridge always, Disconnect only in single-bridge mode; removes duplicate Disconnect section at bottom of single-bridge layout Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Capture the id of the Z2M log entry whose message contains "restart required" when bridgeInfo.restartRequired flips false → true (and clear on the reverse). The Settings and per-bridge "Restart Required" notice gains a context menu with "Go to Log" that deep-links into LogsView filtered to that entry, with a live-scan fallback when no id is captured. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
Per-device options (e.g. Hue Native Control, transition, color sync) were
either missing, broken, or rendered with custom non-native controls.
- Source option values from bridge/info.config.devices[ieee] (real Z2M
source of truth); fall back to device.options for the docker seeder.
- Render expose.description as a Section footer per option, native row
height for each control.
- Replace pill-style tristate with native Toggle / Picker.
- Title-case labels ("Hue Native Control", "State Action") while
preserving multi-uppercase acronyms ("QoS", "RGB").
- Numeric input is Double-aware via value_step, only writes on commit
(was auto-publishing every option on screen appear), and shows the unit
always — including inferred "s" for transition/throttle/debounce/retention.
- Send options keyed by ieee_address (survives mid-flight rename).
- Seeder now mirrors options into bridge/info.config.devices to match
real Z2M, and seeds hue_native_control + effect_color_mode on Philips
color lamp fixtures.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Z2M never logs the words "restart required" — the flag is set silently inside settings.apply() and settings.changeEntityOptions(). Our keyword search could never match, so "Go to Log" was just opening the Logs page unfiltered. Match windfront's honest minimalism: tap to restart, stop. Reverts the AppStore tracking, route type, navigation state, and context menu added in #85. Fixes #88 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When LogDetailView is opened from a device's own log feed, the device hero card it renders should not navigate back to that same device — a dead-end interaction. Thread an originDeviceIEEE through both call sites and suppress the NavigationLink overlay when it matches. Fixes #89 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Save flow already lived in the navigation toolbar; the Connect flow floated over the form. Use the same confirmationAction toolbar slot for both modes — same placement, same affordance — and gate the enabled state per mode (connect: canConnect; save: also requires a real edit). Fixes #86 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
State-change diffs (and ON/OFF-only publishes) omit brightness when it didn't change between events. The snapshot card was reading context.brightnessPercent — which falls back to range.upperBound (100%) when brightnessValue is nil — and rendering an invented "100%" instead of the actual prior value. Require a real brightnessValue before showing the percent; otherwise show "On"/"Off". Fixes #90 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the Device Detail pattern: a Logs section at the bottom showing the most recent entries scoped to the group's friendly name, plus a "See All Logs" navigation link to a dedicated GroupLogsView with search. Fixes #92 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two paper-cuts after tapping Restart from a deep settings page: - The user stayed on the page they came from, with no signal that the action took. Pop back to Settings root via @Environment(\.dismiss) on ServerDetailView and BridgeSettingsView so the restart has a clear endpoint. - Home tile kept rendering pre-restart uptime / published / received counts because Z2M won't republish bridge/health until reconnect. Optimistically clear bridgeHealth and bridgeOnline in AppEnvironment.restartBridge so those stats vanish immediately; they'll repopulate on the next bridge/health publish. Routed scope.restart() through environment.restartBridge so every restart surface gets the same clear. Fixes #87 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LogDetailView's hero card pushes a DeviceRoute or GroupRoute when tapped. The Devices and Groups tabs each register handlers on their own stacks, but LogsView's NavigationStack didn't — so opening a log via Settings → Logs and tapping the device/group card emitted a SwiftUI runtime warning and silently failed. Register both destinations on the LogsView stack so the link works regardless of entry surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The state-change diff suppressed any "empty → value" transition to avoid flooding logs with retained-state arrivals after MQTT connect. That's correct for devices, but Z2M only publishes group state on an actual change — so the very first arrival for a group IS the user's toggle and was being silently swallowed. Group Detail's Logs section then showed "No logs" until the second toggle made the first one visible. Skip the empty-previous suppression when the entity is a group so the first interaction shows up immediately. Devices still rely on the suppression to keep startup quiet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Z2M's "Failed to ping" warnings encode device, attempt counter, ZCL
command, an options JSON blob, and a nested failure reason — all jammed
into one text line. Render them as a labelled summary plus structured
CopyableRow fields, with the options blob as a separate grouped section.
The raw text remains accessible via the existing { } toolbar toggle.
Adds a small parser registry (LogMessageParser) so other recurring z2m
warning shapes can be added without touching the view; unrecognized
messages fall back to the original raw rendering.
Fixes #93
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Synthesized state-change entries built from `bridge/state` events used to carry only the diff — so the snapshot card collapsed to the single changed property even when the payload had brightness, color_temp, and color all present. Capture the full state on `LogContext.payload` at mapping time and prefer it in `logTimeState` so the device hero card renders the complete light state at log time. Diff-only fallback preserved for older entries. For groups with light-shaped payloads, also render the same Light Card (snapshot mode) keyed off a light member device. Non-light payloads still fall through to the generic field breakdown. Fixes #94 #95 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tapping the device or group hero card from a log opened via Home → Recent Events (or a notification) silently failed. The LogSheetHost sheet wraps LogDetailView in its own NavigationStack but never registered handlers for DeviceRoute / GroupRoute, so the push emitted a runtime warning and the user saw the sheet sit unresponsive on top of Home — visually as if the tap "navigated to homepage". Wire the same destinations the in-tab Logs stack already registers; covers both the single-entry and notificationSheetStyle (multi-entry) branches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the status-dot + URL row in the multi-bridge Bridges section with the same BridgeConnectionCardLabel the single-bridge Connection card uses. Same tinted bridge-color icon, same display name, status text in place of the URL. Restart-required badge and connect toggle preserved; status dot dropped (the colored icon + status text already communicate connection state). Fixes #96 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The small filled circle next to the snapshot value (e.g. next to "2202 K" on the color-temperature row) duplicated information already conveyed by the card's eyebrow tint and the ON badge. Drop it; the gradient and state pill carry the cue. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two intertwined bugs landed the snapshot card on the color-temperature surface during a green color change: - The card compared color_mode against "color_xy"/"color_hs", but Z2M publishes the field as "xy"/"hs"/"color_temp" (no "color_" prefix). Verified against Z2M's own publish.test.ts. The check never matched so any real color change fell through to the temperature branch. - Hue lights with hue_native_control enabled publish color_mode set to "color_temp" alongside a fresh color object during color changes — the bulb reports the equivalent CT and the renderable color in the same payload. Trusting color_mode there shows "Color Temperature 6535 K" with a peach tint while the bulb is rendering green. Replace literal string comparisons with a new LightControlContext.isColorMode that prefers a recognizable color object (x/y, hue/saturation, h/s, or r/g/b) over the color_mode signal. LightDisplayColor.resolve gets the same priority swap so the eyebrow tint follows the actual color. hasColorTemperatureReading is now suppressed when isColorMode wins so the snapshot row matches the card's surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tches Three intertwined fixes for the Log Detail Light Card surface: - exposesScopedState filtered the payload by `flattenedLeaves` of the device's exposes. For the color_xy / color_hs feature whose `property` resolves to "color", that strips the entire nested color object from the state passed to the snapshot card — the leaves are `x` / `y`, but z2m publishes the value at the parent key. Use `flattened` so the parent property survives the filter and the color object reaches LightControlContext intact. This is why every color change still rendered as Color Temperature regardless of any earlier mode detection work. - isColorMode now trusts `color_mode` authoritatively (`"xy"` or `"hs"`). The earlier override that preferred a recognizable color object misread the OFF group case where Hue publishes a stale color object alongside `color_mode: "color_temp"` — the snapshot then wrongly painted a Color row. LightDisplayColor.resolve restored to matching: color_mode == "color_temp" wins for tint, color object next, temperature fallback last. - Drop the redundant color swatch from the COLOR snapshot row to match the earlier removal on the COLOR TEMPERATURE row. The eyebrow tint and ON pill already convey state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ally differ The previous formula pinned red at 1.0 and ramped green/blue linearly from 1000–6500K. The result: every white above ~5000K landed on a peach tint that was nearly indistinguishable from neighbouring values, and genuinely cool whites (6500K+) came out pinkish instead of the characteristic blue-white. Replace with the standard Tanner Helland approximation: 2000K reads amber, 2700K tungsten, 4000K neutral, 5500K daylight, 6500K+ cool blue-white. Clamp to 1000–10000K so unusual bulb reports still render usefully. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Polish release. Bundles the v1.6.1 milestone — eight tracked issues plus the v1.6.0-bug device-options parity fix that landed on
devmid-cycle, and a handful of related fixes uncovered during testing.Home & Settings polish (initial bump)
+toolbar button with a More (ellipsis) menu — single-bridge mode shows Add Bridge + Disconnect; multi-bridge mode shows just Add Bridge. Removes the duplicate Disconnect section at the bottom of single-bridge layout.Tracked issues fixed
lib/extension/bridge.ts/lib/util/settings.ts); the keyword search could never match. Match windfront's honest minimalism: tap to restart, stop. Reverts the AppStore tracking, route type, navigation state, and context menu that were briefly added earlier in this cycle.originDeviceIEEEthrough both call sites and suppresses the NavigationLink overlay when it matches.@Environment(\.dismiss)fromServerDetailViewandBridgeSettingsView) and optimistically clearsbridgeHealth+bridgeOnlineso Home stops rendering pre-restart uptime / published / received counts during the reconnect gap. Routedscope.restart()throughenvironment.restartBridgeso every restart surface gets the same clear.brightnessValuebefore showing the percent — otherwise just "On" / "Off".GroupLogsView), mirroring the Device Detail pattern.CopyableRowfields, with the request options as a separate grouped section. Adds a smallLogMessageParserregistry; unrecognized shapes fall back to raw rendering. Raw text remains accessible via the{ }toolbar toggle.LogContext.payloadat mapping time. Group log entries with light-shaped payloads also render the same Light Card (snapshot mode), keyed off a light member device. Non-light payloads still fall through to the generic field breakdown.Bug fix carried in (#91, merged on dev)
Drive-by fixes uncovered during testing
DeviceRoute/GroupRoutedestinations so tapping the device/group hero card inside a log detail actually navigates instead of emitting a SwiftUI runtime warning.LogSheetHostand thenotificationSheetStylebranch ofLogsView. Without this, taps on the device/group card from those entry points silently failed and looked like the sheet had become unresponsive.bridge/statearrival because the diff suppression assumed every empty → value transition was a retained-state flood. That's correct for devices (z2m re-floods on connect) but wrong for groups (z2m only publishes group state on real changes). Skip the suppression for groups so the very first toggle shows up immediately in Group Detail's Logs section.Test plan
Home
Settings
Devices & Logs
{ }toggles to raw text.Groups
GroupLogsView.GroupLogsViewfilters entries.Restart
ServerDetailViewMore menu → Restart: confirm → bridge restarts, view pops back to Settings root.BridgeSettingsView(multi-bridge) Restart: same — pops back to Settings root.bridge/health.🤖 Generated with Claude Code