github.com/amimof/huego@v1.2.1/light.go (about) 1 package huego 2 3 import ( 4 "context" 5 "image/color" 6 "math" 7 ) 8 9 // Light represents a bridge light https://developers.meethue.com/documentation/lights-api 10 type Light struct { 11 State *State `json:"state,omitempty"` 12 Type string `json:"type,omitempty"` 13 Name string `json:"name,omitempty"` 14 ModelID string `json:"modelid,omitempty"` 15 ManufacturerName string `json:"manufacturername,omitempty"` 16 UniqueID string `json:"uniqueid,omitempty"` 17 SwVersion string `json:"swversion,omitempty"` 18 SwConfigID string `json:"swconfigid,omitempty"` 19 ProductName string `json:"productname,omitempty"` 20 ID int `json:"-"` 21 bridge *Bridge 22 } 23 24 // State defines the attributes and properties of a light 25 type State struct { 26 On bool `json:"on"` 27 Bri uint8 `json:"bri,omitempty"` 28 Hue uint16 `json:"hue,omitempty"` 29 Sat uint8 `json:"sat,omitempty"` 30 Xy []float32 `json:"xy,omitempty"` 31 Ct uint16 `json:"ct,omitempty"` 32 Alert string `json:"alert,omitempty"` 33 Effect string `json:"effect,omitempty"` 34 TransitionTime uint16 `json:"transitiontime,omitempty"` 35 BriInc int `json:"bri_inc,omitempty"` 36 SatInc int `json:"sat_inc,omitempty"` 37 HueInc int `json:"hue_inc,omitempty"` 38 CtInc int `json:"ct_inc,omitempty"` 39 XyInc int `json:"xy_inc,omitempty"` 40 ColorMode string `json:"colormode,omitempty"` 41 Reachable bool `json:"reachable,omitempty"` 42 Scene string `json:"scene,omitempty"` 43 } 44 45 // NewLight defines a list of lights discovered the last time the bridge performed a light discovery. 46 // Also stores the timestamp the last time a discovery was performed. 47 type NewLight struct { 48 Lights []string 49 LastScan string `json:"lastscan"` 50 } 51 52 // SetState sets the state of the light to s. 53 func (l *Light) SetState(s State) error { 54 return l.SetStateContext(context.Background(), s) 55 } 56 57 // SetStateContext sets the state of the light to s. 58 func (l *Light) SetStateContext(ctx context.Context, s State) error { 59 _, err := l.bridge.SetLightStateContext(ctx, l.ID, s) 60 if err != nil { 61 return err 62 } 63 l.State = &s 64 return nil 65 } 66 67 // Off sets the On state of one light to false, turning it off 68 func (l *Light) Off() error { 69 return l.OffContext(context.Background()) 70 } 71 72 // OffContext sets the On state of one light to false, turning it off 73 func (l *Light) OffContext(ctx context.Context) error { 74 state := State{On: false} 75 _, err := l.bridge.SetLightStateContext(ctx, l.ID, state) 76 if err != nil { 77 return err 78 } 79 l.State.On = false 80 return nil 81 } 82 83 // On sets the On state of one light to true, turning it on 84 func (l *Light) On() error { 85 return l.OnContext(context.Background()) 86 } 87 88 // OnContext sets the On state of one light to true, turning it on 89 func (l *Light) OnContext(ctx context.Context) error { 90 state := State{On: true} 91 _, err := l.bridge.SetLightStateContext(ctx, l.ID, state) 92 if err != nil { 93 return err 94 } 95 l.State.On = true 96 return nil 97 } 98 99 // IsOn returns true if light state On property is true 100 func (l *Light) IsOn() bool { 101 return l.State.On 102 } 103 104 // Rename sets the name property of the light 105 func (l *Light) Rename(new string) error { 106 return l.RenameContext(context.Background(), new) 107 } 108 109 // RenameContext sets the name property of the light 110 func (l *Light) RenameContext(ctx context.Context, new string) error { 111 update := Light{Name: new} 112 _, err := l.bridge.UpdateLightContext(ctx, l.ID, update) 113 if err != nil { 114 return err 115 } 116 l.Name = new 117 return nil 118 } 119 120 // Bri sets the light brightness state property 121 func (l *Light) Bri(new uint8) error { 122 return l.BriContext(context.Background(), new) 123 } 124 125 // BriContext sets the light brightness state property 126 func (l *Light) BriContext(ctx context.Context, new uint8) error { 127 update := State{On: true, Bri: new} 128 _, err := l.bridge.SetLightStateContext(ctx, l.ID, update) 129 if err != nil { 130 return err 131 } 132 l.State.Bri = new 133 l.State.On = true 134 return nil 135 } 136 137 // Hue sets the light hue state property (0-65535) 138 func (l *Light) Hue(new uint16) error { 139 return l.HueContext(context.Background(), new) 140 } 141 142 // HueContext sets the light hue state property (0-65535) 143 func (l *Light) HueContext(ctx context.Context, new uint16) error { 144 update := State{On: true, Hue: new} 145 _, err := l.bridge.SetLightStateContext(ctx, l.ID, update) 146 if err != nil { 147 return err 148 } 149 l.State.Hue = new 150 l.State.On = true 151 return nil 152 } 153 154 // Sat sets the light saturation state property (0-254) 155 func (l *Light) Sat(new uint8) error { 156 return l.SatContext(context.Background(), new) 157 } 158 159 // SatContext sets the light saturation state property (0-254) 160 func (l *Light) SatContext(ctx context.Context, new uint8) error { 161 update := State{On: true, Sat: new} 162 _, err := l.bridge.SetLightStateContext(ctx, l.ID, update) 163 if err != nil { 164 return err 165 } 166 l.State.Sat = new 167 l.State.On = true 168 return nil 169 } 170 171 // Xy sets the x and y coordinates of a color in CIE color space. (0-1 per value) 172 func (l *Light) Xy(new []float32) error { 173 return l.XyContext(context.Background(), new) 174 } 175 176 // XyContext sets the x and y coordinates of a color in CIE color space. (0-1 per value) 177 func (l *Light) XyContext(ctx context.Context, new []float32) error { 178 update := State{On: true, Xy: new} 179 _, err := l.bridge.SetLightStateContext(ctx, l.ID, update) 180 if err != nil { 181 return err 182 } 183 l.State.Xy = new 184 l.State.On = true 185 return nil 186 } 187 188 // Ct sets the light color temperature state property 189 func (l *Light) Ct(new uint16) error { 190 return l.CtContext(context.Background(), new) 191 } 192 193 // CtContext sets the light color temperature state property 194 func (l *Light) CtContext(ctx context.Context, new uint16) error { 195 update := State{On: true, Ct: new} 196 _, err := l.bridge.SetLightStateContext(ctx, l.ID, update) 197 if err != nil { 198 return err 199 } 200 l.State.Ct = new 201 l.State.On = true 202 return nil 203 } 204 205 // Col sets the light color as RGB (will be converted to xy) 206 func (l *Light) Col(new color.Color) error { 207 return l.ColContext(context.Background(), new) 208 } 209 210 // ColContext sets the light color as RGB (will be converted to xy) 211 func (l *Light) ColContext(ctx context.Context, new color.Color) error { 212 xy, bri := ConvertRGBToXy(new) 213 214 update := State{On: true, Xy: xy, Bri: bri} 215 _, err := l.bridge.SetLightStateContext(ctx, l.ID, update) 216 if err != nil { 217 return err 218 } 219 l.State.Xy = xy 220 l.State.Bri = bri 221 l.State.On = true 222 return nil 223 } 224 225 // TransitionTime sets the duration of the transition from the light’s current state to the new state 226 func (l *Light) TransitionTime(new uint16) error { 227 return l.TransitionTimeContext(context.Background(), new) 228 } 229 230 // TransitionTimeContext sets the duration of the transition from the light’s current state to the new state 231 func (l *Light) TransitionTimeContext(ctx context.Context, new uint16) error { 232 update := State{On: l.State.On, TransitionTime: new} 233 _, err := l.bridge.SetLightStateContext(ctx, l.ID, update) 234 if err != nil { 235 return err 236 } 237 l.State.TransitionTime = new 238 return nil 239 } 240 241 // Effect the dynamic effect of the light, currently “none” and “colorloop” are supported 242 func (l *Light) Effect(new string) error { 243 return l.EffectContext(context.Background(), new) 244 } 245 246 // EffectContext the dynamic effect of the light, currently “none” and “colorloop” are supported 247 func (l *Light) EffectContext(ctx context.Context, new string) error { 248 update := State{On: true, Effect: new} 249 _, err := l.bridge.SetLightStateContext(ctx, l.ID, update) 250 if err != nil { 251 return err 252 } 253 l.State.Effect = new 254 l.State.On = true 255 return nil 256 } 257 258 // Alert makes the light blink in its current color. Supported values are: 259 // “none” – The light is not performing an alert effect. 260 // “select” – The light is performing one breathe cycle. 261 // “lselect” – The light is performing breathe cycles for 15 seconds or until alert is set to "none". 262 func (l *Light) Alert(new string) error { 263 return l.AlertContext(context.Background(), new) 264 } 265 266 // AlertContext makes the light blink in its current color. Supported values are: 267 // “none” – The light is not performing an alert effect. 268 // “select” – The light is performing one breathe cycle. 269 // “lselect” – The light is performing breathe cycles for 15 seconds or until alert is set to "none". 270 func (l *Light) AlertContext(ctx context.Context, new string) error { 271 update := State{On: true, Alert: new} 272 _, err := l.bridge.SetLightStateContext(ctx, l.ID, update) 273 if err != nil { 274 return err 275 } 276 l.State.Effect = new 277 l.State.On = true 278 return nil 279 } 280 281 // ConvertRGBToXy converts a given RGB color to the xy color of the ligth. 282 // implemented as in https://developers.meethue.com/develop/application-design-guidance/color-conversion-formulas-rgb-to-xy-and-back/ 283 func ConvertRGBToXy(newcolor color.Color) ([]float32, uint8) { 284 r, g, b, _ := newcolor.RGBA() 285 rf := float64(r) / 65536.0 286 gf := float64(g) / 65536.0 287 bf := float64(b) / 65536.0 288 289 rf = gammaCorrect(rf) 290 gf = gammaCorrect(gf) 291 bf = gammaCorrect(bf) 292 293 X := float32(rf*0.649926 + gf*0.103455 + bf*0.197109) 294 Y := float32(rf*0.234327 + gf*0.743075 + bf*0.022598) 295 Z := float32(rf*0.0000000 + gf*0.053077 + bf*1.035763) 296 297 x := X / (X + Y + Z) 298 y := Y / (X + Y + Z) 299 300 xy := []float32{x, y} 301 return xy, uint8(Y * 254) 302 } 303 304 func gammaCorrect(value float64) float64 { 305 if value > 0.04045 { 306 return math.Pow((value+0.055)/(1.0+0.055), 2.4) 307 } 308 return (value / 12.92) 309 }