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 }