aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md11
-rw-r--r--bridge.go102
-rw-r--r--group.go4
-rw-r--r--light.go58
-rw-r--r--scene.go12
-rw-r--r--schedule.go6
6 files changed, 97 insertions, 96 deletions
diff --git a/README.md b/README.md
index 9096f8b..b67ba90 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,13 @@
Package hue interfaces Philips Hue devices to control lights, scenes, schedules, and groups.
[![GoDoc](https://camo.githubusercontent.com/b3b2a2b7fad4e76052830945cd839a3bba5be723/687474703a2f2f696d672e736869656c64732e696f2f62616467652f676f646f632d7265666572656e63652d3532373242342e706e67)](https://godoc.org/github.com/Collinux/GoHue)
+[![Go Report Card](https://goreportcard.com/badge/github.com/Collinux/GoHue)](https://goreportcard.com/report/github.com/Collinux/GoHue)
+
+## See GoHue in action!
+##### Have a cool project you made using GoHue? Add yours here in a pull request!
+[HueBeat](https://github.com/Mobilpadde/HueBeat) by [Mobilpadde](https://github.com/mobilpadde) - Light up a room in sync with your heartbeat.
+
+[BitHue](https://github.com/realytcracker/go-bithue) by [ytcracker](https://github.com/realytcracker) - Light color according to profit gain/loss in bitcoin price
## Installation
```
@@ -110,10 +117,6 @@ func main() {
- [ ] Update rule
- [ ] Delete rule
-## See GoHue in action!
-##### Have a cool project you made using GoHue? Add yours here in a pull request!
-[HueBeat](https://github.com/Mobilpadde/HueBeat) by Mobilpadde - Light up a room in sync with your heartbeat.
-
## API Documentation
For official Philips Hue documentation check out the [Philips Hue website](http://www.developers.meethue.com/philips-hue-api)
diff --git a/bridge.go b/bridge.go
index 2a6fb26..c89d5f4 100644
--- a/bridge.go
+++ b/bridge.go
@@ -24,6 +24,7 @@ import (
"runtime"
"strconv"
"strings"
+ "time"
)
// Bridge struct defines hardware that is used to communicate with the lights.
@@ -51,78 +52,76 @@ type BridgeInfo struct {
} `xml:"device"`
}
-// bridge.Get sends an http GET to the bridge
+// Get sends an http GET to the bridge
func (bridge *Bridge) Get(path string) ([]byte, io.Reader, error) {
uri := fmt.Sprintf("http://" + bridge.IPAddress + path)
- resp, err := http.Get(uri)
+ client := &http.Client{Timeout: time.Second * 5}
+ resp, err := client.Get(uri)
+
if err != nil {
- err = errors.New("Error: Unable to access bridge. ")
- log.Println(err)
+ err = errors.New("unable to access bridge")
return []byte{}, nil, err
}
return HandleResponse(resp)
}
-// Bridge.Put sends an http PUT to the bridge with
+// Put sends an http PUT to the bridge with
// a body formatted with parameters (in a generic interface)
func (bridge *Bridge) Put(path string, params interface{}) ([]byte, io.Reader, error) {
uri := fmt.Sprintf("http://" + bridge.IPAddress + path)
- client := &http.Client{}
+ client := &http.Client{Timeout: time.Second * 5}
data, err := json.Marshal(params)
if err != nil {
- err = errors.New("Error: Unable marshal PUT request interface.")
- log.Println(err)
+ err = errors.New("unable to marshal PUT request interface")
return []byte{}, nil, err
}
//fmt.Println("\n\nPARAMS: ", params)
- //log.Println("\nSending PUT body: ", string(data))
- request, err := http.NewRequest("PUT", uri, bytes.NewReader(data))
+ request, _ := http.NewRequest("PUT", uri, bytes.NewReader(data))
resp, err := client.Do(request)
if err != nil {
- err = errors.New("Error: Unable to access bridge.")
- log.Println(err)
+ err = errors.New("unable to access bridge")
return []byte{}, nil, err
}
return HandleResponse(resp)
}
-// bridge.Post sends an http POST to the bridge with
+// Post sends an http POST to the bridge with
// a body formatted with parameters (in a generic interface).
// If `params` is nil then it will send an empty body with the post request.
func (bridge *Bridge) Post(path string, params interface{}) ([]byte, io.Reader, error) {
- // Add the params to the request or allow an empty body
- request := []byte{}
- if params != nil {
- reqBody, err := json.Marshal(params)
- if err != nil {
- err = errors.New("Error: Unable to add POST body parameters due to json marshal error.")
- log.Println(err)
- return []byte{}, nil, err
- }
- request = reqBody
- }
+ // Add the params to the request or allow an empty body
+ request := []byte{}
+ if params != nil {
+ reqBody, err := json.Marshal(params)
+ if err != nil {
+ err = errors.New("unable to add POST body parameters due to json marshalling error")
+ return []byte{}, nil, err
+ }
+ request = reqBody
+ }
// Send the request and handle the response
uri := fmt.Sprintf("http://" + bridge.IPAddress + path)
- resp, err := http.Post(uri, "text/json", bytes.NewReader(request))
+ client := &http.Client{Timeout: time.Second * 5}
+ resp, err := client.Post(uri, "text/json", bytes.NewReader(request))
+
if err != nil {
- err = errors.New("Error: Unable to access bridge.")
- log.Println(err)
+ err = errors.New("unable to access bridge")
return []byte{}, nil, err
}
- return HandleResponse(resp)
+ return HandleResponse(resp)
}
-// Bridge.Delete sends an http DELETE to the bridge
+// Delete sends an http DELETE to the bridge
func (bridge *Bridge) Delete(path string) error {
uri := fmt.Sprintf("http://" + bridge.IPAddress + path)
- client := &http.Client{}
- req, err := http.NewRequest("DELETE", uri, nil)
+ client := &http.Client{Timeout: time.Second * 5}
+ req, _ := http.NewRequest("DELETE", uri, nil)
resp, err := client.Do(req)
+
if err != nil {
- err = errors.New("Error: Unable to access bridge.")
- log.Println(err)
+ err = errors.New("unable to access bridge")
return err
}
_, _, err = HandleResponse(resp)
@@ -134,7 +133,7 @@ func (bridge *Bridge) Delete(path string) error {
// and invalid return types.
func HandleResponse(resp *http.Response) ([]byte, io.Reader, error) {
body, err := ioutil.ReadAll(resp.Body)
- defer resp.Body.Close()
+ defer resp.Body.Close()
if err != nil {
trace("Error parsing bridge description xml.", nil)
return []byte{}, nil, err
@@ -146,7 +145,6 @@ func HandleResponse(resp *http.Response) ([]byte, io.Reader, error) {
errDesc := errString[strings.Index(errString, "description\":\"")+14 : strings.Index(errString, "\"}}")]
errOut := fmt.Sprintf("Error type %s: %s.", errNum, errDesc)
err = errors.New(errOut)
- log.Println(err)
return []byte{}, nil, err
}
return body, reader, nil
@@ -158,15 +156,14 @@ func FindBridges() ([]Bridge, error) {
bridge := Bridge{IPAddress: "www.meethue.com"}
body, _, err := bridge.Get("/api/nupnp")
if err != nil {
- err = errors.New("Error: Unable to locate bridge.")
- log.Fatal(err)
+ err = errors.New("unable to locate bridge")
return []Bridge{}, err
}
- bridges := []Bridge{}
- err = json.Unmarshal(body, &bridges)
- if err != nil {
- return []Bridge{}, errors.New("Unable to parse FindBridges response. ")
- }
+ bridges := []Bridge{}
+ err = json.Unmarshal(body, &bridges)
+ if err != nil {
+ return []Bridge{}, errors.New("unable to parse FindBridges response")
+ }
return bridges, nil
}
@@ -181,13 +178,12 @@ func NewBridge(ip string) (*Bridge, error) {
// Test the connection by attempting to get the bridge info.
err := bridge.GetInfo()
if err != nil {
- log.Fatal("Error: Unable to access bridge. ", err)
return &Bridge{}, err
}
return &bridge, nil
}
-// GetBridgeInfo retreives the description.xml file from the bridge.
+// GetInfo retreives the description.xml file from the bridge.
// This is used as a check to see if the bridge is accessible
// and any error will be fatal as the bridge is required for nearly
// all functions.
@@ -200,27 +196,25 @@ func (bridge *Bridge) GetInfo() error {
err = xml.NewDecoder(reader).Decode(&data)
if err != nil {
err = errors.New("Error: Unable to decode XML response from bridge. ")
- log.Fatal(err)
return err
}
bridge.Info = data
return nil
}
-// Bridge.Login verifies that the username token has bridge access
+// Login verifies that the username token has bridge access
// and only assigns the bridge its Username value if verification is successful.
func (bridge *Bridge) Login(username string) error {
uri := fmt.Sprintf("/api/%s", username)
_, _, err := bridge.Get(uri)
if err != nil {
- log.Fatal(err)
return err
}
bridge.Username = username
return nil
}
-// Bridge.CreateUser adds a new user token on the whitelist.
+// CreateUser adds a new user token on the whitelist.
// The token is the first return value in this function which must
// be used with `Bridge.Login`. You cannot use a plaintext username
// like the argument provided in this function.
@@ -229,20 +223,15 @@ func (bridge *Bridge) CreateUser(deviceType string) (string, error) {
params := map[string]string{"devicetype": deviceType}
body, _, err := bridge.Post("/api", params)
if err != nil {
- log.Fatal("Error: Failed to create user. ", err)
return "", err
}
content := string(body)
username := content[strings.LastIndex(content, ":\"")+2 : strings.LastIndex(content, "\"")]
- userOut := fmt.Sprintf(
- "Created user token '%s'. Use Bridge.Login with this token from now on.",
- username)
- log.Println(userOut)
bridge.Username = username
return username, nil
}
-// Bridge.DeleteUser deletes a user given its USER KEY, not the string name.
+// DeleteUser deletes a user given its USER KEY, not the string name.
// See http://www.developers.meethue.com/documentation/configuration-api
// for description on `username` deprecation in place of the devicetype key.
func (bridge *Bridge) DeleteUser(username string) error {
@@ -307,7 +296,7 @@ func (bridge *Bridge) GetLightByIndex(index int) (Light, error) {
return light, nil
}
-// Bridge.FindNewLights makes the bridge search the zigbee spectrum for
+// FindNewLights makes the bridge search the zigbee spectrum for
// lights in the area and will add them to the list of lights available.
// If successful these new lights can be used by `Bridge.GetAllLights`
//
@@ -321,13 +310,12 @@ func (bridge *Bridge) FindNewLights() error {
uri := fmt.Sprintf("/api/%s/lights", bridge.Username)
_, _, err := bridge.Post(uri, nil)
if err != nil {
- log.Println(err)
return err
}
return nil
}
-// GetLight returns a light struct containing data on a given name.
+// GetLightByName returns a light struct containing data on a given name.
func (bridge *Bridge) GetLightByName(name string) (Light, error) {
lights, _ := bridge.GetAllLights()
for _, light := range lights {
diff --git a/group.go b/group.go
index f4ed687..db878d6 100644
--- a/group.go
+++ b/group.go
@@ -35,7 +35,7 @@ type Group struct {
Type string `json:"type"`
}
-// Bridge.GetGroups gets the attributes for each group of lights.
+// GetGroups gets the attributes for each group of lights.
// TODO: NOT TESTED, NOT FULLY IMPLEMENTED
func (bridge *Bridge) GetGroups() ([]Group, error) {
uri := fmt.Sprintf("/api/%s/groups", bridge.Username)
@@ -56,7 +56,7 @@ func (bridge *Bridge) GetGroups() ([]Group, error) {
return []Group{}, nil
}
-// Bridge.SetGroupState sends an action to group
+// SetGroupState sends an action to group
func (bridge *Bridge) SetGroupState(group int, action *Action) error {
uri := fmt.Sprintf("/api/%s/groups/%d/action", bridge.Username, group)
_, _, err := bridge.Put(uri, action)
diff --git a/light.go b/light.go
index fc1229e..aef5954 100644
--- a/light.go
+++ b/light.go
@@ -11,7 +11,6 @@ package hue
import (
"errors"
"fmt"
- "log"
"time"
)
@@ -58,7 +57,7 @@ type LightState struct {
Name string `json:"name,omitempty"`
}
-// Light.SetName assigns a new name in the light's
+// SetName assigns a new name in the light's
// attributes as recognized by the bridge.
func (light *Light) SetName(name string) error {
uri := fmt.Sprintf("/api/%s/lights/%d", light.Bridge.Username, light.Index)
@@ -71,27 +70,25 @@ func (light *Light) SetName(name string) error {
return nil
}
-// Light.Off turns the light source off
+// Off turns the light source off
func (light *Light) Off() error {
return light.SetState(LightState{On: false})
}
-// Light.Off turns the light source on
+// On turns the light source on
func (light *Light) On() error {
return light.SetState(LightState{On: true})
}
-// Light.Toggle switches the light source on and off
+// Toggle switches the light source on and off
func (light *Light) Toggle() error {
if light.State.On {
return light.Off()
- } else {
- return light.On()
}
- return nil
+ return light.On()
}
-// Light.Delete removes the light from the
+// Delete removes the light from the
// list of lights available on the bridge.
func (light *Light) Delete() error {
uri := fmt.Sprintf("/api/%s/lights/%d", light.Bridge.Username, light.Index)
@@ -102,7 +99,7 @@ func (light *Light) Delete() error {
return nil
}
-// Light.Blink increases and decrease the brightness
+// Blink increases and decrease the brightness
// repeatedly for a given seconds interval and return the
// light back to its off or on state afterwards.
// Note: time will vary based on connection speed and algorithm speed.
@@ -141,7 +138,7 @@ func (light *Light) Blink(seconds int) error {
return nil
}
-// Light.ColorLoop sets the light state to 'colorloop' if `active`
+// ColorLoop sets the light state to 'colorloop' if `active`
// is true or it sets the light state to "none" if `activate` is false.
func (light *Light) ColorLoop(activate bool) error {
var state = "none"
@@ -165,7 +162,7 @@ var (
WHITE = &[2]float32{0.3227, 0.3290}
)
-// Light.SetColor requires a selection from the above light
+// SetColor requires a selection from the above light
// color variable section and sets the light to that XY HSL color
func (light *Light) SetColor(color *[2]float32) error {
lightState := LightState{On: true, XY: color}
@@ -176,7 +173,25 @@ func (light *Light) SetColor(color *[2]float32) error {
return nil
}
-// Light.Dim lowers the brightness by a percent.
+// SetColorXY requires a selection from the above light
+// color variable section and sets the light to that XY HSL color
+// aliased for clarity
+func (light *Light) SetColorXY(color *[2]float32) {
+ light.SetColor(color)
+}
+
+// SetColorHS requires a selection from the above light
+// color variable section and sets the light to the Hue value
+func (light *Light) SetColorHS(color uint16) error {
+ lightState := LightState{On: true, Hue: color}
+ err := light.SetState(lightState)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Dim lowers the brightness by a percent.
// Note the required value is an integer, for example "20" is converted to 20%.
func (light *Light) Dim(percent int) error {
if percent > 0 && percent <= 100 {
@@ -185,7 +200,6 @@ func (light *Light) Dim(percent int) error {
newBri := uint8(originalBri - uint8(decreaseBri))
if newBri < 0 {
newBri = 0
- log.Println("Light.Dim state set under 0%, setting brightness to 0. ")
}
lightState := LightState{On: true, Bri: newBri}
err := light.SetState(lightState)
@@ -193,12 +207,11 @@ func (light *Light) Dim(percent int) error {
return err
}
return nil
- } else {
- return errors.New("Light.Dim percentage given is not between 1 and 100. ")
}
+ return errors.New("Light.Dim percentage given is not between 1 and 100. ")
}
-// Light.SetBrightness sets the brightness to a percentage of the maximum
+// SetBrightness sets the brightness to a percentage of the maximum
// maximum brightness as an integer (`LightStruct.Bri between 1 and 254 inclusive`)
func (light *Light) SetBrightness(percent int) error {
if percent > 0 && percent <= 100 {
@@ -209,12 +222,11 @@ func (light *Light) SetBrightness(percent int) error {
return err
}
return nil
- } else {
- return errors.New("Light.SetBrightness percentage is not between 1 and 100. ")
}
+ return errors.New("Light.SetBrightness percentage is not between 1 and 100. ")
}
-// Light.Brighten will increase LightStruct.Bri by a given percent (integer)
+// Brighten will increase LightStruct.Bri by a given percent (integer)
func (light *Light) Brighten(percent int) error {
if percent > 0 && percent <= 100 {
originalBri := light.State.Bri
@@ -222,7 +234,6 @@ func (light *Light) Brighten(percent int) error {
newBri := uint8(originalBri + uint8(increaseBri))
if newBri > 254 { // LightState.Bri must be between 1 and 254 inclusive
newBri = 254
- log.Println("Light.Brighten state set over 100%, setting brightness to 100%. ")
}
lightState := LightState{On: true, Bri: newBri}
err := light.SetState(lightState)
@@ -230,12 +241,11 @@ func (light *Light) Brighten(percent int) error {
return err
}
return nil
- } else {
- return errors.New("Light.Brighten percentage is not between 1 and 100. ")
}
+ return errors.New("Light.Brighten percentage is not between 1 and 100. ")
}
-// Light.SetState modifyies light attributes. See `LightState` struct for attributes.
+// SetState modifyies light attributes. See `LightState` struct for attributes.
// Brightness must be between 1 and 254 (inclusive)
// Hue must be between 0 and 65535 (inclusive)
// Sat must be between 0 and 254 (inclusive)
diff --git a/scene.go b/scene.go
index e972de7..e53b2a3 100644
--- a/scene.go
+++ b/scene.go
@@ -31,7 +31,7 @@ type Scene struct {
ID string `json:",omitempty"`
}
-// Bridge.GetScenes gets the attributes for all scenes.
+// GetAllScenes gets the attributes for all scenes.
func (bridge *Bridge) GetAllScenes() ([]Scene, error) {
uri := fmt.Sprintf("/api/%s/scenes", bridge.Username)
body, _, err := bridge.Get(uri)
@@ -54,7 +54,7 @@ func (bridge *Bridge) GetAllScenes() ([]Scene, error) {
return scenesList, nil
}
-// Bridge.GetScene gets the attributes for an individual scene.
+// GetScene gets the attributes for an individual scene.
// This is used to optimize time when updating the state of the scene.
// Note: The ID is not an index, it's a unique key generated for each scene.
func (bridge *Bridge) GetScene(id string) (Scene, error) {
@@ -72,7 +72,7 @@ func (bridge *Bridge) GetScene(id string) (Scene, error) {
return scene, nil
}
-// Bridge.GetSceneByName gets the attributes for the scene identified by a name
+// GetSceneByName gets the attributes for the scene identified by a name
func (bridge *Bridge) GetSceneByName(name string) (Scene, error) {
scenes, _ := bridge.GetAllScenes()
@@ -88,13 +88,13 @@ func (bridge *Bridge) GetSceneByName(name string) (Scene, error) {
return Scene{}, errors.New(errOut)
}
-// Bridge.RecallScene recalls a scene
+// RecallScene recalls a scene
func (bridge *Bridge) RecallScene(id string) error {
action := &Action{Scene: id}
return bridge.SetGroupState(0, action)
}
-// Bridge.RecallSceneByName recalls a scene
+// RecallSceneByName recalls a scene
func (bridge *Bridge) RecallSceneByName(name string) error {
scene, err := bridge.GetSceneByName(name)
if err != nil {
@@ -103,7 +103,7 @@ func (bridge *Bridge) RecallSceneByName(name string) error {
return bridge.RecallScene(scene.ID)
}
-// Bridge.CreateScene posts a new scene configuration to the bridge.
+// CreateScene posts a new scene configuration to the bridge.
func (bridge *Bridge) CreateScene(scene Scene) error {
uri := fmt.Sprintf("/api/%s/scenes/", bridge.Username)
_, _, err := bridge.Post(uri, scene)
diff --git a/schedule.go b/schedule.go
index 50755ef..52315f9 100644
--- a/schedule.go
+++ b/schedule.go
@@ -32,7 +32,7 @@ type Schedule struct {
ID string
}
-// Bridge.GetAllSchedules gets Alarms and Timers in a Schedule struct.
+// GetAllSchedules gets Alarms and Timers in a Schedule struct.
func (bridge *Bridge) GetAllSchedules() ([]Schedule, error) {
uri := fmt.Sprintf("/api/%s/schedules", bridge.Username)
body, _, err := bridge.Get(uri)
@@ -57,7 +57,7 @@ func (bridge *Bridge) GetAllSchedules() ([]Schedule, error) {
return scheduleList, nil
}
-// Bridge.GetSchedule gets the attributes for an individual schedule.
+// GetSchedule gets the attributes for an individual schedule.
// This is used to optimize time when updating the state of a schedule item.
// Note: The ID is not an index, it's a unique key generated for each schedule.
func (bridge *Bridge) GetSchedule(id string) (Schedule, error) {
@@ -75,7 +75,7 @@ func (bridge *Bridge) GetSchedule(id string) (Schedule, error) {
return schedule, nil
}
-// TODO: NOT TESTED, NOT FULLY IMPLEMENTED
+// CreateSchedule TODO: NOT TESTED, NOT FULLY IMPLEMENTED
func (bridge *Bridge) CreateSchedule(schedule Schedule) error {
uri := fmt.Sprintf("/api/%s/schedules", bridge.Username)
body, _, err := bridge.Post(uri, schedule)