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 }