From ec0ccab3a95b979c1e1c924dde4a3cadfdc76b2d Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Mon, 24 Jan 2022 21:57:36 +0100 Subject: [PATCH] First commit --- .air.toml | 36 ++++++++ .dockerignore | 4 + .drone.yml | 47 +++++++++++ .gitignore | 3 + Dockerfile | 18 ++++ go.mod | 16 ++++ go.sum | 32 +++++++ main.go | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 386 insertions(+) create mode 100644 .air.toml create mode 100644 .dockerignore create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..891c361 --- /dev/null +++ b/.air.toml @@ -0,0 +1,36 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = "tmp" + +[build] + bin = "./tmp/main" + cmd = "go build -o ./tmp/main ." + delay = 1000 + exclude_dir = ["assets", "tmp", "vendor", "testdata", "storage"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html"] + kill_delay = "0s" + log = "build-errors.log" + send_interrupt = false + stop_on_error = true + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + time = false + +[misc] + clean_on_exit = false + +[screen] + clear_on_rebuild = false diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e826079 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.git/ +storage/ +automation +.env diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..aac7d3c --- /dev/null +++ b/.drone.yml @@ -0,0 +1,47 @@ +kind: pipeline +type: docker +name: default + +steps: + - name: build + image: docker + volumes: + - name: socket + path: /var/run/docker.sock + commands: + - docker build -t inventory . + + - name: deploy + image: docker + volumes: + - name: socket + path: /var/run/docker.sock + environment: + MQTT_HOST: + from_secret: MQTT_HOST + MQTT_PORT: + from_secret: MQTT_PORT + MQTT_USER: + from_secret: MQTT_USER + MQTT_PASS: + from_secret: MQTT_PASS + HUE_BRIDGE: + from_secret: HUE_BRIDGE + commands: + - docker stop automation || true + + - 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 + + when: + branch: + - master + event: + exclude: + - pull_request + +volumes: + - name: socket + host: + path: /var/run/docker.sock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1cdc3bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +storage +automation +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0a474f7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM golang:alpine as build-automation + +WORKDIR /src +COPY ./go.mod . +COPY ./go.sum . +RUN go mod download + +COPY . . + +RUN go build + + +FROM golang:alpine + +WORKDIR /app +COPY --from=build-automation /src/automation /app/automation + +CMD ["/app/automation"] diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..77066fe --- /dev/null +++ b/go.mod @@ -0,0 +1,16 @@ +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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5b25028 --- /dev/null +++ b/go.sum @@ -0,0 +1,32 @@ +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 new file mode 100644 index 0000000..c0560d9 --- /dev/null +++ b/main.go @@ -0,0 +1,230 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/signal" + "syscall" + "time" + + "github.com/amimof/huego" + MQTT "github.com/eclipse/paho.mqtt.golang" + "github.com/joho/godotenv" + "github.com/kelvins/sunrisesunset" +) + +type Location struct { + Longitude float32 `json:"lon"` + Latitude float32 `json:"lat"` + Altitude int `json:"alt"` + InRegions []string `json:"inregions"` +} + +var defaultHandler MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) { + fmt.Printf("TOPIC: %s\n", msg.Topic()) + fmt.Printf("MSG: %s\n", msg.Payload()) +} + +const groupId = 1 +const brightness = 100 + +func getNextSunriseSunset() (time.Time, time.Time) { + p := sunrisesunset.Parameters{ + Latitude: 51.9808334, + Longitude: 4.347818, + Date: time.Now(), + } + + sunrise, sunset, err := p.GetSunriseSunset() + if err != nil { + panic(err) + } + + p2 := sunrisesunset.Parameters{ + Latitude: 51.9808334, + Longitude: 4.347818, + Date: time.Now().Add(time.Hour * 24), + } + sunrise2, sunset2, err := p2.GetSunriseSunset() + + now := time.Now() + if now.After(sunrise) { + sunrise = sunrise2 + } + + if now.After(sunset) { + sunset = sunset2 + } + + return sunrise, sunset +} + +func isDay() bool { + p := sunrisesunset.Parameters{ + Latitude: 51.9808334, + Longitude: 4.347818, + Date: time.Now(), + } + + sunrise, sunset, err := p.GetSunriseSunset() + if err != nil { + panic(err) + } + + return time.Now().After(sunrise) && time.Now().Before(sunset) +} + +func allLightsOff(bridge *huego.Bridge) { + lights, _ := bridge.GetLights() + for _, l := range lights { + l.Off() + } +} + +func main() { + _ = godotenv.Load() + + host, ok := os.LookupEnv("MQTT_HOST") + if !ok { + host = "localhost" + } + port, ok := os.LookupEnv("MQTT_PORT") + if !ok { + port = "1883" + } + user, ok := os.LookupEnv("MQTT_USER") + if !ok { + user = "test" + } + pass, ok := os.LookupEnv("MQTT_PASS") + if !ok { + pass = "test" + } + login, ok := os.LookupEnv("HUE_BRIDGE") + + fmt.Println(host, port, user, pass, login) + + halt := make(chan os.Signal, 1) + signal.Notify(halt, os.Interrupt, syscall.SIGTERM) + + bridge, _ := huego.Discover() + bridge = bridge.Login(login) + livingRoom, _ := bridge.GetGroup(groupId) + + opts := MQTT.NewClientOptions().AddBroker(fmt.Sprintf("%s:%s", host, port)) + opts.SetClientID("automation") + opts.SetDefaultPublishHandler(defaultHandler) + opts.SetUsername(user) + opts.SetPassword(pass) + + c := MQTT.NewClient(opts) + if token := c.Connect(); token.Wait() && token.Error() != nil { + panic(token.Error()) + } + + home := make(chan bool, 1) + var locationHandler MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) { + var location Location + json.Unmarshal(msg.Payload(), &location) + + fmt.Println(location) + temp := false + for _, region := range location.InRegions { + if region == "home" { + temp = true + } + } + fmt.Println(temp) + + home <- temp + } + + if token := c.Subscribe("owntracks/mqtt/apollo", 0, locationHandler); token.Wait() && token.Error() != nil { + fmt.Println(token.Error()) + 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 = 0 + + // Event loop + events: + for { + select { + case temp := <-home: + // Only do stuff if the state changes + if temp == isHome { + break + } + isHome = temp + + if isHome { + fmt.Println("Coming home") + if !isDay() { + fmt.Println("\tTurning on lights in the living room") + livingRoom.Bri(0xff) + } + } else { + // Stop the ticker in case it is running + ticker.Stop() + + fmt.Println("Leaving home") + allLightsOff(bridge) + break + } + + case <-sunriseTimer.C: + fmt.Println("Sun is rising, turning off all lights") + allLightsOff(bridge) + + // Set new timer + sunrise, _ := getNextSunriseSunset() + sunriseTimer.Reset(sunrise.Sub(time.Now())) + + case <-sunsetTimer.C: + 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) + } + + // Set new timer + _, sunset := getNextSunriseSunset() + sunsetTimer.Reset(sunset.Sub(time.Now())) + + case <-ticker.C: + fmt.Println("Setting brightness:", brightness) + livingRoom.Bri(brightness) + + brightness++ + if brightness == 0xff { + fmt.Println("Lights are now on, stopping ticker") + ticker.Stop() + brightness = 0 + } + + case <-halt: + break events + } + } + + // Cleanup + if token := c.Unsubscribe("owntracks/mqtt/apollo"); token.Wait() && token.Error() != nil { + fmt.Println(token.Error()) + os.Exit(1) + } + + c.Disconnect(250) +}