From c97a3c03bdd25ed5e9bf14495c520c1ba00608ad Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Wed, 14 Sep 2022 03:24:18 +0200 Subject: [PATCH] Directly use the hue api v2 as this gives more control --- .drone.yml | 2 +- go.mod | 18 +++---- go.sum | 18 ------- main.go | 144 +++++++++++++++-------------------------------------- 4 files changed, 49 insertions(+), 133 deletions(-) diff --git a/.drone.yml b/.drone.yml index 23c79b5..0a5dc47 100644 --- a/.drone.yml +++ b/.drone.yml @@ -32,7 +32,7 @@ steps: - docker rm automation || true - - docker run -e MQTT_HOST=$MQTT_HOST -e MQTT_PORT=$MQTT_PORT -e MQTT_USER=$MQTT_USER -e MQTT_PASS=$MQTT_PASS -e HUE_BRIDGE=$HUE_BRIDGE --network mqtt --name automation -d automation + - docker run -e MQTT_HOST=$MQTT_HOST -e MQTT_PORT=$MQTT_PORT -e MQTT_USER=$MQTT_USER -e MQTT_PASS=$MQTT_PASS MQTT_CLIENT_ID=$MQTT_CLIENT_ID -e HUE_BRIDGE=$HUE_BRIDGE --network mqtt --name automation -d automation when: branch: diff --git a/go.mod b/go.mod index 77066fe..dea6abb 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,12 @@ module automation go 1.17 require ( - github.com/amimof/huego v1.2.1 // indirect - github.com/eclipse/paho.mqtt.golang v1.3.5 // indirect - github.com/gorilla/websocket v1.4.2 // indirect - github.com/joho/godotenv v1.4.0 // indirect - github.com/k0kubun/pp v3.0.1+incompatible // indirect - github.com/kelvins/sunrisesunset v0.0.0-20210220141756-39fa1bd816d5 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect - golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect + github.com/eclipse/paho.mqtt.golang v1.3.5 + github.com/joho/godotenv v1.4.0 + github.com/kelvins/sunrisesunset v0.0.0-20210220141756-39fa1bd816d5 +) + +require ( + github.com/gorilla/websocket v1.4.2 // indirect + golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect ) diff --git a/go.sum b/go.sum index 5b25028..764d1a9 100644 --- a/go.sum +++ b/go.sum @@ -1,32 +1,14 @@ -github.com/amimof/huego v1.2.1 h1:kd36vsieclW4fZ4Vqii9DNU2+6ptWWtkp4OG0AXM8HE= -github.com/amimof/huego v1.2.1/go.mod h1:z1Sy7Rrdzmb+XsGHVEhODrRJRDq4RCFW7trCI5cKmeA= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y= github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= -github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/kelvins/sunrisesunset v0.0.0-20210220141756-39fa1bd816d5 h1:ouekCqYkMw4QXFCaLyYqjBe99/MUW4Qf3DJhCRh1G18= github.com/kelvins/sunrisesunset v0.0.0-20210220141756-39fa1bd816d5/go.mod h1:3oZ7G+fb8Z8KF+KPHxeDO3GWpEjgvk/f+d/yaxmDRT4= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U= golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 937b123..3fc563b 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,10 @@ package main import ( + "bytes" + "crypto/tls" "fmt" + "net/http" "os" "os/signal" "strconv" @@ -9,49 +12,11 @@ import ( "syscall" "time" - "github.com/amimof/huego" MQTT "github.com/eclipse/paho.mqtt.golang" "github.com/joho/godotenv" "github.com/kelvins/sunrisesunset" ) -// @TODO Make this environment variables -// SETTINGS -// Group id of the lights in the living room -const groupId = 1 -// The color temperature to use for the lights -// @TODO Not sure how this is calulcated -const Temperature uint16 = 366 - -// All the different message types -type Type string -const ( - Beacon Type = "beacon" - Card = "card" - Cmd = "cmd" - Configuration = "configuration" - Encrypted = "encrypted" - Location = "location" - Lwt = "lwt" - Steps = "steps" - Transition = "transition" - Waypoint = "waypoint" - Waypoints = "waypoints" -) - -// Struct for parsing message type -type Identifier struct { - Type Type `json:"_type"` -} - -// Struct with all the data from location messages -type LocationData struct { - Longitude float32 `json:"lon"` - Latitude float32 `json:"lat"` - Altitude int `json:"alt"` - InRegions []string `json:"inregions"` -} - // Get the time of the next sunrise and sunset func getNextSunriseSunset() (time.Time, time.Time) { p := sunrisesunset.Parameters{ @@ -106,14 +71,6 @@ func isDay() bool { return time.Now().After(sunrise) && time.Now().Before(sunset) } -// Turn off all the lights attached to the bridge -func allLightsOff(bridge *huego.Bridge) { - lights, _ := bridge.GetLights() - for _, l := range lights { - l.Off() - } -} - // This is the default message handler, it just prints out the topic and message var defaultHandler MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) { fmt.Printf("TOPIC: %s\n", msg.Topic()) @@ -140,6 +97,30 @@ func presenceHandler(status chan DeviceStatus) func(MQTT.Client, MQTT.Message) { } } +type Hue struct { + ip string + login string +} + +func (hue *Hue) putRequest(resource string, data string) { + url := fmt.Sprintf("https://%s/clip/v2/resource/%s", hue.ip, resource) + + client := &http.Client{} + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + + req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer([]byte(data))) + if err != nil { + panic(err) + } + + req.Header.Set("hue-application-key", hue.login) + + _, err = client.Do(req) + if err != nil { + panic(err) + } +} + func main() { _ = godotenv.Load() @@ -159,21 +140,20 @@ func main() { if !ok { pass = "test" } + clientID, ok := os.LookupEnv("MQTT_CLIENT_ID") + if !ok { + clientID = "automation" + } login, _ := os.LookupEnv("HUE_BRIDGE") halt := make(chan os.Signal, 1) signal.Notify(halt, os.Interrupt, syscall.SIGTERM) - // bridge, _ := huego.Discover() - // bridge = bridge.Login(login) - // @TODO Let's hope the IP does not change, should probably set a static IP - bridge := huego.New("10.0.0.146", login) - if bridge == nil { - panic("Bridge is nil") - } + // @TODO Discover the bridge here + hue := Hue{ip: "10.0.0.146", login: login} opts := MQTT.NewClientOptions().AddBroker(fmt.Sprintf("%s:%s", host, port)) - opts.SetClientID("automation") + opts.SetClientID(clientID) opts.SetDefaultPublishHandler(defaultHandler) opts.SetUsername(user) opts.SetPassword(pass) @@ -189,23 +169,15 @@ func main() { os.Exit(1) } - // Setup initial states - isHome := false - sunrise, sunset := getNextSunriseSunset() sunriseTimer := time.NewTimer(sunrise.Sub(time.Now())) sunsetTimer := time.NewTimer(sunset.Sub(time.Now())) - // Create the ticker, but stop it - ticker := time.NewTicker(time.Second) - ticker.Stop() - - var brightness uint8 = 1 + devices := make(map[string]bool) + isHome := false fmt.Println("Starting event loop") - devices := make(map[string]bool) - // Event loop events: for { @@ -234,16 +206,11 @@ events: fmt.Println("Coming home") if !isDay() { fmt.Println("\tTurning on lights in the living room") - livingRoom, _ := bridge.GetGroup(groupId) - livingRoom.Bri(0xff) - livingRoom.Ct(Temperature) + hue.putRequest("scene/1847ec79-3459-4d79-ae73-803a0c6e7ac2", `{"recall": { "action": "active", "status": "active"}}`) } } else { - // Stop the ticker in case it is running - ticker.Stop() - fmt.Println("Leaving home") - allLightsOff(bridge) + hue.putRequest("grouped_light/91c400ed-7eda-4b5c-ac3f-bfff226188d7", `{"on": { "on": false}}`) break } @@ -251,7 +218,7 @@ events: case <-sunriseTimer.C: fmt.Println("Sun is rising, turning off all lights") - allLightsOff(bridge) + hue.putRequest("grouped_light/91c400ed-7eda-4b5c-ac3f-bfff226188d7", `{"on": { "on": false}}`) // Set new timer sunrise, _ := getNextSunriseSunset() @@ -261,44 +228,13 @@ events: fmt.Println("Sun is setting") if isHome { fmt.Println("\tGradually turning on lights in the living room") - // Start the ticker to gradually turn on the living room lights - ticker.Reset(1200 * time.Millisecond) - - livingRoom, _ := bridge.GetGroup(groupId) - - fmt.Println("DEBUG STUFG") - fmt.Println(livingRoom.IsOn()) - fmt.Println(livingRoom.State.On) - fmt.Println(livingRoom.State.Bri) - fmt.Println(livingRoom.State.Ct) - fmt.Println(brightness) - - if (!livingRoom.IsOn() || livingRoom.State.Bri < brightness) { - fmt.Println("Setting brightness:", brightness) - livingRoom.Bri(brightness) - livingRoom.Ct(Temperature) - } + hue.putRequest("scene/1847ec79-3459-4d79-ae73-803a0c6e7ac2", `{"recall": { "action": "active", "status": "active", "duration": 300000}}`) } // Set new timer _, sunset := getNextSunriseSunset() sunsetTimer.Reset(sunset.Sub(time.Now())) - case <-ticker.C: - brightness++ - livingRoom, _ := bridge.GetGroup(groupId) - if (!livingRoom.IsOn() || livingRoom.State.Bri < brightness) { - fmt.Println("Setting brightness:", brightness) - livingRoom.Bri(brightness) - livingRoom.Ct(Temperature) - } - - if brightness == 0xff { - fmt.Println("Lights are now on, stopping ticker") - ticker.Stop() - brightness = 1 - } - case <-halt: break events }