Skip to content

Commit e38a197

Browse files
committed
Better split between high- and low-level clients
1 parent 8412bd5 commit e38a197

27 files changed

+85
-44
lines changed

Makefile

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
.PHONY: doc
1+
.PHONY: doc test
22

33
doc:
44
go get github.com/robertkrimen/godocdown/godocdown
55
godocdown > doc/package.md
66

7-
integration:
8-
go test -tags=integration
7+
test:
8+
go test -v ./...
9+
10+
integration-test:
11+
go test -tags=integration
12+
13+
coverage:
14+
go test -coverprofile=coverage.out
15+
go tool cover -html=coverage.out

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
<p align="center"><a href="https://circleci.com/gh/sgrimee/kizcool"><img src="https://circleci.com/gh/sgrimee/kizcool.svg?style=shield" alt="Build Status"></a></p>
66
</p>
77

8-
# Get kizcool
8+
## Get kizcool command line tool
99

1010
```
1111
go get github.com/sgrimee/kizcool
12-
cd $GOPATH/src/github.com/sgrimee/kizcool/kizcli
12+
cd $GOPATH/src/github.com/sgrimee/kizcool/exe/kizcmd
1313
go get
1414
go install
15-
kizcli configure
15+
kizcmd configure
1616
```
1717

1818
## Go package documentation

client/client.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Package client provides a low-level client to the overkiz api
2+
// JSON responses are not unmarshalled and are returned as-is.
23
package client
34

45
import (
@@ -109,7 +110,6 @@ func (c *Client) Login() error {
109110
if err := checkStatusOk(resp); err != nil {
110111
return err
111112
}
112-
u, _ := url.Parse(c.baseURL)
113113
for _, cookie := range resp.Cookies() {
114114
if (cookie.Name == "JSESSIONID") && (cookie.Value != "") {
115115
return nil
@@ -153,6 +153,24 @@ func (c *Client) DoWithAuth(req *http.Request) (*http.Response, error) {
153153
return resp, nil
154154
}
155155

156+
// GetDevices returns the raw response to retrieving all devices
157+
func (c *Client) GetDevices() (*http.Response, error) {
158+
return c.GetWithAuth("/enduserAPI/setup/devices")
159+
}
160+
161+
// GetDevice returns the raw response to retrieving one device by URL
162+
func (c *Client) GetDevice(deviceURL string) (*http.Response, error) {
163+
query := "/enduserAPI/setup/devices/" + url.QueryEscape(deviceURL)
164+
return c.GetWithAuth(query)
165+
}
166+
167+
// GetDeviceState returns the current state with name for the device with URL deviceURL
168+
func (c *Client) GetDeviceState(deviceURL, stateName string) (*http.Response, error) {
169+
query := "/enduserAPI/setup/devices/" + url.QueryEscape(deviceURL) +
170+
"/states/" + url.QueryEscape(stateName)
171+
return c.GetWithAuth(query)
172+
}
173+
156174
// RefreshStates tells the server to refresh states.
157175
// But not sure yet what it really means`?
158176
func (c *Client) RefreshStates() error {
@@ -183,7 +201,7 @@ func (c *Client) Execute(json []byte) (*http.Response, error) {
183201
}
184202

185203
// RegisterListener registers for events and returns a listener id
186-
func (c *Client) RegisterListener() (ListenerID, error) {
204+
func (c *Client) RegisterListener() (string, error) {
187205
req, err := http.NewRequest(http.MethodPost, c.baseURL+"/events/register", nil)
188206
if err != nil {
189207
return "", err
@@ -194,17 +212,17 @@ func (c *Client) RegisterListener() (ListenerID, error) {
194212
}
195213
defer resp.Body.Close()
196214
type Result struct {
197-
lid ListenerID
215+
ID string
198216
}
199217
var result Result
200218
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
201219
return "", err
202220
}
203-
return result.lid, nil
221+
return result.ID, nil
204222
}
205223

206224
// UnregisterListener unregisters the listener
207-
func (c *Client) UnregisterListener(l ListenerID) error {
225+
func (c *Client) UnregisterListener(l string) error {
208226
query := fmt.Sprintf("%s/events/%s/unregister", c.baseURL, l)
209227
req, err := http.NewRequest(http.MethodPost, query, nil)
210228
if err != nil {

client/client_test.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,31 @@ func TestNewWithHTTPClientGetsSessionCookie(t *testing.T) {
6767
}
6868

6969
func TestRegisterListener(t *testing.T) {
70-
t.Skip("Need to write test")
70+
const lid = "77777777-3333-5555-2222-cccccccccccc"
71+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
72+
assert.Equal(t, "/events/register", req.URL.String())
73+
rw.Write([]byte(`{"id":"` + lid + `"}`))
74+
}))
75+
defer server.Close()
76+
c, err := NewWithHTTPClient("user", "pass", server.URL, "", server.Client())
77+
assert.NoError(t, err)
78+
l, err := c.RegisterListener()
79+
assert.NoError(t, err)
80+
assert.Equal(t, lid, l)
7181
}
7282

7383
func TestUnregisterListener(t *testing.T) {
74-
t.Skip("Need to write test")
84+
const lid = "77777777-3333-5555-2222-cccccccccccc"
85+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
86+
assert.Equal(t, "/events/"+lid+"/unregister", req.URL.String())
87+
}))
88+
defer server.Close()
89+
c, err := NewWithHTTPClient("user", "pass", server.URL, "", server.Client())
90+
assert.NoError(t, err)
91+
err = c.UnregisterListener(lid)
92+
assert.NoError(t, err)
7593
}
7694

7795
func TestFetchEvents(t *testing.T) {
78-
t.Skip("Need to write test")
96+
t.Skip("Implement me")
7997
}

client/interface.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ package client
22

33
import "net/http"
44

5-
// APIClient interface defines a low-level api client
5+
// APIClient interface defines a low-level api client to the kiz server
66
type APIClient interface {
77
SessionID() string
88
Login() error
99
GetWithAuth(query string) (*http.Response, error)
1010
DoWithAuth(req *http.Request) (*http.Response, error)
11+
GetDevices() (*http.Response, error)
12+
GetDevice(deviceURL string) (*http.Response, error)
13+
GetDeviceState(deviceURL, stateName string) (*http.Response, error)
1114
RefreshStates() error
1215
Execute(json []byte) (*http.Response, error)
16+
RegisterListener() (string, error)
17+
UnregisterListener(string) error
1318
}

config/config.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
package config
33

44
import (
5-
"fmt"
6-
75
homedir "github.com/mitchellh/go-homedir"
86
"github.com/spf13/viper"
97
)
@@ -79,7 +77,6 @@ func Read(create bool) error {
7977

8078
// Write saves the current config to file
8179
func Write() error {
82-
fmt.Printf("Writing to config file: %s\n", viper.ConfigFileUsed())
8380
return viper.WriteConfig()
8481
}
8582

doc.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
// Package kizcool provides a client to the Overkiz IoT API, used by velux, somfy and other vendors to control velux devices with a Tahoma box.
1+
// Package kizcool provides a high-level client to the Overkiz IoT API,
2+
// used by velux, somfy and other vendors to control velux devices with
3+
// a Tahoma box.
24
package kizcool

doc/package.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
--
33
import "github.com/sgrimee/kizcool"
44

5-
Package kizcool provides a client to the Overkiz IoT API, used by velux, somfy
6-
and other vendors to control velux devices with a Tahoma box.
5+
Package kizcool provides a high-level client to the Overkiz IoT API, used by
6+
velux, somfy and other vendors to control velux devices with a Tahoma box.
77

88
## Usage
99

@@ -171,7 +171,7 @@ type Kiz struct {
171171
}
172172
```
173173

174-
Kiz provides high-level methods and structs to interact with the server.
174+
Kiz high-level client
175175

176176
#### func New
177177

@@ -273,7 +273,7 @@ Open opens a device
273273
func (k *Kiz) RefreshStates() error
274274
```
275275
RefreshStates tells the server to refresh states. But not sure yet what it
276-
really means`?
276+
really does...
277277

278278
#### func (*Kiz) SessionID
279279

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

kizcli/cmd/cmd_get_device.go renamed to exe/kizcmd/cmd/cmd_get_device.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ var deviceCmd = &cobra.Command{
1010
Use: "device",
1111
Short: "Get a single device",
1212
Long: `Get infos on a single device identified by its device url or label (case insensitive)
13-
kizclient get device "io://1111-0000-4444/15332221"`,
13+
kizcmd get device "io://1111-0000-4444/15332221"`,
1414
Run: func(cmd *cobra.Command, args []string) {
1515
if len(args) == 0 {
1616
log.Fatal("You must specify a device.")
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

kizcli/cmd/cmd_root.go renamed to exe/kizcmd/cmd/cmd_root.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ var kiz *kizcool.Kiz
1313

1414
// RootCmd represents the base command when called without any subcommands
1515
var RootCmd = &cobra.Command{
16-
Use: "kizcli",
16+
Use: "kizcmd",
1717
Short: "Overkiz command-line client",
18-
Long: `kizclient implements a partial client for the Overkiz home automation api.`,
18+
Long: `kizcmd implements a partial client for the Overkiz home automation api.`,
1919
}
2020

2121
// Execute adds all child commands to the root command sets flags appropriately.
File renamed without changes.
File renamed without changes.
File renamed without changes.

exe/kizcmd/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package main
2+
3+
import "github.com/sgrimee/kizcool/exe/kizcmd/cmd"
4+
5+
func main() {
6+
cmd.Execute()
7+
}

kizcli/main.go

Lines changed: 0 additions & 9 deletions
This file was deleted.

kizcool.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ package kizcool
33
import (
44
"encoding/json"
55
"errors"
6-
"net/url"
76
"regexp"
87
"strings"
98

109
"github.com/sgrimee/kizcool/client"
1110
)
1211

13-
// Kiz provides high-level methods and structs to interact with the server.
12+
// Kiz high-level client
1413
type Kiz struct {
1514
clt client.APIClient
1615
}
@@ -46,7 +45,7 @@ func (k *Kiz) Login() error {
4645

4746
// GetDevices returns the list of devices
4847
func (k *Kiz) GetDevices() ([]Device, error) {
49-
resp, err := k.clt.GetWithAuth("/enduserAPI/setup/devices")
48+
resp, err := k.clt.GetDevices()
5049
if err != nil {
5150
return nil, err
5251
}
@@ -58,8 +57,7 @@ func (k *Kiz) GetDevices() ([]Device, error) {
5857

5958
// GetDevice returns a single device
6059
func (k *Kiz) GetDevice(deviceURL DeviceURL) (Device, error) {
61-
query := "/enduserAPI/setup/devices/" + url.QueryEscape(string(deviceURL))
62-
resp, err := k.clt.GetWithAuth(query)
60+
resp, err := k.clt.GetDevice(string(deviceURL))
6361
if err != nil {
6462
return Device{}, err
6563
}
@@ -113,9 +111,7 @@ func (k *Kiz) GetDeviceByText(text string) (Device, error) {
113111

114112
// GetDeviceState returns the current state with name stateName for the device with URL deviceURL
115113
func (k *Kiz) GetDeviceState(deviceURL DeviceURL, stateName StateName) (State, error) {
116-
query := "/enduserAPI/setup/devices/" + url.QueryEscape(string(deviceURL)) +
117-
"/states/" + url.QueryEscape(string(stateName))
118-
resp, err := k.clt.GetWithAuth(query)
114+
resp, err := k.clt.GetDeviceState(string(deviceURL), string(stateName))
119115
if err != nil {
120116
return State{}, err
121117
}
@@ -126,7 +122,7 @@ func (k *Kiz) GetDeviceState(deviceURL DeviceURL, stateName StateName) (State, e
126122
}
127123

128124
// RefreshStates tells the server to refresh states.
129-
// But not sure yet what it really means`?
125+
// But not sure yet what it really does...
130126
func (k *Kiz) RefreshStates() error {
131127
return k.clt.RefreshStates()
132128
}

0 commit comments

Comments
 (0)