func genCharAttr(c *ble.Characteristic, h uint16) (uint16, []*attr) { vh := h + 1 a := &attr{ h: h, typ: ble.CharacteristicUUID, v: append([]byte{byte(c.Property), byte(vh), byte((vh) >> 8)}, c.UUID...), } va := &attr{ h: vh, typ: c.UUID, v: c.Value, rh: c.ReadHandler, wh: c.WriteHandler, } c.Handle = h c.ValueHandle = vh if c.NotifyHandler != nil || c.IndicateHandler != nil { c.CCCD = newCCCD(c) c.Descriptors = append(c.Descriptors, c.CCCD) } h += 2 attrs := []*attr{a, va} for _, d := range c.Descriptors { attrs = append(attrs, genDescAttr(d, h)) h++ } a.endh = h - 1 return h, attrs }
// DiscoverDescriptors finds all the descriptors within a characteristic. [Vol 3, Part G, 4.7.1] // If filter is specified, only filtered descriptors are returned. func (p *Client) DiscoverDescriptors(filter []ble.UUID, c *ble.Characteristic) ([]*ble.Descriptor, error) { p.Lock() defer p.Unlock() start := c.ValueHandle + 1 for start <= c.EndHandle { fmt, b, err := p.ac.FindInformation(start, c.EndHandle) if err == ble.ErrAttrNotFound { break } else if err != nil { return nil, err } length := 2 + 2 if fmt == 0x02 { length = 2 + 16 } for len(b) != 0 { h := binary.LittleEndian.Uint16(b[:2]) u := ble.UUID(b[2:length]) d := &ble.Descriptor{UUID: u, Handle: h} if filter == nil || ble.Contains(filter, u) { c.Descriptors = append(c.Descriptors, d) } if u.Equal(ble.ClientCharacteristicConfigUUID) { c.CCCD = d } start = h + 1 b = b[length:] } } return c.Descriptors, nil }
// DiscoverCharacteristics finds all the characteristics within a service. [Vol 3, Part G, 4.6.1] // If filter is specified, only filtered characteristics are returned. func (p *Client) DiscoverCharacteristics(filter []ble.UUID, s *ble.Service) ([]*ble.Characteristic, error) { p.Lock() defer p.Unlock() start := s.Handle var lastChar *ble.Characteristic for start <= s.EndHandle { length, b, err := p.ac.ReadByType(start, s.EndHandle, ble.CharacteristicUUID) if err == ble.ErrAttrNotFound { break } else if err != nil { return nil, err } for len(b) != 0 { h := binary.LittleEndian.Uint16(b[:2]) p := ble.Property(b[2]) vh := binary.LittleEndian.Uint16(b[3:5]) u := ble.UUID(b[5:length]) c := &ble.Characteristic{ UUID: u, Property: p, Handle: h, ValueHandle: vh, EndHandle: s.EndHandle, } if filter == nil || ble.Contains(filter, u) { s.Characteristics = append(s.Characteristics, c) } if lastChar != nil { lastChar.EndHandle = c.Handle - 1 } lastChar = c start = vh + 1 b = b[length:] } } return s.Characteristics, nil }
// DiscoverDescriptors finds all the descriptors within a characteristic. [Vol 3, Part G, 4.7.1] // If filter is specified, only filtered descriptors are returned. func (cln *Client) DiscoverDescriptors(ds []ble.UUID, c *ble.Characteristic) ([]*ble.Descriptor, error) { rsp := cln.conn.sendReq(70, xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgCharacteristicHandle": c.Handle, "kCBMsgArgCharacteristicValueHandle": c.ValueHandle, "kCBMsgArgUUIDs": uuidSlice(ds), }) for _, xds := range rsp.descriptors() { xd := msg(xds.(xpc.Dict)) c.Descriptors = append(c.Descriptors, &ble.Descriptor{ UUID: ble.MustParse(xd.uuid()), Handle: uint16(xd.descriptorHandle()), }) } return c.Descriptors, nil }