|
|
package nanoleaf
import ( "context" "encoding/json" "fmt" "git.aiterp.net/lucifer/new-server/models" "net/http" "sync" "time" )
type Driver struct { mu sync.Mutex bridges []*bridge }
// SearchBridge checks the bridge at the address. If it's not a dry-run, you must hold down the power button
// before calling this function and wait for the pattern.
func (d *Driver) SearchBridge(ctx context.Context, address, _ string, dryRun bool) ([]models.Bridge, error) { res, err := http.Get(fmt.Sprintf("http://%s/device_info", address)) if err != nil { return nil, err } defer res.Body.Close()
deviceInfo := DeviceInfo{} err = json.NewDecoder(res.Body).Decode(&deviceInfo) if err != nil { return nil, err } if deviceInfo.ModelNumber == "" { return nil, models.ErrUnexpectedResponse }
token := "" if !dryRun { req, err := http.NewRequest("POST", fmt.Sprintf("http://%s:16021/api/v1/new/", address), nil) if err != nil { return nil, err }
res, err := http.DefaultClient.Do(req.WithContext(ctx)) if err != nil { return nil, err } defer res.Body.Close()
if res.StatusCode != 200 { return nil, models.ErrBridgeSearchFailed }
tokenResponse := TokenResponse{} err = json.NewDecoder(res.Body).Decode(&tokenResponse) if err != nil { return nil, err }
token = tokenResponse.Token }
return []models.Bridge{{ ID: -1, Name: fmt.Sprintf("Nanoleaf Controller (MN: %s, SN: %s, HV: %s, FV: %s, BV: %s)", deviceInfo.ModelNumber, deviceInfo.SerialNumber, deviceInfo.HardwareVersion, deviceInfo.FirmwareVersion, deviceInfo.BootloaderVersion, ), Driver: models.DTNanoLeaf, Address: address, Token: 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 }
if timeout > time.Millisecond { timeoutCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel()
ctx = timeoutCtx }
err = b.Refresh(ctx) if err != nil { return nil, err }
return b.Devices(), 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) 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, ch) }
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.Update(devices)
return nil }
func (d *Driver) ensureBridge(ctx context.Context, info models.Bridge) (*bridge, error) { d.mu.Lock() for _, bridge := range d.bridges { if bridge.host == info.Address { d.mu.Unlock() return bridge, nil } } d.mu.Unlock()
bridge := &bridge{ host: info.Address, apiKey: info.Token, externalID: info.ID, panelIDMap: make(map[uint16]int, 9), }
// If this fails, then the authorization failed.
err := bridge.Refresh(ctx) 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.host == info.Address { d.mu.Unlock() return bridge, nil } } d.bridges = append(d.bridges, bridge) d.mu.Unlock()
return bridge, nil }
|