package lifx import ( "context" "fmt" "git.aiterp.net/lucifer/new-server/models" "net" "sync" "time" ) type Driver struct { mu sync.Mutex bridges []*Bridge } func (d *Driver) SearchBridge(ctx context.Context, address string, _ bool) ([]models.Bridge, error) { if address == "" { ifaces, err := net.Interfaces() if err != nil { return nil, err } bridges := make([]models.Bridge, 0, len(ifaces)) for _, iface := range ifaces { if iface.Name == "lo" { continue } addrs, err := iface.Addrs() if err != nil || len(addrs) == 0 { continue } for _, addr := range addrs { bridges = append(bridges, models.Bridge{ ID: -1, Name: fmt.Sprintf("%s (%s)", iface.Name, addr), Driver: models.DTLIFX, Address: addr.String(), }) } } return bridges, nil } ctx2, cancel := context.WithCancel(ctx) defer cancel() _, err := createClient(ctx2, address, false) if err != nil { return nil, err } return []models.Bridge{{ ID: 0, Name: "Your network card", Driver: models.DTLIFX, Address: address, Token: "", }}, nil } func (d *Driver) SearchDevices(ctx context.Context, bridge models.Bridge, timeout time.Duration) ([]models.Device, error) { b, err := d.ensureBridge(ctx, bridge) if err != nil { return nil, err } before, err := d.ListDevices(ctx, bridge) if err != nil { return nil, err } err = b.StartSearch() if err != nil { return nil, err } if timeout < time.Second/10 { timeout = time.Second / 10 } select { case <-ctx.Done(): return nil, ctx.Err() case <-time.After(timeout): } after, err := d.ListDevices(ctx, bridge) if err != nil { return nil, err } intersection := make([]models.Device, 0, 4) for _, device := range after { found := false for _, device2 := range before { if device2.InternalID == device.InternalID { found = true break } } if !found { intersection = append(intersection, device) } } return intersection, nil } func (d *Driver) ListDevices(ctx context.Context, bridge models.Bridge) ([]models.Device, error) { b, err := d.ensureBridge(ctx, bridge) if err != nil { return nil, err } return b.Devices(), nil } func (d *Driver) Publish(ctx context.Context, bridge models.Bridge, devices []models.Device) error { b, err := d.ensureBridge(ctx, bridge) if err != nil { return err } b.Publish(devices) return nil } func (d *Driver) Run(ctx context.Context, bridge models.Bridge, ch chan<- models.Event) error { b, err := d.ensureBridge(ctx, bridge) if err != nil { return err } return b.Run(ctx, bridge.Token == "debug") } func (d *Driver) ensureBridge(ctx context.Context, info models.Bridge) (*Bridge, error) { d.mu.Lock() for _, bridge := range d.bridges { if bridge.ip == info.Address { d.mu.Unlock() return bridge, nil } } d.mu.Unlock() bridge := &Bridge{ ip: info.Address, externalID: info.ID, } // Try to create a short-lived client to make sure it works. ctx2, cancel := context.WithCancel(ctx) defer cancel() _, err := createClient(ctx2, info.Address, info.Token == "debug") if err != nil { return nil, err } // To avoid a potential duplicate, try looking for it again before inserting d.mu.Lock() for _, bridge := range d.bridges { if bridge.ip == info.Address { d.mu.Unlock() return bridge, nil } } d.bridges = append(d.bridges, bridge) d.mu.Unlock() return bridge, nil }