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

     1  package characteristic
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"sync"
     7  
     8  	"code.pfad.fr/gohmekit/hapip"
     9  	"code.pfad.fr/gohmekit/tlv8"
    10  )
    11  
    12  // TLV8 is a pointer to []byte to be comparable.
    13  type TLV8 *[]byte
    14  
    15  func TLV8Value(b []byte) TLV8 {
    16  	return &b
    17  }
    18  
    19  var _ hapip.CharacteristicReader = StaticTLV8[string]{}
    20  
    21  // Static is a static characteristic. Its value can't be changed after instanciation.
    22  //
    23  // To make it dynamic use Static.WithRemoteRead.
    24  type StaticTLV8[Response any] struct {
    25  	Typ         string
    26  	Description string
    27  
    28  	value Response
    29  }
    30  
    31  func (s StaticTLV8[Response]) Type() string {
    32  	return s.Typ
    33  }
    34  func (s StaticTLV8[Response]) Meta() hapip.CharacteristicMeta {
    35  	return hapip.CharacteristicMeta{
    36  		Format:      "tlv8",
    37  		Description: s.Description,
    38  	}
    39  }
    40  
    41  // Read fullfils the hapip.CharacteristicReader interface (for use by hapip.Handler).
    42  func (s StaticTLV8[Response]) Read(_ context.Context) (interface{}, error) {
    43  	return tlv8.Marshal(s.value)
    44  }
    45  
    46  // UpdatableTLV8 is an updatableTLV8 characteristic, which value can be updated with Update.
    47  //
    48  // When the updated value is different from the last value, all listeners will be notified.
    49  type UpdatableTLV8[Response any] struct {
    50  	mu sync.Mutex
    51  	StaticTLV8[Response]
    52  	notify func(ctx context.Context, val, old interface{})
    53  }
    54  
    55  // Read fullfils the hapip.CharacteristicReader interface (for use by hapip.Handler).
    56  func (u *UpdatableTLV8[Response]) Read(ctx context.Context) (interface{}, error) {
    57  	u.mu.Lock()
    58  	defer u.mu.Unlock()
    59  	return u.StaticTLV8.Read(ctx)
    60  }
    61  
    62  // Notify fullfils the hapip.CharacteristicNotifier interface (for use by hapip.Handler).
    63  func (u *UpdatableTLV8[Response]) Notify(cb func(ctx context.Context, val, old interface{})) {
    64  	u.mu.Lock()
    65  	defer u.mu.Unlock()
    66  	u.notify = cb
    67  }
    68  
    69  // Update updates the underlying value and notifies the listeners on change.
    70  func (u *UpdatableTLV8[Response]) Update(val Response) {
    71  	u.update(context.Background(), val)
    72  }
    73  
    74  func (u *UpdatableTLV8[Response]) update(ctx context.Context, val Response) {
    75  	u.mu.Lock()
    76  	defer u.mu.Unlock()
    77  	old := u.StaticTLV8.value
    78  	// always update the value
    79  	// if val == old {
    80  	// 	return
    81  	// }
    82  
    83  	u.StaticTLV8.value = val
    84  	cb := u.notify
    85  
    86  	if cb != nil {
    87  		go cb(ctx, val, old)
    88  	}
    89  }
    90  
    91  // WritableTLV8 is a writableTLV8 characteristic, which value can be updated by the remote controller or
    92  // by the software.
    93  //
    94  // When the updated (or written) value is different from the last value, all listeners will be notified.
    95  type WritableTLV8[Request any, Response any] struct {
    96  	UpdatableTLV8[Response]
    97  	write func(val Request) (Response, error)
    98  }
    99  
   100  // Write fullfils the hapip.CharacteristicWriter interface (for use by hapip.Handler).
   101  func (u *WritableTLV8[Request, Response]) Write(ctx context.Context, s json.RawMessage) error {
   102  	var buf []byte
   103  	if err := json.Unmarshal(s, &buf); err != nil {
   104  		return err
   105  	}
   106  	var val Request
   107  	if err := tlv8.Unmarshal(buf, &val); err != nil {
   108  		return err
   109  	}
   110  	resp, err := u.write(val)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	u.update(ctx, resp)
   115  	return nil
   116  }
   117  
   118  type WriteOnlyTLV8[Request any] struct {
   119  	StaticTLV8[struct{}]
   120  	write func(val Request) error
   121  }
   122  
   123  func (w WriteOnlyTLV8[Request]) Write(ctx context.Context, s json.RawMessage) error {
   124  	var buf []byte
   125  	if err := json.Unmarshal(s, &buf); err != nil {
   126  		return err
   127  	}
   128  	var val Request
   129  	if err := tlv8.Unmarshal(buf, &val); err != nil {
   130  		return err
   131  	}
   132  	return w.write(val)
   133  }