package main import ( "bufio" "bytes" "encoding/json" "fmt" "io" "log" "net" "net/http" "os" "strings" "time" "github.com/grandcat/zeroconf" "golang.org/x/net/context" ) type Bridge struct { IP net.IP Name string } type RegisteredApplication struct { bridge Bridge applicationName string deviceName string token string } func discoverHueBridge() ([]Bridge, error) { // Create a new resolver resolver, err := zeroconf.NewResolver(nil) if err != nil { log.Fatalf("Failed to initialize resolver: %v", err) } // Instantiate a new bridges slice bridges := make([]Bridge, 0) // Set up a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) defer cancel() // Channel to recive the results entries := make(chan *zeroconf.ServiceEntry) // Start the lookup for the Hue Bridge go func() { err = resolver.Browse(ctx, "_hue._tcp", "local.", entries) if err != nil { log.Fatalf("Failed to browse for Hue Bridge: %v", err) } }() // Process the results go func() { for entry := range entries { bridges = append(bridges, Bridge{IP: entry.AddrIPv4[0], Name: entry.HostName}) } }() // Wait for the context to expire <-ctx.Done() fmt.Println("mDNS query finished") fmt.Printf("Number of devices found: %d\n", len(bridges)) for _, device := range bridges { fmt.Printf("Device: %s\n", device.Name) fmt.Printf("IP Address: %s\n", device.IP) } return bridges, nil } func registerApplication(bridge Bridge) RegisteredApplication { var applicationName string var deviceName string var token string fmt.Printf("Enter name of your application: ") fmt.Scan(&applicationName) fmt.Printf("Enter name of your device: ") fmt.Scan(&deviceName) for { resp, err := http.Post( "http://"+bridge.IP.String()+"/api", "application/json", bytes.NewBuffer([]byte(`{"devicetype":"`+applicationName+`#`+deviceName+`"}`))) if err != nil { log.Fatal(err) } body, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { log.Fatal(err) } type Success struct { Username string `json:"username"` } if !strings.Contains(string(body), "link button not pressed") { // Define a struct matching the expected JSON response structure type SuccessResponse struct { Success struct { Username string `json:"username"` } `json:"success"` } // Unmarshal the response body into the struct var successResponse []SuccessResponse if err := json.Unmarshal(body, &successResponse); err != nil { log.Fatalf("Error parsing response: %v", err) } // Assign the username to the token if len(successResponse) > 0 { token = successResponse[0].Success.Username } break } fmt.Println("Link button not pressed. Press link button on bridge and press Enter to continue.") bufio.NewReader(os.Stdin).ReadBytes('\n') } registeredApplication := RegisteredApplication{ bridge: bridge, applicationName: applicationName, deviceName: deviceName, token: token, } return registeredApplication } func main() { bridges, err := discoverHueBridge() if err != nil { log.Fatal(err) } registeredApplications := make([]RegisteredApplication, 0) for _, bridge := range bridges { newApplication := registerApplication(bridge) registeredApplications = append(registeredApplications, newApplication) } fmt.Println(registeredApplications) }