Dependencies

Core Dependencies

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
require (
    github.com/tj-smith47/shelly-go v0.1.6  // Shelly device library
    github.com/spf13/cobra v1.8.x           // CLI framework
    github.com/spf13/viper v1.18.x          // Configuration
    github.com/charmbracelet/bubbletea      // TUI framework
    github.com/charmbracelet/bubbles        // TUI components
    github.com/charmbracelet/lipgloss       // Styling (CLI + TUI)
    github.com/charmbracelet/glamour        // Markdown rendering (help screens)
    github.com/charmbracelet/x/exp/teatest  // TUI testing
    github.com/lrstanley/bubbletint         // Themes for ALL output (CLI + TUI)
    github.com/AlecAivazis/survey/v2        // Interactive prompts
    github.com/briandowns/spinner           // Progress spinners
    github.com/olekukonko/tablewriter       // Table output (non-TUI)
    github.com/skip2/go-qrcode              // QR code generation (ASCII terminal output)
    golang.org/x/sync/errgroup              // Concurrent operations
    gopkg.in/yaml.v3                        // YAML support
)

Development Dependencies

1
2
3
4
require (
    github.com/stretchr/testify             // Testing assertions
    github.com/golang/mock                  // Mocking (or use Go 1.25 testing features)
)

Usage Guidelines

Theming (bubbletint)

github.com/lrstanley/bubbletint provides theming for ALL CLI output, not just TUI:

  • Table output colors
  • Status indicators (success/warning/error)
  • Device state colors (online/offline/updating)
  • Spinner colors
  • All lipgloss-styled output
  • TUI dashboard components

Example:

1
2
3
4
5
6
7
8
9
import "github.com/lrstanley/bubbletint"

// Get current theme
t := theme.Current()

// Use theme colors
style := lipgloss.NewStyle().
    Foreground(t.Green()).  // Success color
    Bold(true)

Spinners (briandowns/spinner)

Use for long-running operations (>1 second):

  • Device discovery (mDNS, BLE, subnet scan)
  • Firmware updates and downloads
  • Backup/restore operations
  • Bulk provisioning
  • Cloud authentication

Example:

1
2
3
4
5
6
7
8
import "github.com/briandowns/spinner"

s := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
s.Suffix = " Discovering devices..."
s.Start()
defer s.Stop()

// Do work...

Interactive Prompts (survey)

Use github.com/AlecAivazis/survey/v2 for interactive input (outside TUI):

  • Confirmations (factory reset, delete operations)
  • Selections (choose device, select WiFi network)
  • Text input (device names, credentials)
  • Multi-select (choose devices for batch)
  • Password input (auth credentials, encryption)

Example:

1
2
3
4
5
6
7
import "github.com/AlecAivazis/survey/v2"

var confirm bool
prompt := &survey.Confirm{
    Message: "Factory reset device? This cannot be undone.",
}
survey.AskOne(prompt, &confirm)

Concurrent Operations (errgroup)

Use golang.org/x/sync/errgroup for batch operations:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import "golang.org/x/sync/errgroup"

g, ctx := errgroup.WithContext(ctx)
g.SetLimit(10)  // Max concurrent

for _, device := range devices {
    d := device
    g.Go(func() error {
        return svc.TurnOn(ctx, d)
    })
}

return g.Wait()

TUI Framework (BubbleTea)

Use Charm ecosystem for TUI:

  • bubbletea - Core framework (Elm Architecture)
  • bubbles - Pre-built components (table, list, textinput, viewport)
  • lipgloss - Styling and layout
  • glamour - Markdown rendering

Component pattern:

1
2
3
4
5
6
7
type Model struct {
    // State
}

func (m Model) Init() tea.Cmd { return nil }
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { /* handle messages */ }
func (m Model) View() string { /* render */ }

Go 1.25.5 Features

Use these Go 1.25 features where applicable:

FeatureUse Case
sync.WaitGroup.Go()Cleaner goroutine spawning in batch ops
testing/synctestVirtualized time for time-dependent tests
Range over functionsIterator patterns
Swiss mapAutomatic (faster maps)
GreenTea GCAutomatic (better GC)
Container-aware GOMAXPROCSAutomatic (K8s/Docker)

Example - WaitGroup.Go():

1
2
3
4
5
6
7
8
var wg sync.WaitGroup
for _, item := range items {
    i := item
    wg.Go(func() {  // Instead of Add(1) + go func() { defer Done() }
        process(i)
    })
}
wg.Wait()