penguin/homekey-go

homekey-go

drivers/sesame/connections.go

raw ยท 2193 bytes

package sesame

import (
	"context"
	"errors"
	"sort"
	"strings"

	"github.com/godbus/dbus/v5"
)

// Connection is a BlueZ link, independent of local lock enrollment.
type Connection struct {
	Adapter string `json:"adapter"`
	Address string `json:"address"`
	Name    string `json:"name"`
}

func sesameDevice(p map[string]dbus.Variant) bool {
	if _, ok := advertised(p); ok {
		return true
	}
	uuids, _ := p["UUIDs"].Value().([]string)
	for _, uuid := range uuids {
		if strings.EqualFold(uuid, ServiceUUID) {
			return true
		}
	}
	return false
}

func connectedSesames(o objects) []Connection {
	out := []Connection{}
	for _, ifs := range o {
		p := ifs["org.bluez.Device1"]
		connected, _ := p["Connected"].Value().(bool)
		if !connected || !sesameDevice(p) {
			continue
		}
		adapter, _ := p["Adapter"].Value().(dbus.ObjectPath)
		parts := strings.Split(string(adapter), "/")
		out = append(out, Connection{parts[len(parts)-1], value(p, "Address"), value(p, "Alias")})
	}
	sort.Slice(out, func(i, j int) bool {
		return out[i].Adapter+out[i].Address < out[j].Adapter+out[j].Address
	})
	return out
}

// Connections reads current controller state without starting discovery.
func Connections(ctx context.Context) ([]Connection, error) {
	c, err := dbus.ConnectSystemBus()
	if err != nil {
		return nil, err
	}
	defer c.Close()
	o, err := getObjects(ctx, c)
	if err != nil {
		return nil, err
	}
	return connectedSesames(o), nil
}

// Disconnect affects only the explicitly selected Sesame device, never its keys.
func Disconnect(ctx context.Context, adapter, address string) error {
	c, err := dbus.ConnectSystemBus()
	if err != nil {
		return err
	}
	defer c.Close()
	o, err := getObjects(ctx, c)
	if err != nil {
		return err
	}
	for path, ifs := range o {
		p := ifs["org.bluez.Device1"]
		a, _ := p["Adapter"].Value().(dbus.ObjectPath)
		if string(a) != "/org/bluez/"+adapter || !strings.EqualFold(value(p, "Address"), address) || !sesameDevice(p) {
			continue
		}
		connected, _ := p["Connected"].Value().(bool)
		if !connected {
			return nil
		}
		return disconnectDevice(ctx, c, path)
	}
	return errors.New("Sesame device no longer found on this adapter; refresh connections")
}