aboutsummaryrefslogtreecommitdiff
path: root/bridge.go
diff options
context:
space:
mode:
authorCollin Guarino <collin.guarino@gmail.com>2016-02-28 11:06:00 -0500
committerCollin Guarino <collin.guarino@gmail.com>2016-02-28 11:06:00 -0500
commitd32ef65eeedf6236bc007cbd1e8c874087167eaa (patch)
treed7798fa306b5343688a499c2c374e8c829355b1b /bridge.go
parent76d36f528ea622a66b3e65991082fd53120737ee (diff)
Ran go fmt on all files.
Diffstat (limited to 'bridge.go')
-rw-r--r--bridge.go446
1 files changed, 221 insertions, 225 deletions
diff --git a/bridge.go b/bridge.go
index d9c0c5c..8b7df9f 100644
--- a/bridge.go
+++ b/bridge.go
@@ -3,7 +3,7 @@
* GoHue library for Philips Hue
* Copyright (C) 2016 Collin Guarino (Collinux) collin.guarino@gmail.com
* License: GPL version 2 or higher http://www.gnu.org/licenses/gpl.html
-*/
+ */
// All things start with the bridge. You will find many Bridge.Func() items
// to use once a bridge has been created and identified.
// See the getting started guide on the Philips hue website:
@@ -12,163 +12,160 @@
package hue
import (
- "log"
- "encoding/xml"
- "encoding/json"
- "net/http"
- "io/ioutil"
- "runtime"
- "fmt"
- "strings"
- "bytes"
- "io"
- "errors"
- "strconv"
+ "bytes"
+ "encoding/json"
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "runtime"
+ "strconv"
+ "strings"
)
// Bridge struct defines hardware that is used to communicate with the lights.
type Bridge struct {
- IPAddress string
- Username string
- Info BridgeInfo
+ IPAddress string
+ Username string
+ Info BridgeInfo
}
// BridgeInfo struct is the format for parsing xml from a bridge.
type BridgeInfo struct {
- XMLName xml.Name `xml:"root"`
- Device struct {
- XMLName xml.Name `xml:"device"`
- DeviceType string `xml:"deviceType"`
- FriendlyName string `xml:"friendlyName"`
- Manufacturer string `xml:"manufacturer"`
- ManufacturerURL string `xml:"manufacturerURL"`
- ModelDescription string `xml:"modelDescription"`
- ModelName string `xml:"modelName"`
- ModelNumber string `xml:"modelNumber"`
- ModelURL string `xml:"modelURL"`
- SerialNumber string `xml:"serialNumber"`
- UDN string `xml:"UDN"`
- } `xml:"device"`
+ XMLName xml.Name `xml:"root"`
+ Device struct {
+ XMLName xml.Name `xml:"device"`
+ DeviceType string `xml:"deviceType"`
+ FriendlyName string `xml:"friendlyName"`
+ Manufacturer string `xml:"manufacturer"`
+ ManufacturerURL string `xml:"manufacturerURL"`
+ ModelDescription string `xml:"modelDescription"`
+ ModelName string `xml:"modelName"`
+ ModelNumber string `xml:"modelNumber"`
+ ModelURL string `xml:"modelURL"`
+ SerialNumber string `xml:"serialNumber"`
+ UDN string `xml:"UDN"`
+ } `xml:"device"`
}
// 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)
- if err != nil {
- err = errors.New("Error: Unable to access bridge. ")
- log.Println(err)
- return []byte{}, nil, err
- }
- return HandleResponse(resp)
+ uri := fmt.Sprintf("http://" + bridge.IPAddress + path)
+ resp, err := http.Get(uri)
+ if err != nil {
+ err = errors.New("Error: Unable to access bridge. ")
+ log.Println(err)
+ return []byte{}, nil, err
+ }
+ return HandleResponse(resp)
}
// Bridge.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{}
+ uri := fmt.Sprintf("http://" + bridge.IPAddress + path)
+ client := &http.Client{}
- data, err := json.Marshal(params)
- if err != nil {
- err = errors.New("Error: Unable marshal PUT request interface.")
- log.Println(err)
- return []byte{}, nil, err
- }
- //fmt.Println("\n\nPARAMS: ", params)
- //log.Println("\nSending PUT body: ", string(data))
+ data, err := json.Marshal(params)
+ if err != nil {
+ err = errors.New("Error: Unable marshal PUT request interface.")
+ log.Println(err)
+ 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))
- resp, err := client.Do(request)
- if err != nil {
- err = errors.New("Error: Unable to access bridge.")
- log.Println(err)
- return []byte{}, nil, err
- }
- return HandleResponse(resp)
+ resp, err := client.Do(request)
+ if err != nil {
+ err = errors.New("Error: Unable to access bridge.")
+ log.Println(err)
+ return []byte{}, nil, err
+ }
+ return HandleResponse(resp)
}
// bridge.Post sends an http POST to the bridge with
// a body formatted with parameters (in a generic interface)
func (bridge *Bridge) Post(path string, params interface{}) ([]byte, io.Reader, error) {
- // Add the params to the request
- request, err := json.Marshal(params)
- if err != nil {
- err = errors.New("Error: Unable to marshal request from bridge http POST")
- log.Println(err)
- return []byte{}, nil, err
- }
+ // Add the params to the request
+ request, err := json.Marshal(params)
+ if err != nil {
+ err = errors.New("Error: Unable to marshal request from bridge http POST")
+ log.Println(err)
+ return []byte{}, nil, err
+ }
- // Send the request and handle the response
- uri := fmt.Sprintf("http://" + bridge.IPAddress + path)
- resp, err := http.Post(uri, "text/json", bytes.NewReader(request))
- if err != nil {
- err = errors.New("Error: Unable to access bridge.")
- log.Println(err)
- return []byte{}, nil, err
- }
- return HandleResponse(resp)
+ // Send the request and handle the response
+ uri := fmt.Sprintf("http://" + bridge.IPAddress + path)
+ resp, err := http.Post(uri, "text/json", bytes.NewReader(request))
+ if err != nil {
+ err = errors.New("Error: Unable to access bridge.")
+ log.Println(err)
+ return []byte{}, nil, err
+ }
+ return HandleResponse(resp)
}
// 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)
- resp, err := client.Do(req)
- if err != nil {
- err = errors.New("Error: Unable to access bridge.")
- log.Println(err)
- return err
- }
- _, _, err = HandleResponse(resp)
- return err
+ uri := fmt.Sprintf("http://" + bridge.IPAddress + path)
+ client := &http.Client{}
+ req, err := http.NewRequest("DELETE", uri, nil)
+ resp, err := client.Do(req)
+ if err != nil {
+ err = errors.New("Error: Unable to access bridge.")
+ log.Println(err)
+ return err
+ }
+ _, _, err = HandleResponse(resp)
+ return err
}
// HandleResponse manages the http.Response content from a
// bridge Get/Put/Post/Delete by checking it for errors
// and invalid return types.
func HandleResponse(resp *http.Response) ([]byte, io.Reader, error) {
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- trace("Error parsing bridge description xml.", nil)
- return []byte{}, nil, err
- }
- reader := bytes.NewReader(body)
- if strings.Contains(string(body), "\"error\"") {
- errString := string(body)
- errNum := errString[strings.Index(errString, "type\":")+6 :
- strings.Index(errString, ",\"address")]
- 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
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ trace("Error parsing bridge description xml.", nil)
+ return []byte{}, nil, err
+ }
+ reader := bytes.NewReader(body)
+ if strings.Contains(string(body), "\"error\"") {
+ errString := string(body)
+ errNum := errString[strings.Index(errString, "type\":")+6 : strings.Index(errString, ",\"address")]
+ 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
}
// FindBridge will visit www.meethue.com/api/nupnp to see if a bridge
// is available on the local network. This feature currently only supports one
// bridge on the network.
func FindBridge() (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)
- return Bridge{}, err
- }
- content := string(body)
- ip := content[strings.LastIndex(content, ":\"")+2 :
- strings.LastIndex(content, "\"}]")]
- bridge.IPAddress = ip
- err = bridge.GetInfo()
- if err != nil {
- return Bridge{}, err
- }
- return bridge, nil
+ 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)
+ return Bridge{}, err
+ }
+ content := string(body)
+ ip := content[strings.LastIndex(content, ":\"")+2 : strings.LastIndex(content, "\"}]")]
+ bridge.IPAddress = ip
+ err = bridge.GetInfo()
+ if err != nil {
+ return Bridge{}, err
+ }
+ return bridge, nil
}
// NewBridge defines hardware that is compatible with Hue.
@@ -176,16 +173,16 @@ func FindBridge() (Bridge, error) {
// to call `NewBridge` and `Login` or `CreateUser` to access any
// lights, scenes, groups, etc.
func NewBridge(ip string) (*Bridge, error) {
- bridge := Bridge {
- IPAddress: ip,
- }
- // 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
+ bridge := Bridge{
+ IPAddress: ip,
+ }
+ // 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.
@@ -193,32 +190,32 @@ func NewBridge(ip string) (*Bridge, error) {
// and any error will be fatal as the bridge is required for nearly
// all functions.
func (bridge *Bridge) GetInfo() error {
- _, reader, err := bridge.Get("/description.xml")
- if err != nil {
- return err
- }
- data := BridgeInfo{}
- 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
+ _, reader, err := bridge.Get("/description.xml")
+ if err != nil {
+ return err
+ }
+ data := BridgeInfo{}
+ 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
// 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
+ 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.
@@ -227,110 +224,109 @@ func (bridge *Bridge) Login(username string) error {
// like the argument provided in this function.
// This was done by Philips Hue for security reasons.
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
+ 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.
// 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 {
- uri := fmt.Sprintf("/api/%s/config/whitelist/%s", bridge.Username, username)
- err := bridge.Delete(uri)
- if err != nil {
- return err
- }
- return nil
+ uri := fmt.Sprintf("/api/%s/config/whitelist/%s", bridge.Username, username)
+ err := bridge.Delete(uri)
+ if err != nil {
+ return err
+ }
+ return nil
}
// GetAllLights retreives the state of all lights that the bridge is aware of.
func (bridge *Bridge) GetAllLights() ([]Light, error) {
- uri := fmt.Sprintf("/api/%s/lights", bridge.Username)
- body, _, err := bridge.Get(uri)
- if err != nil {
- return []Light{}, err
- }
+ uri := fmt.Sprintf("/api/%s/lights", bridge.Username)
+ body, _, err := bridge.Get(uri)
+ if err != nil {
+ return []Light{}, err
+ }
- // An index is at the top of every Light in the array
- lightMap := map[string]Light{}
- err = json.Unmarshal(body, &lightMap)
- if err != nil {
- return []Light{}, errors.New("Unable to marshal GetAllLights response. ")
- }
+ // An index is at the top of every Light in the array
+ lightMap := map[string]Light{}
+ err = json.Unmarshal(body, &lightMap)
+ if err != nil {
+ return []Light{}, errors.New("Unable to marshal GetAllLights response. ")
+ }
- // Parse the index, add the light to the list, and return the array
- lights := []Light{}
- for index, light := range lightMap {
- light.Index, err = strconv.Atoi(index)
- if err != nil {
- return []Light{}, errors.New("Unable to convert light index to integer. ")
- }
- light.Bridge = bridge
- lights = append(lights, light)
- }
- return lights, nil
+ // Parse the index, add the light to the list, and return the array
+ lights := []Light{}
+ for index, light := range lightMap {
+ light.Index, err = strconv.Atoi(index)
+ if err != nil {
+ return []Light{}, errors.New("Unable to convert light index to integer. ")
+ }
+ light.Bridge = bridge
+ lights = append(lights, light)
+ }
+ return lights, nil
}
// GetLightByIndex returns a light struct containing data on
// a light given its index stored on the bridge. This is used for
// quickly updating an individual light.
func (bridge *Bridge) GetLightByIndex(index int) (Light, error) {
- // Send an http GET and inspect the response
- uri := fmt.Sprintf("/api/%s/lights/%d", bridge.Username, index)
- body, _, err := bridge.Get(uri)
- if err != nil {
- return Light{}, err
- }
- if strings.Contains(string(body), "not available") {
- return Light{}, errors.New("Error: Light selection index out of bounds. ")
- }
+ // Send an http GET and inspect the response
+ uri := fmt.Sprintf("/api/%s/lights/%d", bridge.Username, index)
+ body, _, err := bridge.Get(uri)
+ if err != nil {
+ return Light{}, err
+ }
+ if strings.Contains(string(body), "not available") {
+ return Light{}, errors.New("Error: Light selection index out of bounds. ")
+ }
- // Parse and load the response into the light array
- light := Light{}
- err = json.Unmarshal(body, &light)
- if err != nil {
- return Light{}, errors.New("Error: Unable to unmarshal light data. ")
- }
- light.Index = index
- light.Bridge = bridge
- return light, nil
+ // Parse and load the response into the light array
+ light := Light{}
+ err = json.Unmarshal(body, &light)
+ if err != nil {
+ return Light{}, errors.New("Error: Unable to unmarshal light data. ")
+ }
+ light.Index = index
+ light.Bridge = bridge
+ return light, nil
}
// GetLight 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 {
- if light.Name == name {
- return light, nil
- }
- }
- errOut := fmt.Sprintf("Error: Light name '%s' not found. ", name)
- return Light{}, errors.New(errOut)
+ lights, _ := bridge.GetAllLights()
+ for _, light := range lights {
+ if light.Name == name {
+ return light, nil
+ }
+ }
+ errOut := fmt.Sprintf("Error: Light name '%s' not found. ", name)
+ return Light{}, errors.New(errOut)
}
// Log the date, time, file location, line number, and function.
// Message can be "" or Err can be nil (not both)
func trace(message string, err error) {
- pc := make([]uintptr, 10)
- runtime.Callers(2, pc)
- f := runtime.FuncForPC(pc[0])
- file, line := f.FileLine(pc[0])
- if err != nil {
- log.Printf("%s:%d %s: %s\n", file, line, f.Name(), err)
- } else {
- log.Printf("%s:%d %s: %s\n", file, line, f.Name(), message)
- }
+ pc := make([]uintptr, 10)
+ runtime.Callers(2, pc)
+ f := runtime.FuncForPC(pc[0])
+ file, line := f.FileLine(pc[0])
+ if err != nil {
+ log.Printf("%s:%d %s: %s\n", file, line, f.Name(), err)
+ } else {
+ log.Printf("%s:%d %s: %s\n", file, line, f.Name(), message)
+ }
}