package internal import ( "github.com/godbus/dbus/v5" "sort" ) type Scanner struct { conn *dbus.Conn } func NewScanner() (*Scanner, error) { conn, err := dbus.SystemBus() if err != nil { return nil, err } return &Scanner{conn: conn}, nil } func (s *Scanner) GetDevices() ([]Device, error) { var result map[dbus.ObjectPath]map[string]map[string]dbus.Variant err := s.conn.Object("org.bluez", "/").Call("org.freedesktop.DBus.ObjectManager.GetManagedObjects", 0).Store(&result) if err != nil { return nil, err } var devices []Device for path, interfaces := range result { if props, ok := interfaces["org.bluez.Device1"]; ok { name, hasName := props["Name"] address, hasAddress := props["Address"] if hasName && hasAddress { nameStr, _ := name.Value().(string) addrStr, _ := address.Value().(string) if nameStr != "" && nameStr != addrStr { devices = append(devices, Device{nameStr, addrStr, path}) } } } } sort.Slice(devices, func(i, j int) bool { return devices[i].Name < devices[j].Name }) return devices, nil } func (s *Scanner) FindDevice(address string) (Device, dbus.ObjectPath, error) { devices, err := s.GetDevices() if err != nil { return Device{}, "", err } for _, device := range devices { if device.Address == address { return device, device.Path, nil } } return Device{}, "", nil } func (s *Scanner) IsConnected(path dbus.ObjectPath) bool { var connected bool err := s.conn.Object("org.bluez", path).Call("org.freedesktop.DBus.Properties.Get", 0, "org.bluez.Device1", "Connected").Store(&connected) return err == nil && connected } func (s *Scanner) GetManagedObjects() (map[dbus.ObjectPath]map[string]map[string]dbus.Variant, error) { var result map[dbus.ObjectPath]map[string]map[string]dbus.Variant err := s.conn.Object("org.bluez", "/").Call("org.freedesktop.DBus.ObjectManager.GetManagedObjects", 0).Store(&result) return result, err } func (s *Scanner) ReadCharacteristic(path dbus.ObjectPath) ([]byte, error) { call := s.conn.Object("org.bluez", path).Call("org.bluez.GattCharacteristic1.ReadValue", 0, map[string]dbus.Variant{}) if call.Err != nil { return nil, call.Err } if len(call.Body) > 0 { if data, ok := call.Body[0].([]byte); ok { return data, nil } } return nil, nil } func (s *Scanner) GetLegacyBattery(path dbus.ObjectPath) (int, error) { var level byte err := s.conn.Object("org.bluez", path).Call("org.freedesktop.DBus.Properties.Get", 0, "org.bluez.Battery1", "Percentage").Store(&level) return int(level), err } func (s *Scanner) Close() { if s.conn != nil { s.conn.Close() } }