package ble import ( "fmt" "log" "sync" "time" "git.wow.st/gmp/ble/gatt" ) type BLE struct { state bleState events chan interface{} peripherals Peripherals handle bleHandle ready, wantScan bool sync.Mutex connections Connections } type PeripheralListItem struct { p Peripheral seen time.Time } type Peripherals struct { items []PeripheralListItem sync.Mutex } type ConnectionListItem struct { p Peripheral state chan string } type Connections struct { items []ConnectionListItem close chan struct{} sync.Mutex } func (c *Connections) UpdateState(p Peripheral, s string) { var ch chan string c.Lock() for _, item := range c.items { if p.Identifier == item.p.Identifier { ch = item.state } } c.Unlock() ch <- s } func (ps *Peripherals) Add(x Peripheral) bool { ps.Lock() defer ps.Unlock() found := false for n, item := range ps.items { if item.p.Identifier == x.Identifier { item.p = x item.seen = time.Now() ps.items[n] = item found = true break } } if found { return false } //take ownership of this object x.Retain() item := PeripheralListItem{p: x, seen: time.Now()} ps.items = append(ps.items, item) return true } func (cs *Connections) Add(x ConnectionListItem) bool { cs.Lock() defer cs.Unlock() found := false for _, item := range cs.items { if item.p.Identifier == x.p.Identifier { // are we allowed to have multiple connections to the same peripheral? return false } } if found { return false } cs.items = append(cs.items, x) return true } type UpdateStateEvent struct { State string } type DiscoverPeripheralEvent struct { Peripheral Peripheral } //FIXME: create a Service type that is platform dependent to make the //event platform independent type DiscoverServiceEvent struct { Peripheral Peripheral Gatt gatt.Service Service Service } //FIXME: create a Characteristic type that is platform dependent to make the //event platform independent type DiscoverCharacteristicEvent struct { Peripheral Peripheral Gatt gatt.Characteristic Service Service Characteristic Characteristic } //FIXME: create a Characteristic type that is platform dependent to make the //event platform independent type UpdateValueEvent struct { Peripheral Peripheral Characteristic Characteristic Data []byte } type ConnectEvent struct { Peripheral Peripheral } type DisconnectEvent struct { Peripheral Peripheral } type ConnectTimeoutEvent struct { Peripheral Peripheral } type UpdageValueEvent struct { Peripheral Peripheral Characteristic gatt.Characteristic } func (b *BLE) Events() chan interface{} { return b.events } func (b *BLE) State() string { b.Lock() defer b.Unlock() return b.stringState() } func (b *BLE) Scan() { b.Lock() defer b.Unlock() if b.readyToScan() { log.Printf("ready to scan") b.peripherals.Lock() b.peripherals.items = b.peripherals.items[:0] b.peripherals.Unlock() fmt.Printf("Go: Scanning\n") b.scan() } else { b.wantScan = true } } func (b *BLE) StopScan() { b.Lock() if !b.ready { b.Unlock() return } b.Unlock() fmt.Printf("Go: stopping scan\n") b.stopScan() } func CancelConnection(p Peripheral) { b := peripheralLookup(p) if b == nil { return } b.connections.Lock() var ch chan string; for _, item := range b.connections.items { if item.p.Identifier == p.Identifier { ch = item.state break } } b.connections.Unlock() if ch != nil { ch <- "cancel" } } func connectTracker(b *BLE, x ConnectionListItem) bool { fmt.Printf("connectTracker(): %s\n", x.p.Name) b.connections.Lock() for _, item := range b.connections.items { if item.p.Identifier == x.p.Identifier { fmt.Printf("connectTracker(): already connecting to %s\n", x.p.Name) b.connections.Unlock() return true } } b.connections.items = append(b.connections.items, x) b.connections.Unlock() b.connectPeripheral(x.p) cancel := func() { b.cancelConnection(x.p) b.connections.Lock() for n, item := range b.connections.items { if item.p.Identifier == x.p.Identifier { b.connections.items = append(b.connections.items[:n], b.connections.items[n+1:]...) } } b.connections.Unlock() } go func() { tick := time.NewTicker(time.Second * 60) select { case <-b.connections.close: fmt.Printf("connectTracker(): Closing connection to %s\n", x.p.Name) b.cancelConnection(x.p) case <-tick.C: fmt.Printf("connectTracker(): Connection to %s timed out\n", x.p.Name) cancel() b.events <- ConnectTimeoutEvent{x.p} case state := <-x.state: if state == "cancel" { cancel() } fmt.Printf("connectTracker(): state %s\n", state) } }() return true } func (b *BLE) Connect(p Peripheral) bool { b.Lock() if !b.ready { b.Unlock() fmt.Printf("--BLE not ready\n") return false } b.Unlock() if p.IsIncomplete() { var ok bool p, ok = b.knownPeripheral(p) if !ok { return false } } item := ConnectionListItem{p, make(chan string)} fmt.Printf("BLE.Connect() calling connectTracker\n") return connectTracker(b, item) } func Disconnect(p Peripheral) { b := peripheralLookup(p) found := false b.connections.Lock() for i, item := range b.connections.items { if item.p.Identifier == p.Identifier { found = true b.connections.items = append(b.connections.items[:i], b.connections.items[i+1:]...) break } } b.connections.Unlock() if !found { return } b.cancelConnection(p) }