package uistate import ( lucifer3 "git.aiterp.net/lucifer3/server" "git.aiterp.net/lucifer3/server/commands" "git.aiterp.net/lucifer3/server/effects" "git.aiterp.net/lucifer3/server/events" "git.aiterp.net/lucifer3/server/internal/color" "git.aiterp.net/lucifer3/server/internal/gentools" "git.aiterp.net/lucifer3/server/services/script" "github.com/google/uuid" "sync" ) func NewService() lucifer3.ActiveService { return &service{ data: Data{ Devices: map[string]Device{}, Assignments: map[uuid.UUID]Assignment{}, Scripts: map[string][]script.Line{}, }, } } type service struct { mu sync.Mutex data Data listener []chan Patch } func (s *service) Active() bool { return true } func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) { var patches []Patch switch command := command.(type) { case commands.SetState: rgb := color.Color{} if command.State.Color != nil { rgb, _ = command.State.Color.ToRGB() } patches = []Patch{{Device: &DevicePatch{ ID: command.ID, DesiredState: &command.State, DesiredColorRGB: rgb.RGB, }}} case commands.SetStateBatch: for id, state := range command { rgb := color.Color{} if state.Color != nil { rgb, _ = state.Color.ToRGB() } patches = append(patches, Patch{ Device: &DevicePatch{ ID: id, DesiredState: gentools.ShallowCopy(&state), DesiredColorRGB: rgb.RGB, }, }) } case script.Update: patches = append(patches, Patch{ Script: &ScriptPatch{Name: command.Name, Lines: command.Lines}, }) } if len(patches) > 0 { s.mu.Lock() s.data = s.data.WithPatch(patches...) s.mu.Unlock() for _, patch := range patches { bus.RunEvent(patch) } } } func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) { var patches []Patch switch event := event.(type) { case events.AliasAdded: patches = []Patch{{Device: &DevicePatch{ID: event.ID, AddAlias: &event.Alias}}} case events.AliasRemoved: patches = []Patch{{Device: &DevicePatch{ID: event.ID, RemoveAlias: &event.Alias}}} case events.HardwareState: patches = []Patch{{Device: &DevicePatch{ID: event.ID, HWState: &event}}} if event.State.Color != nil { rgb, _ := event.State.Color.ToRGB() patches = append(patches, Patch{Device: &DevicePatch{ ID: event.ID, ActiveColorRGB: gentools.Ptr(rgb.RGB.Round()), }}) } else { patches = append(patches, Patch{Device: &DevicePatch{ ID: event.ID, ClearActiveColorRGB: false, }}) } case events.HardwareMetadata: patches = []Patch{{Device: &DevicePatch{ID: event.ID, HWMetadata: &event}}} case events.AssignmentCreated: patches = []Patch{{Assignment: &AssignmentPatch{ ID: event.ID, Effect: &effects.Serializable{Effect: event.Effect}, }}} case events.AssignmentRemoved: patches = []Patch{{Assignment: &AssignmentPatch{ ID: event.ID, Delete: true, }}} case events.MotionSensed: patches = []Patch{{Device: &DevicePatch{ ID: event.ID, Sensors: &DeviceSensors{LastMotion: gentools.Ptr(event.SecondsSince)}, }}} case events.TemperatureChanged: patches = []Patch{{Device: &DevicePatch{ ID: event.ID, Sensors: &DeviceSensors{Temperature: gentools.Ptr(event.Temperature)}, }}} case events.DeviceAssigned: // Un-assign from current assignment (if any) if d, ok := s.data.Devices[event.DeviceID]; ok && d.Assignment != nil { patches = append(patches, Patch{Assignment: &AssignmentPatch{ ID: *d.Assignment, RemoveDeviceID: &d.ID, }}) } // Assign to current assignment (if it's not cleared) if event.AssignmentID != nil { patches = append(patches, Patch{Assignment: &AssignmentPatch{ ID: *event.AssignmentID, AddDeviceID: &event.DeviceID, }}) } // Always set the assignment patches = append(patches, Patch{Device: &DevicePatch{ ID: event.DeviceID, Assignment: gentools.ShallowCopy(event.AssignmentID), ClearAssignment: event.AssignmentID == nil, }}) case events.AssignmentVariables: // Always set the assignment if len(event.Map) > 0 { patches = append(patches, Patch{Assignment: &AssignmentPatch{ ID: event.ID, Variables: event.Map, }}) } } if len(patches) > 0 { s.mu.Lock() s.data = s.data.WithPatch(patches...) s.mu.Unlock() for _, patch := range patches { bus.RunEvent(patch) } } }