aboutsummaryrefslogtreecommitdiff
path: root/main.go
blob: 49ed9f9288861e1ebe23efddcd3670193e2beb37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"time"

	hue "github.com/benburwell/gohue"
)

const USERNAME = "phlux"

func main() {
	var config PhluxConfig
	config.Read()

	flag.Float64Var(&config.Latitude, "latitude", config.Latitude, "Latitude (used to calculate sunrise and sunset)")
	flag.Float64Var(&config.Longitude, "longitude", config.Longitude, "Longitude (used to calculate sunrise and sunset)")
	flag.Int64Var(&config.Interval, "interval", config.Interval, "Interval (in seconds) at which to run the update if --forever is set")
	flag.StringVar(&config.TransitionTime, "transitionTime", config.TransitionTime, "Transition time for adjusting the color temperature")
	forever := flag.Bool("forever", false, "Run the update every [interval], use Ctrl-C to cancel")
	flag.Parse()

	log.Println("Config:", config)

	updateColorTemps(&config)
	if *forever {
		ticker := time.NewTicker(time.Duration(config.Interval) * time.Second)
		for {
			<-ticker.C
			updateColorTemps(&config)
		}
	}
}

func updateColorTemps(config *PhluxConfig) {
	bridges, err := hue.FindBridges()
	if err != nil {
		log.Fatalf("Error finding bridges: %s\n", err.Error())
	}
	log.Printf("Found %d bridge(s)\n", len(bridges))
	desiredColorTemp := getDesiredColorTemperature(time.Now(), config.Latitude, config.Longitude)
	for _, bridge := range bridges {
		log.Printf("Bridge: %s\n", bridge.IPAddress)
		updateBridge(&bridge, desiredColorTemp, config)
	}
}

// In case we don't know the bridge's serial number for some reason, we won't
// be able to look up the token, nor will we be able to save it. There is still
// a chance we will be able to successfully log in though: if the link button
// has been pressed, we should be able to create ourselves a temporary token.
func authenticateOnce(bridge *hue.Bridge) error {
	token, err := bridge.CreateUser(USERNAME)
	if err != nil {
		return errors.New(fmt.Sprintf("Could not create temporary token: %s", err.Error()))
	}
	log.Printf("Made token %s for bridge %s\n", token, bridge.IPAddress)
	err = bridge.Login(token)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to log in with temporary token: %s", err.Error()))
	}
	log.Printf("Logged in to bridge %s\n", bridge.IPAddress)
	return nil
}

func createToken(bridge *hue.Bridge, config *PhluxConfig) error {
	if err := authenticateOnce(bridge); err != nil {
		return err
	}
	config.SetBridgeToken(bridge.Info.Device.SerialNumber, bridge.Username)
	config.Save()
	return nil
}

// Attempt to authenticate to the bridge using a variety of techniques,
// including looking up a saved token for the bridge's serial number and
// attempting to generate a new token for the bridge assuming the link button
// has been pressed.
func authenticate(bridge *hue.Bridge, config *PhluxConfig) error {
	// get bridge info, which contains serial number
	err := bridge.GetInfo()
	if err != nil {
		return authenticateOnce(bridge)
	}
	token, err := config.GetBridgeToken(bridge.Info.Device.SerialNumber)
	if err != nil {
		return createToken(bridge, config)
	}
	err = bridge.Login(token)
	if err != nil {
		return errors.New(fmt.Sprintf("Could not log in to bridge: %s", err.Error()))
	}
	return nil
}

func updateBridge(bridge *hue.Bridge, ct ColorTemperature, config *PhluxConfig) error {
	err := authenticate(bridge, config)
	if err != nil {
		return err
	}
	log.Println("Logged in to bridge")
	lights, err := bridge.GetAllLights()
	if err != nil {
		return errors.New(fmt.Sprintf("Error getting lights: %s", err.Error()))
	}
	log.Printf("Found %d lights\n", len(lights))
	for _, light := range lights {
		updateLight(light, ct, config)
	}
	return nil
}

func updateLight(light hue.Light, ct ColorTemperature, config *PhluxConfig) {
	log.Printf("Light %d: %s (%s)\n", light.Index, light.Name, light.Type)
	if supportsColorTemp(light) {
		log.Printf("  CT range: %d-%d\n", light.Capabilities.Control.CT.Min, light.Capabilities.Control.CT.Max)
		newCt := ct.TranslateForLight(light)
		log.Printf("  Setting CT to %d\n", newCt)
		light.SetState(hue.LightState{
			On:             light.State.On,
			CT:             newCt,
			TransitionTime: config.TransitionTime,
		})
	}
}