package mill import ( "errors" lucifer3 "git.aiterp.net/lucifer3/server" "git.aiterp.net/lucifer3/server/commands" "git.aiterp.net/lucifer3/server/device" "git.aiterp.net/lucifer3/server/events" "sync" ) func NewService() lucifer3.ActiveService { return &service{ bridges: make([]Bridge, 0, 8), } } type service struct { mu sync.Mutex bridges []Bridge } func (s *service) Active() bool { return true } func (s *service) HandleEvent(*lucifer3.EventBus, lucifer3.Event) { // NOP } func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) { defer s.mu.Unlock() s.mu.Lock() switch cmd := command.(type) { case commands.PairDevice: // Only mill if _, ok := cmd.Matches("mill"); !ok { return } // Run connection if needed err := s.connect(bus, cmd.ID, &cmd.APIKey, nil) if err != nil { bus.RunEvent(events.DeviceFailed{ ID: cmd.ID, Error: err.Error(), }) return } // Connection successful bus.RunEvent(events.DeviceAccepted{ ID: cmd.ID, APIKey: cmd.APIKey, }) case commands.ConnectDevice: // Connect if necessary err := s.connect(bus, cmd.ID, &cmd.APIKey, nil) if err != nil { bus.RunEvent(events.DeviceFailed{ ID: cmd.ID, Error: err.Error(), }) return } case commands.SetState: err := s.connect(bus, cmd.ID, nil, &cmd.State) if err != nil { bus.RunEvent(events.DeviceFailed{ ID: cmd.ID, Error: err.Error(), }) return } case commands.SetStateBatch: for id, state := range cmd { err := s.connect(bus, id, nil, &state) if err != nil { bus.RunEvent(events.DeviceFailed{ ID: id, Error: err.Error(), }) return } } } } func (s *service) connect( bus *lucifer3.EventBus, id string, apiKey *string, state *device.State, ) error { hasState := state != nil if !hasState { state = &device.State{} } // Check if connection is active for _, bridge := range s.bridges { // Don't block with dead bridges if !bridge.IsStarted() { continue } // Setting a dummy state to the bridge to check if it's online bridge.SetBus(bus) if ok := bridge.SetState(id, *state); ok { return nil } } if apiKey == nil { return errors.New("not connected to device " + id) } // Make a bridge, if not a mill ID, this will return ok false bridge, ok := MakeBridge(id, *apiKey) if !ok { return nil } bridge.SetBus(bus) // Start bridge, and throw error if it failed to start bridge.Start() if !bridge.IsStarted() { return errors.New("failed to connect to " + id) } // Add bridge to lists s.bridges = append(s.bridges, bridge) // Update state, if needed, after first connection if hasState { if ok := bridge.SetState(id, *state); !ok { return errors.New("unable to set state to " + id + " after connecting") } } return nil }