Skip to content

rschio/huestream

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

huestream

Go Reference

This package implements the Philips Hue Entertainment API. You can use it to change the lights Philips Hue in a fast to way, for example to sync with a music or a video.

Example

package huestream_test

import (
	"context"
	"image/color"
	"log"
	"math/rand/v2"
	"os"
	"os/signal"
	"time"

	"github.com/amimof/huego"
	"github.com/rschio/huestream"
)

// WARNING: this example makes use of fast changing light effects conditions
// and it may trigger previously undetected epileptic symptoms or seizures
// in persons who have no history of prior seizures or epilepsy.
func Example() {
	// Run this only the first time and store the creds in an ENV var.
	host, username, clientKey, err := genClientCreds()
	if err != nil {
		log.Fatal(err)
	}

	// If you don't have an entertainment area yet, create it using the
	// Philips Hue App:
	// Settings > Entertainment areas > +.
	//
	// Use this command to get the ID of your first entertainment area,
	// if you have more than one and want to choose, adapt the command:
	//
	// curl -s -k \
	//    -H 'hue-application-key: <username>' \
	//    https://<host>/clip/v2/resource/entertainment_configuration | jq '.data.[0].id'
	//
	// Yes, I'm too lazy to write this function.
	areaID := ""

	// Create a context that listens to signals so we can gracefully shutdown the
	// stream.
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
	defer stop()

	// Create the stream client.
	client := huestream.New(host, username, clientKey)

	// Start a stream in the selected Entertainment area.
	stream, err := client.StartStream(ctx, areaID)
	if err != nil {
		log.Fatal(err)
	}
	defer stream.Close()

	log.Println("Connected")

	// From Hue Docs:
	// "The streaming makes use of UDP, which can result in that certain messages
	// get lost, that is why it is important to continuously stream messages, even
	// when it would mean repeating the same messages or light values, typically a
	// streaming rate of 50-60Hz is used."
	//
	// 50 Hz = 1 message each 20 ms.
	sendRate := time.NewTicker(20 * time.Millisecond)
	defer sendRate.Stop()

	// From Hue Docs:
	// "The bridge sends maximum at 25 Hz messages over ZigBee.
	// Thus, the (fastest) effect rate should be 2 – 3 times slower
	// than this 25 Hz, i.e < 12.5 Hz."
	//
	// 12.5 Hz = 1 message each 80 ms.
	changeColorRate := time.NewTicker(80 * time.Millisecond)
	defer changeColorRate.Stop()

	c0, c1 := randColor(), randColor()
	for {
		select {
		case <-ctx.Done():
			return

		// Log the errors.
		case err := <-stream.Error:
			log.Println(err)

		case <-changeColorRate.C:
			c0, c1 = randColor(), randColor()

		case <-sendRate.C:
			// Here we are sending two colors because my Entertainment Area has 2 lights.
			// The slice index represents the Channel ID (the light).
			// If you have 5 lights in your area, send a slice of 5 colors.
			stream.Send <- []color.Color{c0, c1}
		}
	}

}

func genClientCreds() (host, username, clientKey string, err error) {
	bridge, err := huego.Discover()
	if err != nil {
		return "", "", "", err
	}
	host = bridge.Host

	// Press the Bridge link button.
	user, err := bridge.CreateUserWithClientKey("my entertainment app")
	if err != nil {
		return "", "", "", err
	}

	return host, user.Username, user.ClientKey, nil
}

func randColor() color.Color {
	rnd := func() uint8 { return uint8(rand.IntN(256)) }
	return color.RGBA{R: rnd(), G: rnd(), B: rnd()}
}