code.pfad.fr/gohmekit@v0.2.1/hapip/characteristic/updatable.go (about)

     1  package characteristic
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  
     7  	"code.pfad.fr/gohmekit/hapip"
     8  )
     9  
    10  var _ hapip.CharacteristicNotifier = &Updatable[string]{}
    11  
    12  // Updatable is an updatable characteristic, which value can be updated with Update.
    13  //
    14  // When the updated value is different from the last value, all listeners will be notified.
    15  type Updatable[Format format] struct {
    16  	mu sync.Mutex
    17  	Static[Format]
    18  	notify func(ctx context.Context, val, old interface{})
    19  	read   func(ctx context.Context) (Format, error)
    20  }
    21  
    22  // Read fullfils the hapip.CharacteristicReader interface (for use by hapip.Handler).
    23  func (u *Updatable[Format]) Read(ctx context.Context) (interface{}, error) {
    24  	if u.read != nil {
    25  		v, err := u.read(ctx)
    26  		if err != nil {
    27  			return v, err
    28  		}
    29  		u.update(ctx, v)
    30  		return v, nil
    31  	}
    32  
    33  	u.mu.Lock()
    34  	defer u.mu.Unlock()
    35  	return u.Static.value, nil
    36  }
    37  
    38  // Notify fullfils the hapip.CharacteristicNotifier interface (for use by hapip.Handler).
    39  func (u *Updatable[Format]) Notify(cb func(ctx context.Context, val, old interface{})) {
    40  	u.mu.Lock()
    41  	defer u.mu.Unlock()
    42  	u.notify = cb
    43  }
    44  
    45  // Update updates the underlying value and notifies the listeners on change.
    46  func (u *Updatable[Format]) Update(val Format) {
    47  	u.update(context.Background(), val)
    48  }
    49  func (u *Updatable[Format]) update(ctx context.Context, val Format) {
    50  	u.mu.Lock()
    51  	defer u.mu.Unlock()
    52  	old := u.Static.value
    53  	if val == old {
    54  		return
    55  	}
    56  
    57  	u.Static.value = val
    58  	cb := u.notify
    59  
    60  	if cb != nil {
    61  		go cb(ctx, val, old)
    62  	}
    63  }
    64  
    65  // WithRemoteRead updates the characteristic (and returns itself), so that it gets its
    66  // value from the given read function on Read.
    67  // Must be called before any Read happens.
    68  func (u *Updatable[Format]) WithRemoteRead(read func(ctx context.Context) (Format, error)) *Updatable[Format] {
    69  	u.read = read
    70  	return u
    71  }