You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

177 lines
3.4 KiB

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(ctx)
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
}