// Package main provides the generate command for generating Home Assistant entity constants package main import ( "flag" "fmt" "os" "path/filepath" "strings" "text/template" "gopkg.in/yaml.v3" ga "saml.dev/gome-assistant" ) type Config struct { URL string `yaml:"url"` HAAuthToken string `yaml:"ha_auth_token"` HomeZoneEntityId string `yaml:"home_zone_entity_id"` } type Domain struct { Name string Entities []Entity } type Entity struct { FieldName string EntityID string } func toFieldName(entityID string) string { parts := strings.Split(entityID, ".") if len(parts) != 2 { return "" } return toCamelCase(parts[1]) } func toCamelCase(s string) string { if s == "" { return "" } parts := strings.Split(s, "_") var result strings.Builder // If first character is numeric firstChar := parts[0][0] if firstChar >= '0' && firstChar <= '9' { result.WriteString("_") } for _, part := range parts { if part == "" { continue } result.WriteString(strings.ToUpper(string(part[0]))) if len(part) > 1 { result.WriteString(part[1:]) } } return result.String() } // generate creates the entities.go file with constants for all Home Assistant entities func generate(config Config) error { app, err := ga.NewApp(ga.NewAppRequest{ URL: config.URL, HAAuthToken: config.HAAuthToken, HomeZoneEntityId: config.HomeZoneEntityId, }) if err != nil { return fmt.Errorf("failed to create app: %w", err) } defer app.Cleanup() entities, err := app.GetState().ListEntities() if err != nil { return fmt.Errorf("failed to list entities: %w", err) } // Group entities by domain domainMap := make(map[string]*Domain) for _, entity := range entities { if entity.State == "unavailable" { continue } parts := strings.Split(entity.EntityID, ".") if len(parts) != 2 { continue } domain := parts[0] if _, exists := domainMap[domain]; !exists { domainMap[domain] = &Domain{ Name: toCamelCase(domain), } } domainMap[domain].Entities = append(domainMap[domain].Entities, Entity{ FieldName: toFieldName(entity.EntityID), EntityID: entity.EntityID, }) } // Map to slice for template domains := make([]Domain, 0) for _, domain := range domainMap { domains = append(domains, *domain) } // Create entities directory if it doesn't exist err = os.MkdirAll("entities", 0755) if err != nil { return fmt.Errorf("failed to create entities directory: %w", err) } // Create the file tmpl := template.Must(template.New("entities").Parse(`// Code generated by go generate; DO NOT EDIT. package entities {{ range .Domains }} type {{ .Name }}Domain struct { {{- range .Entities }} {{ .FieldName }} string {{- end }} } var {{ .Name }} = {{ .Name }}Domain{ {{- range .Entities }} {{ .FieldName }}: "{{ .EntityID }}", {{- end }} } {{ end }} `)) f, err := os.Create(filepath.Join("entities", "entities.go")) if err != nil { return fmt.Errorf("failed to create entities.go: %w", err) } defer f.Close() err = tmpl.Execute(f, struct{ Domains []Domain }{domains}) if err != nil { return fmt.Errorf("failed to execute template: %w", err) } return nil } func main() { println("Generating entities.go...") configFile := flag.String("config", "gen.yaml", "Path to config file") flag.Parse() absConfigPath, err := filepath.Abs(*configFile) if err != nil { fmt.Printf("Error resolving config path: %v\n", err) os.Exit(1) } configBytes, err := os.ReadFile(absConfigPath) if err != nil { fmt.Printf("Error reading config file: %v\n", err) os.Exit(1) } var config Config if err := yaml.Unmarshal(configBytes, &config); err != nil { fmt.Printf("Error parsing config file: %v\n", err) os.Exit(1) } if config.HAAuthToken == "" { config.HAAuthToken = os.Getenv("HA_AUTH_TOKEN") } if config.URL == "" || config.HAAuthToken == "" || config.HomeZoneEntityId == "" { fmt.Println("Error: url, ha_auth_token and home_zone_entity_id are required in config") os.Exit(1) } if err := generate(config); err != nil { fmt.Printf("Error generating entities: %v\n", err) os.Exit(1) } fmt.Println("Generated entities/entities.go") }