Skip to content

Instantly share code, notes, and snippets.

@DanyHenriquez
Created December 16, 2016 11:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanyHenriquez/e12b88db436efb043283f6af2a5c5624 to your computer and use it in GitHub Desktop.
Save DanyHenriquez/e12b88db436efb043283f6af2a5c5624 to your computer and use it in GitHub Desktop.
Ambihue
package main
import (
"log"
"fmt"
"time"
"runtime"
"os"
"github.com/BurntSushi/xgb/xproto"
"github.com/BurntSushi/xgbutil"
"github.com/BurntSushi/xgbutil/xgraphics"
"github.com/go-resty/resty"
"github.com/lucasb-eyer/go-colorful"
"github.com/BurntSushi/toml"
"github.com/tidwall/gjson"
"sync"
)
type Configuration struct {
BridgeAddress string
User string
Top int
Right int
Bottom int
Left int
DimLights []int
DimValue int
TransitionTime int
BrightnessCorrection int
SignalInterval int
}
type LightState struct {
number int
state string
}
func parseConfig(file string) (Configuration) {
var configuration Configuration
if _, err := toml.DecodeFile(file, &configuration); err != nil {
log.Fatal(err)
}
return configuration
}
func getLightStates(config Configuration) ([]LightState) {
lightStates := make([]LightState, len(config.DimLights) + 4)
r, _ := resty.R().Get(fmt.Sprintf("http://%s/api/%s/lights/%d", config.BridgeAddress, config.User, config.Top))
value := gjson.Get(string(r.Body()), "state.*")
lightStates = append(lightStates, LightState{number: config.Top, state: value.String()})
r, _ = resty.R().Get(fmt.Sprintf("http://%s/api/%s/lights/%d", config.BridgeAddress, config.User, config.Right))
value = gjson.Get(string(r.Body()), "state.*")
lightStates = append(lightStates, LightState{number: config.Right, state: value.String()})
r, _ = resty.R().Get(fmt.Sprintf("http://%s/api/%s/lights/%d", config.BridgeAddress, config.User, config.Bottom))
value = gjson.Get(string(r.Body()), "state.*")
lightStates = append(lightStates, LightState{number: config.Bottom, state: value.String()})
r, _ = resty.R().Get(fmt.Sprintf("http://%s/api/%s/lights/%d", config.BridgeAddress, config.User, config.Left))
value = gjson.Get(string(r.Body()), "state.*")
lightStates = append(lightStates, LightState{number: config.Left, state: value.String()})
for _, light := range config.DimLights {
r, _ = resty.R().Get(fmt.Sprintf("http://%s/api/%s/lights/%d", config.BridgeAddress, config.User, light))
value = gjson.Get(string(r.Body()), "state.*")
lightStates = append(lightStates, LightState{number: light, state: value.String()})
}
return lightStates
}
func restoreState(lightStates []LightState, config Configuration) {
for _, light := range lightStates {
resty.R().
SetBody(light.state).
Put(fmt.Sprintf("http://%s/api/%s/lights/%d", config.BridgeAddress, config.User, light.number))
}
}
func changeState(ximg *xgraphics.Image, light int, XCoordinate int, YCoordinate int, config Configuration) {
R, G, B, _ := ximg.At(XCoordinate, YCoordinate).RGBA()
c := colorful.Color{(1.0 / 65535.0) * float64(R), (1.0 / 65535.0) * float64(G), (1.0 / 65535.0) * float64(B)}
x, y, z := c.Xyz()
bri := int(1.0 / 254.0 * y)
body := fmt.Sprintf(`{"transitiontime": %d, "bri":%d,"xy": [%f, %f]}`, config.TransitionTime, bri + config.BrightnessCorrection, x / (x + y + z), y / (x + y + z))
resty.R().
SetHeader("Content-Type", "application/json").
SetBody(body).
Put(fmt.Sprintf("http://%s/api/%s/lights/%d/state", config.BridgeAddress, config.User, light))
}
func DimLights(config Configuration) {
for _, light := range config.DimLights {
go DimLight(light, config)
}
}
func DimLight(light int, config Configuration) {
body := fmt.Sprintf(`{"bri": %d}`, config.DimValue)
resty.R().
SetHeader("Content-Type", "application/json").
SetBody(body).
Put(fmt.Sprintf("http://%s/api/%s/lights/%d/state", config.BridgeAddress, config.User, light))
}
func process(X *xgbutil.XUtil, config Configuration) {
ximg, err := xgraphics.NewDrawable(X, xproto.Drawable(X.RootWin()))
if err != nil {
log.Fatal(err)
}
var wg sync.WaitGroup
wg.Add(4)
if config.Top != 0 {
go changeState(ximg, config.Top, ximg.Bounds().Max.X / 2, ximg.Bounds().Min.Y + 100, config)
}
if config.Right != 0 {
go changeState(ximg, config.Right, ximg.Bounds().Max.X - 100, ximg.Bounds().Max.Y / 2, config)
}
if config.Bottom != 0 {
go changeState(ximg, config.Bottom, ximg.Bounds().Max.X / 2, ximg.Bounds().Max.Y - 100, config)
}
if config.Left != 0 {
go changeState(ximg, config.Left, ximg.Bounds().Min.X + 100, ximg.Bounds().Max.Y / 2, config)
}
}
func main() {
runtime.GOMAXPROCS(2)
config := parseConfig(os.Args[1])
lightStates := getLightStates(config)
DimLights(config)
X, err := xgbutil.NewConn()
if err != nil {
log.Fatal(err)
}
defer restoreState(lightStates, config)
for {
go process(X, config)
time.Sleep(time.Duration(config.SignalInterval) * time.Millisecond)
}
}
BridgeAddress = "<ip address>"
User = "<Bridge user>"
Top = 0
Right = 2
Bottom = 0
Left = 1
DimLights = []
DimValue = 150
TransitionTime = 4
BrightnessCorrection = 200
SignalInterval = 250
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment