/ internal / tui / detail_view.go
detail_view.go
  1  package tui
  2  
  3  import (
  4  	"fmt"
  5  	"sort"
  6  	"strings"
  7  
  8  	"github.com/charmbracelet/lipgloss"
  9  	tea "github.com/charmbracelet/bubbletea"
 10  )
 11  
 12  func (m *model) updateDetailView(msg tea.Msg) (tea.Model, tea.Cmd) {
 13  	switch msg := msg.(type) {
 14  	case tea.KeyMsg:
 15  		switch msg.String() {
 16  		case "q", "esc":
 17  			m.view = m.previousView
 18  			m.previousView = DetailView
 19  			return m, nil
 20  		case " ":
 21  			if m.selected >= 0 && m.selected < len(m.filtered) {
 22  				entity := m.filtered[m.selected]
 23  				domain := entity.GetDomain()
 24  				if domain == "light" || domain == "switch" || domain == "input_boolean" || domain == "script" || domain == "automation" || domain == "scene" {
 25  					service := "turn_on"
 26  					if domain == "automation" {
 27  						service = "trigger"
 28  					}
 29  					if domain == "script" {
 30  						service = entity.State
 31  						if entity.State == "on" {
 32  							service = "turn_off"
 33  						}
 34  					}
 35  					if entity.IsOn() && (domain == "light" || domain == "switch" || domain == "input_boolean") {
 36  						service = "turn_off"
 37  					}
 38  					m.haClient.CallService(domain, service, map[string]interface{}{
 39  						"entity_id": entity.EntityID,
 40  					})
 41  				}
 42  			}
 43  
 44  		case "f", "F":
 45  			if m.selected >= 0 && m.selected < len(m.filtered) {
 46  				entityID := m.filtered[m.selected].EntityID
 47  				if m.favorites[entityID] {
 48  					delete(m.favorites, entityID)
 49  				} else {
 50  					m.favorites[entityID] = true
 51  				}
 52  				m.saveFavorites()
 53  				return m, m.refreshEntities()
 54  			}
 55  		}
 56  	}
 57  	return m, nil
 58  }
 59  
 60  func (m *model) viewDetailView() string {
 61  	if m.selected < 0 || m.selected >= len(m.filtered) {
 62  		return "No entity selected"
 63  	}
 64  
 65  	entity := m.filtered[m.selected]
 66  	width := m.width - 4
 67  
 68  	content := strings.Builder{}
 69  
 70  	content.WriteString(titleStyle.Render("Entity Details\n\n"))
 71  
 72  	plainLabel := baseStyle.Foreground(darkBlue)
 73  	content.WriteString(fmt.Sprintf("%s: %s\n\n", plainLabel.Render("Name"), entity.GetName()))
 74  	content.WriteString(fmt.Sprintf("%s: %s\n\n", plainLabel.Render("Entity ID"), entity.EntityID))
 75  	content.WriteString(fmt.Sprintf("%s: %s\n\n", plainLabel.Render("Domain"), entity.GetDomain()))
 76  
 77  	state := entity.State
 78  	if entity.IsOn() {
 79  		state = statusOnStyle.Render(state)
 80  	} else {
 81  		state = statusOffStyle.Render(state)
 82  	}
 83  	content.WriteString(fmt.Sprintf("%s: %s\n\n", plainLabel.Render("State"), state))
 84  
 85  	if len(entity.Attributes) > 0 {
 86  		content.WriteString(titleStyle.Render("Attributes\n\n"))
 87  		keys := make([]string, 0, len(entity.Attributes))
 88  		for k := range entity.Attributes {
 89  			if k != "friendly_name" && k != "icon" {
 90  				keys = append(keys, k)
 91  			}
 92  		}
 93  
 94  		sort.Strings(keys)
 95  
 96  		maxKeyLen := 0
 97  		for _, key := range keys {
 98  			if len(key) > maxKeyLen {
 99  				maxKeyLen = len(key)
100  			}
101  		}
102  
103  		for _, key := range keys {
104  			value := fmt.Sprintf("%v", entity.Attributes[key])
105  			valueStr := value
106  			if len(valueStr) > width-maxKeyLen-4 {
107  				valueStr = valueStr[:width-maxKeyLen-7] + "..."
108  			}
109  			keyLabel := plainLabel.Width(maxKeyLen)
110  			content.WriteString(fmt.Sprintf("%s: %s\n", keyLabel.Render(key), valueStr))
111  		}
112  	}
113  
114  	content.WriteString("\n\n")
115  	content.WriteString(helpStyle.Render("Press [space] toggle | [F] favorite | [q/esc] go back"))
116  
117  	contentLines := strings.Split(content.String(), "\n")
118  	contentHeight := len(contentLines) + 2
119  
120  	availableHeight := m.height - 4
121  	displayHeight := min(contentHeight, availableHeight)
122  
123  	border := lipgloss.NewStyle().
124  		Border(lipgloss.RoundedBorder()).
125  		BorderForeground(blue).
126  		Padding(1).
127  		Width(min(width, 80)).
128  		Height(displayHeight)
129  
130  	return border.Render(content.String())
131  }
132  
133  func min(a, b int) int {
134  	if a < b {
135  		return a
136  	}
137  	return b
138  }