| package gatt |
| |
| // Supported statuses for GATT characteristic read/write operations. |
| // These correspond to att constants in the BLE spec |
| const ( |
| StatusSuccess = 0 |
| StatusInvalidOffset = 1 |
| StatusUnexpectedError = 2 |
| ) |
| |
| // A Request is the context for a request from a connected central device. |
| // TODO: Replace this with more general context, such as: |
| // http://godoc.org/golang.org/x/net/context |
| type Request struct { |
| Central Central |
| } |
| |
| // A ReadRequest is a characteristic read request from a connected device. |
| type ReadRequest struct { |
| Request |
| Cap int // maximum allowed reply length |
| Offset int // request value offset |
| } |
| |
| type Property int |
| |
| // Characteristic property flags (spec 3.3.3.1) |
| const ( |
| CharBroadcast Property = 0x01 // may be brocasted |
| CharRead Property = 0x02 // may be read |
| CharWriteNR Property = 0x04 // may be written to, with no reply |
| CharWrite Property = 0x08 // may be written to, with a reply |
| CharNotify Property = 0x10 // supports notifications |
| CharIndicate Property = 0x20 // supports Indications |
| CharSignedWrite Property = 0x40 // supports signed write |
| CharExtended Property = 0x80 // supports extended properties |
| ) |
| |
| func (p Property) String() (result string) { |
| if (p & CharBroadcast) != 0 { |
| result += "broadcast " |
| } |
| if (p & CharRead) != 0 { |
| result += "read " |
| } |
| if (p & CharWriteNR) != 0 { |
| result += "writeWithoutResponse " |
| } |
| if (p & CharWrite) != 0 { |
| result += "write " |
| } |
| if (p & CharNotify) != 0 { |
| result += "notify " |
| } |
| if (p & CharIndicate) != 0 { |
| result += "indicate " |
| } |
| if (p & CharSignedWrite) != 0 { |
| result += "authenticateSignedWrites " |
| } |
| if (p & CharExtended) != 0 { |
| result += "extendedProperties " |
| } |
| return |
| } |
| |
| // A Service is a BLE service. |
| type Service struct { |
| uuid UUID |
| chars []*Characteristic |
| |
| h uint16 |
| endh uint16 |
| } |
| |
| // NewService creates and initialize a new Service using u as it's UUID. |
| func NewService(u UUID) *Service { |
| return &Service{uuid: u} |
| } |
| |
| // AddCharacteristic adds a characteristic to a service. |
| // AddCharacteristic panics if the service already contains another |
| // characteristic with the same UUID. |
| func (s *Service) AddCharacteristic(u UUID) *Characteristic { |
| for _, c := range s.chars { |
| if c.uuid.Equal(u) { |
| panic("service already contains a characteristic with uuid " + u.String()) |
| } |
| } |
| c := &Characteristic{uuid: u, svc: s} |
| s.chars = append(s.chars, c) |
| return c |
| } |
| |
| // UUID returns the UUID of the service. |
| func (s *Service) UUID() UUID { return s.uuid } |
| |
| // Name returns the specificatin name of the service according to its UUID. |
| // If the UUID is not assigne, Name returns an empty string. |
| func (s *Service) Name() string { |
| return knownServices[s.uuid.String()].Name |
| } |
| |
| // Characteristic returns the contained characteristic of this service. |
| func (s *Service) Characteristics() []*Characteristic { return s.chars } |
| |
| // A Characteristic is a BLE characteristic. |
| type Characteristic struct { |
| uuid UUID |
| props Property // enabled properties |
| secure Property // security enabled properties |
| svc *Service |
| cccd *Descriptor |
| descs []*Descriptor |
| |
| value []byte |
| |
| // All the following fields are only used in peripheral/server implementation. |
| rhandler ReadHandler |
| whandler WriteHandler |
| nhandler NotifyHandler |
| |
| h uint16 |
| vh uint16 |
| endh uint16 |
| } |
| |
| // UUID returns the UUID of the characteristic. |
| func (c *Characteristic) UUID() UUID { |
| return c.uuid |
| } |
| |
| // Name returns the specificatin name of the characteristic. |
| // If the UUID is not assigned, Name returns empty string. |
| func (c *Characteristic) Name() string { |
| return knownCharacteristics[c.uuid.String()].Name |
| } |
| |
| // Service returns the containing service of this characteristic. |
| func (c *Characteristic) Service() *Service { |
| return c.svc |
| } |
| |
| // Properties returns the properties of this characteristic. |
| func (c *Characteristic) Properties() Property { |
| return c.props |
| } |
| |
| // Descriptors returns the contained descriptors of this characteristic. |
| func (c *Characteristic) Descriptors() []*Descriptor { |
| return c.descs |
| } |
| |
| // AddDescriptor adds a descriptor to a characteristic. |
| // AddDescriptor panics if the characteristic already contains another |
| // descriptor with the same UUID. |
| func (c *Characteristic) AddDescriptor(u UUID) *Descriptor { |
| for _, d := range c.descs { |
| if d.uuid.Equal(u) { |
| panic("service already contains a characteristic with uuid " + u.String()) |
| } |
| } |
| d := &Descriptor{uuid: u, char: c} |
| c.descs = append(c.descs, d) |
| return d |
| } |
| |
| // SetValue makes the characteristic support read requests, and returns a |
| // static value. SetValue must be called before the containing service is |
| // added to a server. |
| // SetValue panics if the characteristic has been configured with a ReadHandler. |
| func (c *Characteristic) SetValue(b []byte) { |
| if c.rhandler != nil { |
| panic("charactristic has been configured with a read handler") |
| } |
| c.props |= CharRead |
| // c.secure |= CharRead |
| c.value = make([]byte, len(b)) |
| copy(c.value, b) |
| } |
| |
| // HandleRead makes the characteristic support read requests, and routes read |
| // requests to h. HandleRead must be called before the containing service is |
| // added to a server. |
| // HandleRead panics if the characteristic has been configured with a static value. |
| func (c *Characteristic) HandleRead(h ReadHandler) { |
| if c.value != nil { |
| panic("charactristic has been configured with a static value") |
| } |
| c.props |= CharRead |
| // c.secure |= CharRead |
| c.rhandler = h |
| } |
| |
| // HandleReadFunc calls HandleRead(ReadHandlerFunc(f)). |
| func (c *Characteristic) HandleReadFunc(f func(rsp ResponseWriter, req *ReadRequest)) { |
| c.HandleRead(ReadHandlerFunc(f)) |
| } |
| |
| // HandleWrite makes the characteristic support write and write-no-response |
| // requests, and routes write requests to h. |
| // The WriteHandler does not differentiate between write and write-no-response |
| // requests; it is handled automatically. |
| // HandleWrite must be called before the containing service is added to a server. |
| func (c *Characteristic) HandleWrite(h WriteHandler) { |
| c.props |= CharWrite | CharWriteNR |
| // c.secure |= CharWrite | CharWriteNR |
| c.whandler = h |
| } |
| |
| // HandleWriteFunc calls HandleWrite(WriteHandlerFunc(f)). |
| func (c *Characteristic) HandleWriteFunc(f func(r Request, data []byte) (status byte)) { |
| c.HandleWrite(WriteHandlerFunc(f)) |
| } |
| |
| // HandleNotify makes the characteristic support notify requests, and routes |
| // notification requests to h. HandleNotify must be called before the |
| // containing service is added to a server. |
| func (c *Characteristic) HandleNotify(h NotifyHandler) { |
| if c.cccd != nil { |
| return |
| } |
| p := CharNotify | CharIndicate |
| c.props |= p |
| c.nhandler = h |
| |
| // add ccc (client characteristic configuration) descriptor |
| secure := Property(0) |
| // If the characteristic requested secure notifications, |
| // then set ccc security to r/w. |
| if c.secure&p != 0 { |
| secure = CharRead | CharWrite |
| } |
| cd := &Descriptor{ |
| uuid: attrClientCharacteristicConfigUUID, |
| props: CharRead | CharWrite | CharWriteNR, |
| secure: secure, |
| // FIXME: currently, we always return 0, which is inaccurate. |
| // Each connection should have it's own copy of this value. |
| value: []byte{0x00, 0x00}, |
| char: c, |
| } |
| c.cccd = cd |
| c.descs = append(c.descs, cd) |
| } |
| |
| // HandleNotifyFunc calls HandleNotify(NotifyHandlerFunc(f)). |
| func (c *Characteristic) HandleNotifyFunc(f func(r Request, n Notifier)) { |
| c.HandleNotify(NotifyHandlerFunc(f)) |
| } |
| |
| // TODO |
| // func (c *Characteristic) SubscribedCentrals() []Central{ |
| // } |
| |
| // Descriptor is a BLE descriptor |
| type Descriptor struct { |
| uuid UUID |
| char *Characteristic |
| props Property // enabled properties |
| secure Property // security enabled properties |
| |
| h uint16 |
| value []byte |
| |
| rhandler ReadHandler |
| whandler WriteHandler |
| } |
| |
| // UUID returns the UUID of the descriptor. |
| func (d *Descriptor) UUID() UUID { |
| return d.uuid |
| } |
| |
| // Name returns the specificatin name of the descriptor. |
| // If the UUID is not assigned, returns an empty string. |
| func (d *Descriptor) Name() string { |
| return knownDescriptors[d.uuid.String()].Name |
| } |
| |
| // Characteristic returns the containing characteristic of the descriptor. |
| func (d *Descriptor) Characteristic() *Characteristic { |
| return d.char |
| } |
| |
| // SetValue makes the descriptor support read requests, and returns a static value. |
| // SetValue must be called before the containing service is added to a server. |
| // SetValue panics if the descriptor has already configured with a ReadHandler. |
| func (d *Descriptor) SetValue(b []byte) { |
| if d.rhandler != nil { |
| panic("descriptor has been configured with a read handler") |
| } |
| d.props |= CharRead |
| // d.secure |= CharRead |
| d.value = make([]byte, len(b)) |
| copy(d.value, b) |
| } |
| |
| // HandleRead makes the descriptor support read requests, and routes read requests to h. |
| // HandleRead must be called before the containing service is added to a server. |
| // HandleRead panics if the descriptor has been configured with a static value. |
| func (d *Descriptor) HandleRead(h ReadHandler) { |
| if d.value != nil { |
| panic("descriptor has been configured with a static value") |
| } |
| d.props |= CharRead |
| // d.secure |= CharRead |
| d.rhandler = h |
| } |
| |
| // HandleReadFunc calls HandleRead(ReadHandlerFunc(f)). |
| func (d *Descriptor) HandleReadFunc(f func(rsp ResponseWriter, req *ReadRequest)) { |
| d.HandleRead(ReadHandlerFunc(f)) |
| } |
| |
| // HandleWrite makes the descriptor support write and write-no-response requests, and routes write requests to h. |
| // The WriteHandler does not differentiate between write and write-no-response requests; it is handled automatically. |
| // HandleWrite must be called before the containing service is added to a server. |
| func (d *Descriptor) HandleWrite(h WriteHandler) { |
| d.props |= CharWrite | CharWriteNR |
| // d.secure |= CharWrite | CharWriteNR |
| d.whandler = h |
| } |
| |
| // HandleWriteFunc calls HandleWrite(WriteHandlerFunc(f)). |
| func (d *Descriptor) HandleWriteFunc(f func(r Request, data []byte) (status byte)) { |
| d.HandleWrite(WriteHandlerFunc(f)) |
| } |