-
Notifications
You must be signed in to change notification settings - Fork 6
/
features.go
197 lines (171 loc) · 5.28 KB
/
features.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
package lifxlan
import (
"fmt"
"sort"
)
// OptionalBool defines a helper type for optional boolean fields
type OptionalBool bool
// OptionalBoolPtr is a helper function for writing *OptionalBool literal.
func OptionalBoolPtr(v OptionalBool) *OptionalBool {
return &v
}
// Get returns false when ob is unset, otherwise it returns the set value.
func (ob *OptionalBool) Get() bool {
if ob == nil {
return false
}
return bool(*ob)
}
// Fallback returns, in this order:
//
// - ob, if it's set,
//
// - a copy of fallback if fallback is set,
//
// - nil if neither ob nor fallback is set.
func (ob *OptionalBool) Fallback(fallback *OptionalBool) *OptionalBool {
if ob != nil {
return ob
}
if fallback != nil {
v := *fallback
return &v
}
return nil
}
// TemperatureRange defines the json format of temperature range of a product.
//
// It would be either a slice of length 0 (meaning this is not a light device),
// or length 2 (min, max).
type TemperatureRange []uint16
// The magic length of TemperatureRange to be considered as valid (min, max).
const (
ValidTemperatureRangeLength = 2
)
// Fallback returns, in this order:
//
// - tr if it's valid,
//
// - a copy of fallback if it's valid,
//
// - nil if neither tr nor fallback is valid.
func (tr TemperatureRange) Fallback(fallback TemperatureRange) TemperatureRange {
if tr.Valid() {
return tr
}
if fallback.Valid() {
v := make(TemperatureRange, ValidTemperatureRangeLength)
copy(v, fallback)
return v
}
return nil
}
// Valid returns true if tr has a length of exactly 2.
func (tr TemperatureRange) Valid() bool {
return len(tr) == ValidTemperatureRangeLength
}
// Min returns the min temperature if tr is valid, 0 otherwise.
func (tr TemperatureRange) Min() uint16 {
if tr.Valid() {
return tr[0]
}
return 0
}
// Max returns the max temperature if tr is valid, 0 otherwise.
func (tr TemperatureRange) Max() uint16 {
if tr.Valid() {
return tr[1]
}
return 0
}
// Features defines the json format of features of a product.
type Features struct {
HEV *OptionalBool `json:"hev,omitempty"`
Color *OptionalBool `json:"color,omitempty"`
Chain *OptionalBool `json:"chain,omitempty"`
Matrix *OptionalBool `json:"matrix,omitempty"`
Relays *OptionalBool `json:"relays,omitempty"`
Buttons *OptionalBool `json:"buttons,omitempty"`
Infrared *OptionalBool `json:"infrared,omitempty"`
Multizone *OptionalBool `json:"multizone,omitempty"`
ExtendedMultizone *OptionalBool `json:"extended_multizone,omitempty"`
TemperatureRange TemperatureRange `json:"temperature_range,omitempty"`
}
// MergeFeatures merges the features defined in features,
// Each feature falls back to the next one in features if it's unset.
func MergeFeatures(features ...Features) Features {
var result Features
for _, f := range features {
result.HEV = result.HEV.Fallback(f.HEV)
result.Color = result.Color.Fallback(f.Color)
result.Chain = result.Chain.Fallback(f.Chain)
result.Matrix = result.Matrix.Fallback(f.Matrix)
result.Relays = result.Relays.Fallback(f.Relays)
result.Buttons = result.Buttons.Fallback(f.Buttons)
result.Infrared = result.Infrared.Fallback(f.Infrared)
result.Multizone = result.Multizone.Fallback(f.Multizone)
result.ExtendedMultizone = result.ExtendedMultizone.Fallback(f.ExtendedMultizone)
result.TemperatureRange = result.TemperatureRange.Fallback(f.TemperatureRange)
}
return result
}
// Vendor defines a vendor.
type Vendor struct {
ID uint32
Name string
Defaults Features
}
// FirmwareUpgrade defines a firmware version with optional upgrade features.
type FirmwareUpgrade struct {
Major uint16 `json:"major"`
Minor uint16 `json:"minor"`
Features Features `json:"features"`
}
// Less returns true if fu's firmware version is smaller than other's firmware
// version.
func (fu FirmwareUpgrade) Less(other FirmwareUpgrade) bool {
if fu.Major < other.Major {
return true
}
if fu.Major > other.Major {
return false
}
return fu.Minor < other.Minor
}
func (fu FirmwareUpgrade) String() string {
return fmt.Sprintf("(%d, %d)", fu.Major, fu.Minor)
}
// EmptyFirmware is the constant to be compared against
// Device.Firmware().String().
const EmptyFirmware = "(0, 0)"
// Upgrades defines sortable interface of FirmwareUpgrade.
type Upgrades []FirmwareUpgrade
var _ sort.Interface = Upgrades(nil)
func (u Upgrades) Len() int { return len(u) }
func (u Upgrades) Less(i, j int) bool { return u[i].Less(u[j]) }
func (u Upgrades) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
// Product defines a product.
type Product struct {
VendorID uint32 `json:"-"`
VendorName string `json:"-"`
ProductID uint32 `json:"pid"`
ProductName string `json:"name"`
Features Features `json:"features"`
Upgrades Upgrades `json:"upgrades"`
}
// FeaturesAt gets the features at the given firmware version,
// with appropriate upgrades applied.
//
// Calling with zero firmware will return the same features as the Features
// field.
func (p Product) FeaturesAt(firmware FirmwareUpgrade) Features {
features := make([]Features, 0, len(p.Upgrades)+1)
for _, u := range p.Upgrades {
if firmware.Less(u) {
continue
}
features = append(features, u.Features)
}
features = append(features, p.Features)
return MergeFeatures(features...)
}