package hue2 import ( "context" "encoding/json" "encoding/xml" "fmt" "git.aiterp.net/lucifer/new-server/internal/lerrors" "git.aiterp.net/lucifer/new-server/models" "net/http" "sync" "time" ) type Driver struct { mu sync.Mutex bridges []*Bridge } func (d *Driver) SearchBridge(ctx context.Context, address, token string, dryRun bool) ([]models.Bridge, error) { if address == "" { if !dryRun { return nil, lerrors.ErrAddressOnlyDryRunnable } res, err := http.Get("https://discovery.meethue.com") if err != nil { return nil, err } defer res.Body.Close() entries := make([]DiscoveryEntry, 0, 8) err = json.NewDecoder(res.Body).Decode(&entries) if err != nil { return nil, err } bridges := make([]models.Bridge, 0, len(entries)) for _, entry := range entries { bridges = append(bridges, models.Bridge{ ID: -1, Name: entry.Id, Driver: models.DTHue2, Address: entry.InternalIPAddress, Token: "", }) } return bridges, nil } deviceInfo := BridgeDeviceInfo{} res, err := http.Get(fmt.Sprintf("http://%s/description.xml", address)) if err != nil { return nil, err } defer res.Body.Close() err = xml.NewDecoder(res.Body).Decode(&deviceInfo) if err != nil { return nil, err } bridge := models.Bridge{ ID: -1, Name: deviceInfo.Device.FriendlyName, Driver: models.DTHue2, Address: address, Token: "", } if !dryRun { client := NewClient(address, "") timeout, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() bridge.Token, err = client.Register(timeout) if err != nil { return nil, err } } return []models.Bridge{bridge}, nil } func (d *Driver) SearchDevices(ctx context.Context, bridge models.Bridge, timeout time.Duration) ([]models.Device, error) { return d.ensureBridge(bridge).SearchDevices(ctx, timeout) } func (d *Driver) ListDevices(_ context.Context, bridge models.Bridge) ([]models.Device, error) { return d.ensureBridge(bridge).GenerateDevices(), nil } func (d *Driver) Publish(_ context.Context, bridge models.Bridge, devices []models.Device) error { d.ensureBridge(bridge).Update(devices...) return nil } func (d *Driver) Run(ctx context.Context, bridge models.Bridge, ch chan<- models.Event) error { return d.ensureBridge(bridge).Run(ctx, ch) } func (d *Driver) ForgetDevice(ctx context.Context, bridge models.Bridge, device models.Device) error { return d.ensureBridge(bridge).Forget(ctx, device) } func (d *Driver) ensureBridge(info models.Bridge) *Bridge { d.mu.Lock() for _, bridge := range d.bridges { if bridge.client.host == info.Address { d.mu.Unlock() return bridge } } bridge := NewBridge(NewClient(info.Address, info.Token)) bridge.externalID = info.ID d.bridges = append(d.bridges, bridge) d.mu.Unlock() return bridge }