|
|
package services
import ( "context" "fmt" "git.aiterp.net/lucifer/new-server/app/config" "git.aiterp.net/lucifer/new-server/app/services/publisher" "git.aiterp.net/lucifer/new-server/models" "log" "math" "sort" "strconv" "strings" "sync" "time" )
func StartEventHandler() { config.EventChannel <- models.Event{Name: "LuciferStarted"}
go func() { for event := range config.EventChannel { responses := handleEvent(event)
for _, response := range responses { handleEvent(response) } } }()
// Generate TimeChanged event
go func() { drift := time.Now().Add(time.Minute).Truncate(time.Minute).Sub(time.Now()) time.Sleep(drift + time.Millisecond*5)
for range time.NewTicker(time.Minute).C { config.EventChannel <- models.Event{Name: "TimeChanged", Payload: map[string]string{ "weekdayName": time.Now().In(loc).Format("Monday"), "month": time.Now().In(loc).Format("01"), "year": time.Now().In(loc).Format("2006"), }} } }() }
var loc, _ = time.LoadLocation("Europe/Oslo") var ctx = context.Background()
func handleEvent(event models.Event) (responses []models.Event) { var deadHandlers []models.EventHandler
startTime := time.Now() defer func() { duration := time.Since(startTime) if duration > time.Millisecond*250 { log.Println(event.Name, "took long to handle:", duration) } }()
if !event.HasPayload("hour") { event.AddPayload("hour", time.Now().In(loc).Format("15")) } if !event.HasPayload("minute") { event.AddPayload("minute", time.Now().In(loc).Format("04")) }
err := handleSpecial(event) if err != nil { log.Printf("Special event handler error (%s): %v", event.Name, err) return }
paramStrings := make([]string, 0, 8) for key, value := range event.Payload { paramStrings = append(paramStrings, fmt.Sprintf("%s=%s", key, value)) } sort.Strings(paramStrings)
log.Printf("Event %s(%s)", event.Name, strings.Join(paramStrings, ", "))
handlers, err := config.EventHandlerRepository().FetchAll(ctx) if err != nil { log.Printf("Error fetchin' event halders: %s", err) return }
allDevices := make([]models.Device, 0, 16) actions := make(map[int]models.EventAction, 16) priorities := make(map[int]int, 16) highestPriority := 0 var prioritizedEvent *models.Event
for _, handler := range handlers { if handler.EventName != event.Name { continue } if !handler.IsWithinTimeRange() { continue }
devices, err := config.DeviceRepository().FetchByReference(ctx, handler.TargetKind, handler.TargetValue) if err != nil { log.Printf("Error fetchin' event devices: %s", err) return }
if !handler.MatchesEvent(event, devices) { continue }
if handler.OneShot { deadHandlers = append(deadHandlers, handler) }
if handler.Priority > highestPriority { highestPriority = handler.Priority prioritizedEvent = handler.Actions.FireEvent }
for _, device := range devices { _, ok := actions[device.ID] if !ok { allDevices = append(allDevices, device) actions[device.ID] = handler.Actions priorities[device.ID] = handler.Priority } else if handler.Priority > priorities[device.ID] { actions[device.ID] = handler.Actions priorities[device.ID] = handler.Priority } } }
if prioritizedEvent != nil { responses = append(responses, *prioritizedEvent) }
for i, device := range allDevices { action := actions[device.ID] newState := models.NewDeviceState{}
newState.Color = action.SetColor newState.Power = action.SetPower
if action.SetIntensity != nil { newState.Intensity = action.SetIntensity } else if action.AddIntensity != nil { newIntensity := math.Max(0, math.Min(1, device.State.Intensity+*action.AddIntensity)) newState.Intensity = &newIntensity }
if action.SetTemperature != nil { newState.Temperature = action.SetTemperature }
err = allDevices[i].SetState(newState) if err != nil { log.Println("Error updating state for device", device.ID, "err:", err) }
if action.SetScene != nil { action.SetScene.StartTime = time.Now() allDevices[i].SceneAssignments = []models.DeviceSceneAssignment{*action.SetScene} } if action.PushScene != nil { action.PushScene.StartTime = time.Now() allDevices[i].SceneAssignments = append(allDevices[i].SceneAssignments, *action.PushScene) } }
config.PublishChannel <- allDevices
wg := sync.WaitGroup{}
if len(allDevices) > 0 { wg.Add(1) go func() { err := config.DeviceRepository().SaveMany(context.Background(), models.SMState, allDevices) if err != nil { log.Println("Failed to save devices' state:", err) }
wg.Done() }() }
for _, handler := range deadHandlers { wg.Add(1)
go func(handler models.EventHandler) { err := config.EventHandlerRepository().Delete(context.Background(), &handler) if err != nil { log.Println("Failed to delete spent one-shot event handler:", err) }
wg.Done() }(handler) }
wg.Wait()
return }
func handleSpecial(event models.Event) error { switch event.Name { case models.ENBridgeConnected: bridgeId, _ := strconv.Atoi(event.Payload["bridgeId"]) bridge, err := config.BridgeRepository().Find(ctx, bridgeId) if err != nil { return err }
devices, err := config.DeviceRepository().FetchByReference(ctx, models.RKBridgeID, event.Payload["bridgeId"]) if err != nil { return err }
driver, err := config.DriverProvider().Provide(bridge.Driver) if err != nil { return err }
return driver.Publish(ctx, bridge, devices) case models.ENSensorPresenceStarted, models.ENSensorPresenceEnded, models.ENSensorTemperature: id, _ := strconv.Atoi(event.Payload["deviceId"]) updateTime, _ := strconv.ParseInt(event.Payload["lastUpdated"], 10, 64) state := models.SceneSensor{ ID: id, UpdateTime: time.Unix(updateTime, 0), }
switch event.Name { case models.ENSensorPresenceStarted: presence := true state.Presence = &presence case models.ENSensorPresenceEnded: presence := false state.Presence = &presence case models.ENSensorTemperature: temperature, _ := strconv.ParseFloat(event.Payload["temperature"], 64) state.Temperature = &temperature }
publisher.Global().UpdateSensor(state)
return nil default: return nil } }
|