package nanoleaf import ( "context" "fmt" lucifer3 "git.aiterp.net/lucifer3/server" "git.aiterp.net/lucifer3/server/commands" "git.aiterp.net/lucifer3/server/events" "strings" "time" ) func NewService() lucifer3.ActiveService { return &service{ bridges: make(map[string]*bridge), cancels: make(map[string]context.CancelFunc), } } type service struct { bridges map[string]*bridge cancels map[string]context.CancelFunc } func (s *service) Active() bool { return true } func (s *service) HandleEvent(*lucifer3.EventBus, lucifer3.Event) {} func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) { switch command := command.(type) { case commands.SetState: if sub, ok := command.Matches("nanoleaf"); ok && s.bridges[sub] != nil { s.bridges[sub].Update(command.ID, command.State) } case commands.SetStateBatch: for _, b := range s.bridges { b.UpdateBatch(command) } case commands.SearchDevices: if sub, ok := command.Matches("nanoleaf"); ok { if s.bridges[sub] != nil { go func(bridge *bridge) { newIDs, err := bridge.Refresh(context.Background()) if err != nil { bus.RunEvent(events.DeviceFailed{ ID: command.ID, Error: fmt.Sprintf("Search failed: %s", err), }) return } if len(newIDs) > 0 { for _, event := range bridge.HardwareEvents() { bus.RunEvent(event) } for _, id := range newIDs { bus.RunEvent(events.DeviceReady{ID: id}) } } }(s.bridges[sub]) } } case commands.PairDevice: if address, ok := command.Matches("nanoleaf"); ok { go func() { timeout, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() apiKey, info, err := Discover(timeout, address, true) if err != nil { bus.RunEvent(events.DeviceFailed{ ID: command.ID, Error: fmt.Sprintf("Pairing failed: %s", err), }) return } bus.RunEvent(events.DeviceAccepted{ ID: command.ID, APIKey: apiKey, Extras: nil, }) bus.RunEvent(events.HardwareMetadata{ ID: command.ID, Icon: "bridge", SerialNumber: info.SerialNumber, FirmwareVersion: strings.Join([]string{ info.FirmwareVersion, info.BootloaderVersion, info.HardwareVersion, }, "; "), }) }() } case commands.ConnectDevice: if sub, ok := command.Matches("nanoleaf"); ok { if s.bridges[sub] != nil { s.cancels[sub]() } ctx, cancel := context.WithCancel(context.Background()) s.bridges[sub] = &bridge{ host: sub, apiKey: command.APIKey, panels: make([]*panel, 0, 64), panelIDMap: make(map[uint16]string), } s.cancels[sub] = cancel go func() { for ctx.Err() == nil { ctx2, cancel2 := context.WithCancel(ctx) err := s.bridges[sub].Run(ctx2, bus) cancel2() if err != nil { bus.RunEvent(events.DeviceFailed{ ID: command.ID, Error: fmt.Sprintf("Run failed: %s", err), }) } select { case <-time.After(time.Second * 5): case <-ctx.Done(): return } } }() } } }