KVMote.go/internal/transport/ble_windows.go

147 lines
3.2 KiB
Go

package transport
import (
"context"
"errors"
"fmt"
"sync"
"time"
"tinygo.org/x/bluetooth"
)
var (
NusServiceUUID = bluetooth.NewUUID([16]byte{0x6e, 0x40, 0x00, 0x01, 0xb5, 0xa3, 0xf3, 0x93, 0xe0, 0xa9, 0xe5, 0x0e, 0x24, 0xdc, 0xca, 0x9e})
NusRxUUID = bluetooth.NewUUID([16]byte{0x6e, 0x40, 0x00, 0x02, 0xb5, 0xa3, 0xf3, 0x93, 0xe0, 0xa9, 0xe5, 0x0e, 0x24, 0xdc, 0xca, 0x9e})
)
type RealBleTransport struct {
adapter *bluetooth.Adapter
device bluetooth.Device
rxChar bluetooth.DeviceCharacteristic
connected bool
mu sync.Mutex
address bluetooth.Address
found bool
}
func NewBleTransport() *RealBleTransport {
return &RealBleTransport{
adapter: bluetooth.DefaultAdapter,
}
}
func (t *RealBleTransport) Detect(ctx context.Context) (bool, error) {
t.mu.Lock()
if t.found {
t.mu.Unlock()
return true, nil
}
t.mu.Unlock()
fmt.Println("[BLE] Iniciando Scan...")
if err := t.adapter.Enable(); err != nil {
return false, err
}
foundChan := make(chan bluetooth.Address, 1)
err := t.adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) {
if result.LocalName() == "KVMote" {
adapter.StopScan()
foundChan <- result.Address
}
})
if err != nil {
return false, err
}
select {
case addr := <-foundChan:
t.mu.Lock()
t.address = addr
t.found = true
t.mu.Unlock()
fmt.Println("[BLE] Encontrado!")
return true, nil
case <-ctx.Done():
t.adapter.StopScan()
return false, ctx.Err()
case <-time.After(5 * time.Second):
t.adapter.StopScan()
return false, errors.New("não encontrado")
}
}
func (t *RealBleTransport) Connect(ctx context.Context) error {
t.mu.Lock()
addr := t.address
t.mu.Unlock()
fmt.Println("[BLE] Conectando...")
device, err := t.adapter.Connect(addr, bluetooth.ConnectionParams{})
if err != nil {
return err
}
t.mu.Lock()
t.device = device
t.mu.Unlock()
fmt.Println("[BLE] Buscando Serviço...")
services, err := device.DiscoverServices([]bluetooth.UUID{NusServiceUUID})
if err != nil || len(services) == 0 {
return errors.New("serviço não encontrado")
}
fmt.Println("[BLE] Buscando RX...")
chars, err := services[0].DiscoverCharacteristics([]bluetooth.UUID{NusRxUUID})
if err != nil || len(chars) == 0 {
return errors.New("característica não encontrada")
}
t.mu.Lock()
t.rxChar = chars[0]
t.connected = true
t.mu.Unlock()
fmt.Println("[BLE] PRONTO!")
t.Send([]byte{'H'})
return nil
}
func (t *RealBleTransport) Disconnect() error {
t.mu.Lock()
defer t.mu.Unlock()
if !t.connected { return nil }
// Proteção contra crash no Windows
go func() {
defer func() { recover() }()
t.device.Disconnect()
}()
t.connected = false
return nil
}
func (t *RealBleTransport) IsConnected() bool {
t.mu.Lock()
defer t.mu.Unlock()
return t.connected
}
func (t *RealBleTransport) Send(data []byte) error {
t.mu.Lock()
defer t.mu.Unlock()
if !t.connected { return nil }
_, err := t.rxChar.WriteWithoutResponse(data)
return err
}
func (t *RealBleTransport) SendLossy(data []byte) error {
return t.Send(data)
}
func (t *RealBleTransport) DeviceLabel() string { return "KVMote (BLE)" }
func (t *RealBleTransport) ClipboardConfig() (int, int, bool) { return 65536, 5, true }