-
Notifications
You must be signed in to change notification settings - Fork 0
/
nuki.go
249 lines (223 loc) · 6.93 KB
/
nuki.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// Copyright 2018 The Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// Package nuki provides an simple implementation of the bridge api
// Versoin: v1.7 (30.03.2018)
package nuki
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
// Client is the interface implemented by types that can deliver a http response
// based on the given request.
type Client interface {
Do(req *http.Request) (*http.Response, error)
}
// Nuki holds you access token and connection information
type Nuki struct {
address string
token string
http Client
}
// Option allows additional options to be specified for a nuki client
type Option func(*Nuki)
// WithToken allows to set a token when creating a Nuki.
func WithToken(t string) Option {
return func(n *Nuki) {
n.token = t
}
}
// WithHTTPClient allows to set a Client when creating a Nuki.
func WithHTTPClient(c Client) Option {
return func(n *Nuki) {
n.http = c
}
}
// NewNuki returns a new nuki entrypoint without a token
// to request a new token, the auth function can be called.
// this connects to the bridge and stores the token internally.
func NewNuki(address string, opts ...Option) *Nuki {
n := &Nuki{
address: address,
http: &http.Client{
Timeout: 10 * time.Second,
},
}
for _, o := range opts {
o(n)
}
return n
}
// Token returns a token provided by nuki
func (n *Nuki) Token() string {
return n.token
}
func (n *Nuki) doRequest(method, path string, i interface{}, param url.Values) error {
url := fmt.Sprintf("http://%s/%s", n.address, path)
r, err := http.NewRequest(method, url, nil)
if err != nil {
return err
}
r.URL.RawQuery = param.Encode()
res, err := n.http.Do(r)
if err != nil {
return err
}
if err := json.NewDecoder(res.Body).Decode(i); err != nil {
return err
}
return ErrorFromStatus(res.StatusCode)
}
// Auth Enables the api (if not yet enabled) and returns the api token.
// If no api token has yet been set, a new (random) one is generated.
// When issuing this API-call the bridge turns on its LED for 30 seconds.
// The button of the bridge has to be pressed within this timeframe.
// Otherwise the bridge returns a negative success and no token.
func (n *Nuki) Auth() (*AuthReponse, error) {
const path = "auth"
auth := &AuthReponse{}
if err := n.doRequest(http.MethodGet, path, &auth, url.Values{}); err != nil {
return nil, err
}
n.token = auth.Token
return auth, nil
}
// ConfigAuth enables or disables the authorization via /auth and the
// publication of the local IP and port to the discovery URL
// The api token configured via the Nuki app when enabling the API
// enable: Flag (0 or 1) indicating whether or not the authorization
// should be enabled
func (n *Nuki) ConfigAuth(enable bool) (*ConfigAuthResponse, error) {
const path = "configAuth"
config := &ConfigAuthResponse{}
if err := n.doRequest(http.MethodGet, path, &config, nil); err != nil {
return nil, err
}
return config, nil
}
// List returns all paired Smart Locks a valid token is required
func (n *Nuki) List() ([]ListResponse, error) {
const path = "list"
param := url.Values{}
param.Set("token", n.token)
var list []ListResponse
if err := n.doRequest(http.MethodGet, path, &list, param); err != nil {
return nil, err
}
return list, nil
}
// LockState returns the current lock state of a smart lock by give id
func (n *Nuki) LockState(nukiID int) (*LockStateResponse, error) {
const path = "lockState"
param := url.Values{}
param.Set("nukiId", strconv.Itoa(nukiID))
param.Set("token", n.token)
lockState := &LockStateResponse{}
if err := n.doRequest(http.MethodGet, path, &lockState, param); err != nil {
return nil, err
}
return lockState, nil
}
// LockAction performs a lock operation on the given smart lock given by the id
// action: the desired lock action
// noWait: indicating whether or not to wait for the lock action to complete and
// return its result
func (n *Nuki) LockAction(nukiID int, action Action, noWait bool) (*LockActionResponse, error) {
const path = "lockAction"
param := url.Values{}
param.Set("nukiId", strconv.Itoa(nukiID))
param.Set("action", strconv.Itoa(int(action)))
param.Set("noWait", strconv.FormatBool(noWait))
param.Set("token", n.token)
lockAction := &LockActionResponse{}
if err := n.doRequest(http.MethodGet, path, &lockAction, param); err != nil {
return nil, err
}
return lockAction, nil
}
// Unpair removes the pairing with a given smart lock
func (n *Nuki) Unpair(nukiID int) (*UnpairResponse, error) {
const path = "unpair"
param := url.Values{}
param.Set("token", n.token)
param.Set("nukiId", strconv.Itoa(nukiID))
unpair := &UnpairResponse{}
if err := n.doRequest(http.MethodGet, path, &unpair, param); err != nil {
return nil, err
}
return unpair, nil
}
// Info returns all smart locks in range and some device information of the
// bridge itself
func (n *Nuki) Info() (*InfoResponse, error) {
const path = "info"
param := url.Values{}
param.Set("token", n.token)
info := &InfoResponse{}
if err := n.doRequest(http.MethodGet, path, &info, param); err != nil {
return nil, err
}
return info, nil
}
// CallbackAdd registers a new callback url
func (n *Nuki) CallbackAdd(nukiURL string) (*CallbackReponse, error) {
const path = "callback/add"
param := url.Values{}
param.Set("token", n.token)
param.Set("url", nukiURL)
callback := &CallbackReponse{}
if err := n.doRequest(http.MethodGet, path, &callback, param); err != nil {
return nil, err
}
return callback, nil
}
// CallbackList returns a CallbackReponse with all registered url callbacks
func (n *Nuki) CallbackList() (*CallbackReponse, error) {
const path = "callback/list"
param := url.Values{}
param.Set("token", n.token)
callback := &CallbackReponse{}
if err := n.doRequest(http.MethodGet, path, &callback, param); err != nil {
return nil, err
}
return callback, nil
}
// CallbackRemove removes a previously added callback by ID
func (n *Nuki) CallbackRemove(callbackID int) (*CallbackReponse, error) {
const path = "callback/remove"
param := url.Values{}
param.Set("token", n.token)
param.Set("id", strconv.Itoa(callbackID))
callback := &CallbackReponse{}
if err := n.doRequest(http.MethodGet, path, &callback, param); err != nil {
return nil, err
}
return callback, nil
}
// The following endpoints are available for maintenance purposes of the
// hardware bridge. Therefore they are not available on the software bridge.
// TODO
// Log returns a log of the Bridge
func (n *Nuki) Log() (*LogResponse, error) {
return nil, ErrNotImplemented
}
// ClearLog clears the log of the Bridge
func (n *Nuki) ClearLog() error {
return ErrNotImplemented
}
// FWUpdate immediately checks for a new firmware update and installs it
func (n *Nuki) FWUpdate() error {
return ErrNotImplemented
}
// Reboot s the bridge
func (n *Nuki) Reboot() error {
return ErrNotImplemented
}
// FactoryReset performs a factory reset
func (n *Nuki) FactoryReset() error {
return ErrNotImplemented
}