Gisle Aune
2 years ago
22 changed files with 368 additions and 43 deletions
-
3cmd/bustest/main.go
-
5cmd/lucifer4-server/main.go
-
8commands/edit.go
-
2device/pointer.go
-
14device/state.go
-
5effects/serializable.go
-
46effects/vrange.go
-
7interface.go
-
39internal/color/color.go
-
2internal/gentools/maps.go
-
68services/effectenforcer/service.go
-
9services/effectenforcer/service_test.go
-
6services/httpapiv1/service.go
-
13services/hue/bridge.go
-
2services/nanoleaf/client.go
-
13services/resolver.go
-
7services/uistate/data.go
-
4services/uistate/patch.go
-
7services/uistate/service.go
-
27services/variables/data.go
-
17services/variables/patch.go
-
103services/variables/service.go
@ -0,0 +1,46 @@ |
|||||
|
package effects |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"git.aiterp.net/lucifer3/server/device" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
type VRange struct { |
||||
|
States []device.State `json:"states,omitempty"` |
||||
|
Variable string `json:"variable"` |
||||
|
Min float64 `json:"min"` |
||||
|
Max float64 `json:"max"` |
||||
|
} |
||||
|
|
||||
|
func (e VRange) VariableName() string { |
||||
|
return e.Variable |
||||
|
} |
||||
|
|
||||
|
func (e VRange) VariableState(_, _ int, value float64) device.State { |
||||
|
if value <= e.Min { |
||||
|
return e.States[0] |
||||
|
} else if value >= e.Max { |
||||
|
return e.States[len(e.States)-1] |
||||
|
} |
||||
|
|
||||
|
return gradientStateFactor(e.States, true, (value-e.Min)/(e.Max-e.Min)) |
||||
|
} |
||||
|
|
||||
|
func (e VRange) State(_, _, _ int) device.State { |
||||
|
if len(e.States) == 0 { |
||||
|
return device.State{} |
||||
|
} |
||||
|
return e.States[0] |
||||
|
} |
||||
|
|
||||
|
func (e VRange) Frequency() time.Duration { |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
func (e VRange) EffectDescription() string { |
||||
|
return fmt.Sprintf( |
||||
|
"VRange(states:%s, name:%s, range:%.1f..%.1f)", |
||||
|
statesDescription(e.States), e.Variable, e.Min, e.Max, |
||||
|
) |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
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"` |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
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) |
||||
|
} |
@ -0,0 +1,103 @@ |
|||||
|
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 |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue