Skip to content

Release v1.6.1#85

Merged
tashda merged 20 commits into
mainfrom
dev
May 4, 2026
Merged

Release v1.6.1#85
tashda merged 20 commits into
mainfrom
dev

Conversation

@tashda
Copy link
Copy Markdown
Owner

@tashda tashda commented May 4, 2026

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 dev mid-cycle, and a handful of related fixes uncovered during testing.

Home & Settings polish (initial bump)

  • Hide the Groups card on the Home screen by default for upgraders (one-time migration), matching new-install behavior.
  • Trim the Devices card stats so Online/Offline/Untracked only render when count > 0.
  • Replace the Settings + 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

Bug fix carried in (#91, merged on dev)

  • Device-specific settings parity with the Z2M frontend.

Drive-by fixes uncovered during testing

  • Settings → Logs stack now registers DeviceRoute / GroupRoute destinations so tapping the device/group hero card inside a log detail actually navigates instead of emitting a SwiftUI runtime warning.
  • Home → Recent Events (and notification-tap log sheets) now register the same destinations on LogSheetHost and the notificationSheetStyle branch of LogsView. Without this, taps on the device/group card from those entry points silently failed and looked like the sheet had become unresponsive.
  • Group state changes were swallowing the first bridge/state arrival 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

  • Fresh install: Home shows Bridge / Devices / Mesh / Recent Events (no Groups). Edit mode reveals Groups in the hidden list.
  • Upgrade from 1.6.0 with Groups visible: after launch Groups is hidden. Re-show via Edit, kill app, relaunch — Groups stays visible (migration runs only once).
  • Devices card with no offline/untracked devices renders only Total + Online. Take a device offline → Offline appears with red count.
  • Tap a Recent Event → log opens in a sheet. Tap the device/group card → device or group detail page loads inside the sheet.

Settings

  • Single-bridge More menu shows Add Bridge + Disconnect (Disconnect hidden when never connected).
  • Multi-bridge More menu shows only Add Bridge.
  • Restart Required notice: tap → restart confirm alert. No long-press menu, no "Go to Log".
  • Connect editor: Connect / Save lives in toolbar in both modes; floating bottom button is gone.
  • Settings → Logs → tap any log → tap the device/group card → navigates to detail.

Devices & Logs

  • Open a device's Logs sheet → tap a log entry → the Device Card at the top of the detail is visible but non-tappable for that same device. Other device refs in the log still navigate.
  • State change card for an ON↔OFF transition that didn't include brightness shows "On" / "Off" — never "100%".
  • State change card for a publish that DID include brightness/color/color_temp shows the full Light Card with those values, not just the changed field.
  • Open a "Failed to ping" warning entry → renders summary + Attempt / ZCL Command / Reason rows + an Options group. { } toggles to raw text.

Groups

  • Group Detail shows a Logs section at the bottom with up to 5 recent entries plus a "See All Logs" link to GroupLogsView.
  • Search inside GroupLogsView filters entries.
  • Toggle a group's lights from a fresh page (no prior logs for the group) → the very first state change shows in the Logs section.
  • Toggle a group's lights → log detail renders a Light Card populated from the payload (state + brightness/color/color_temp).

Restart

  • From ServerDetailView More menu → Restart: confirm → bridge restarts, view pops back to Settings root.
  • From BridgeSettingsView (multi-bridge) Restart: same — pops back to Settings root.
  • Immediately after restart, Home tile drops uptime / published / received until Z2M republishes bridge/health.

🤖 Generated with Claude Code

tashda and others added 2 commits May 4, 2026 08:26
…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>
@tashda tashda added this to the v1.6.1 milestone May 4, 2026
@tashda tashda added enhancement New feature or request area:ui UI / UX redesign area:diagnostics Diagnostics / mesh health priority:medium labels May 4, 2026
@tashda tashda self-assigned this May 4, 2026
tashda and others added 8 commits May 4, 2026 09:07
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>
@tashda tashda changed the title Release v1.6.1: Home polish + deep-link Restart Required to Logs Release v1.6.1 May 4, 2026
@tashda tashda added bug Something isn't working area:multi-bridge Multi-bridge / multi-instance Z2M support area:onboarding Onboarding / pairing / first-launch labels May 4, 2026
tashda and others added 5 commits May 4, 2026 09:27
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>
tashda and others added 5 commits May 4, 2026 09:47
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>
@tashda tashda merged commit 16d4b4b into main May 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:diagnostics Diagnostics / mesh health area:multi-bridge Multi-bridge / multi-instance Z2M support area:onboarding Onboarding / pairing / first-launch area:ui UI / UX redesign bug Something isn't working enhancement New feature or request priority:medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant