From 4ace28c79adf3787d8d6e482f971119556b9831e Mon Sep 17 00:00:00 2001 From: Max Broomfield Date: Mon, 12 Jan 2026 23:11:42 -0600 Subject: [PATCH 1/4] fix windows ui text color --- pkg/ui/ui.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/ui/ui.go b/pkg/ui/ui.go index af44dd6..e826c7c 100644 --- a/pkg/ui/ui.go +++ b/pkg/ui/ui.go @@ -351,6 +351,7 @@ func (t *ShadcnTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Co secondary := color.RGBA{103, 103, 228, 255} zinc50 := color.RGBA{250, 250, 250, 255} zinc800 := color.RGBA{39, 39, 42, 255} + zinc900 := color.RGBA{24, 24, 27, 255} switch n { case theme.ColorNameBackground: return black @@ -360,6 +361,12 @@ func (t *ShadcnTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Co return secondary case theme.ColorNameButton: return zinc800 + case theme.ColorNameInputBackground: + return zinc900 + case theme.ColorNamePlaceHolder: + return color.RGBA{113, 113, 122, 255} + case theme.ColorNameDisabled: + return color.RGBA{113, 113, 122, 255} } return theme.DefaultTheme().Color(n, v) } From df4ee18c6e50b917b9d84f09836b346224619a35 Mon Sep 17 00:00:00 2001 From: Max Broomfield Date: Tue, 13 Jan 2026 21:12:14 -0600 Subject: [PATCH 2/4] fix version and health checks --- Dockerfile | 7 +++-- cmd/bridge/run_cli.go | 51 ++-------------------------------- pkg/bridge/bridge.go | 64 ++++++++++++++++++++----------------------- 3 files changed, 35 insertions(+), 87 deletions(-) diff --git a/Dockerfile b/Dockerfile index fc2eb6e..08e2e52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,9 +18,10 @@ ARG TARGETOS ARG TARGETARCH ARG TARGETVARIANT -# build the binary (Docker mode, no GUI - default build excludes gui tag) -RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} \ - go build -ldflags="-w -s" -o /bridge ./cmd/bridge +# Read version from VERSION file and embed it at build time +RUN VERSION=$(cat VERSION | tr -d '[:space:]') && \ + CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} \ + go build -ldflags="-w -s -X bridge/pkg/bridge.Version=${VERSION}" -o /bridge ./cmd/bridge FROM alpine:latest diff --git a/cmd/bridge/run_cli.go b/cmd/bridge/run_cli.go index bd0a0cf..fe06d6d 100644 --- a/cmd/bridge/run_cli.go +++ b/cmd/bridge/run_cli.go @@ -19,7 +19,6 @@ func runCLI() { serialPort := "/dev/ttyUSB0" // Check if it exists, if not give the user a semi-helpful error message - // TODO: probably come back and re-word this, im not sure it it's 100% clear to non-technical users.. if _, err := os.Stat(serialPort); os.IsNotExist(err) { log.Printf(` Oops! Looks like the serial port is not configured correctly. @@ -118,8 +117,6 @@ Ex: /dev/:/dev/ttyUSB0 lastSuccessTime = time.Now() errChan := make(chan error, 1) - healthCheckTicker := time.NewTicker(60 * time.Second) // Increased from 30s to give MQTT more time - defer healthCheckTicker.Stop() go func() { err := b.Start(nil, func(msg string) { @@ -128,10 +125,7 @@ Ex: /dev/:/dev/ttyUSB0 errChan <- err }() - // Health monitoring loop - healthCheckFailures := 0 - maxHealthCheckFailures := 10 // Increased from 5 to be more tolerant - + // Main event loop - just handle shutdown and errors for { select { case <-sigChan: @@ -176,47 +170,6 @@ Ex: /dev/:/dev/ttyUSB0 // Break out of inner loop to attempt reconnection goto reconnectLoop } - case <-healthCheckTicker.C: - // Periodic health check with smarter MQTT reconnection awareness - if !b.IsHealthy() { - healthCheckFailures++ - - // Check if MQTT is actively attempting to auto-reconnect - isMQTTReconnecting := b.IsMQTTReconnecting() - disconnectedDuration, reconnectAttempts := b.GetMQTTReconnectionInfo() - - // Be more tolerant if MQTT is actively reconnecting - effectiveMaxFailures := maxHealthCheckFailures - if isMQTTReconnecting { - // Give MQTT more time to recover during session takeover scenarios - effectiveMaxFailures = maxHealthCheckFailures * 2 - log.Printf("Health check failed (%d/%d) - MQTT reconnecting (disconnected for %v, attempt %d)", - healthCheckFailures, effectiveMaxFailures, disconnectedDuration, reconnectAttempts) - } else { - log.Printf("Health check failed (%d/%d)", healthCheckFailures, effectiveMaxFailures) - } - - if healthCheckFailures >= effectiveMaxFailures { - if isMQTTReconnecting && disconnectedDuration < 10*time.Minute { - // MQTT is still trying and hasn't been disconnected too long - // Log but don't force reconnection yet - log.Printf("MQTT actively reconnecting for %v - allowing more time before forcing reset", disconnectedDuration) - } else { - log.Printf("Device appears to be in unrecoverable state after %d failed health checks", healthCheckFailures) - b.Disconnect() - - // Force a longer delay and reset to try recovery - consecutiveFailures = maxConsecutiveFailures - 1 // Trigger near-circuit-breaker behavior - goto reconnectLoop - } - } - } else { - // Reset health check failures on successful check - if healthCheckFailures > 0 { - log.Printf("Health check recovered") - healthCheckFailures = 0 - } - } } } @@ -231,4 +184,4 @@ func max(a, b int) int { return a } return b -} +} \ No newline at end of file diff --git a/pkg/bridge/bridge.go b/pkg/bridge/bridge.go index 13f5fcb..3574297 100644 --- a/pkg/bridge/bridge.go +++ b/pkg/bridge/bridge.go @@ -26,6 +26,10 @@ const ( SerialReadTimeout = 100 * time.Millisecond ) +// Version can be set at build time via ldflags: +// go build -ldflags="-X bridge/pkg/bridge.Version=1.0.0" +var Version = "" + // Message structures for MQTT communication type SettingsMessage struct { ConnectedBaud int `json:"connected_baud"` @@ -90,23 +94,27 @@ func isUnusablePort(name string) bool { return strings.Contains(low, "bluetooth") && !strings.Contains(low, "usb") } -// getAppVersion reads the version from the VERSION file +// getAppVersion returns the application version func getAppVersion() string { - // Get the executable path to help locate VERSION file + // First check if version was set at build time + if Version != "" { + return Version + } + + // Fall back to reading from VERSION file (for local development) execPath, _ := os.Executable() execDir := filepath.Dir(execPath) - // Try multiple possible locations for the VERSION file possiblePaths := []string{ - "VERSION", // Current working directory - filepath.Join(execDir, "VERSION"), // Same directory as executable - filepath.Join(execDir, "..", "VERSION"), // Parent of executable directory - filepath.Join(execDir, "..", "..", "VERSION"), // Two levels up from executable - filepath.Join(execDir, "..", "..", "..", "VERSION"), // Three levels up - "../VERSION", // Relative parent directory - "../../VERSION", // Two levels up relative - "../../../VERSION", // Three levels up relative - "./VERSION", // Explicit current directory + "VERSION", + filepath.Join(execDir, "VERSION"), + filepath.Join(execDir, "..", "VERSION"), + filepath.Join(execDir, "..", "..", "VERSION"), + filepath.Join(execDir, "..", "..", "..", "VERSION"), + "../VERSION", + "../../VERSION", + "../../../VERSION", + "./VERSION", } for _, path := range possiblePaths { @@ -118,7 +126,6 @@ func getAppVersion() string { } } - // Fallback version if file not found return "unknown" } @@ -274,11 +281,6 @@ type Bridge struct { statusMutex sync.Mutex // Changed from RWMutex for simplicity dataTimeoutDuration time.Duration dataActivityStopChan chan struct{} - - // Health monitoring fields - lastHealthyTime time.Time - healthCheckInterval time.Duration - maxUnhealthyDuration time.Duration } func New(bridgeID string) *Bridge { @@ -292,26 +294,28 @@ func New(bridgeID string) *Bridge { currentStatus: "offline", dataTimeoutDuration: DataActivityTimeout, dataActivityStopChan: make(chan struct{}), - lastHealthyTime: time.Now(), - healthCheckInterval: 30 * time.Second, - maxUnhealthyDuration: 5 * time.Minute, } } func (b *Bridge) SetConnectionLostHandler(h func(error)) { b.onConnectionLost = h } // IsHealthy checks if the bridge is in a healthy state +// A bridge is healthy if both serial and MQTT connections are active func (b *Bridge) IsHealthy() bool { b.mu.RLock() defer b.mu.RUnlock() // Check if we have both serial and MQTT connections - if b.serialPort == nil || b.mqttClient == nil || !b.mqttClient.IsConnected() { + if b.serialPort == nil { return false } - // Check if we've been unhealthy for too long - if time.Since(b.lastHealthyTime) > b.maxUnhealthyDuration { + if b.mqttClient == nil { + return false + } + + // MQTT connected or actively reconnecting counts as healthy + if !b.mqttClient.IsConnected() && !b.mqttClient.IsReconnecting() { return false } @@ -349,13 +353,6 @@ func (b *Bridge) GetMQTTReconnectionInfo() (disconnectedDuration time.Duration, return disconnectedDuration, attempts } -// markHealthy updates the last healthy timestamp -func (b *Bridge) markHealthy() { - b.mu.Lock() - b.lastHealthyTime = time.Now() - b.mu.Unlock() -} - // updateStatus updates the bridge status with proper retention control // LOCK ORDER: Always acquire mu.RLock BEFORE statusMutex to prevent deadlock with Disconnect() func (b *Bridge) updateStatus(status string, retained bool) error { @@ -451,9 +448,6 @@ func (b *Bridge) onDataReceived() { } }() - // Mark bridge as healthy when receiving data - b.markHealthy() - // If we were waiting, transition back to online if b.getCurrentStatus() == "waiting" { if err := b.updateStatus("online", true); err != nil { @@ -798,4 +792,4 @@ func (b *Bridge) IsConnected() bool { b.mu.RLock() defer b.mu.RUnlock() return b.serialPort != nil && b.mqttClient != nil && b.mqttClient.IsConnected() -} +} \ No newline at end of file From c91c2d5aa7a5cf43271c6ebbdb274aa4cdf1c133 Mon Sep 17 00:00:00 2001 From: Max Broomfield Date: Tue, 13 Jan 2026 21:14:14 -0600 Subject: [PATCH 3/4] go fmt --- cmd/bridge/run_cli.go | 2 +- pkg/bridge/bridge.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/bridge/run_cli.go b/cmd/bridge/run_cli.go index fe06d6d..4f6cce9 100644 --- a/cmd/bridge/run_cli.go +++ b/cmd/bridge/run_cli.go @@ -184,4 +184,4 @@ func max(a, b int) int { return a } return b -} \ No newline at end of file +} diff --git a/pkg/bridge/bridge.go b/pkg/bridge/bridge.go index 3574297..003a089 100644 --- a/pkg/bridge/bridge.go +++ b/pkg/bridge/bridge.go @@ -792,4 +792,4 @@ func (b *Bridge) IsConnected() bool { b.mu.RLock() defer b.mu.RUnlock() return b.serialPort != nil && b.mqttClient != nil && b.mqttClient.IsConnected() -} \ No newline at end of file +} From 52fceecbb353bedf8b698075219fbe274b509ef6 Mon Sep 17 00:00:00 2001 From: Max Broomfield Date: Tue, 13 Jan 2026 21:27:58 -0600 Subject: [PATCH 4/4] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index b09a54c..5592e6e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.3 \ No newline at end of file +0.7.35 \ No newline at end of file