From 483b758f27e212c0684cdb6ca065c56765818e26 Mon Sep 17 00:00:00 2001 From: Ben Burwell Date: Fri, 25 May 2018 00:01:10 -0400 Subject: Run dep init --- vendor/github.com/benburwell/gohue/.gitignore | 1 + vendor/github.com/benburwell/gohue/LICENSE | 340 ++++++++++++++++++++++++ vendor/github.com/benburwell/gohue/README.md | 129 ++++++++++ vendor/github.com/benburwell/gohue/bridge.go | 342 +++++++++++++++++++++++++ vendor/github.com/benburwell/gohue/group.go | 67 +++++ vendor/github.com/benburwell/gohue/light.go | 266 +++++++++++++++++++ vendor/github.com/benburwell/gohue/scene.go | 129 ++++++++++ vendor/github.com/benburwell/gohue/schedule.go | 109 ++++++++ 8 files changed, 1383 insertions(+) create mode 100644 vendor/github.com/benburwell/gohue/.gitignore create mode 100644 vendor/github.com/benburwell/gohue/LICENSE create mode 100644 vendor/github.com/benburwell/gohue/README.md create mode 100644 vendor/github.com/benburwell/gohue/bridge.go create mode 100644 vendor/github.com/benburwell/gohue/group.go create mode 100644 vendor/github.com/benburwell/gohue/light.go create mode 100644 vendor/github.com/benburwell/gohue/scene.go create mode 100644 vendor/github.com/benburwell/gohue/schedule.go (limited to 'vendor/github.com/benburwell') diff --git a/vendor/github.com/benburwell/gohue/.gitignore b/vendor/github.com/benburwell/gohue/.gitignore new file mode 100644 index 0000000..1377554 --- /dev/null +++ b/vendor/github.com/benburwell/gohue/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/vendor/github.com/benburwell/gohue/LICENSE b/vendor/github.com/benburwell/gohue/LICENSE new file mode 100644 index 0000000..798f51c --- /dev/null +++ b/vendor/github.com/benburwell/gohue/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + GoHue + A library written in the Go Programming Language for the Philips Hue API. + Copyright (C) 2016 Collin Guarino (Collinux) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/vendor/github.com/benburwell/gohue/README.md b/vendor/github.com/benburwell/gohue/README.md new file mode 100644 index 0000000..06b9bbd --- /dev/null +++ b/vendor/github.com/benburwell/gohue/README.md @@ -0,0 +1,129 @@ +# GoHue +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 +``` +go get github.com/collinux/GoHue +``` + +## Usage +``` +package main + +import ( + "github.com/collinux/GoHue" +) + +func main() { + // It is recommended that you save the username from bridge.CreateUser + // so you don't have to press the link button every time and re-auth. + // When CreateUser is called it will print the generated user token. + bridgesOnNetwork, _ := hue.FindBridges() + bridge := bridgesOnNetwork[0] + username, _ := bridge.CreateUser("someusernamehere") + bridge.Login(username) + + lights, _ := bridge.GetAllLights() + for _, light := range lights { + light.SetBrightness(100) + light.ColorLoop(true) + } + + nightstandLight, _ := bridge.GetLightByName("Nightstand") + nightstandLight.Blink(5) + nightstandLight.SetName("Bedroom Lamp") + + lights[0].SetColor(hue.RED) + lights[1].SetColor(hue.BLUE) + lights[2].SetColor(hue.GREEN) + + for _, light := range lights { + light.Off() + } +} +``` + +## Features +##### Lights +- [x] Get all lights +- [x] Get light by name +- [x] Get light by index on bridge +- [x] Get lights attributes and state +- [x] Set lights attributes (rename) +- [x] Set light state (color, effects, brightness, etc) +- [x] Delete light +- [x] Turn On, Off, Toggle +- [x] Blink +- [x] Colorloop On/Off + +##### Bridge +- [x] Create user +- [x] Delete user +- [x] Get configuration +- [ ] Modify configuration +- [ ] Get full state (datastore) +- [x] Search for bridges +- [x] Search for new lights +- [ ] Get all timezones + +##### Schedules +- [x] Get all schedules +- [x] Get schedule by ID +- [x] Get schedule attributes +- [ ] Create schedules +- [ ] Set schedule attributes +- [ ] Delete schedule + +##### Scenes +- [x] Get all scenes +- [x] Get scene by ID +- [x] Create scene +- [ ] Modify scene +- [ ] Recall scene +- [ ] Delete scene + +##### Groups +- [ ] Get all groups +- [ ] Create group +- [ ] Get group attributes +- [ ] Set group attributes +- [ ] Set group state +- [ ] Delete Group + +##### Sensors +- [ ] Get all sensors +- [ ] Create sensor +- [ ] Find new sensors +- [ ] Get new sensors +- [ ] Get sensor +- [ ] Update sensor +- [ ] Delete sensor +- [ ] Change sensor configuration + +##### Rules +- [ ] Get all rules +- [ ] Get rule +- [ ] Create rule +- [ ] Update rule +- [ ] Delete rule + +## API Documentation +This repository is featured on the Philips Hue® developer site and was not developed by "Philips Lighting Holding B.V"... +for official Hue® documentation check out the [Philips Hue® website](http://www.developers.meethue.com/philips-hue-api). This codebase comes with no guaranetees. Use at your own risk. + +## License +GoHue - Third party golang library for Philips Hue® gateway interface. +Copyright (C) 2016 Collinux +GPL version 2 or higher http://www.gnu.org/licenses/gpl.html + +## Contributing +Pull requests happily accepted on GitHub diff --git a/vendor/github.com/benburwell/gohue/bridge.go b/vendor/github.com/benburwell/gohue/bridge.go new file mode 100644 index 0000000..c89d5f4 --- /dev/null +++ b/vendor/github.com/benburwell/gohue/bridge.go @@ -0,0 +1,342 @@ +/* +* bridge.go +* 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: +// http://www.developers.meethue.com/documentation/getting-started + +package hue + +import ( + "bytes" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "runtime" + "strconv" + "strings" + "time" +) + +// Bridge struct defines hardware that is used to communicate with the lights. +type Bridge struct { + IPAddress string `json:"internalipaddress"` + 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"` +} + +// 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) + client := &http.Client{Timeout: time.Second * 5} + resp, err := client.Get(uri) + + if err != nil { + err = errors.New("unable to access bridge") + return []byte{}, nil, err + } + return HandleResponse(resp) +} + +// 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{Timeout: time.Second * 5} + + data, err := json.Marshal(params) + if err != nil { + err = errors.New("unable to marshal PUT request interface") + return []byte{}, nil, err + } + //fmt.Println("\n\nPARAMS: ", params) + + request, _ := http.NewRequest("PUT", uri, bytes.NewReader(data)) + resp, err := client.Do(request) + if err != nil { + err = errors.New("unable to access bridge") + return []byte{}, nil, err + } + return HandleResponse(resp) +} + +// 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("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) + client := &http.Client{Timeout: time.Second * 5} + resp, err := client.Post(uri, "text/json", bytes.NewReader(request)) + + if err != nil { + err = errors.New("unable to access bridge") + return []byte{}, nil, err + } + return HandleResponse(resp) +} + +// 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{Timeout: time.Second * 5} + req, _ := http.NewRequest("DELETE", uri, nil) + resp, err := client.Do(req) + + if err != nil { + err = errors.New("unable to access bridge") + 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) + defer resp.Body.Close() + 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) + return []byte{}, nil, err + } + return body, reader, nil +} + +// FindBridges will visit www.meethue.com/api/nupnp to see a list of +// bridges on the local network. +func FindBridges() ([]Bridge, error) { + bridge := Bridge{IPAddress: "www.meethue.com"} + body, _, err := bridge.Get("/api/nupnp") + if err != nil { + 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") + } + return bridges, nil +} + +// NewBridge defines hardware that is compatible with Hue. +// The function is the core of all functionality, it's necessary +// 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 { + return &Bridge{}, err + } + return &bridge, nil +} + +// 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. +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. ") + return err + } + bridge.Info = data + return nil +} + +// 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 { + return err + } + bridge.Username = username + return nil +} + +// 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. +// 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 { + return "", err + } + content := string(body) + username := content[strings.LastIndex(content, ":\"")+2 : strings.LastIndex(content, "\"")] + bridge.Username = username + return username, nil +} + +// 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 +} + +// 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 + } + + // 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 +} + +// 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. ") + } + + // 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 +} + +// 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` +// +// Notes from Philips Hue API documentation: +// The bridge will search for 1 minute and will add a maximum of 15 new +// lights. To add further lights, the command needs to be sent again after +// the search has completed. If a search is already active, it will be +// aborted and a new search will start. +// http://www.developers.meethue.com/documentation/lights-api#13_search_for_new_lights +func (bridge *Bridge) FindNewLights() error { + uri := fmt.Sprintf("/api/%s/lights", bridge.Username) + _, _, err := bridge.Post(uri, nil) + if err != nil { + return err + } + return nil +} + +// 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 { + 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) + } +} diff --git a/vendor/github.com/benburwell/gohue/group.go b/vendor/github.com/benburwell/gohue/group.go new file mode 100644 index 0000000..db878d6 --- /dev/null +++ b/vendor/github.com/benburwell/gohue/group.go @@ -0,0 +1,67 @@ +/* +* group.go +* 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 + */ +// http://www.developers.meethue.com/documentation/groups-api + +package hue + +import ( + "encoding/json" + "fmt" +) + +// Action struct defines the state of a group +type Action struct { + Alert string `json:"alert,omitempty"` + Bri int `json:"bri,omitempty"` + Colormode string `json:"colormode,omitempty"` + Ct int `json:"ct,omitempty"` + Effect string `json:"effect,omitempty"` + Hue *int `json:"hue,omitempty"` + On *bool `json:"on,omitempty"` + Sat *int `json:"sat,omitempty"` + XY []float64 `json:"xy,omitempty"` + Scene string `json:"scene,omitempty"` +} + +// Group struct defines the attributes for a group of lights. +type Group struct { + Action Action `json:"action"` + Lights []string `json:"lights"` + Name string `json:"name"` + Type string `json:"type"` +} + +// 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) + body, _, err := bridge.Get(uri) + if err != nil { + return []Group{}, err + } + + //fmt.Println("GROUP GET: ", string(body)) + + groups := map[string]Group{} + err = json.Unmarshal(body, &groups) + if err != nil { + return []Group{}, err + } + //fmt.Println("GROUPS: ", groups) + + return []Group{}, nil +} + +// 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) + if err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/benburwell/gohue/light.go b/vendor/github.com/benburwell/gohue/light.go new file mode 100644 index 0000000..aef5954 --- /dev/null +++ b/vendor/github.com/benburwell/gohue/light.go @@ -0,0 +1,266 @@ +/* +* light.go +* 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 + */ +// http://www.developers.meethue.com/documentation/lights-api + +package hue + +import ( + "errors" + "fmt" + "time" +) + +// Light struct defines attributes of a light. +type Light struct { + State struct { + On bool `json:"on"` // On or Off state of the light ("true" or "false") + Bri uint8 `json:"bri"` // Brightness value 1-254 + Hue uint16 `json:"hue"` // Hue value 1-65535 + Saturation uint8 `json:"sat"` // Saturation value 0-254 + Effect string `json:"effect"` // "None" or "Colorloop" + XY [2]float32 `json:"xy"` // Coordinates of color in CIE color space + CT int `json:"ct"` // Mired Color Temperature (google it) + Alert string `json:"alert"` // "selected" or "none" + ColorMode string `json:"colormode"` // HS or XY mode for choosing color + Reachable bool `json:"reachable"` + } `json:"state"` + Type string `json:"type"` + Name string `json:"name"` + ModelID string `json:"modelid"` + ManufacturerName string `json:"manufacturername"` + UniqueID string `json:"uniqueid"` + SWVersion string `json:"swversion"` + Index int // Set by index of light array response + Bridge *Bridge // Set by the bridge when the light is found +} + +// LightState used in Light.SetState to amend light attributes. +type LightState struct { + On bool `json:"on"` + Bri uint8 `json:"bri,omitempty"` + Hue uint16 `json:"hue,omitempty"` + Sat uint8 `json:"sat,omitempty"` + XY *[2]float32 `json:"xy,omitempty"` + CT uint16 `json:"ct,omitempty"` + Effect string `json:"effect,omitempty"` + Alert string `json:"alert,omitempty"` + TransitionTime string `json:"transitiontime,omitempty"` + SaturationIncrement int16 `json:"sat_inc,omitempty"` + HueIncrement int32 `json:"hue_inc,omitempty"` + BrightnessIncrement int16 `json:"bri_inc,omitempty"` + CTIncrement int32 `json:"ct_inc,omitempty"` + XYIncrement *[2]float32 `json:"xy_inc,omitempty"` + Name string `json:"name,omitempty"` +} + +// 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) + body := make(map[string]string) + body["name"] = name + _, _, err := light.Bridge.Put(uri, body) + if err != nil { + return err + } + return nil +} + +// Off turns the light source off +func (light *Light) Off() error { + return light.SetState(LightState{On: false}) +} + +// On turns the light source on +func (light *Light) On() error { + return light.SetState(LightState{On: true}) +} + +// Toggle switches the light source on and off +func (light *Light) Toggle() error { + if light.State.On { + return light.Off() + } + return light.On() +} + +// 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) + err := light.Bridge.Delete(uri) + if err != nil { + return err + } + return nil +} + +// 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. +func (light *Light) Blink(seconds int) error { + originalPosition := light.State.On + originalBrightness := light.State.Bri + blinkMax := 75 // Percent brightness + blinkMin := 25 // Percent brightness + + // Start with near maximum brightness and toggle between that and + // a lesser brightness to create a blinking effect. + for i := 0; i <= seconds*2; i++ { + if i == 0 { + err := light.SetBrightness(blinkMax) + if err != nil { + return err + } + } else if i%2 == 0 { + err := light.SetBrightness(blinkMax) + if err != nil { + return err + } + } else { + err := light.SetBrightness(blinkMin) + if err != nil { + return err + } + } + time.Sleep(time.Second / 2) + } + + // Return the light to its original on or off state and brightness + if light.State.Bri != originalBrightness || light.State.On != originalPosition { + light.SetState(LightState{On: originalPosition, Bri: originalBrightness}) + } + return nil +} + +// 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" + if activate { + state = "colorloop" + } + return light.SetState(LightState{On: true, Effect: state}) +} + +// XY positions on the HSL color spectrum used in `Light.SetColor` +// https://en.wikipedia.org/wiki/HSL_and_HSV +var ( + RED = &[2]float32{0.6915, 0.3083} + YELLOW = &[2]float32{0.4023, 0.4725} + ORANGE = &[2]float32{0.4693, 0.4007} + GREEN = &[2]float32{0.1700, 0.7000} + CYAN = &[2]float32{0.1610, 0.3549} + BLUE = &[2]float32{0.1530, 0.0480} + PURPLE = &[2]float32{0.2363, 0.1154} + PINK = &[2]float32{0.3645, 0.1500} + WHITE = &[2]float32{0.3227, 0.3290} +) + +// 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} + err := light.SetState(lightState) + if err != nil { + return err + } + return nil +} + +// 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 { + originalBri := light.State.Bri + decreaseBri := float32(originalBri) * float32((float32(percent) / 100.0)) + newBri := uint8(originalBri - uint8(decreaseBri)) + if newBri < 0 { + newBri = 0 + } + lightState := LightState{On: true, Bri: newBri} + err := light.SetState(lightState) + if err != nil { + return err + } + return nil + } + return errors.New("Light.Dim percentage given is not between 1 and 100. ") +} + +// 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 { + brightness := uint8(float32(percent) * 2.54) // 100=254x --> 2.54 + lightState := LightState{On: true, Bri: brightness} + err := light.SetState(lightState) + if err != nil { + return err + } + return nil + } + return errors.New("Light.SetBrightness percentage is not between 1 and 100. ") +} + +// 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 + increaseBri := float32(originalBri) * float32((float32(percent) / 100.0)) + newBri := uint8(originalBri + uint8(increaseBri)) + if newBri > 254 { // LightState.Bri must be between 1 and 254 inclusive + newBri = 254 + } + lightState := LightState{On: true, Bri: newBri} + err := light.SetState(lightState) + if err != nil { + return err + } + return nil + } + return errors.New("Light.Brighten percentage is not between 1 and 100. ") +} + +// 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) +// See http://www.developers.meethue.com/documentation/lights-api for more info +func (light *Light) SetState(newState LightState) error { + uri := fmt.Sprintf("/api/%s/lights/%d/state", light.Bridge.Username, light.Index) + _, _, err := light.Bridge.Put(uri, newState) + if err != nil { + return err + } + + // Get the new light state and update the current Light struct + *light, err = light.Bridge.GetLightByIndex(light.Index) + if err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/benburwell/gohue/scene.go b/vendor/github.com/benburwell/gohue/scene.go new file mode 100644 index 0000000..e53b2a3 --- /dev/null +++ b/vendor/github.com/benburwell/gohue/scene.go @@ -0,0 +1,129 @@ +/* +* scene.go +* 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 + */ +// http://www.developers.meethue.com/documentation/scenes-api + +package hue + +import ( + "encoding/json" + "errors" + "fmt" +) + +// Scene struct defines attributes for Scene items +type Scene struct { + Appdata *struct { + Data string `json:"data,omitempty"` + Version int `json:"version,omitempty"` + } `json:"appdata,omitempty"` + Lastupdated string `json:"lastupdated,omitempty"` + Lights []string `json:"lights,omitempty"` + Locked bool `json:"locked,omitempty"` + Name string `json:"name,omitempty"` + Owner string `json:"owner,omitempty"` + Picture string `json:"picture,omitempty"` + Recycle bool `json:"recycle,omitempty"` + Version int `json:"version,omitempty"` + ID string `json:",omitempty"` +} + +// 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) + if err != nil { + return []Scene{}, err + } + + scenes := map[string]Scene{} + err = json.Unmarshal(body, &scenes) + if err != nil { + return []Scene{}, err + } + scenesList := []Scene{} + for key, value := range scenes { + scene := Scene{} + scene = value + scene.ID = key + scenesList = append(scenesList, scene) + } + return scenesList, nil +} + +// 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) { + uri := fmt.Sprintf("/api/%s/scenes/%s", bridge.Username, id) + body, _, err := bridge.Get(uri) + if err != nil { + return Scene{}, err + } + + scene := Scene{} + err = json.Unmarshal(body, &scene) + if err != nil { + return Scene{}, err + } + return scene, nil +} + +// GetSceneByName gets the attributes for the scene identified by a name +func (bridge *Bridge) GetSceneByName(name string) (Scene, error) { + + scenes, _ := bridge.GetAllScenes() + + // Iterate in reverse, as later entries seem to be the newest + for i := len(scenes) - 1; i >= 0; i-- { + if scenes[i].Name == name { + return scenes[i], nil + } + } + + errOut := fmt.Sprintf("Error: Scene name '%s' not found. ", name) + return Scene{}, errors.New(errOut) +} + +// RecallScene recalls a scene +func (bridge *Bridge) RecallScene(id string) error { + action := &Action{Scene: id} + return bridge.SetGroupState(0, action) +} + +// RecallSceneByName recalls a scene +func (bridge *Bridge) RecallSceneByName(name string) error { + scene, err := bridge.GetSceneByName(name) + if err != nil { + return err + } + return bridge.RecallScene(scene.ID) +} + +// 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) + if err != nil { + return err + } + return nil +} + +// Bridge.ModifySceneState amends light states for lights +// included in a scene list. See `Bridge.ModifyScene` for +// changing the lights included in the scene list. +// func (bridge *Bridge) ModifySceneState() error { +// +// } + +// Bridge.ModifyScene amends the lights included for a given scene or +// it can be used to change the scene name. To amend light states for +// lights included in a scene list see `Bridge.ModifySceneState`. +// func (bridge *Bridge) ModifyScene() error { +// uri := fmt.Sprintf("/api/%s/scenes/%s/lightstates/%s", +// bridge.Username, oldScene.ID, ) +// } diff --git a/vendor/github.com/benburwell/gohue/schedule.go b/vendor/github.com/benburwell/gohue/schedule.go new file mode 100644 index 0000000..52315f9 --- /dev/null +++ b/vendor/github.com/benburwell/gohue/schedule.go @@ -0,0 +1,109 @@ +/* +* schedule.go +* 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 + */ +// http://www.developers.meethue.com/documentation/schedules-api-0 + +package hue + +import ( + "encoding/json" + "fmt" +) + +// Schedule struct defines attributes of Alarms and Timers +type Schedule struct { + Name string `json:"name"` + Description string `json:"description"` + Command struct { + Address string `json:"address"` + Body struct { + Scene string `json:"scene"` + } `json:"body"` + Method string `json:"method"` + } `json:"command"` + Localtime string `json:"localtime"` + Time string `json:"time"` + Created string `json:"created"` + Status string `json:"status"` + Autodelete bool `json:"autodelete"` + ID string +} + +// 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) + if err != nil { + return []Schedule{}, err + } + + // Each index key is the topmost element of the json array. + // Unmarshal the array, loop through each index key, and add it to the list + schedules := map[string]Schedule{} + err = json.Unmarshal(body, &schedules) + if err != nil { + return []Schedule{}, err + } + scheduleList := []Schedule{} + for key, value := range schedules { + schedule := Schedule{} + schedule = value + schedule.ID = key + scheduleList = append(scheduleList, schedule) + } + return scheduleList, nil +} + +// 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) { + uri := fmt.Sprintf("/api/%s/schedules/%s", bridge.Username, id) + body, _, err := bridge.Get(uri) + if err != nil { + return Schedule{}, err + } + + schedule := Schedule{} + err = json.Unmarshal(body, &schedule) + if err != nil { + return Schedule{}, err + } + return schedule, nil +} + +// 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) + if err != nil { + return err + } + + fmt.Println("CREATE SCHEDULE BODY: ", string(body)) + return nil +} + +// func (schedule *Schedule) Disable() { +// +// } +// +// func (schedule *Schedule) Enable() { +// +// } +// +// +// func (bridge *Bridge) GetSchedule(index int) (interface{}, error) { +// return []interface{}, nil +// } +// +// func (bridge *Bridge) SetSchedule(index int, schedule interface{}) error { +// return nil +// } +// +// func (bridge *Bridge) DeleteSchedule(index int) error { +// return nil +// } -- cgit v1.2.3