penguin/homekey-go

homekey-go

internal/physical/registration.go

raw ยท 1963 bytes

package physical

import (
	"sort"
	"strings"

	"homekey.local/provisioner/drivers/sesame"
)

// Prepare connects unregistered scan results without claiming a lock. Repeated
// scans reuse our live preparations; a device seen on two adapters is dialed once.
// The manager lifetime, rather than the HTTP scan request, owns these links.
func (m *Manager) Prepare(targets []sesame.Target) {
	m.enroll.Lock()
	defer m.enroll.Unlock()
	if m.ctx.Err() != nil {
		return
	}
	m.mu.Lock()
	defer m.mu.Unlock()
	for _, target := range targets {
		if target.Registered {
			continue
		}
		uuid, err := sesame.NormalizeUUID(target.UUID)
		if err != nil {
			continue
		}
		target.UUID = uuid
		id, err := identity(target)
		if err != nil || m.records[id].ID != "" {
			continue
		}
		if old := m.pending[id]; old != nil {
			switch old.State().Phase {
			case "failed", "disconnected":
				old.Close()
			default:
				continue
			}
		}
		m.pending[id] = sesame.PrepareRegistration(m.ctx, target, m.dial)
	}
}

func (m *Manager) RegistrationConnections() []sesame.RegistrationState {
	m.mu.RLock()
	defer m.mu.RUnlock()
	out := []sesame.RegistrationState{}
	for _, p := range m.pending {
		out = append(out, p.State())
	}
	sort.Slice(out, func(i, j int) bool {
		return out[i].Target.Adapter+out[i].Target.UUID < out[j].Target.Adapter+out[j].Target.UUID
	})
	return out
}

// DisconnectRegistration releases a preparation owned by us, including a setup
// still in progress. Unknown external links remain the BlueZ route's concern.
func (m *Manager) DisconnectRegistration(adapter, address string) bool {
	m.enroll.Lock()
	defer m.enroll.Unlock()
	m.mu.Lock()
	var selected *sesame.RegistrationConnection
	for id, p := range m.pending {
		t := p.State().Target
		if t.Adapter == adapter && strings.EqualFold(t.Address, address) {
			selected = p
			delete(m.pending, id)
			break
		}
	}
	m.mu.Unlock()
	if selected == nil {
		return false
	}
	selected.Close()
	return true
}