go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/experiments/huectl/pkg/hue/group.go (about)

     1  /*
     2  
     3  Copyright (c) 2023 - Present. Will Charczuk. All rights reserved.
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository.
     5  
     6  */
     7  
     8  package hue
     9  
    10  import (
    11  	"context"
    12  	"errors"
    13  	"time"
    14  )
    15  
    16  // Group represents a bridge group https://developers.meethue.com/documentation/groups-api
    17  type Group struct {
    18  	bridge Bridge
    19  
    20  	ID int `json:"id,omitempty"`
    21  
    22  	Class      string               `json:"class,omitempty"`
    23  	GroupState GroupState           `json:"state,omitempty"`
    24  	Lights     []string             `json:"lights,omitempty"`
    25  	Locations  map[string][]float64 `json:"locations,omitempty"`
    26  	Name       string               `json:"name,omitempty"`
    27  	Recycle    bool                 `json:"recycle,omitempty"`
    28  	State      LightState           `json:"action,omitempty"`
    29  	Stream     Stream               `json:"stream,omitempty"`
    30  	Type       string               `json:"type,omitempty"`
    31  }
    32  
    33  // GroupState defines the state on a group.
    34  // Can be used to control the state of all lights in a group rather than controlling them individually
    35  type GroupState struct {
    36  	AllOn bool `json:"all_on,omitempty"`
    37  	AnyOn bool `json:"any_on,omitempty"`
    38  }
    39  
    40  // Stream define the stream status of a group
    41  type Stream struct {
    42  	ProxyMode string  `json:"proxymode,omitempty"`
    43  	ProxyNode string  `json:"proxynode,omitempty"`
    44  	ActiveRaw *bool   `json:"active,omitempty"`
    45  	OwnerRaw  *string `json:"owner,omitempty"`
    46  }
    47  
    48  // Active returns the stream active state, and will return false if ActiveRaw is nil
    49  func (s *Stream) Active() bool {
    50  	if s.ActiveRaw == nil {
    51  		return false
    52  	}
    53  
    54  	return *s.ActiveRaw
    55  }
    56  
    57  // Owner returns the stream Owner, and will return an empty string if OwnerRaw is nil
    58  func (s *Stream) Owner() string {
    59  	if s.OwnerRaw == nil {
    60  		return ""
    61  	}
    62  
    63  	return *s.OwnerRaw
    64  }
    65  
    66  // SetState sets the state of the group to s.
    67  func (g *Group) SetState(ctx context.Context, s LightState) error {
    68  	_, err := g.bridge.SetGroupState(ctx, g.ID, s)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	g.State = s
    73  	return nil
    74  }
    75  
    76  // Rename sets the name property of the group
    77  func (g *Group) Rename(ctx context.Context, new string) error {
    78  	update := Group{Name: new}
    79  	_, err := g.bridge.UpdateGroup(ctx, g.ID, update)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	g.Name = new
    84  	return nil
    85  }
    86  
    87  // Off sets the On state of one group to false, turning all lights in the group off
    88  func (g *Group) Off(ctx context.Context) error {
    89  	state := LightState{On: false}
    90  	_, err := g.bridge.SetGroupState(ctx, g.ID, state)
    91  	if err != nil {
    92  		return err
    93  	}
    94  	g.State.On = false
    95  	return nil
    96  }
    97  
    98  // On sets the On state of one group to true, turning all lights in the group on
    99  func (g *Group) On(ctx context.Context) error {
   100  	state := LightState{On: true}
   101  	_, err := g.bridge.SetGroupState(ctx, g.ID, state)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	g.State.On = true
   106  	return nil
   107  }
   108  
   109  // IsOn returns true if light state On property is true
   110  func (g *Group) IsOn() bool {
   111  	return g.State.On
   112  }
   113  
   114  // Brightness sets the light brightness state property
   115  func (g *Group) Brightness(ctx context.Context, new uint8) error {
   116  	update := LightState{On: true, Bri: new}
   117  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	g.State.Bri = new
   122  	g.State.On = true
   123  	return nil
   124  }
   125  
   126  // Hue sets the light hue state property (0-65535)
   127  func (g *Group) Hue(ctx context.Context, new uint16) error {
   128  	update := LightState{On: true, Hue: new}
   129  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	g.State.Hue = new
   134  	g.State.On = true
   135  	return nil
   136  }
   137  
   138  // Saturation sets the light saturation state property (0-254)
   139  func (g *Group) Saturation(ctx context.Context, new uint8) error {
   140  	update := LightState{On: true, Sat: new}
   141  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   142  	if err != nil {
   143  		return err
   144  	}
   145  	g.State.Sat = new
   146  	g.State.On = true
   147  	return nil
   148  }
   149  
   150  // Xy sets the x and y coordinates of a color in CIE color space. (0-1 per value)
   151  func (g *Group) Xy(ctx context.Context, new []float32) error {
   152  	update := LightState{On: true, Xy: new}
   153  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	g.State.Xy = new
   158  	g.State.On = true
   159  	return nil
   160  }
   161  
   162  // ColorTemperature sets the light color temperature state property
   163  func (g *Group) ColorTemperature(ctx context.Context, new uint16) error {
   164  	update := LightState{On: true, Ct: new}
   165  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   166  	if err != nil {
   167  		return err
   168  	}
   169  	g.State.Ct = new
   170  	g.State.On = true
   171  	return nil
   172  }
   173  
   174  // Color sets the light color as RGB (will be converted to xy)
   175  func (g *Group) Color(ctx context.Context, c Color) error {
   176  	xy := c.XY(GamutC)
   177  
   178  	update := LightState{
   179  		On:  true,
   180  		Bri: g.State.Bri,
   181  		Xy:  xy,
   182  	}
   183  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   184  	if err != nil {
   185  		return err
   186  	}
   187  	g.State.Xy = xy
   188  	g.State.On = true
   189  	return nil
   190  }
   191  
   192  // ColorTransition sets the light color as RGB (will be converted to xy) with a given transition time.
   193  func (g *Group) ColorTransition(ctx context.Context, c Color, transition time.Duration) error {
   194  	xy := c.XY(GamutC)
   195  
   196  	update := LightState{
   197  		On:             true,
   198  		Bri:            g.State.Bri,
   199  		Xy:             xy,
   200  		TransitionTime: uint16(transition / (100 * time.Millisecond)),
   201  	}
   202  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   203  	if err != nil {
   204  		return err
   205  	}
   206  	g.State.Xy = xy
   207  	g.State.On = true
   208  	return nil
   209  }
   210  
   211  // Scene sets the scene by it's identifier of the scene you wish to recall
   212  func (g *Group) Scene(ctx context.Context, scene string) error {
   213  	update := LightState{On: true, Scene: scene}
   214  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	g.State.Scene = scene
   219  	g.State.On = true
   220  	return nil
   221  }
   222  
   223  // TransitionTimeContext sets the duration of the transition from the light’s current state to the new state
   224  func (g *Group) TransitionTimeContext(ctx context.Context, new uint16) error {
   225  	update := LightState{On: g.State.On, TransitionTime: new}
   226  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   227  	if err != nil {
   228  		return err
   229  	}
   230  	g.State.TransitionTime = new
   231  	return nil
   232  }
   233  
   234  // EffectContext the dynamic effect of the lights in the group, currently “none” and “colorloop” are supported
   235  func (g *Group) EffectContext(ctx context.Context, new string) error {
   236  	update := LightState{On: true, Effect: new}
   237  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   238  	if err != nil {
   239  		return err
   240  	}
   241  	g.State.Effect = new
   242  	g.State.On = true
   243  	return nil
   244  }
   245  
   246  // AlertContext makes the lights in the group blink in its current color. Supported values are:
   247  // “none” – The light is not performing an alert effect.
   248  // “select” – The light is performing one breathe cycle.
   249  // “lselect” – The light is performing breathe cycles for 15 seconds or until alert is set to "none".
   250  func (g *Group) AlertContext(ctx context.Context, new string) error {
   251  	update := LightState{On: true, Alert: new}
   252  	_, err := g.bridge.SetGroupState(ctx, g.ID, update)
   253  	if err != nil {
   254  		return err
   255  	}
   256  	g.State.Effect = new
   257  	g.State.On = true
   258  	return nil
   259  }
   260  
   261  // EnableStreamingContext enables streaming for the group by setting the Stream Active property to true
   262  func (g *Group) EnableStreamingContext(ctx context.Context) error {
   263  	if g.Type != "Entertainment" {
   264  		return errors.New("must be an entertainment group to enable streaming")
   265  	}
   266  
   267  	active := true
   268  	update := Group{
   269  		Stream: Stream{
   270  			ActiveRaw: &active,
   271  		},
   272  	}
   273  	_, err := g.bridge.UpdateGroup(ctx, g.ID, update)
   274  	if err != nil {
   275  		return err
   276  	}
   277  
   278  	g.Stream.ActiveRaw = &active
   279  	g.Stream.OwnerRaw = &g.bridge.Username
   280  	return nil
   281  }
   282  
   283  // DisableStreamingContext disabled streaming for the group by setting the Stream Active property to false
   284  func (g *Group) DisableStreamingContext(ctx context.Context) error {
   285  	if g.Type != "Entertainment" {
   286  		return errors.New("must be an entertainment group to disable streaming")
   287  	}
   288  
   289  	active := false
   290  	update := Group{
   291  		Stream: Stream{
   292  			ActiveRaw: &active,
   293  		},
   294  	}
   295  	_, err := g.bridge.UpdateGroup(ctx, g.ID, update)
   296  	if err != nil {
   297  		return err
   298  	}
   299  
   300  	g.Stream.ActiveRaw = &active
   301  	g.Stream.OwnerRaw = nil
   302  
   303  	return nil
   304  }