|
|
@ -7,9 +7,7 @@ import ( |
|
|
|
"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/variables" |
|
|
|
"github.com/google/uuid" |
|
|
|
"strings" |
|
|
|
"sync" |
|
|
|
"sync/atomic" |
|
|
|
"time" |
|
|
@ -24,6 +22,8 @@ func NewService(resolver device.Resolver, sceneMap device.SceneMap) lucifer3.Act |
|
|
|
|
|
|
|
supportFlags: make(map[string]device.SupportFlags), |
|
|
|
colorFlags: make(map[string]device.ColorFlags), |
|
|
|
temperatures: make(map[string]float64), |
|
|
|
motions: make(map[string]float64), |
|
|
|
} |
|
|
|
|
|
|
|
return s |
|
|
@ -36,7 +36,8 @@ type effectEnforcer struct { |
|
|
|
|
|
|
|
supportFlags map[string]device.SupportFlags |
|
|
|
colorFlags map[string]device.ColorFlags |
|
|
|
variables variables.Variables |
|
|
|
temperatures map[string]float64 |
|
|
|
motions map[string]float64 |
|
|
|
|
|
|
|
started uint32 |
|
|
|
|
|
|
@ -48,7 +49,7 @@ func (s *effectEnforcer) Active() bool { |
|
|
|
return true |
|
|
|
} |
|
|
|
|
|
|
|
func (s *effectEnforcer) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event) { |
|
|
|
func (s *effectEnforcer) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) { |
|
|
|
switch event := event.(type) { |
|
|
|
case events.HardwareState: |
|
|
|
s.mu.Lock() |
|
|
@ -75,19 +76,21 @@ func (s *effectEnforcer) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event) |
|
|
|
} |
|
|
|
s.mu.Unlock() |
|
|
|
|
|
|
|
case variables.PropertyPatch: |
|
|
|
case events.DeviceAssigned: |
|
|
|
s.triggerVariableEffects(bus, event.DeviceID) |
|
|
|
|
|
|
|
case events.MotionSensed: |
|
|
|
s.mu.Lock() |
|
|
|
s.variables = s.variables.WithPropertyPatch(event) |
|
|
|
s.motions[event.ID] = event.SecondsSince |
|
|
|
s.mu.Unlock() |
|
|
|
|
|
|
|
case events.AliasAdded: |
|
|
|
s.triggerVariableEffects() |
|
|
|
case events.AliasRemoved: |
|
|
|
s.triggerVariableEffects() |
|
|
|
case events.MotionSensed: |
|
|
|
s.triggerVariableEffects() |
|
|
|
s.triggerVariableEffects(bus, event.ID) |
|
|
|
case events.TemperatureChanged: |
|
|
|
s.triggerVariableEffects() |
|
|
|
s.mu.Lock() |
|
|
|
s.temperatures[event.ID] = event.Temperature |
|
|
|
s.mu.Unlock() |
|
|
|
|
|
|
|
s.triggerVariableEffects(bus, event.ID) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -159,11 +162,68 @@ func (s *effectEnforcer) HandleCommand(bus *lucifer3.EventBus, command lucifer3. |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (s *effectEnforcer) triggerVariableEffects() { |
|
|
|
func (s *effectEnforcer) triggerVariableEffects(bus *lucifer3.EventBus, id string) { |
|
|
|
now := time.Now() |
|
|
|
|
|
|
|
s.mu.Lock() |
|
|
|
for _, run := range s.list { |
|
|
|
found := false |
|
|
|
for _, id2 := range run.ids { |
|
|
|
if id2 == id { |
|
|
|
found = true |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
if !found { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
run.variables = map[string]float64{} |
|
|
|
|
|
|
|
totalMotion := 0.0 |
|
|
|
motionSamples := 0 |
|
|
|
minMotion := 0.0 |
|
|
|
maxMotion := 0.0 |
|
|
|
totalTemperature := 0.0 |
|
|
|
temperatureSamples := 0 |
|
|
|
minTemperature := 0.0 |
|
|
|
maxTemperature := 0.0 |
|
|
|
for _, id := range run.ids { |
|
|
|
if motion, ok := s.motions[id]; ok { |
|
|
|
totalMotion += motion |
|
|
|
motionSamples += 1 |
|
|
|
if motion < minMotion || motionSamples == 1 { |
|
|
|
minMotion = motion |
|
|
|
} |
|
|
|
if motion > maxMotion { |
|
|
|
maxMotion = motion |
|
|
|
} |
|
|
|
} |
|
|
|
if temperature, ok := s.temperatures[id]; ok { |
|
|
|
totalTemperature += temperature |
|
|
|
temperatureSamples += 1 |
|
|
|
if temperature < minTemperature || temperatureSamples == 1 { |
|
|
|
minTemperature = temperature |
|
|
|
} |
|
|
|
if temperature > maxTemperature { |
|
|
|
maxTemperature = temperature |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if temperatureSamples > 0 { |
|
|
|
run.variables["temperature.avg"] = totalTemperature / float64(temperatureSamples) |
|
|
|
run.variables["temperature.min"] = minTemperature |
|
|
|
run.variables["temperature.max"] = maxTemperature |
|
|
|
} |
|
|
|
if motionSamples > 0 { |
|
|
|
run.variables["motion.avg"] = totalMotion / float64(motionSamples) |
|
|
|
run.variables["motion.min"] = minMotion |
|
|
|
run.variables["motion.max"] = maxMotion |
|
|
|
} |
|
|
|
|
|
|
|
bus.RunEvent(events.AssignmentVariables{ID: run.id, Map: gentools.CopyMap(run.variables)}) |
|
|
|
|
|
|
|
if _, ok := run.effect.(lucifer3.VariableEffect); ok { |
|
|
|
run.due = now |
|
|
|
} |
|
|
@ -200,37 +260,13 @@ func (s *effectEnforcer) runLoop(bus *lucifer3.EventBus) { |
|
|
|
|
|
|
|
state := run.effect.State(j, len(run.ids), run.round) |
|
|
|
if vEff, ok := run.effect.(lucifer3.VariableEffect); ok { |
|
|
|
variableName := strings.Split(vEff.VariableName(), ".") |
|
|
|
if len(variableName) == 0 { |
|
|
|
variableName = append(variableName, "avg") |
|
|
|
} |
|
|
|
|
|
|
|
var value *variables.AvgMinMax |
|
|
|
switch variableName[0] { |
|
|
|
case "temperature": |
|
|
|
if value2, ok := s.variables.Temperature[run.match]; ok { |
|
|
|
value = &value2 |
|
|
|
} |
|
|
|
case "motion": |
|
|
|
if value2, ok := s.variables.Motion[run.match]; ok { |
|
|
|
value = &value2 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if value != nil { |
|
|
|
switch variableName[1] { |
|
|
|
case "min": |
|
|
|
state = vEff.VariableState(j, len(run.ids), value.Min) |
|
|
|
case "max": |
|
|
|
state = vEff.VariableState(j, len(run.ids), value.Max) |
|
|
|
case "avg": |
|
|
|
state = vEff.VariableState(j, len(run.ids), value.Avg) |
|
|
|
} |
|
|
|
variableName := vEff.VariableName() |
|
|
|
if variable, ok := run.variables[variableName]; ok { |
|
|
|
state = vEff.VariableState(j, len(run.ids), variable) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
batch[id] = state |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if freq := run.effect.Frequency(); freq > 0 { |
|
|
@ -312,13 +348,14 @@ func (s *effectEnforcer) runLoop(bus *lucifer3.EventBus) { |
|
|
|
} |
|
|
|
|
|
|
|
type effectEnforcerRun struct { |
|
|
|
id uuid.UUID |
|
|
|
match string |
|
|
|
due time.Time |
|
|
|
ids []string |
|
|
|
effect lucifer3.Effect |
|
|
|
dead bool |
|
|
|
round int |
|
|
|
id uuid.UUID |
|
|
|
match string |
|
|
|
due time.Time |
|
|
|
ids []string |
|
|
|
effect lucifer3.Effect |
|
|
|
dead bool |
|
|
|
round int |
|
|
|
variables map[string]float64 |
|
|
|
} |
|
|
|
|
|
|
|
// remove takes out an id from the effect, and returns whether the effect is empty.
|
|
|
|
xxxxxxxxxx