func (p *Client) Unsubscribe(c *ble.Characteristic, ind bool) error {
	p.Lock()
	defer p.Unlock()
	if c.CCCD == nil {
		return fmt.Errorf("CCCD not found")
	}
	if ind {
		return p.setHandlers(c.CCCD.Handle, c.ValueHandle, cccIndicate, nil)
	}
	return p.setHandlers(c.CCCD.Handle, c.ValueHandle, cccNotify, nil)
}
Unsubscribe unsubscribes to indication (if ind is set true), or notification
of a specified characteristic value. [Vol 3, Part G, 4.10 & 4.11]
package gatt
import (
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"sync"
	"time"
	"github.com/rigado/ble"
	"github.com/rigado/ble/linux/att"
)
const (
	cccNotify   = uint16(0x0001)
	cccIndicate = uint16(0x0002)
)
type Client struct {
	sync.RWMutex
	profile *ble.Profile
	name    string
	subs    map[uint16]*sub
	ac *att.Client
	conn  ble.Conn
	cache ble.GattCache
	ble.Logger
}
type sub struct {
	cccdh    uint16
	ccc      uint16
	nHandler ble.NotificationHandler
	iHandler ble.NotificationHandler
	id       uint
}
func NewClient(conn ble.Conn, cache ble.GattCache, done chan bool, l ble.Logger) (*Client, error) {
	cl := l.ChildLogger(map[string]interface{}{"client": hex.EncodeToString(conn.RemoteAddr().Bytes())})
	p := &Client{
		subs:   make(map[uint16]*sub),
		conn:   conn,
		cache:  cache,
		Logger: cl,
	}
	p.ac = att.NewClient(conn, p, done, cl)
	go p.ac.Loop()
	return p, nil
}
func ClientWithServer(c *Client, db *att.DB) *Client {
	c.ac = c.ac.WithServer(db)
	return c
}
func (p *Client) Addr() ble.Addr {
	p.RLock()
	defer p.RUnlock()
	return p.conn.RemoteAddr()
}
func (p *Client) Name() string {
	p.RLock()
	defer p.RUnlock()
	return p.name
}
func (p *Client) Profile() *ble.Profile {
	p.RLock()
	defer p.RUnlock()
	return p.profile
}
func (p *Client) DiscoverProfile(force bool) (*ble.Profile, error) {
	if p.profile != nil && !force {
		return p.profile, nil
	}
	ss, err := p.DiscoverServices(nil)
	if err != nil {
		return nil, fmt.Errorf("can't discover services: %s", err)
	}
	for _, s := range ss {
		cs, err := p.DiscoverCharacteristics(nil, s)
		if err != nil {
			return nil, fmt.Errorf("can't discover characteristics: %s", err)
		}
		for _, c := range cs {
			_, err := p.DiscoverDescriptors(nil, c)
			if err != nil {
				return nil, fmt.Errorf("can't discover descriptors: %s", err)
			}
		}
	}
	p.profile = &ble.Profile{Services: ss}
	return p.profile, nil
}
func (p *Client) DiscoverAndCacheProfile(force bool) (*ble.Profile, error) {
	if !force {
		if p.cache != nil {
			profile, err := p.cache.Load(p.Addr())
			if err == nil {
				p.profile = &profile
				return &profile, nil
			}
		}
	}
	profile, err := p.DiscoverProfile(force)
	if err != nil {
		return nil, err
	}
	err = p.cache.Store(p.Addr(), *profile, true)
	if err != nil {
		return profile, err
	}
	return profile, nil
}
func (p *Client) DiscoverServices(filter []ble.UUID) ([]*ble.Service, error) {
	p.Lock()
	defer p.Unlock()
	if p.profile == nil {
		p.profile = &ble.Profile{}
	}
	start := uint16(0x0001)
	for {
		length, b, err := p.ac.ReadByGroupType(start, 0xFFFF, ble.PrimaryServiceUUID)
		if err == ble.ErrAttrNotFound {
			return p.profile.Services, nil
		}
		if err != nil {
			return nil, err
		}
		for len(b) != 0 {
			h := binary.LittleEndian.Uint16(b[:2])
			endh := binary.LittleEndian.Uint16(b[2:4])
			u := ble.UUID(b[4:length])
			if filter == nil || ble.Contains(filter, u) {
				s := &ble.Service{
					UUID:      u,
					Handle:    h,
					EndHandle: endh,
				}
				p.profile.Services = append(p.profile.Services, s)
			}
			if endh == 0xFFFF {
				return p.profile.Services, nil
			}
			if len(p.profile.Services) == len(filter) {
				return p.profile.Services, nil
			}
			start = endh + 1
			b = b[length:]
		}
	}
}
func (p *Client) DiscoverIncludedServices(ss []ble.UUID, s *ble.Service) ([]*ble.Service, error) {
	p.Lock()
	defer p.Unlock()
	return nil, nil
}
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
}
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
}
func (p *Client) ReadCharacteristic(c *ble.Characteristic) ([]byte, error) {
	p.Lock()
	defer p.Unlock()
	val, err := p.ac.Read(c.ValueHandle)
	if err != nil {
		return nil, err
	}
	c.Value = val
	return val, nil
}
func (p *Client) ReadLongCharacteristic(c *ble.Characteristic) ([]byte, error) {
	p.Lock()
	defer p.Unlock()
	buffer := make([]byte, 0, 512)
	read, err := p.ac.Read(c.ValueHandle)
	if err != nil {
		return nil, err
	}
	buffer = append(buffer, read...)
	for len(read) >= p.conn.TxMTU()-1 {
		if read, err = p.ac.ReadBlob(c.ValueHandle, uint16(len(buffer))); err != nil {
			return nil, err
		}
		buffer = append(buffer, read...)
	}
	c.Value = buffer
	return buffer, nil
}
func (p *Client) WriteCharacteristic(c *ble.Characteristic, v []byte, noRsp bool) error {
	p.Lock()
	defer p.Unlock()
	if noRsp {
		return p.ac.WriteCommand(c.ValueHandle, v)
	}
	return p.ac.Write(c.ValueHandle, v)
}
func (p *Client) ReadDescriptor(d *ble.Descriptor) ([]byte, error) {
	p.Lock()
	defer p.Unlock()
	val, err := p.ac.Read(d.Handle)
	if err != nil {
		return nil, err
	}
	d.Value = val
	return val, nil
}
func (p *Client) WriteDescriptor(d *ble.Descriptor, v []byte) error {
	p.Lock()
	defer p.Unlock()
	return p.ac.Write(d.Handle, v)
}
func (p *Client) ReadRSSI() int {
	p.Lock()
	defer p.Unlock()
	return 0
}
func (p *Client) ExchangeMTU(mtu int) (int, error) {
	p.Lock()
	defer p.Unlock()
	return p.ac.ExchangeMTU(mtu)
}
func (p *Client) Subscribe(c *ble.Characteristic, ind bool, h ble.NotificationHandler) error {
	p.Lock()
	defer p.Unlock()
	if c.CCCD == nil {
		return fmt.Errorf("CCCD not found")
	}
	flag := cccNotify
	if ind {
		flag = cccIndicate
	}
	return p.setHandlers(c.CCCD.Handle, c.ValueHandle, flag, h)
}