refactor variable system to be the responsibility of effect enforcer.
This commit is contained in:
@@ -10,7 +10,6 @@ import (
|
|||||||
"git.aiterp.net/lucifer3/server/services/mysqldb"
|
"git.aiterp.net/lucifer3/server/services/mysqldb"
|
||||||
"git.aiterp.net/lucifer3/server/services/nanoleaf"
|
"git.aiterp.net/lucifer3/server/services/nanoleaf"
|
||||||
"git.aiterp.net/lucifer3/server/services/uistate"
|
"git.aiterp.net/lucifer3/server/services/uistate"
|
||||||
"git.aiterp.net/lucifer3/server/services/variables"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -46,7 +45,6 @@ func main() {
|
|||||||
bus.Join(nanoleaf.NewService())
|
bus.Join(nanoleaf.NewService())
|
||||||
bus.Join(hue.NewService())
|
bus.Join(hue.NewService())
|
||||||
bus.Join(uistate.NewService())
|
bus.Join(uistate.NewService())
|
||||||
bus.Join(variables.NewService(resolver))
|
|
||||||
bus.Join(database)
|
bus.Join(database)
|
||||||
bus.Join(httpAPI)
|
bus.Join(httpAPI)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package events
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
lucifer3 "git.aiterp.net/lucifer3/server"
|
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||||
|
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AssignmentCreated struct {
|
type AssignmentCreated struct {
|
||||||
@@ -32,3 +34,14 @@ type DeviceAssigned struct {
|
|||||||
func (e DeviceAssigned) EventDescription() string {
|
func (e DeviceAssigned) EventDescription() string {
|
||||||
return fmt.Sprintf("DeviceAssigned(device=%s, %s)", e.DeviceID, e.AssignmentID)
|
return fmt.Sprintf("DeviceAssigned(device=%s, %s)", e.DeviceID, e.AssignmentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AssignmentVariables struct {
|
||||||
|
ID uuid.UUID
|
||||||
|
Map map[string]float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e AssignmentVariables) EventDescription() string {
|
||||||
|
return fmt.Sprintf("AssignmentVariables(id=%s, keys=[%s])",
|
||||||
|
e.ID, strings.Join(gentools.MapKeys(e.Map), ","),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,3 +14,12 @@ func OneItemMap[K comparable, V any](key K, value V) map[K]V {
|
|||||||
m[key] = value
|
m[key] = value
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MapKeys[K comparable, V any](m map[K]V) []K {
|
||||||
|
keys := make([]K, 0, len(m))
|
||||||
|
for key := range m {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
@@ -7,9 +7,7 @@ import (
|
|||||||
"git.aiterp.net/lucifer3/server/events"
|
"git.aiterp.net/lucifer3/server/events"
|
||||||
"git.aiterp.net/lucifer3/server/internal/color"
|
"git.aiterp.net/lucifer3/server/internal/color"
|
||||||
"git.aiterp.net/lucifer3/server/internal/gentools"
|
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||||
"git.aiterp.net/lucifer3/server/services/variables"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -24,6 +22,8 @@ func NewService(resolver device.Resolver, sceneMap device.SceneMap) lucifer3.Act
|
|||||||
|
|
||||||
supportFlags: make(map[string]device.SupportFlags),
|
supportFlags: make(map[string]device.SupportFlags),
|
||||||
colorFlags: make(map[string]device.ColorFlags),
|
colorFlags: make(map[string]device.ColorFlags),
|
||||||
|
temperatures: make(map[string]float64),
|
||||||
|
motions: make(map[string]float64),
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
@@ -36,7 +36,8 @@ type effectEnforcer struct {
|
|||||||
|
|
||||||
supportFlags map[string]device.SupportFlags
|
supportFlags map[string]device.SupportFlags
|
||||||
colorFlags map[string]device.ColorFlags
|
colorFlags map[string]device.ColorFlags
|
||||||
variables variables.Variables
|
temperatures map[string]float64
|
||||||
|
motions map[string]float64
|
||||||
|
|
||||||
started uint32
|
started uint32
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ func (s *effectEnforcer) Active() bool {
|
|||||||
return true
|
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) {
|
switch event := event.(type) {
|
||||||
case events.HardwareState:
|
case events.HardwareState:
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
@@ -75,19 +76,21 @@ func (s *effectEnforcer) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event)
|
|||||||
}
|
}
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
case variables.PropertyPatch:
|
case events.DeviceAssigned:
|
||||||
|
s.triggerVariableEffects(bus, event.DeviceID)
|
||||||
|
|
||||||
|
case events.MotionSensed:
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.variables = s.variables.WithPropertyPatch(event)
|
s.motions[event.ID] = event.SecondsSince
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
case events.AliasAdded:
|
s.triggerVariableEffects(bus, event.ID)
|
||||||
s.triggerVariableEffects()
|
|
||||||
case events.AliasRemoved:
|
|
||||||
s.triggerVariableEffects()
|
|
||||||
case events.MotionSensed:
|
|
||||||
s.triggerVariableEffects()
|
|
||||||
case events.TemperatureChanged:
|
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()
|
now := time.Now()
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
for _, run := range s.list {
|
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 {
|
if _, ok := run.effect.(lucifer3.VariableEffect); ok {
|
||||||
run.due = now
|
run.due = now
|
||||||
}
|
}
|
||||||
@@ -200,37 +260,13 @@ func (s *effectEnforcer) runLoop(bus *lucifer3.EventBus) {
|
|||||||
|
|
||||||
state := run.effect.State(j, len(run.ids), run.round)
|
state := run.effect.State(j, len(run.ids), run.round)
|
||||||
if vEff, ok := run.effect.(lucifer3.VariableEffect); ok {
|
if vEff, ok := run.effect.(lucifer3.VariableEffect); ok {
|
||||||
variableName := strings.Split(vEff.VariableName(), ".")
|
variableName := vEff.VariableName()
|
||||||
if len(variableName) == 0 {
|
if variable, ok := run.variables[variableName]; ok {
|
||||||
variableName = append(variableName, "avg")
|
state = vEff.VariableState(j, len(run.ids), variable)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
batch[id] = state
|
batch[id] = state
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if freq := run.effect.Frequency(); freq > 0 {
|
if freq := run.effect.Frequency(); freq > 0 {
|
||||||
@@ -312,13 +348,14 @@ func (s *effectEnforcer) runLoop(bus *lucifer3.EventBus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type effectEnforcerRun struct {
|
type effectEnforcerRun struct {
|
||||||
id uuid.UUID
|
id uuid.UUID
|
||||||
match string
|
match string
|
||||||
due time.Time
|
due time.Time
|
||||||
ids []string
|
ids []string
|
||||||
effect lucifer3.Effect
|
effect lucifer3.Effect
|
||||||
dead bool
|
dead bool
|
||||||
round int
|
round int
|
||||||
|
variables map[string]float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove takes out an id from the effect, and returns whether the effect is empty.
|
// remove takes out an id from the effect, and returns whether the effect is empty.
|
||||||
|
|||||||
@@ -388,7 +388,7 @@ func (b *Bridge) Run(ctx context.Context, bus *lucifer3.EventBus) interface{} {
|
|||||||
for id, value := range lastMotion {
|
for id, value := range lastMotion {
|
||||||
since := time.Since(value)
|
since := time.Since(value)
|
||||||
sinceMod := since % (time.Second * 30)
|
sinceMod := since % (time.Second * 30)
|
||||||
if sinceMod >= time.Second*27 || sinceMod <= time.Second*3 {
|
if (since > time.Second*20) && (sinceMod >= time.Second*27 || sinceMod <= time.Second*3) {
|
||||||
bus.RunEvent(events.MotionSensed{ID: id, SecondsSince: since.Seconds()})
|
bus.RunEvent(events.MotionSensed{ID: id, SecondsSince: since.Seconds()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ import (
|
|||||||
"git.aiterp.net/lucifer3/server/events"
|
"git.aiterp.net/lucifer3/server/events"
|
||||||
"git.aiterp.net/lucifer3/server/internal/color"
|
"git.aiterp.net/lucifer3/server/internal/color"
|
||||||
"git.aiterp.net/lucifer3/server/internal/gentools"
|
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||||
"git.aiterp.net/lucifer3/server/services/variables"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Data struct {
|
type Data struct {
|
||||||
Devices map[string]Device `json:"devices"`
|
Devices map[string]Device `json:"devices"`
|
||||||
Assignments map[uuid.UUID]Assignment `json:"assignments"`
|
Assignments map[uuid.UUID]Assignment `json:"assignments"`
|
||||||
Variables variables.Variables `json:"variables"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Data) WithPatch(patches ...Patch) Data {
|
func (d *Data) WithPatch(patches ...Patch) Data {
|
||||||
@@ -81,6 +79,9 @@ func (d *Data) WithPatch(patches ...Patch) Data {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if patch.Assignment.Variables != nil {
|
||||||
|
pa.Variables = gentools.CopyMap(patch.Assignment.Variables)
|
||||||
|
}
|
||||||
|
|
||||||
if patch.Assignment.Delete {
|
if patch.Assignment.Delete {
|
||||||
delete(newData.Assignments, pa.ID)
|
delete(newData.Assignments, pa.ID)
|
||||||
@@ -88,10 +89,6 @@ func (d *Data) WithPatch(patches ...Patch) Data {
|
|||||||
newData.Assignments[pa.ID] = pa
|
newData.Assignments[pa.ID] = pa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if patch.VariableProperty != nil {
|
|
||||||
newData.Variables = newData.Variables.WithPropertyPatch(*patch.VariableProperty)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newData
|
return newData
|
||||||
@@ -101,7 +98,6 @@ func (d *Data) Copy() Data {
|
|||||||
return Data{
|
return Data{
|
||||||
Devices: gentools.CopyMap(d.Devices),
|
Devices: gentools.CopyMap(d.Devices),
|
||||||
Assignments: gentools.CopyMap(d.Assignments),
|
Assignments: gentools.CopyMap(d.Assignments),
|
||||||
Variables: d.Variables,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,4 +138,5 @@ type Assignment struct {
|
|||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
DeviceIDs []string `json:"deviceIds"`
|
DeviceIDs []string `json:"deviceIds"`
|
||||||
Effect *effects.Serializable `json:"effect"`
|
Effect *effects.Serializable `json:"effect"`
|
||||||
|
Variables map[string]float64 `json:"variables"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ import (
|
|||||||
"git.aiterp.net/lucifer3/server/effects"
|
"git.aiterp.net/lucifer3/server/effects"
|
||||||
"git.aiterp.net/lucifer3/server/events"
|
"git.aiterp.net/lucifer3/server/events"
|
||||||
"git.aiterp.net/lucifer3/server/internal/color"
|
"git.aiterp.net/lucifer3/server/internal/color"
|
||||||
"git.aiterp.net/lucifer3/server/services/variables"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Patch struct {
|
type Patch struct {
|
||||||
Assignment *AssignmentPatch `json:"assignment,omitempty"`
|
Assignment *AssignmentPatch `json:"assignment,omitempty"`
|
||||||
Device *DevicePatch `json:"device,omitempty"`
|
Device *DevicePatch `json:"device,omitempty"`
|
||||||
VariableProperty *variables.PropertyPatch `json:"variableProperty,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Patch) VerboseKey() string {
|
func (e Patch) VerboseKey() string {
|
||||||
@@ -24,7 +22,7 @@ func (e Patch) EventDescription() string {
|
|||||||
if e.Device != nil {
|
if e.Device != nil {
|
||||||
switch {
|
switch {
|
||||||
case e.Device.Name != nil:
|
case e.Device.Name != nil:
|
||||||
return fmt.Sprintf("uistate.Patch(device=%s, name=%s)", e.Device.ID, e.Device.Name)
|
return fmt.Sprintf("uistate.Patch(device=%s, name=%s)", e.Device.ID, *e.Device.Name)
|
||||||
case e.Device.DesiredState != nil:
|
case e.Device.DesiredState != nil:
|
||||||
return fmt.Sprintf("uistate.Patch(device=%s, desiredState=%s)", e.Device.ID, e.Device.DesiredState.String())
|
return fmt.Sprintf("uistate.Patch(device=%s, desiredState=%s)", e.Device.ID, e.Device.DesiredState.String())
|
||||||
case e.Device.HWState != nil:
|
case e.Device.HWState != nil:
|
||||||
@@ -43,8 +41,6 @@ func (e Patch) EventDescription() string {
|
|||||||
}
|
}
|
||||||
} else if e.Assignment != nil {
|
} else if e.Assignment != nil {
|
||||||
return fmt.Sprintf("uistate.Patch(assignment=%s)", e.Assignment.ID)
|
return fmt.Sprintf("uistate.Patch(assignment=%s)", e.Assignment.ID)
|
||||||
} else if e.VariableProperty != nil {
|
|
||||||
return fmt.Sprintf("uistate.Patch(variableProperty=%s)", e.VariableProperty.Key)
|
|
||||||
} else {
|
} else {
|
||||||
return "uistate.Patch"
|
return "uistate.Patch"
|
||||||
}
|
}
|
||||||
@@ -69,8 +65,9 @@ type DevicePatch struct {
|
|||||||
|
|
||||||
type AssignmentPatch struct {
|
type AssignmentPatch struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
AddDeviceID *string `json:"addDeviceId"`
|
AddDeviceID *string `json:"addDeviceId,omitempty"`
|
||||||
RemoveDeviceID *string `json:"removeDeviceId"`
|
RemoveDeviceID *string `json:"removeDeviceId,omitempty"`
|
||||||
Effect *effects.Serializable `json:"effect"`
|
Effect *effects.Serializable `json:"effect,omitempty"`
|
||||||
Delete bool `json:"delete"`
|
Variables map[string]float64 `json:"variables,omitempty"`
|
||||||
|
Delete bool `json:"delete,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"git.aiterp.net/lucifer3/server/effects"
|
"git.aiterp.net/lucifer3/server/effects"
|
||||||
"git.aiterp.net/lucifer3/server/events"
|
"git.aiterp.net/lucifer3/server/events"
|
||||||
"git.aiterp.net/lucifer3/server/internal/gentools"
|
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||||
"git.aiterp.net/lucifer3/server/services/variables"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -116,8 +115,12 @@ func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
|
|||||||
Assignment: gentools.ShallowCopy(event.AssignmentID),
|
Assignment: gentools.ShallowCopy(event.AssignmentID),
|
||||||
ClearAssignment: event.AssignmentID == nil,
|
ClearAssignment: event.AssignmentID == nil,
|
||||||
}})
|
}})
|
||||||
case variables.PropertyPatch:
|
case events.AssignmentVariables:
|
||||||
patches = append(patches, Patch{VariableProperty: &event})
|
// Always set the assignment
|
||||||
|
patches = append(patches, Patch{Assignment: &AssignmentPatch{
|
||||||
|
ID: event.ID,
|
||||||
|
Variables: event.Map,
|
||||||
|
}})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(patches) > 0 {
|
if len(patches) > 0 {
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package variables
|
|
||||||
|
|
||||||
import "git.aiterp.net/lucifer3/server/internal/gentools"
|
|
||||||
|
|
||||||
type Variables struct {
|
|
||||||
Temperature map[string]AvgMinMax `json:"temperature"`
|
|
||||||
Motion map[string]AvgMinMax `json:"motion"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Variables) WithPropertyPatch(patch PropertyPatch) Variables {
|
|
||||||
if patch.Motion != nil {
|
|
||||||
v.Motion = gentools.CopyMap(v.Motion)
|
|
||||||
v.Motion[patch.Key] = *patch.Motion
|
|
||||||
}
|
|
||||||
if patch.Temperature != nil {
|
|
||||||
v.Temperature = gentools.CopyMap(v.Temperature)
|
|
||||||
v.Temperature[patch.Key] = *patch.Temperature
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
type AvgMinMax struct {
|
|
||||||
Avg float64 `json:"avg"`
|
|
||||||
Min float64 `json:"min"`
|
|
||||||
Max float64 `json:"max"`
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package variables
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type PropertyPatch struct {
|
|
||||||
Key string `json:"id"`
|
|
||||||
Temperature *AvgMinMax `json:"temperature,omitempty"`
|
|
||||||
Motion *AvgMinMax `json:"motion,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e PropertyPatch) VerboseKey() string {
|
|
||||||
return "variables.PropertyPatch"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e PropertyPatch) EventDescription() string {
|
|
||||||
return fmt.Sprintf("variables.PropertyPatch(key=%s)", e.Key)
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
package variables
|
|
||||||
|
|
||||||
import (
|
|
||||||
lucifer3 "git.aiterp.net/lucifer3/server"
|
|
||||||
"git.aiterp.net/lucifer3/server/events"
|
|
||||||
"git.aiterp.net/lucifer3/server/services"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewService(resolver *services.Resolver) lucifer3.Service {
|
|
||||||
return &service{
|
|
||||||
resolver: resolver,
|
|
||||||
temperatures: map[string]float64{},
|
|
||||||
motions: map[string]float64{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type service struct {
|
|
||||||
resolver *services.Resolver
|
|
||||||
temperatures map[string]float64
|
|
||||||
motions map[string]float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) Active() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
|
|
||||||
var patchEvents []lucifer3.Event
|
|
||||||
|
|
||||||
switch event := event.(type) {
|
|
||||||
case events.MotionSensed:
|
|
||||||
s.motions[event.ID] = event.SecondsSince
|
|
||||||
patchEvents = s.updateDevice(event.ID)
|
|
||||||
case events.TemperatureChanged:
|
|
||||||
s.temperatures[event.ID] = event.Temperature
|
|
||||||
patchEvents = s.updateDevice(event.ID)
|
|
||||||
case events.AliasAdded:
|
|
||||||
patchEvents = s.updateDevice(event.ID)
|
|
||||||
case events.AliasRemoved:
|
|
||||||
patchEvents = s.updateDevice(event.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
bus.RunEvents(patchEvents)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) updateDevice(id string) []lucifer3.Event {
|
|
||||||
ptr := s.resolver.GetByID(id)
|
|
||||||
if ptr == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
res := make([]lucifer3.Event, 0, len(ptr.Aliases)+1)
|
|
||||||
|
|
||||||
for _, alias := range append([]string{ptr.ID}, ptr.Aliases...) {
|
|
||||||
patch := PropertyPatch{Key: alias}
|
|
||||||
motionSamples := 0.0
|
|
||||||
motionTotal := 0.0
|
|
||||||
temperatureSamples := 0.0
|
|
||||||
temperatureTotal := 0.0
|
|
||||||
|
|
||||||
for _, ptr := range s.resolver.Resolve(alias) {
|
|
||||||
if temp, ok := s.temperatures[ptr.ID]; ok {
|
|
||||||
if patch.Temperature == nil {
|
|
||||||
patch.Temperature = &AvgMinMax{}
|
|
||||||
if temp < patch.Temperature.Min || temperatureSamples == 0.0 {
|
|
||||||
patch.Temperature.Min = temp
|
|
||||||
}
|
|
||||||
if temp > patch.Temperature.Max {
|
|
||||||
patch.Temperature.Max = temp
|
|
||||||
}
|
|
||||||
|
|
||||||
temperatureSamples += 1.0
|
|
||||||
temperatureTotal += temp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if motion, ok := s.motions[ptr.ID]; ok {
|
|
||||||
if patch.Motion == nil {
|
|
||||||
patch.Motion = &AvgMinMax{}
|
|
||||||
if motion < patch.Motion.Min || motionSamples == 0.0 {
|
|
||||||
patch.Motion.Min = motion
|
|
||||||
}
|
|
||||||
if motion > patch.Motion.Max {
|
|
||||||
patch.Motion.Max = motion
|
|
||||||
}
|
|
||||||
|
|
||||||
motionSamples += 1.0
|
|
||||||
motionTotal += motion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if temperatureSamples > 0.5 {
|
|
||||||
patch.Temperature.Avg = temperatureTotal / temperatureSamples
|
|
||||||
}
|
|
||||||
if motionSamples > 0.5 {
|
|
||||||
patch.Motion.Avg = motionTotal / motionSamples
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, patch)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user