-
Notifications
You must be signed in to change notification settings - Fork 1
/
pilot.go
347 lines (302 loc) · 9.33 KB
/
pilot.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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
// Copyright (c) 2021 David Vogel
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
package wiz
import (
"encoding/json"
"fmt"
)
// Pilot contains the parameters regarding light output.
//
// A valid pilot can either contain a scene, a color value defined by temperature or a color defined by RGB(W) values.
// There must be only one at a time.
// An exception is that the device sends a scene ID of 0 back when no scene is defined.
type Pilot struct {
//Cnx string `json:"cnx,omitempty"`
Mac string `json:"mac,omitempty"` // Mac address.
RSSI int `json:"rssi,omitempty"` // Signal strength.
Src string `json:"src,omitempty"` // No idea.
State bool `json:"state"` // On off state.
Dimming *uint `json:"dimming,omitempty"` // Dimming value in percent. There is a limit as to how low the dimming value can go.
Speed *uint `json:"speed,omitempty"` // Color changing speed in percent. This only seems to influence the scene playback speed.
Temp *uint `json:"temp,omitempty"` // Color temperature in Kelvin.
SchdPsetID *uint `json:"schdPsetId,omitempty"` // Not sure. Scheduled preset?
Scene *Scene `json:"sceneId,omitempty"` // The scene ID.
R *uint8 `json:"r,omitempty"` // Red luminance in range 0-255.
G *uint8 `json:"g,omitempty"` // Green luminance range 0-255.
B *uint8 `json:"b,omitempty"` // Blue luminance range 0-255.
CW *uint8 `json:"c,omitempty"` // Cold white luminance range 0-255.
WW *uint8 `json:"w,omitempty"` // Warm white luminance range 0-255.
}
// NewPilot returns a pilot with the given light state.
func NewPilot(state bool) Pilot {
return Pilot{
State: state,
}
}
// NewPilotWithRGB returns a pilot with the given RGB values.
// No color transformation is done, all values are passed directly to the device.
func NewPilotWithRGB(dimming uint, r, g, b uint8) Pilot {
// Edge case, if all values are 0, set the state to false.
// Otherwise the lamp will ignore the pilot.
var state bool
if r > 0 || g > 0 || b > 0 {
state = true
} else {
state = false
}
return Pilot{
State: state,
Dimming: &dimming,
R: &r,
G: &g,
B: &b,
}
}
// NewPilotWithRGBW returns a pilot with the given RGBW values.
// No color transformation is done, all values are passed directly to the device.
func NewPilotWithRGBW(dimming uint, r, g, b, cw, ww uint8) Pilot {
// Edge case, if all values are 0, set the state to false.
// Otherwise the lamp will ignore the pilot.
var state bool
if r > 0 || g > 0 || b > 0 || cw > 0 || ww > 0 {
state = true
} else {
state = false
}
return Pilot{
State: state,
Dimming: &dimming,
R: &r,
G: &g,
B: &b,
CW: &cw,
WW: &ww,
}
}
// NewPilotWithWhite returns a pilot with the given cold and warm white values.
// No color transformation is done, all values are passed directly to the device.
func NewPilotWithWhite(dimming uint, cw, ww uint8) Pilot {
// Edge case, if all values are 0, set the state to false.
// Otherwise the lamp will ignore the pilot.
var state bool
if cw > 0 || ww > 0 {
state = true
} else {
state = false
}
return Pilot{
State: state,
Dimming: &dimming,
CW: &cw,
WW: &ww,
}
}
// NewPilotWithTemp returns a pilot with the given color temperature values.
// No color transformation is done, all values are passed directly to the device.
func NewPilotWithTemp(dimming, temperature uint) Pilot {
return Pilot{
State: true,
Dimming: &dimming,
Temp: &temperature,
}
}
// NewPilotWithScene returns a pilot with the given scene.
//
// Some scenes need a dimming and/or speed value, see
//
// s.NeedsDimming()
// s.NeedsSpeed()
//
// In case a scene doesn't need such value, it is ignored by this function.
func NewPilotWithScene(s Scene, dimming, speed uint) Pilot {
p := Pilot{
State: true,
Scene: &s,
}
if s.NeedsDimming() {
p.Dimming = &dimming
}
if s.NeedsSpeed() {
p.Speed = &speed
}
return p
}
// WithDimming returns a copy of the pilot set to the given dimming value.
func (p Pilot) WithDimming(dimming uint) Pilot {
p.Dimming = &dimming
return p
}
// WithLightOff returns a copy of the pilot with the light turned off.
func (p Pilot) WithLightOff() Pilot {
p.State = false
return p
}
// WithLightOn returns a copy of the pilot with the light turned on.
func (p Pilot) WithLightOn() Pilot {
p.State = true
return p
}
// WithLightToggled returns a copy of the pilot with the light toggled.
func (p Pilot) WithLightToggled() Pilot {
p.State = !p.State
return p
}
// WithRGB returns a copy of the pilot with the given color values set.
// This will change the on off state.
// This will reset any other competing value like scene ID or temperature.
func (p Pilot) WithRGB(r, g, b uint8) Pilot {
p.Scene, p.Temp, p.Speed = nil, nil, nil
p.R, p.G, p.B, p.CW, p.WW = &r, &g, &b, nil, nil
if r > 0 || g > 0 || b > 0 {
p.State = true
} else {
p.State = false
}
return p
}
// WithRGBW returns a copy of the pilot with the given color values set.
// This will change the on off state.
// This will reset any other competing value like scene ID or temperature.
func (p Pilot) WithRGBW(r, g, b, cw, ww uint8) Pilot {
p.Scene, p.Temp, p.Speed = nil, nil, nil
p.R, p.G, p.B, p.CW, p.WW = &r, &g, &b, &cw, &ww
if r > 0 || g > 0 || b > 0 || cw > 0 || ww > 0 {
p.State = true
} else {
p.State = false
}
return p
}
// WithWhite returns a copy of the pilot with the given color values set.
// This will change the on off state.
// This will reset any other competing value like scene ID or temperature.
func (p Pilot) WithWhite(cw, ww uint8) Pilot {
p.Scene, p.Temp, p.Speed = nil, nil, nil
p.R, p.G, p.B, p.CW, p.WW = nil, nil, nil, &cw, &ww
if cw > 0 || ww > 0 {
p.State = true
} else {
p.State = false
}
return p
}
// WithTemp returns a copy of the pilot with the given color temperature set.
// This will not change the on/off state or dimming value of the pilot.
// This will reset any other competing value like scene ID or RGBW values.
func (p Pilot) WithTemp(temp uint) Pilot {
p.Scene, p.Temp, p.Speed = nil, &temp, nil
p.R, p.G, p.B, p.CW, p.WW = nil, nil, nil, nil, nil
return p
}
// WithScene returns a copy of the pilot with the given scene set.
// This will not change the on/off state of the pilot.
// This will reset any other competing value like RGB values.
func (p Pilot) WithScene(s Scene, speed uint) Pilot {
p.R, p.G, p.B, p.CW, p.WW, p.Temp = nil, nil, nil, nil, nil, nil
p.Scene = &s
if s.NeedsSpeed() {
p.Speed = &speed
} else {
p.Speed = nil
}
if !s.NeedsDimming() {
p.Dimming = nil
}
return p
}
// HasRGB returns true, if the pilot contains RGB values, including all Zero values.
// This ensures that you can dereference the R, G, and B fields.
func (p Pilot) HasRGB() bool {
if p.R != nil && p.G != nil && p.B != nil {
return true
}
return false
}
// HasRGBW returns true, if the pilot contains RGBW values, including all Zero values.
// This ensures that you can dereference the R, G, B, CW, and WW fields.
func (p Pilot) HasRGBW() bool {
if p.R != nil && p.G != nil && p.B != nil && p.CW != nil && p.WW != nil {
return true
}
return false
}
// HasWhite returns true, if the pilot contains cold and warm white values, including all Zero values.
// This ensures that you can dereference the CW and WW fields.
func (p Pilot) HasWhite() bool {
if p.CW != nil && p.WW != nil {
return true
}
return false
}
// HasScene returns true, if the pilot contains a scene value.
// This ensures that you can dereference the Scene field.
func (p Pilot) HasScene() bool {
return p.Scene != nil
}
// HasTemp returns true, if the pilot contains a color temperature value.
// This ensures that you can dereference the Temp field.
func (p Pilot) HasTemp() bool {
return p.Temp != nil
}
// HasSpeed returns true, if the pilot contains a speed value.
// This ensures that you can dereference the Speed field.
func (p Pilot) HasSpeed() bool {
return p.Speed != nil
}
// HasDimming returns true, if the pilot contains a dimming value.
// This ensures that you can dereference the Dimming field.
func (p Pilot) HasDimming() bool {
return p.Dimming != nil
}
// UnmarshalJSON implements the JSON unmarshaler interface.
func (p *Pilot) UnmarshalJSON(data []byte) error {
type tempType Pilot
var temp tempType
if err := json.Unmarshal(data, &temp); err != nil {
return err
}
// Special case: If the scene ID is 0, remove the scene.
// The device sends that scene ID, instead of leaving the scene field empty.
if temp.Scene != nil && temp.Scene.id == 0 {
temp.Scene = nil
}
*p = Pilot(temp)
return nil
}
func (p Pilot) String() string {
result := fmt.Sprintf("{State: %v", p.State)
if p.Dimming != nil {
result += fmt.Sprintf(", Dimming: %d %%", *p.Dimming)
}
if p.Speed != nil {
result += fmt.Sprintf(", Speed: %d %%", *p.Speed)
}
if p.Temp != nil {
result += fmt.Sprintf(", Temp: %d K", *p.Temp)
}
if p.SchdPsetID != nil {
result += fmt.Sprintf(", SchdPsetID: %v", *p.SchdPsetID)
}
if p.Scene != nil {
result += fmt.Sprintf(", Scene: %v", *p.Scene)
}
if p.R != nil {
result += fmt.Sprintf(", R: %d", *p.R)
}
if p.G != nil {
result += fmt.Sprintf(", G: %d", *p.G)
}
if p.B != nil {
result += fmt.Sprintf(", B: %d", *p.B)
}
if p.CW != nil {
result += fmt.Sprintf(", CW: %d", *p.CW)
}
if p.WW != nil {
result += fmt.Sprintf(", WW: %d", *p.WW)
}
result += "}"
return result
}